/**
   * Note - caller should reset {@link #_pointer} upon <code>null</code> result
   *
   * @return The sheet name as an identifier <code>null</code> if '!' is not found in the right
   *     place
   */
  private SheetIdentifier parseSheetName() {

    String bookName;
    if (look == '[') {
      StringBuilder sb = new StringBuilder();
      GetChar();
      while (look != ']') {
        sb.append(look);
        GetChar();
      }
      GetChar();
      bookName = sb.toString();
    } else {
      bookName = null;
    }

    if (look == '\'') {
      StringBuffer sb = new StringBuffer();

      Match('\'');
      boolean done = look == '\'';
      while (!done) {
        sb.append(look);
        GetChar();
        if (look == '\'') {
          Match('\'');
          done = look != '\'';
        }
      }

      Identifier iden = new Identifier(sb.toString(), true);
      // quoted identifier - can't concatenate anything more
      SkipWhite();
      if (look == '!') {
        GetChar();
        return new SheetIdentifier(bookName, iden, _book);
      }
      return null;
    }

    // unquoted sheet names must start with underscore or a letter
    if (look == '_' || Character.isLetter(look)) {
      StringBuilder sb = new StringBuilder();
      // can concatenate idens with dots
      while (isUnquotedSheetNameChar(look)) {
        sb.append(look);
        GetChar();
      }
      SkipWhite();
      if (look == '!') {
        GetChar();
        return new SheetIdentifier(bookName, new Identifier(sb.toString(), false), _book);
      }
      return null;
    }
    return null;
  }
 /** factors (without ^ or % ) */
 private ParseNode parseSimpleFactor() {
   SkipWhite();
   switch (look) {
     case '#':
       return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
     case '-':
       Match('-');
       return parseUnary(false);
     case '+':
       Match('+');
       return parseUnary(true);
     case '(':
       Match('(');
       ParseNode inside = comparisonExpression();
       Match(')');
       return new ParseNode(ParenthesisPtg.instance, inside);
     case '"':
       return new ParseNode(new StringPtg(parseStringLiteral()));
     case '{':
       Match('{');
       ParseNode arrayNode = parseArray();
       Match('}');
       return arrayNode;
   }
   if (IsAlpha(look) || Character.isDigit(look) || look == '\'' || look == '[') {
     return parseRangeExpression();
   }
   if (look == '.') {
     return new ParseNode(parseNumber());
   }
   throw expected("cell reference or constant literal");
 }
 private Object parseArrayItem() {
   SkipWhite();
   switch (look) {
     case '"':
       return parseStringLiteral();
     case '#':
       return ErrorConstant.valueOf(parseErrorLiteral());
     case 'F':
     case 'f':
     case 'T':
     case 't':
       return parseBooleanLiteral();
     case '-':
       Match('-');
       SkipWhite();
       return convertArrayNumber(parseNumber(), false);
   }
   // else assume number
   return convertArrayNumber(parseNumber(), true);
 }
 private ParseNode percentFactor() {
   ParseNode result = parseSimpleFactor();
   while (true) {
     SkipWhite();
     if (look != '%') {
       return result;
     }
     Match('%');
     result = new ParseNode(PercentPtg.instance, result);
   }
 }
 /** Parse and Translate a Math Factor */
 private ParseNode powerFactor() {
   ParseNode result = percentFactor();
   while (true) {
     SkipWhite();
     if (look != '^') {
       return result;
     }
     Match('^');
     ParseNode other = percentFactor();
     result = new ParseNode(PowerPtg.instance, result, other);
   }
 }
 private ParseNode concatExpression() {
   ParseNode result = additiveExpression();
   while (true) {
     SkipWhite();
     if (look != '&') {
       break; // finished with concat expression
     }
     Match('&');
     ParseNode other = additiveExpression();
     result = new ParseNode(ConcatPtg.instance, result, other);
   }
   return result;
 }
  /**
   * Parses simple factors that are not primitive ranges or range components i.e. '!', ':'(and equiv
   * '...') do not appear Examples
   *
   * <pre>
   *   my.named...range.
   *   foo.bar(123.456, "abc")
   *   123.456
   *   "abc"
   *   true
   * </pre>
   */
  private ParseNode parseNonRange(int savePointer) {
    resetPointer(savePointer);

    if (Character.isDigit(look)) {
      return new ParseNode(parseNumber());
    }
    if (look == '"') {
      return new ParseNode(new StringPtg(parseStringLiteral()));
    }
    // from now on we can only be dealing with non-quoted identifiers
    // which will either be named ranges or functions
    StringBuilder sb = new StringBuilder();

    // defined names may begin with a letter or underscore
    if (!Character.isLetter(look) && look != '_') {
      throw expected("number, string, or defined name");
    }
    while (isValidDefinedNameChar(look)) {
      sb.append(look);
      GetChar();
    }
    SkipWhite();
    String name = sb.toString();
    if (look == '(') {
      return function(name);
    }
    if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
      return new ParseNode(BoolPtg.valueOf(name.equalsIgnoreCase("TRUE")));
    }
    if (_book == null) {
      // Only test cases omit the book (expecting it not to be needed)
      throw new IllegalStateException("Need book to evaluate name '" + name + "'");
    }
    // 20101115, [email protected]: shall provide a temporary defined named record
    // EvaluationName evalName = _book.getName(name, _sheetIndex);
    EvaluationName evalName = _book.getOrCreateName(name, _sheetIndex);
    if (evalName == null) {
      // 20101112, [email protected]: shall return #NAME? error
      // throw new FormulaParseException("Specified named range '"
      //		+ name + "' does not exist in the current workbook.");
      return new ParseNode(ErrPtg.NAME_INVALID);
    }
    // 20101115, [email protected]: unnecessary check
    // if (evalName.isRange()) {
    //	return new ParseNode(evalName.createPtg());
    // }
    // TODO - what about NameX ?
    // throw new FormulaParseException("Specified name '"
    //		+ name + "' is not a range as expected.");
    return new ParseNode(evalName.createPtg());
  }
  /** get arguments to a function */
  private ParseNode[] Arguments() {
    // average 2 args per function
    List<ParseNode> temp = new ArrayList<ParseNode>(2);
    SkipWhite();
    if (look == ')') {
      return ParseNode.EMPTY_ARRAY;
    }

    boolean missedPrevArg = true;
    int numArgs = 0;
    while (true) {
      SkipWhite();
      if (isArgumentDelimiter(look)) {
        if (missedPrevArg) {
          temp.add(new ParseNode(MissingArgPtg.instance));
          numArgs++;
        }
        if (look == ')') {
          break;
        }
        Match(',');
        missedPrevArg = true;
        continue;
      }
      temp.add(comparisonExpression());
      numArgs++;
      missedPrevArg = false;
      SkipWhite();
      if (!isArgumentDelimiter(look)) {
        throw expected("',' or ')'");
      }
    }
    ParseNode[] result = new ParseNode[temp.size()];
    temp.toArray(result);
    return result;
  }
 private ParseNode comparisonExpression() {
   ParseNode result = concatExpression();
   while (true) {
     SkipWhite();
     switch (look) {
       case '=':
       case '>':
       case '<':
         Ptg comparisonToken = getComparisonToken();
         ParseNode other = concatExpression();
         result = new ParseNode(comparisonToken, result, other);
         continue;
     }
     return result; // finished with predicate expression
   }
 }
示例#10
0
 private ParseNode unionExpression() {
   ParseNode result = comparisonExpression();
   boolean hasUnions = false;
   while (true) {
     SkipWhite();
     switch (look) {
       case ',':
         GetChar();
         hasUnions = true;
         ParseNode other = comparisonExpression();
         result = new ParseNode(UnionPtg.instance, result, other);
         continue;
     }
     if (hasUnions) {
       return augmentWithMemPtg(result);
     }
     return result;
   }
 }
示例#11
0
 /** Parse and Translate an Expression */
 private ParseNode additiveExpression() {
   ParseNode result = Term();
   while (true) {
     SkipWhite();
     Ptg operator;
     switch (look) {
       case '+':
         Match('+');
         operator = AddPtg.instance;
         break;
       case '-':
         Match('-');
         operator = SubtractPtg.instance;
         break;
       default:
         return result; // finished with additive expression
     }
     ParseNode other = Term();
     result = new ParseNode(operator, result, other);
   }
 }
示例#12
0
 /** Parse and Translate a Math Term */
 private ParseNode Term() {
   ParseNode result = powerFactor();
   while (true) {
     SkipWhite();
     Ptg operator;
     switch (look) {
       case '*':
         Match('*');
         operator = MultiplyPtg.instance;
         break;
       case '/':
         Match('/');
         operator = DividePtg.instance;
         break;
       default:
         return result; // finished with Term
     }
     ParseNode other = powerFactor();
     result = new ParseNode(operator, result, other);
   }
 }
示例#13
0
  private Object[] parseArrayRow() {
    List<Object> temp = new ArrayList<Object>();
    while (true) {
      temp.add(parseArrayItem());
      SkipWhite();
      switch (look) {
        case '}':
        case ';':
          break;
        case ',':
          Match(',');
          continue;
        default:
          throw expected("'}' or ','");
      }
      break;
    }

    Object[] result = new Object[temp.size()];
    temp.toArray(result);
    return result;
  }
示例#14
0
  /** @return <code>true</code> if the specified name is a valid cell reference */
  private boolean isValidCellReference(String str) {
    // check range bounds against grid max
    boolean result = CellReference.classifyCellReference(str, _ssVersion) == NameType.CELL;

    if (result) {
      /**
       * Check if the argument is a function. Certain names can be either a cell reference or a
       * function name depending on the contenxt. Compare the following examples in Excel 2007: (a)
       * LOG10(100) + 1 (b) LOG10 + 1 In (a) LOG10 is a name of a built-in function. In (b) LOG10 is
       * a cell reference
       */
      boolean isFunc = FunctionMetadataRegistry.getFunctionByName(str.toUpperCase()) != null;
      if (isFunc) {
        int savePointer = _pointer;
        resetPointer(_pointer + str.length());
        SkipWhite();
        // open bracket indicates that the argument is a function,
        // the returning value should be false, i.e. "not a valid cell reference"
        result = look != '(';
        resetPointer(savePointer);
      }
    }
    return result;
  }
示例#15
0
  /**
   * Parses area refs (things which could be the operand of ':') and simple factors Examples
   *
   * <pre>
   *   A$1
   *   $A$1 :  $B1
   *   A1 .......	C2
   *   Sheet1 !$A1
   *   a..b!A1
   *   'my sheet'!A1
   *   .my.sheet!A1
   *   my.named..range.
   *   foo.bar(123.456, "abc")
   *   123.456
   *   "abc"
   *   true
   * </pre>
   */
  private ParseNode parseRangeable() {
    SkipWhite();
    int savePointer = _pointer;
    SheetIdentifier sheetIden = parseSheetName();
    if (sheetIden == null) {
      resetPointer(savePointer);
    } else {
      SkipWhite();
      savePointer = _pointer;
    }

    SimpleRangePart part1 = parseSimpleRangePart();
    if (part1 == null) {
      if (sheetIden != null) {
        if (look == '#') { // error ref like MySheet!#REF!
          return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
        } else {
          throw new FormulaParseException(
              "Cell reference expected after sheet name at index " + _pointer + ".");
        }
      }
      return parseNonRange(savePointer);
    }
    boolean whiteAfterPart1 = IsWhite(look);
    if (whiteAfterPart1) {
      SkipWhite();
    }

    if (look == ':') {
      int colonPos = _pointer;
      GetChar();
      SkipWhite();
      SimpleRangePart part2 = parseSimpleRangePart();
      if (part2 != null && !part1.isCompatibleForArea(part2)) {
        // second part is not compatible with an area ref e.g. S!A1:S!B2
        // where S might be a sheet name (that looks like a column name)

        part2 = null;
      }
      if (part2 == null) {
        // second part is not compatible with an area ref e.g. A1:OFFSET(B2, 1, 2)
        // reset and let caller use explicit range operator
        resetPointer(colonPos);
        if (!part1.isCell()) {
          String prefix;
          if (sheetIden == null) {
            prefix = "";
          } else {
            prefix = "'" + sheetIden.getSheetIdentifier().getName() + '!';
          }
          throw new FormulaParseException(prefix + part1.getRep() + "' is not a proper reference.");
        }
        return createAreaRefParseNode(sheetIden, part1, part2);
      }
      return createAreaRefParseNode(sheetIden, part1, part2);
    }

    if (look == '.') {
      GetChar();
      int dotCount = 1;
      while (look == '.') {
        dotCount++;
        GetChar();
      }
      boolean whiteBeforePart2 = IsWhite(look);

      SkipWhite();
      SimpleRangePart part2 = parseSimpleRangePart();
      String part1And2 = _formulaString.substring(savePointer - 1, _pointer - 1);
      if (part2 == null) {
        if (sheetIden != null) {
          throw new FormulaParseException(
              "Complete area reference expected after sheet name at index " + _pointer + ".");
        }
        return parseNonRange(savePointer);
      }

      if (whiteAfterPart1 || whiteBeforePart2) {
        if (part1.isRowOrColumn() || part2.isRowOrColumn()) {
          // "A .. B" not valid syntax for "A:B"
          // and there's no other valid expression that fits this grammar
          throw new FormulaParseException(
              "Dotted range (full row or column) expression '"
                  + part1And2
                  + "' must not contain whitespace.");
        }
        return createAreaRefParseNode(sheetIden, part1, part2);
      }

      if (dotCount == 1 && part1.isRow() && part2.isRow()) {
        // actually, this is looking more like a number
        return parseNonRange(savePointer);
      }

      if (part1.isRowOrColumn() || part2.isRowOrColumn()) {
        if (dotCount != 2) {
          // [email protected]: shall return #NAME?
          // throw new FormulaParseException("Dotted range (full row or column) expression '" +
          // part1And2
          //		+ "' must have exactly 2 dots.");
          return parseNonRange(savePointer);
        }
      }
      return createAreaRefParseNode(sheetIden, part1, part2);
    }
    if (part1.isCell() && isValidCellReference(part1.getRep())) {
      return createAreaRefParseNode(sheetIden, part1, null);
    }
    if (sheetIden != null) {
      throw new FormulaParseException(
          "Second part of cell reference expected after sheet name at index " + _pointer + ".");
    }

    return parseNonRange(savePointer);
  }