Package org.formulacompiler.spreadsheet.internal.compiler

Source Code of org.formulacompiler.spreadsheet.internal.compiler.SectionModelCompiler$RangeExpressionBuilder

/*
* Copyright (c) 2006-2009 by Abacus Research AG, Switzerland.
* All rights reserved.
*
* This file is part of the Abacus Formula Compiler (AFC).
*
* For commercial licensing, please contact sales(at)formulacompiler.com.
*
* AFC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AFC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AFC.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.formulacompiler.spreadsheet.internal.compiler;

import java.util.Collection;
import java.util.Set;

import org.formulacompiler.compiler.CompilerException;
import org.formulacompiler.compiler.Function;
import org.formulacompiler.compiler.NumericType;
import org.formulacompiler.compiler.internal.expressions.ArrayDescriptor;
import static org.formulacompiler.compiler.internal.expressions.ExpressionBuilder.err;

import org.formulacompiler.compiler.internal.expressions.DataType;
import org.formulacompiler.compiler.internal.expressions.ExpressionNode;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForArrayReference;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForConstantValue;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForFunction;
import org.formulacompiler.compiler.internal.expressions.ExpressionNodeForSubstitution;
import org.formulacompiler.compiler.internal.model.CellModel;
import org.formulacompiler.compiler.internal.model.ExpressionNodeForCellModel;
import org.formulacompiler.compiler.internal.model.SectionModel;
import org.formulacompiler.runtime.New;
import org.formulacompiler.runtime.spreadsheet.CellAddress;
import org.formulacompiler.runtime.spreadsheet.RangeAddress;
import org.formulacompiler.spreadsheet.Orientation;
import org.formulacompiler.spreadsheet.SpreadsheetException;
import org.formulacompiler.spreadsheet.internal.BaseSpreadsheet;
import org.formulacompiler.spreadsheet.internal.CellIndex;
import org.formulacompiler.spreadsheet.internal.CellInstance;
import org.formulacompiler.spreadsheet.internal.CellRange;
import org.formulacompiler.spreadsheet.internal.CellWithConstant;
import org.formulacompiler.spreadsheet.internal.CellWithError;
import org.formulacompiler.spreadsheet.internal.CellWithExpression;
import org.formulacompiler.spreadsheet.internal.ExpressionNodeForCell;
import org.formulacompiler.spreadsheet.internal.ExpressionNodeForRange;
import org.formulacompiler.spreadsheet.internal.ExpressionNodeForRangeShape;
import org.formulacompiler.spreadsheet.internal.binding.CellBinding;
import org.formulacompiler.spreadsheet.internal.binding.InputCellBinding;
import org.formulacompiler.spreadsheet.internal.binding.SectionBinding;
import org.formulacompiler.spreadsheet.internal.binding.WorkbookBinding;


public final class SectionModelCompiler
{
  private final SpreadsheetToModelCompiler compiler;
  private final WorkbookBinding engineDef;
  private final SectionModelCompiler section;
  private final SectionBinding sectionDef;
  private final SectionModel sectionModel;
  private final NumericType numericType;


  public SectionModelCompiler( SpreadsheetToModelCompiler _compiler, SectionModelCompiler _section,
      SectionBinding _sectionDef, SectionModel _model )
  {
    this.compiler = _compiler;
    this.engineDef = _compiler.getEngineDef();
    this.section = _section;
    this.sectionDef = _sectionDef;
    this.sectionModel = _model;
    this.numericType = _compiler.numericType();
    _compiler.addSectionModelCompiler( this );
  }


  public SectionModelCompiler getSection()
  {
    return this.section;
  }

  public SectionBinding getSectionDef()
  {
    return this.sectionDef;
  }

  public SectionModel getSectionModel()
  {
    return this.sectionModel;
  }

  public NumericType numericType()
  {
    return this.numericType;
  }


  public CellModel createCellModel( CellBinding _cellDef ) throws CompilerException
  {
    final boolean isInput = _cellDef instanceof InputCellBinding;
    final CellModel result = createCellModel( _cellDef.getIndex(), isInput );
    if (isInput) {
      result.makeInput( ((InputCellBinding) _cellDef).getCallChainToCall() );
    }
    return result;
  }


  public CellModel createCellModel( CellIndex _cellIndex, boolean _isInput ) throws CompilerException
  {
    final CellInstance cell = _cellIndex.getCell();
    final boolean nonNull = (null != cell);
    if (nonNull || _isInput) {
      final CellAddress cellAddress = _cellIndex.getCellAddress();
      final CellModel result = new CellModel( this.sectionModel, cellAddress );
      this.compiler.addCellModel( _cellIndex, result );
      if (nonNull) {
        buildCellModel( cell, result );
      }
      return result;
    }
    else {
      return null;
    }
  }


  CellModel getOrCreateCellModel( CellIndex _cellIndex ) throws CompilerException
  {
    CellModel result = this.compiler.getCellModel( _cellIndex );
    if (null == result) {
      result = createCellModel( _cellIndex );
    }
    return result;
  }


  CellModel createCellModel( CellIndex _cellIndex ) throws CompilerException
  {
    final InputCellBinding inputDef = this.engineDef.getInputs().get( _cellIndex );
    if (null != inputDef) {
      return createCellModel( inputDef );
    }
    else {
      return createCellModel( _cellIndex, false );
    }
  }


  SectionModelCompiler getOrCreateSectionCompiler( SectionBinding _sectionDef )
  {
    SectionModelCompiler result = this.compiler.getSectionCompiler( _sectionDef );
    if (null == result) {
      result = createSectionCompiler( _sectionDef );
    }
    return result;
  }


  SectionModelCompiler createSectionCompiler( SectionBinding _sectionDef )
  {
    final CellRange range = _sectionDef.getRange();
    final BaseSpreadsheet spreadsheet = range.getFrom().getSheet().getSpreadsheet();
    final Set<String> names = spreadsheet.getNamesFor( range );
    final String name = names != null && !names.isEmpty() ? names.iterator().next() : null;
    final RangeAddress rangeAddress = range.getRangeAddress();
    final SectionModel model = new SectionModel( getSectionModel(), rangeAddress, name,
        _sectionDef.getInputClass(), _sectionDef.getOutputClass() );
    model.makeInput( _sectionDef.getCallChainToCall() );
    if (_sectionDef.getCallToImplement() != null) {
      model.makeOutput( _sectionDef.getCallToImplement() );
    }
    return new SectionModelCompiler( this.compiler, this, _sectionDef, model );
  }


  private void buildCellModel( CellInstance _cell, CellModel _cellModel ) throws CompilerException
  {
    if (null == _cell) throw new IllegalArgumentException();
    if (_cell instanceof CellWithConstant) {
      buildCellModel( _cellModel, (CellWithConstant) _cell );
    }
    else if (_cell instanceof CellWithExpression) {
      buildCellModel( _cellModel, (CellWithExpression) _cell );
    }
    else if (_cell instanceof CellWithError) {
      buildCellModel( _cellModel, (CellWithError) _cell );
    }
    _cellModel.setMaxFractionalDigits( _cell.getMaxFractionalDigits() );
  }


  private void buildCellModel( CellModel _cellModel, CellWithConstant _cell )
  {
    _cellModel.setConstantValue( adjustConstantValue( _cell.getValue() ) );
  }


  private void buildCellModel( CellModel _cellModel, CellWithError _cell )
  {
    /*
     * Invalid values assume a numeric data type. This will lead to errors if they occur in string
     * expressions. We cannot handle these yet.
     */
    if (CellWithError.NA.equals( _cell.getValue() )) {
      _cellModel.setExpression( new ExpressionNodeForFunction( Function.NA ) );
    }
    else {
      _cellModel.setExpression( err( _cell.getError(), DataType.NUMERIC ) );
    }
  }


  private void buildCellModel( CellModel _cellModel, CellWithExpression _cell ) throws CompilerException
  {
    try {
      final ExpressionNode exprModel = buildExpressionModel( _cell.getExpression() );
      _cellModel.setExpression( exprModel );
    }
    catch (SpreadsheetException cause) {
      throw new CompilerException.UnsupportedExpression( cause );
    }
    catch (CompilerException e) {
      e.addMessageContext( "\nReferenced by cell " + _cell.getCanonicalName() + "." );
      throw e;
    }
  }


  private ExpressionNode buildExpressionModel( ExpressionNode _exprDef ) throws CompilerException
  {
    final ExpressionNode result = buildRawExpressionModel( _exprDef );
    if (result != null) {
      result.setDerivedFrom( _exprDef );
    }
    return result;
  }


  private ExpressionNode buildRawExpressionModel( ExpressionNode _exprDef ) throws CompilerException
  {
    if (null == _exprDef) {
      return null;
    }
    else if (_exprDef instanceof ExpressionNodeForConstantValue) {
      final ExpressionNodeForConstantValue cst = (ExpressionNodeForConstantValue) _exprDef;
      return new ExpressionNodeForConstantValue( adjustConstantValue( cst.value() ) );
    }
    else if (_exprDef instanceof ExpressionNodeForCell) {
      final CellIndex cell = ((ExpressionNodeForCell) _exprDef).getCellIndex();
      return buildExpressionModelForCell( cell );
    }
    else if (_exprDef instanceof ExpressionNodeForRangeShape) {
      final ExpressionNode expressionNode = _exprDef.arguments().get( 0 );
      final CellRange range;
      if (expressionNode instanceof ExpressionNodeForRange) {
        range = ((ExpressionNodeForRange) expressionNode).getRange();
      }
      else if (expressionNode instanceof ExpressionNodeForCell) {
        range = ((ExpressionNodeForCell) expressionNode).getCellIndex();
      }
      else {
        throw new IllegalArgumentException();
      }
      return new RangeExpressionBuilder( range, true ).build();
    }
    else if (_exprDef instanceof ExpressionNodeForRange) {
      final CellRange range = ((ExpressionNodeForRange) _exprDef).getRange();
      return new RangeExpressionBuilder( range, false ).build();
    }
    else {
      final ExpressionNode result = _exprDef.cloneWithoutArguments();
      for (ExpressionNode arg : _exprDef.arguments()) {
        final ExpressionNode argResult = buildExpressionModel( arg );
        if (argResult instanceof ExpressionNodeForSubstitution) {
          result.arguments().addAll( argResult.arguments() );
        }
        else {
          result.addArgument( argResult );
        }
      }
      return result;
    }
  }


  ExpressionNode buildExpressionModelForCell( CellIndex _cellIndex ) throws CompilerException
  {
    if (this.sectionDef.contains( _cellIndex )) {
      final SectionBinding containingSection = this.sectionDef.getContainingSection( _cellIndex );
      if (null != containingSection) {
        return new RangeExpressionBuilder( _cellIndex, false ).build();
      }
      return buildExpressionModelForLocalCell( _cellIndex );
    }
    else {
      return buildExpressionModelForOuterCell( _cellIndex );
    }
  }

  private ExpressionNode buildExpressionModelForLocalCell( CellIndex _cellIndex ) throws CompilerException
  {
    final CellModel cellModel = getOrCreateCellModel( _cellIndex );
    return new ExpressionNodeForCellModel( cellModel );
  }


  private ExpressionNode buildExpressionModelForOuterCell( CellIndex _cellIndex ) throws CompilerException
  {
    final SectionPath path = new SectionPath( this );
    path.stepOut();
    path.stepOutTo( _cellIndex );
    return path.wrapAround( path.getSectionCompiler().buildExpressionModelForLocalCell( _cellIndex ) );
  }


  private Object adjustConstantValue( Object _value )
  {
    if (_value instanceof Number) {
      return numericType().valueOf( (Number) _value );
    }
    return _value;
  }


  @SuppressWarnings( { "unqualified-field-access", "hiding" } )
  private final class RangeExpressionBuilder
  {
    private final SectionBinding sectionDef = SectionModelCompiler.this.sectionDef;
    private final RangeExpressionBuilder parent;
    private final boolean shaped;
    private final boolean stepOutOnly;
    private final CellRange range;
    private final int sheets;
    private int rows;
    private int cols;

    private RangeExpressionBuilder( RangeExpressionBuilder _parent, CellRange _range, boolean _shaped,
        boolean _stepOutOnly )
    {
      super();
      this.parent = _parent;
      this.range = _range;
      this.shaped = _shaped;
      this.stepOutOnly = _stepOutOnly;
      final CellIndex from = range.getFrom();
      final CellIndex to = range.getTo();
      this.sheets = to.getSheetIndex() - from.getSheetIndex() + 1;
      this.rows = to.getRowIndex() - from.getRowIndex() + 1;
      this.cols = to.getColumnIndex() - from.getColumnIndex() + 1;
    }

    private RangeExpressionBuilder( RangeExpressionBuilder _parent, CellRange _range, boolean _shaped )
    {
      this( _parent, _range, _shaped, false );
    }

    private RangeExpressionBuilder( CellRange _range, boolean _shaped, boolean _stepOutOnly )
    {
      this( null, _range, _shaped, _stepOutOnly );
    }

    public RangeExpressionBuilder( CellRange _range, boolean _shaped )
    {
      this( null, _range, _shaped );
    }

    private void makeDynamic( Orientation _orient )
    {
      if (_orient == Orientation.HORIZONTAL) {
        this.cols = ArrayDescriptor.DYNAMIC;
      }
      else {
        this.rows = ArrayDescriptor.DYNAMIC;
      }
      if (null != this.parent) {
        this.parent.makeDynamic( _orient );
      }
    }

    public ExpressionNode build() throws CompilerException
    {
      final CellRange[] tiling = range.tilingAround( sectionDef.getRange(), sectionDef.getOrientation() );
      switch (tiling.length) {
        case CellRange.NO_INTERSECTION:
          return buildOuterRange();
        case CellRange.CONTAINED:
          return buildContainedRange();
        default:
          throw new SpreadsheetException.SectionSpan( range.getShortName(), sectionDef.toString() );
      }
    }

    private ExpressionNode buildOuterRange() throws CompilerException
    {
      final SectionPath path = new SectionPath( SectionModelCompiler.this );
      path.stepOut();
      final ExpressionNode outer = path.getSectionCompiler().new RangeExpressionBuilder( range, shaped, true )
          .build();
      if (shaped) {
        return path.wrapAround( outer );
      }
      else {
        return path.wrapAround( outer.arguments() );
      }
    }

    private ExpressionNode buildContainedRange() throws CompilerException
    {
      final Orientation ownOrient = this.sectionDef.getOrientation();
      final Collection<ExpressionNode> elts = New.collection();

      /*
       * This loop relies on the subsections of the current section being sorted in ascending
       * flow order.
       */
      CellRange next = range;
      for (SectionBinding inner : this.sectionDef.getSections()) {
        final CellRange innerRange = inner.getRange();
        final Orientation innerOrient = inner.getOrientation();
        final CellRange[] tiling = (innerOrient == ownOrient) ? next.tilingAround( innerRange, innerOrient ) : next
            .tilingAround( innerRange );
        switch (tiling.length) {

          case CellRange.NO_INTERSECTION:
            break;

          case CellRange.CONTAINED: {
            final ExpressionNode expr = buildExpressionModelForInnerRange( inner, next );
            elts.add( expr );
            next = null;
            break;
          }

          case CellRange.FLOW_TILES: {
            final CellRange before = tiling[ CellRange.FLOW_BEFORE ];
            if (null != before) {
              /*
               * This is where we rely on proper sorting. It ensures that `before` cannot
               * possibly overlap one of the remaining inner section to scan.
               */
              if (shaped) {
                elts.add( buildExpressionModelForLocalRange( before ) );
              }
              else {
                buildExpressionModelsForLocalRangeCells( before, elts );
              }
            }
            elts.add( buildExpressionModelForInnerRange( inner, tiling[ CellRange.FLOW_INNER ] ) );
            next = tiling[ CellRange.FLOW_AFTER ];
            break;
          }

          default:
            throw new SpreadsheetException.SectionSpan( range.getShortName(), inner.toString() );

        }
        if (null == next) break;
      }
      if (null != next) {
        if (0 == elts.size() || !shaped) {
          buildExpressionModelsForLocalRangeCells( next, elts );
        }
        else {
          elts.add( buildExpressionModelForLocalRange( next ) );
        }
      }

      final CellIndex from = range.getFrom();
      final ExpressionNode result = (shaped) ? new ExpressionNodeForArrayReference( new ArrayDescriptor(
          from.getSheetIndex(), from.getRowIndex(), from.getColumnIndex(), sheets, rows, cols ) )
          : new ExpressionNodeForSubstitution();
      result.arguments().addAll( elts );
      return result;
    }

    private ExpressionNode buildExpressionModelForInnerRange( SectionBinding _inner, CellRange _range )
        throws CompilerException
    {
      if (this.stepOutOnly) {
        throw new CompilerException.ReferenceToOuterInnerCell();
      }

      makeDynamic( _inner.getOrientation() );
      final CellRange innerRange = _inner.getPrototypeRange( _range );
      final SectionPath path = new SectionPath( SectionModelCompiler.this );
      path.stepInto( _inner );
      final SectionModelCompiler innerDef = path.getSectionCompiler();
      final ExpressionNode expr = innerDef.new RangeExpressionBuilder( this, innerRange, shaped )
          .buildContainedRange();
      if (shaped) {
        return path.wrapAround( expr );
      }
      else {
        return path.wrapAround( expr.arguments() );
      }
    }

    private ExpressionNode buildExpressionModelForLocalRange( CellRange _range ) throws CompilerException
    {
      final CellIndex from = _range.getFrom();
      final CellIndex to = _range.getTo();
      final int sheets = to.getSheetIndex() - from.getSheetIndex() + 1;
      final int rows = to.getRowIndex() - from.getRowIndex() + 1;
      final int cols = to.getColumnIndex() - from.getColumnIndex() + 1;
      final ExpressionNode result = new ExpressionNodeForArrayReference( new ArrayDescriptor( from.getSheetIndex(),
          from.getRowIndex(), from.getColumnIndex(), sheets, rows, cols ) );
      buildExpressionModelsForLocalRangeCells( _range, result.arguments() );
      return result;
    }

    private void buildExpressionModelsForLocalRangeCells( CellRange _range, Collection<ExpressionNode> _elts )
        throws CompilerException
    {
      for (CellIndex element : _range) {
        final ExpressionNode elementNode = buildExpressionModelForCell( element );
        _elts.add( elementNode );
      }
    }

  }

}
TOP

Related Classes of org.formulacompiler.spreadsheet.internal.compiler.SectionModelCompiler$RangeExpressionBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.