Package com.extentech.formats.XLS

Source Code of com.extentech.formats.XLS.ExpressionParser

/*
* --------- BEGIN COPYRIGHT NOTICE ---------
* Copyright 2002-2012 Extentech Inc.
* Copyright 2013 Infoteria America Corp.
*
* This file is part of OpenXLS.
*
* OpenXLS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* OpenXLS 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with OpenXLS.  If not, see
* <http://www.gnu.org/licenses/>.
* ---------- END COPYRIGHT NOTICE ----------
*/
package com.extentech.formats.XLS;

import java.util.List;
import java.util.Stack;
import java.util.Vector;

import com.extentech.toolkit.*;
import com.extentech.formats.XLS.formulas.*;

/**
    
*/
public final class ExpressionParser implements java.io.Serializable  {
    /**
   *
   *
   */
  private static final long serialVersionUID = 4745215965823234010L;
  private static int DEBUGLEVEL = 0;
    /*  All of the operand values
       
        Section of binary operator PTG's.  These pop the two
        top values out of a stack and perform an operation on
        them before pushing back in
    */
    // really "special" one, read all about it.
    public static final short ptgExp    = 0x1;
    public static final short ptgAdd    = 0x3;
    public static final short ptgSub    = 0x4;
    public static final short ptgMlt    = 0x5;
    public static final short ptgDiv    = 0x6;
    public static final short ptgPower  = 0x7;
    public static final short ptgConcat = 0x8;
    public static final short ptgLT     = 0x09;
    public static final short ptgLE     = 0x0a;
    public static final short ptgEQ     = 0x0b;
    public static final short ptgGE     = 0x0c;
    public static final short ptgGT     = 0x0d;
    public static final short ptgNE     = 0x0e;
    public static final short ptgIsect  = 0x0f;
    public static final short ptgUnion  = 0x10;
    public static final short ptgRange  = 0x11;
    //End of binary operator PTG's
   
    //Unary Operator tokens
    public static final short ptgUPlus   = 0x12;
  public static final short ptgUMinus   = 0x13; //todo
  public static final short ptgPercent   = 0x14; //todo
   
    // Controls
    public static final short ptgParen  = 0x15;
    public static final short ptgAtr    = 0x19;
    // End of Controls
   
    // Constant operators
    public static final short ptgMissArg = 0x16;
    public static final short ptgStr    = 0x17;
    public static final short ptgEndSheet = 0x1b;
    public static final short ptgErr    = 0x1c;
    public static final short ptgBool   = 0x1d;
    public static final short ptgInt    = 0x1e;
    public static final short ptgNum    = 0x1f;
    // End of Constant Operators
   
    public static final short ptgArray  = 0x20;
    public static final short ptgFunc   = 0x21;
    public static final short ptgFuncVar = 0x22;
    public static final short ptgName   = 0x23;
    public static final short ptgRef    = 0x24;
    public static final short ptgArea   = 0x25;
    public static final short ptgMemArea = 0x26;
    public static final short ptgMemErr = 0x27;
  public static final short ptgMemFunc = 0x29;
    public static final short ptgRefErr = 0x2a;
    public static final short ptgAreaErr = 0x2b;
    public static final short ptgRefN   = 0x2c;
    public static final short ptgAreaN  = 0x2d;
    public static final short ptgNameX  = 0x39;
    public static final short ptgRef3d  = 0x3a;
    public static final short ptgArea3d = 0x3b;
    public static final short ptgRefErr3d = 0x3c;

  // who knows, added to fix broken Named ranges -jm 03/26/04
  public static final short ptgAreaErr3d  = 0x3d;  
  public static final short ptgMemAreaA   = 0x66;
  public static final short ptgMemAreaNV   = 0x4e;
  public static final short ptgMemAreaN   = 0x2e;
   
  /**
   * Parse the byte array, create component Ptg's and insert
   * them into a stack.
   *
   *
   * Feb 8, 2010
   * @param function
   * @param rec
   * @return
   */
   public static Stack parseExpression(byte[] function, XLSRecord rec){
    return ExpressionParser.parseExpression(function,rec,function.length);
   }   
   
   /**
    * Parse the byte array, create component Ptg's and insert them into
    *  a stack.
    * 
      * Feb 8, 2010
    * @param function
    * @param rec
    * @param expressionLen
    * @return
    */
    public static Stack parseExpression(byte[] function, XLSRecord rec, int expressionLen){
        Stack stack = new Stack();
        short ptg = 0x0;
        int ptgLen = 0;
        boolean hasArrays = false;
        /* Not really needed
        //boolean hasPtgExtraMem= false;
        //PtgMemArea pma= null;*/       
        CompatibleVector arrayLocs = new CompatibleVector();
        if(expressionLen > function.length)expressionLen = function.length; // deal with out of spec formulas (testJapanese:Open25.xls) -jm
        // KSC: shared formula changes for peformance: now PtgRefN's/PtgAreaN's are instantiated and reference-tracked (of a sort) ...
        XLSRecord p= rec;  // parent
       
        // iterate the expression and create Ptgs.
        for (int i = 0; i <expressionLen;){
            // check if the 40 bit is set, is it a Array class?
            if ((function[i] & 0x40) == 0x40){
                //rec is a value class 
                //we need to strip the high-order bits and set the 0x20 bit
                ptg = (short) ((function[i] | 0x20) & 0x3f);
            } else {
                // the bit is already set, just strip the high order bits
                // rec may be an array class.  need to figure rec one out.
                ptg = (short) (function[i] & 0x3f);
            }
            switch (ptg){
               

                case ptgExp:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgExp Located");
                    if (i == 0){// MUST BE THE ONLY PTG in the formula expression
                        PtgExp px = new PtgExp();
                        ptgLen = px.getLength();
                        byte[] b = new byte[ptgLen];
                        if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                        px.setParentRec(p);
                        px.init(b);
                        stack.push(px);
                        break;
                    }
                // ptgStr is one of the only ptg's that varies in length, so there is some special handling
                // going on for it.
                case ptgStr:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgStr Located");
                    int x = i;
                    x += 1; // move past the opcode to the cch
                    ptgLen = (int)(function[x] & 0xff); // this is the cch
                    short theGrbit = function[x+1];// this is the grbit;
            if ((theGrbit & 0x1) == 0x1){
              // unicode string
              ptgLen = ptgLen*2;
            }
                    ptgLen += 3; // include the PtgId, cch, & grbit;
                    PtgStr pst = new PtgStr();
                    byte[] b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pst.init(b);
                    pst.setParentRec(p);
                    stack.push(pst);
                    break;
        /* */
       
        case ptgMemAreaA:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemAreaA Located" + function[i]);
          x = i;
          x += 5; // move past the opcode & reserved to the cce
          ptgLen = ByteTools.readShort(function[x], function[x+1]); // this is the cce
          ptgLen += 7; // include the PtgId, cce, & reserv;
          PtgMemAreaA pmema = new PtgMemAreaA();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pmema.init(b);
          pmema.setParentRec(p);
          stack.push(pmema);
          break;

        case ptgMemAreaN:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemAreaN Located" + function[i]);
          PtgMemAreaN pmemn = new PtgMemAreaN();
          ptgLen = pmemn.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pmemn.init(b);
          pmemn.setParentRec(p);
          stack.push(pmemn);
          break;
         
        case ptgMemAreaNV:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemAreaNV Located" + function[i]);
          x = i;
          x += 5; // move past the opcode & reserved to the cce
          ptgLen = ByteTools.readShort(function[x], function[x+1]); // this is the cce
          ptgLen += 7; // include the PtgId, cce, & reserv;
          PtgMemAreaNV pmemv = new PtgMemAreaNV();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pmemv.init(b);
          pmemv.setParentRec(p);
          stack.push(pmemv);
          break;

//        ptgMemArea also varies in length...             
        case ptgMemArea:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemArea Located" + function[i]);
          ptgLen= 7;
          PtgMemArea pmem = new PtgMemArea();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pmem.init(b);
          // now grab the rest of the "extra data" that defines the ptgmemarea
          // these are separate ptgs (PtgArea, PtgRef's ... plus PtgUnions)
          // that comprise the PtgMemArea coordinates 
          pmem.setParentRec(p);
          i+= ptgLen;  // after PtgMemArea record, get subexpression
          ptgLen= pmem.getnTokens();
          b= new byte[ptgLen];         
          if((ptgLen+i) <= function.length)
            System.arraycopy(function, (i), b, 0, ptgLen);
          pmem.setSubExpression(b);
          stack.push(pmem);
          //hasPtgExtraMem= true;  // has a PtgExtraMem structure after end of parsed expression: The PtgExtraMem structure specifies a range that corresponds to a PtgMemArea as specified in RgbExtra.
          //pma= pmem;  // save for later
          break;
        
         case ptgMemFunc:
           if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemFunc Located");
           PtgMemFunc pmemf = new PtgMemFunc();
           x = i;
           x += 1; // move past the opcode to the cce
           ptgLen = ByteTools.readShort(function[x], function[x+1]); // this is the cce
           ptgLen += 3; // include the PtgId, cce, & reserv;
           b = new byte[ptgLen];
           if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                   pmemf.setParentRec(p);
                   pmemf.init(b);
           stack.push(pmemf);
           break;
         
                case ptgInt:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgInt Located");
                    PtgInt pi = new PtgInt();
                    ptgLen = pi.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pi.init(b);
                    pi.setParentRec(p);
                    stack.push(pi);
                    break;
                   
                case ptgErr:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgErr Located");
                    PtgErr perr = new PtgErr();
                    ptgLen = perr.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    perr.init(b);
                    perr.setParentRec(p);
                    stack.push(perr);
                    break;
                   
                case ptgNum:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgNum Located");
                    PtgNumber pnum = new PtgNumber();
                    ptgLen = pnum.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pnum.init(b);
                    pnum.setParentRec(p);
                    stack.push(pnum);
                    break;
                   
                case ptgBool:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgBool Located");
                    PtgBool pboo = new PtgBool();
                    ptgLen = pboo.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pboo.init(b);
                    pboo.setParentRec(p);
                    stack.push(pboo);
                    break;
               
                case ptgName:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgName Located");
                    PtgName pn = new PtgName();
                    ptgLen = pn.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length)
                     System.arraycopy(function, (i), b, 0, ptgLen);
                    pn.setParentRec(p);
                    pn.init(b);                   
                    pn.addListener();
                    stack.push(pn);
          int chk = (i+ptgLen);
          if(chk<function.length){
            if(function[i+ptgLen]==0x0){
              if(DEBUGLEVEL > 1)Logger.logWarn("Undocumented Name Record mystery byte encountered in Formula: ");
              i++;
           
          }
                    break;
                   
        case ptgNameX:
          if(DEBUGLEVEL > 1)Logger.logInfo("ptgNameX Located");
            if(DEBUGLEVEL > 0)Logger.logWarn("referencing external spreadsheets unsupported.");
          PtgNameX pnx = new PtgNameX();
          ptgLen = pnx.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pnx.init(b);
          pnx.setParentRec(p);
                    pnx.addListener();
          stack.push(pnx);
          break;
                   
                case ptgRef:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgRef Located ");
                    PtgRef pt = new PtgRef();
                    ptgLen = pt.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pt.setParentRec(p)// parent rec must be set before init
                    pt.init(b);
                    pt.addToRefTracker();
                    stack.push(pt);
                    break;
                   
        case ptgArray:
          hasArrays = true;
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgArray Located ");
          PtgArray pa = new PtgArray();
          ptgLen = 8//7 len + id
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pa.init(b)//setArrVals(b);  // 20090820 KSC: b represents base record not array values
          Integer ingr = Integer.valueOf(stack.size())// constant value array for PtgArray appears at end of stack see hasArrays below
          arrayLocs.add(ingr);
          stack.push(pa);
          break;
                   
                case ptgRefN:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgRefN Located ");
                    PtgRefN ptn = new PtgRefN(false);
                    ptgLen = ptn.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    ptn.setParentRec(rec)// parent rec must be set before init
                    ptn.init(b);
                    if (rec.getOpcode()==XLSConstants.SHRFMLA)                   
                      ptn.addToRefTracker();
                    stack.push(ptn);
                    break;
                   
                case ptgArea:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgArea Located ");
                    PtgArea pg = new PtgArea();
                    ptgLen = pg.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pg.setParentRec(p)// parent rec must be set before init                   
                    pg.init(b);
                    pg.addToRefTracker();
                    stack.push(pg);
                    break;
                                    
                case ptgArea3d:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgArea3d Located ");
                    PtgArea3d pg3 = new PtgArea3d();
                    ptgLen = pg3.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pg3.init(b, p); // we need this to init the sub-ptgs correctly
                    pg3.addListener();
                    pg3.addToRefTracker();
                    stack.push(pg3);
                    break;
                
                case ptgAreaN:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgAreaN Located ");
                    PtgAreaN pgn = new PtgAreaN();
                    ptgLen = pgn.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pgn.setParentRec(rec);
                    pgn.init(b);
                    if (rec.getOpcode()==XLSConstants.SHRFMLA) {                   
                      pgn.addToRefTracker();
                    }
          stack.push(pgn);
                    break;

        case ptgAreaErr3d:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgAreaErr3d Located");
          PtgAreaErr3d ptfa = new PtgAreaErr3d();
          ptgLen = ptfa.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          ptfa.setParentRec(p);
          ptfa.init(b);
          //ptfa.addToRefTracker();
          stack.push(ptfa);
          break;

        case ptgRefErr3d:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgRefErr3d Located");
          PtgRefErr3d ptfr = new PtgRefErr3d();
          ptgLen = ptfr.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          ptfr.setParentRec(p);
          ptfr.init(b);
          //ptfr.addToRefTracker();
          stack.push(ptfr);
          break;
                                       
                case ptgMemErr:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgMemErr Located");
                    PtgMemErr pm = new PtgMemErr();
                    ptgLen = pm.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pm.setParentRec(p);
                    pm.init(b);
                    stack.push(pm);
                    break;   
               
               
                case ptgRefErr:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgRefErr Located");
                    PtgRefErr pr = new PtgRefErr();
                    ptgLen = pr.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pr.setParentRec(p)// parent rec must be set before init
                    pr.init(b);
                    stack.push(pr);
                    break;   
                   
        case ptgEndSheet:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgEndSheet Located");
          PtgEndSheet prs = new PtgEndSheet();
          ptgLen = prs.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          prs.init(b);
          prs.setParentRec(p);
          stack.push(prs);
          break
               
                case ptgRef3d:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgRef3d Located");
                    PtgRef3d pr3 = new PtgRef3d();
                    ptgLen = pr3.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pr3.setParentRec(p);
                    pr3.init(b);
                    pr3.addListener();
                    pr3.addToRefTracker();
                    stack.push(pr3);
                    // if an External Link i.e. defined in another workbook, flag formula as such
                    if (pr3.isExternalLink() && p.getOpcode()==XLSConstants.FORMULA)
                      ((Formula) p).setIsExternalRef(true);                     
                    break;
                /*
                 * PtgAtr is another one of the ugly size-changing ptg's
                 */
                case ptgAtr:
                    PtgAtr pat = new PtgAtr((byte)0);
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgAtr Located");
                    ptgLen = pat.getLength();
            if ((function[i+1] & 0x4)== 0x4){
              ptgLen = ByteTools.readShort(function[i+2], function[i+3]);
              ptgLen++; // one extra for some undocumented reason
              ptgLen = ptgLen*2; //seems to be two bytes per...   
              ptgLen += 4; // add the cch & grbit   
          }
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length)
                    System.arraycopy(function, (i), b, 0, ptgLen);
                    pat.init(b);
                    pat.init();
                    pat.setParentRec(p);
                    stack.push(pat);
                    break;
                   
                case ptgFunc:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgFunc Located");
                    PtgFunc ptf = new PtgFunc();
                    ptgLen = ptf.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    ptf.init(b);
                    ptf.setParentRec(p);
                    stack.push(ptf);
                    break;
                
                 case ptgFuncVar:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgFuncVar Located");
                    PtgFuncVar ptfv = new PtgFuncVar();
                    ptgLen = ptfv.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen)+(i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                   
                    ptfv.init(b);
                    ptfv.setParentRec(p);
                    if(ptfv.getFunctionId()==FunctionConstants.XLF_INDIRECT) {
/*TESTING NEW WAY:                     
// New way does not account for expanded shared formula references, unfortunately  so keep original for new
*      
              Stack indirectStack= new Stack();
                      int z= stack.size()-1;
                      int nparams= 1;
                      for (; z > 0 && nparams > 0; z--) {
                        Ptg p= (Ptg) stack.get(z);
              if (p instanceof PtgAtr) {
                continue;
              }
                            if(p.getIsOperator()||p.getIsControl()||p.getIsFunction()){
                                if(p.getIsControl() ){
                                    if(p.getOpcode() == 0x15) { // its a parens!  
                                        // the parens is already pop'd so just return and it is gone...
                                        continue;
                                    }
                                }
                                int t= 0;
                                // make sure we have the correct amount popped back in..
                                if (p.getIsBinaryOperator()) t=2;
                                if (p.getIsUnaryOperator()) t=1;
                          if (p.getIsStandAloneOperator()) t=0;
                                if (p.getOpcode() == 0x22 || p.getOpcode() == 0x42 || p.getOpcode() == 0x62){t=p.getNumParams();}// it's a ptgfunkvar!
                                if (p.getOpcode() == 0x21 || p.getOpcode() == 0x41 ||  p.getOpcode() == 0x61){t=p.getNumParams();}// guess that ptgfunc is not only one..
                                nparams+=t-1;
                              if (nparams==0)
                                break;
                            } else {
                              nparams--;
                              if (nparams==0)
                                break;
                            }
                      }
                      indirectStack.addAll(stack.subList(z, stack.size()));
                      indirectStack.push(ptfv);
                      rec.getWorkBook().addIndirectFormulaStack(indirectStack);  // must save and calculate indirect reference AFTER all formulas/cells have been added ...
// original is below                                           
/**/                 
/**/                     
            if(rec.getOpcode()==XLSConstants.FORMULA) {
                            ((Formula)rec).setContainsIndirectFunction(true);
                        }else if(rec.getOpcode()==XLSConstants.SHRFMLA) {
                            ((Shrfmla)rec).setContainsIndirectFunction(true);
                        }
/**/
                    }
                    stack.push(ptfv);
                    break;
              
                case ptgAdd:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgAdd Located");
                    PtgAdd pad = new PtgAdd();
                    ptgLen = pad.getLength();
                    b = new byte[ptgLen];
                    //if((ptgLen+i) <= function.length)
                    System.arraycopy(function, (i), b, 0, ptgLen);
                    pad.init(b);
                    pad.setParentRec(p);
                    stack.push(pad);
                    break;
                   
                case ptgMissArg:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgMissArg Located");
          PtgMissArg pmar = new PtgMissArg();
          ptgLen = pmar.getLength();
          b = new byte[ptgLen];
          //if((ptgLen+i) <= function.length)
          System.arraycopy(function, (i), b, 0, ptgLen);
          pmar.init(b);
          pmar.setParentRec(p);
          stack.push(pmar);
          break;
                 
                case ptgSub:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgSub Located");
                    PtgSub psb = new PtgSub();
                    ptgLen = psb.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    psb.init(b);
                    psb.setParentRec(p);
                    stack.push(psb);
                    break;
               
                case ptgMlt:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgMlt Located");
                    PtgMlt pml = new PtgMlt();
                    ptgLen = pml.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pml.init(b);
                    pml.setParentRec(p);
                    stack.push(pml);
                    break;   
               
                case ptgDiv:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgDiv Located");
                    PtgDiv pdiv = new PtgDiv();
                    ptgLen = pdiv.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pdiv.init(b);
                    pdiv.setParentRec(p);
                    stack.push(pdiv);
                    break;
                   
        case ptgUPlus:
          if(DEBUGLEVEL > 5)Logger.logInfo("PtgUPlus Located");
          PtgUPlus puplus = new PtgUPlus();
          ptgLen = puplus.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          puplus.init(b);
          puplus.setParentRec(p);
          stack.push(puplus);
          break;
         
        case ptgUMinus:
          if(DEBUGLEVEL > 5)Logger.logInfo("PtgUminus Located");
          PtgUMinus puminus = new PtgUMinus();
          ptgLen = puminus.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          puminus.init(b);
          puminus.setParentRec(p);
          stack.push(puminus);
          break;
         
        case ptgPercent:
          if(DEBUGLEVEL > 5)Logger.logInfo("ptgPercent Located");
          PtgPercent pperc = new PtgPercent();
          ptgLen = pperc.getLength();
          b = new byte[ptgLen];
          if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
          pperc.init(b);
          pperc.setParentRec(p);
          stack.push(pperc);
          break;
               
                case ptgPower:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgPower Located");
                    PtgPower pow = new PtgPower();
                    ptgLen = pow.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pow.init(b);
                    pow.setParentRec(p);
                    stack.push(pow);
                    break;
                   
                case ptgConcat:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgConcat Located");
                    PtgConcat pcon = new PtgConcat();
                    ptgLen = pcon.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pcon.init(b);
                    pcon.setParentRec(p);
                    stack.push(pcon);
                    break;  
               
                case ptgLT:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgLT Located");
                    PtgLT plt = new PtgLT();
                    ptgLen = plt.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    plt.init(b);
                    plt.setParentRec(p);
                    stack.push(plt);
                    break;
               
                case ptgLE:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgLE Located");
                    PtgLE ple = new PtgLE();
                    ptgLen = ple.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    ple.init(b);
                    ple.setParentRec(p);
                    stack.push(ple);
                    break;
               
                case ptgEQ:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgEQ Located");
                    PtgEQ peq = new PtgEQ();
                    ptgLen = peq.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    peq.init(b);
                    peq.setParentRec(p);
                    stack.push(peq);
                    break;
                 
                case ptgGE:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgGE Located");
                    PtgGE pge = new PtgGE();
                    ptgLen = pge.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pge.init(b);
                    pge.setParentRec(p);
                    stack.push(pge);
                    break;
                   
                case ptgGT:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgGT Located");
                    PtgGT pgt = new PtgGT();
                    ptgLen = pgt.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pgt.init(b);
                    pgt.setParentRec(p);
                    stack.push(pgt);
                    break;
                   
                case ptgNE:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgNE Located");
                    PtgNE pne = new PtgNE();
                    ptgLen = pne.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                   
                    pne.init(b);
                    pne.setParentRec(p);
                    stack.push(pne);
                    break;
                   
                case ptgIsect:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgIsect Located");
                    PtgIsect pist = new PtgIsect();
                    ptgLen = pist.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                   
                    pist.init(b);
                    pist.setParentRec(p);
                    stack.push(pist);
                    break;
                
                case ptgUnion:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgUnion Located");
                    PtgUnion pun = new PtgUnion();
                    ptgLen = pun.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pun.init(b);
                    pun.setParentRec(p);
                    stack.push(pun);
                    break;
                
                case ptgRange:
                    if(DEBUGLEVEL > 5)Logger.logInfo("ptgRange Located");
                    PtgRange pran = new PtgRange();
                    ptgLen = pran.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                    pran.init(b);
                    pran.setParentRec(p);
                    stack.push(pran);
                    break;   
                   
                case ptgParen:
                    if(DEBUGLEVEL > 5)Logger.logInfo("PtgParens Located");
                    PtgParen pp = new PtgParen();
                    ptgLen = pp.getLength();
                    b = new byte[ptgLen];
                    if((ptgLen+i) <= function.length) System.arraycopy(function, (i), b, 0, ptgLen);
                   
                    pp.init(b);
                    pp.setParentRec(p);
                    stack.push(pp);
                    break;              
              
                default:
          PtgMystery pmy = new PtgMystery();
                    ptgLen = function.length-i;
                    b = new byte[ptgLen];
          if(DEBUGLEVEL > 0)Logger.logWarn("Unsupported Formula Function: 0x"+ Integer.toHexString(ptg) + " length: " + ptgLen);
                    System.arraycopy(function, i, b, 0, ptgLen);
                    pmy.init(b);
                    pmy.setParentRec(p);
                    stack.push(pmy);
                    break;
                }
            i += ptgLen;
        }
        if (hasArrays && rec instanceof Formula){  // Array Recs handle extra data differently
          // array data is appended to end of expression
          // for each array in the function list,
          // get saved ptgArray var (stored in stack var),
          // grab data and parse array components
          int startPos= expressionLen;  
          for (int i=0;i<arrayLocs.size();i++){
            Integer ingr = (Integer) arrayLocs.elementAt(i);
            PtgArray parr = (PtgArray)stack.elementAt(ingr.intValue());
           
            // have to assume that remaining data all goes for this ptgarray
            // since length is variable and can only be ascertained by parsing
            // if multiple arrays are present, actual array length will be returned via setArrVals
        byte[] b = new byte[function.length-startPos]; // get "extra" array data
        System.arraycopy(function, startPos, b, 0, b.length);
        try{
          parr.setParentRec(rec);
          startPos+=parr.setArrVals(b);
        }catch(Exception e){//TODO: this needs to be caught due to "name" records being parsed incorrectly.  The problem has to do with the lenght of the name record not including the extra 7 bytes of space.  Temporary fix for infoteria
          if (DEBUGLEVEL > 0)Logger.logInfo("ExpressionParser.parseExpression: Array: " + e);
        }
          }
        } /* no need to keep PtgExtraMem as can regenerate easily else
          if (hasPtgExtraMem && rec instanceof Formula) {
          //The PtgExtraMem structure specifies a range that corresponds to a PtgMemArea as specified in RgbExtra.)
          //     count (2 bytes): An unsigned integer that specifies the areas within the range.
          //      array (variable): An array of Ref8U that specifies the range. The number of elements MUST be equal to count.
            pma.setPostExpression(function, expressionLen);
        }*/
        if(DEBUGLEVEL > 5)Logger.logInfo("finished formula");
        return stack;
       
    }
   
    /*  Returns the ptg that matches the string location sent to it. 
        rec can either be in the format "C5" or a range, such as "C4:D9"
           
    */
    public static List getPtgsByLocation(String loc, Stack expression)
        throws FormulaNotFoundException{
        List lv = new Vector();
        for (int i = 0; i< expression.size(); i++){
            Object o = expression.elementAt(i);
            if(o == null)throw new FormulaNotFoundException("Couldn't get Ptg at: " + loc);
            if (o instanceof Byte){
                // do nothing
            }else if (o instanceof Ptg){
              Ptg part = (Ptg) o;
                String lo = part.getLocation();
                if(lo==null)
                  lo = "none";
                String comp = loc;
                if(loc.indexOf("!")>-1){ // the sheet is referenced
                  if(lo.indexOf("!")==-1){ // and the ptg does not have sheet referenced
                    comp = loc.substring(loc.indexOf("!")+1);
                  }
                }
               
               
                if (comp.equalsIgnoreCase(lo)){
                  lv.add(part);
                }else{
                  // try fq location
                  lo = part.toString();
                  if(loc.equalsIgnoreCase(lo)) {
                    lv.add(part);
                   
                  }else if(o instanceof PtgRef3d){// gotta look into the first & last
                    // already checked
                  }else if(o instanceof PtgArea){// gotta look into the first & last
                      Ptg first = ((PtgArea)o).getFirstPtg();
                      Ptg last = ((PtgArea)o).getLastPtg();
                      if(first.getLocation().equalsIgnoreCase(loc))
                        lv.add(first);
                      if(last.getLocation().equalsIgnoreCase(loc))
                        lv.add(last);
                  }
                }
            }
        }
        return lv;
    }
   
    /**
     * returns the position in the expression stack for the ptg associated with this location
     * @param loc  String
     * @param expression
     * @return
     * @throws FormulaNotFoundException
     */
    public static int getExpressionLocByLocation(String loc, Stack expression)
    throws FormulaNotFoundException{
   
    for (int i = 0; i< expression.size(); i++){
        Object o = expression.elementAt(i);
        if(o == null)throw new FormulaNotFoundException("Couldn't get Ptg at: " + loc);
        if (o instanceof Byte){
            // do nothing
        }else if (o instanceof Ptg){
          Ptg part = (Ptg) o;
            String lo = part.getLocation();
            if (loc.equalsIgnoreCase(lo)){
                return i;
            }
            // try full location
            lo = part.toString();
            if(loc.equalsIgnoreCase(lo)) {
                return i;
            }
            if(o instanceof PtgArea){// gotta look into the first & last
                Ptg first = ((PtgArea)o).getFirstPtg();
                Ptg last = ((PtgArea)o).getLastPtg();
                if(first.getLocation().equalsIgnoreCase(loc))return i;
                if(last.getLocation().equalsIgnoreCase(loc))return i;
            }
        }
    }
    return -1;
}
   
    /**
     * returns the position in the expression stack for the desired ptg 
     * @param ptg Ptg to lookk up
     * @param expression
     * @return
     * @throws FormulaNotFoundException
     */
    public static int getExpressionLocByPtg(Ptg ptg, Stack expression)
    throws FormulaNotFoundException{
     
      for (int i = 0; i< expression.size(); i++){
          Object o = expression.elementAt(i);
          if(o == null)throw new FormulaNotFoundException("Couldn't get Ptg at: " + ptg.toString());
          if (o instanceof Byte){
              // do nothing
          }else if (o instanceof Ptg){
            if (((Ptg) o).equals(ptg))
              return i;
          }
      }
    return -1;
}
   
  /**
     * getCellRangePtgs handles locating which cells are refereced in an expression stack.
     *
     * Essentially the use is we can check a formula if it refereces a cell that is moving, then we have
     * the ability to manipulate these ranges in whatever way makes sense.
     *
     * @expression = a Stack of ptgs that represent an excel calculation.
     * @return an array of ptgs that are location based (ptgRef, PtgArea)
  */   
  public static Ptg[] getCellRangePtgs(Stack expression)
    throws FormulaNotFoundException{
        Vector ret = new Vector();
    for (int i = 0; i< expression.size(); i++){
      Object o = expression.elementAt(i);
      if(o == null)throw new FormulaNotFoundException("Couldn't get Ptg at: " + i);
      if (o instanceof Byte){
        // do nothing
      }else if (o instanceof Ptg){
        Ptg part = (Ptg) o;
        // handle shared formula range
        if (part instanceof PtgExp){
          String lox= part.getLocation();
          PtgRef ref = new PtgRef();
          ref.setParentRec(part.getParentRec())// must be done before setLocation
          ref.setLocation(lox);
          ret.add(ref);
        } else if (part instanceof PtgRefErr ||
            part instanceof PtgAreaErr3d) {
          ret.add("#REF!");
        } else if (part instanceof PtgMemFunc) {
          //Ptg[] p= getCellRangePtgs(((PtgMemFunc)part).getSubExpression());
          Ptg[] p= ((PtgMemFunc)part).getComponents();
          for (int z= 0; z < p.length; z++)
            ret.add(p[z]);
        }else {
          String lox= part.getLocation();
          if (lox!=null)
            ret.add(part);
        }       
      }
    }
    Ptg[] retp = new Ptg[ret.size()];
    return (Ptg[]) ret.toArray(retp);
  }

}
TOP

Related Classes of com.extentech.formats.XLS.ExpressionParser

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.