/**
         * string formula specific exception handling.  Can't really create
         * a formula (as it will look for a cell of that name, so just
         * create a STRING record containing the contents
         *
         * @return the bodged data
         */
        public override byte[] handleFormulaException()
        {
            byte[] expressiondata = null;
            byte[] celldata = base.getCellData();

            // Generate an appropriate dummy formula
            WritableWorkbookImpl w = getSheet().getWorkbook();
            FormulaParser parser = new FormulaParser("\"" + getContents() + "\"", w, w,
                                                     w.getSettings());

            // Get the bytes for the dummy formula
            try
                {
                parser.parse();
                }
            catch (FormulaException e2)
                {
                //logger.warn(e2.Message);
                parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings());
                try
                    {
                    parser.parse();
                    }
                catch (FormulaException e3)
                    {
                    Assert.verify(false);
                    }
                }
            byte[] formulaBytes = parser.getBytes();
            expressiondata = new byte[formulaBytes.Length + 16];
            IntegerHelper.getTwoBytes(formulaBytes.Length, expressiondata, 14);
            System.Array.Copy(formulaBytes, 0, expressiondata, 16,
                             formulaBytes.Length);

            // Set the recalculate on load bit
            expressiondata[8] |= 0x02;

            byte[] data = new byte[celldata.Length +
                                   expressiondata.Length];
            System.Array.Copy(celldata, 0, data, 0, celldata.Length);
            System.Array.Copy(expressiondata, 0, data,
                             celldata.Length, expressiondata.Length);

            // Set the type bits to indicate a string formula
            data[6] = 0;
            unchecked
                {
                data[12] = (byte)-1;
                data[13] = (byte)-1;
                }

            return data;
        }
        /**
         * Called when a column is inserted on the specified sheet.  Notifies all
         * RCIR cells of this change. The default implementation here does nothing
         *
         * @param s the sheet on which the column was inserted
         * @param sheetIndex the sheet index on which the column was inserted
         * @param col the column number which was inserted
         */
        public override void columnInserted(Sheet s, int sheetIndex, int col)
        {
            try
                {
                if (parser == null)
                    {
                    byte[] formulaData = formula.getFormulaData();
                    byte[] formulaBytes = new byte[formulaData.Length - 16];
                    System.Array.Copy(formulaData, 16,
                                     formulaBytes, 0, formulaBytes.Length);
                    parser = new FormulaParser(formulaBytes,
                                               this,
                                               getSheet().getWorkbook(),
                                               getSheet().getWorkbook(),
                                               getSheet().getWorkbookSettings());
                    parser.parse();
                    }

                parser.columnInserted(sheetIndex, col, s == getSheet());
                }
            catch (FormulaException e)
                {
                //logger.warn("cannot insert column within formula:  " + e.Message);
                }
        }
        /**
         * If this formula was on an imported sheet, check that
         * cell references to another sheet are warned appropriately
         *
         * @return TRUE if this formula was able to be imported, FALSE otherwise
         */
        public bool handleImportedCellReferences(ExternalSheet es,
            WorkbookMethods mt,
            WorkbookSettings ws)
        {
            try
                {
                if (parser == null)
                    {
                    byte[] formulaData = formula.getFormulaData();
                    byte[] formulaBytes = new byte[formulaData.Length - 16];
                    System.Array.Copy(formulaData, 16,
                                     formulaBytes, 0, formulaBytes.Length);
                    parser = new FormulaParser(formulaBytes,
                                               this,
                                               es, mt, ws);
                    parser.parse();
                    }

                return parser.handleImportedCellReferences();
                }
            catch (FormulaException e)
                {
                //logger.warn("cannot import formula:  " + e.Message);
                return false;
                }
        }
        /**
         * Gets the raw bytes for the formula.  This will include the
         * parsed tokens array.  Used when copying spreadsheets
         *
         * @return the raw record data
         * @exception FormulaException
         */
        public override byte[] getFormulaData()
        {
            if (!getSheet().getWorkbookBof().isBiff8())
                {
                throw new FormulaException(FormulaException.BIFF8_SUPPORTED);
                }

            // Get the tokens, taking into account the mapping from shared
            // formula specific values into normal values
            FormulaParser fp = new FormulaParser
              (getTokens(),this,
               getExternalSheet(),getNameTable(),
               getSheet().getWorkbook().getSettings());
            fp.parse();
            byte[] rpnTokens = fp.getBytes();

            byte[] data = new byte[rpnTokens.Length + 22];

            // Set the standard info for this cell
            IntegerHelper.getTwoBytes(getRow(),data,0);
            IntegerHelper.getTwoBytes(getColumn(),data,2);
            IntegerHelper.getTwoBytes(getXFIndex(),data,4);

            // Set the two most significant bytes of the value to be 0xff in
            // order to identify this as a string
            data[6] = 0;
            data[12] = (byte)0xff;
            data[13] = (byte)0xff;

            // Now copy in the parsed tokens
            System.Array.Copy(rpnTokens,0,data,22,rpnTokens.Length);
            IntegerHelper.getTwoBytes(rpnTokens.Length,data,20);

            // Lop off the standard information
            byte[] d = new byte[data.Length - 6];
            System.Array.Copy(data,6,d,0,data.Length - 6);

            return d;
        }
        /**
         * Gets the formula as an excel string
         *
         * @return the formula as an excel string
         * @exception FormulaException
         */
        public string getFormula()
        {
            if (formulaString == null)
                {
                byte[] tokens = new byte[data.Length - 22];
                System.Array.Copy(data,22,tokens,0,tokens.Length);
                FormulaParser fp = new FormulaParser
                  (tokens,this,externalSheet,nameTable,
                   getSheet().getWorkbook().getSettings());
                fp.parse();
                formulaString = fp.getFormula();
                }

            return formulaString;
        }
        /**
         * Error formula specific exception handling.  Can't really create
         * a formula (as it will look for a cell of that name, so just
         * create a STRING record containing the contents
         *
         * @return the bodged data
         */
        public override byte[] handleFormulaException()
        {
            byte[] expressiondata = null;
            byte[] celldata = base.getCellData();

            // Generate an appropriate dummy formula
            WritableWorkbookImpl w = getSheet().getWorkbook();
            FormulaParser parser = new FormulaParser(getValue().ToString(), w, w, w.getSettings());

            // Get the bytes for the dummy formula
            try
                {
                parser.parse();
                }
            catch (FormulaException e2)
                {
                //logger.warn(e2.Message);
                }
            byte[] formulaBytes = parser.getBytes();
            expressiondata = new byte[formulaBytes.Length + 16];
            IntegerHelper.getTwoBytes(formulaBytes.Length, expressiondata, 14);
            System.Array.Copy(formulaBytes, 0, expressiondata, 16,
                             formulaBytes.Length);

            // Set the recalculate on load bit
            expressiondata[8] |= 0x02;

            byte[] data = new byte[celldata.Length +
                                   expressiondata.Length];
            System.Array.Copy(celldata, 0, data, 0, celldata.Length);
            System.Array.Copy(expressiondata, 0, data,
                             celldata.Length, expressiondata.Length);

            // Store the value in the formula
            DoubleHelper.getIEEEBytes(getValue(), data, 6);

            return data;
        }
        /**
         * Error formula specific exception handling.  Can't really create
         * a formula (as it will look for a cell of that name, so just
         * create a STRING record containing the contents
         *
         * @return the bodged data
         */
        public override byte[] handleFormulaException()
        {
            byte[] expressiondata = null;
            byte[] celldata = base.getCellData();

            int errorCode = getErrorCode();
            string formulaString = null;

            if (errorCode == FormulaErrorCode.DIV0.getCode())
                formulaString = "1/0";
            else if (errorCode == FormulaErrorCode.VALUE.getCode())
                formulaString = "\"\"/0";
            else if (errorCode == FormulaErrorCode.REF.getCode())
                formulaString = "\"#REF!\"";
            else
                formulaString = "\"ERROR\"";

            // Generate an appropriate dummy formula
            WritableWorkbookImpl w = getSheet().getWorkbook();
            FormulaParser parser = new FormulaParser(formulaString, w, w,
                                                     w.getSettings());

            // Get the bytes for the dummy formula
            try
                {
                parser.parse();
                }
            catch (FormulaException e2)
                {
                //logger.warn(e2.Message);
                }

            byte[] formulaBytes = parser.getBytes();
            expressiondata = new byte[formulaBytes.Length + 16];
            IntegerHelper.getTwoBytes(formulaBytes.Length, expressiondata, 14);
            System.Array.Copy(formulaBytes, 0, expressiondata, 16,formulaBytes.Length);

            // Set the recalculate on load bit
            expressiondata[8] |= 0x02;

            byte[] data = new byte[celldata.Length + expressiondata.Length];
            System.Array.Copy(celldata, 0, data, 0, celldata.Length);
            System.Array.Copy(expressiondata, 0, data,celldata.Length, expressiondata.Length);

            // Set the type bits to indicate an error
            data[6] = 2;
            data[12] = (byte)0xff;
            data[13] = (byte)0xff;

            // Set the error code
            data[8] = (byte)errorCode;

            return data;
        }
        /**
         * Called by the cell value when the cell features are added to the sheet
         */
        public void setCell(int col,
            int row,
            ExternalSheet es,
            WorkbookMethods nt,
            WorkbookSettings ws)
        {
            // If this is part of an extended cells validation, then do nothing
            // as this will already have been called and parsed when the top left
            // cell was added
            if (hasExtendedCellsValidation)
                {
                return;
                }

            row1 = row;
            row2 = row;
            column1 = col;
            column2 = col;

            formula1 = new FormulaParser(formula1String,
                                         es,nt,ws,
                                         ParseContext.DATA_VALIDATION);
            formula1.parse();

            if (formula2String != null)
                {
                formula2 = new FormulaParser(formula2String,
                                             es,nt,ws,
                                             ParseContext.DATA_VALIDATION);
                formula2.parse();
                }
        }
        /**
         * Constructor
         */
        public DVParser(byte[] data,
            ExternalSheet es,
            WorkbookMethods nt,
            WorkbookSettings ws)
        {
            Assert.verify(nt != null);

            wasCopied = false;
            int options = IntegerHelper.getInt(data[0],data[1],data[2],data[3]);

            int typeVal = options & 0xf;
            type = DVType.getType(typeVal);

            int errorStyleVal = (options & 0x70) >> 4;
            errorStyle = ErrorStyle.getErrorStyle(errorStyleVal);

            int conditionVal = (options & 0xf00000) >> 20;
            condition = Condition.getCondition(conditionVal);

            stringListGiven = (options & STRING_LIST_GIVEN_MASK) != 0;
            emptyCellsAllowed = (options & EMPTY_CELLS_ALLOWED_MASK) != 0;
            suppressArrow = (options & SUPPRESS_ARROW_MASK) != 0;
            showPrompt = (options & SHOW_PROMPT_MASK) != 0;
            showError = (options & SHOW_ERROR_MASK) != 0;

            int pos = 4;
            int length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            if (length > 0 && data[pos + 2] == 0)
                {
                promptTitle = StringHelper.getString(data,length,pos + 3,ws);
                pos += length + 3;
                }
            else if (length > 0)
                {
                promptTitle = StringHelper.getUnicodeString(data,length,pos + 3);
                pos += length * 2 + 3;
                }
            else
                {
                pos += 3;
                }

            length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            if (length > 0 && data[pos + 2] == 0)
                {
                errorTitle = StringHelper.getString(data,length,pos + 3,ws);
                pos += length + 3;
                }
            else if (length > 0)
                {
                errorTitle = StringHelper.getUnicodeString(data,length,pos + 3);
                pos += length * 2 + 3;
                }
            else
                {
                pos += 3;
                }

            length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            if (length > 0 && data[pos + 2] == 0)
                {
                promptText = StringHelper.getString(data,length,pos + 3,ws);
                pos += length + 3;
                }
            else if (length > 0)
                {
                promptText = StringHelper.getUnicodeString(data,length,pos + 3);
                pos += length * 2 + 3;
                }
            else
                {
                pos += 3;
                }

            length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            if (length > 0 && data[pos + 2] == 0)
                {
                errorText = StringHelper.getString(data,length,pos + 3,ws);
                pos += length + 3;
                }
            else if (length > 0)
                {
                errorText = StringHelper.getUnicodeString(data,length,pos + 3);
                pos += length * 2 + 3;
                }
            else
                {
                pos += 3;
                }

            int formula1Length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 4;
            int formula1Pos = pos;
            pos += formula1Length;

            int formula2Length = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 4;
            int formula2Pos = pos;
            pos += formula2Length;

            pos += 2;

            row1 = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 2;

            row2 = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 2;

            column1 = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 2;

            column2 = IntegerHelper.getInt(data[pos],data[pos + 1]);
            pos += 2;

            hasExtendedCellsValidation = (row1 == row2 && column1 == column2) ? false : true;

            // Do the formulas
            try
                {
                // First, create a temporary  blank cell for any formula relative
                // references
                EmptyCell tmprt = new EmptyCell(column1,row1);

                if (formula1Length != 0)
                    {
                    byte[] tokens = new byte[formula1Length];
                    System.Array.Copy(data,formula1Pos,tokens,0,formula1Length);
                    formula1 = new FormulaParser(tokens,tmprt,es,nt,ws,
                                                 ParseContext.DATA_VALIDATION);
                    formula1.parse();
                    }

                if (formula2Length != 0)
                    {
                    byte[] tokens = new byte[formula2Length];
                    System.Array.Copy(data,formula2Pos,tokens,0,formula2Length);
                    formula2 = new FormulaParser(tokens,tmprt,es,nt,ws,
                                                 ParseContext.DATA_VALIDATION);
                    formula2.parse();
                    }
                }
            catch (FormulaException e)
                {
                //logger.warn(e.Message + " for cells " +
                //    CellReferenceHelper.getCellReference(column1,row1) + "-" +
                //    CellReferenceHelper.getCellReference(column2,row2));
                }
        }