1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   /*
57    * FormulaRecord.java
58    *
59    * Created on October 28, 2001, 5:44 PM
60    */
61   package org.apache.poi.hssf.record;
62   
63   import java.util.Stack;
64   import java.util.List;
65   
66   import org.apache.poi.util.LittleEndian;
67   import org.apache.poi.hssf.record.formula.*;
68   
69   /**
70    * Formula Record - This is not really supported in this release.  Its here for future use.<P>
71    * REFERENCE:  PG 317/444 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
72    * @author Andrew C. Oliver (acoliver at apache dot org)
73    * @version 2.0-pre
74    */
75   
76   public class FormulaRecord
77       extends Record
78       implements CellValueRecordInterface, Comparable
79   {
80       
81       public static final boolean EXPERIMENTAL_FORMULA_SUPPORT_ENABLED=false;
82       
83       public static final short sid =
84           0x06;   // docs say 406...because of a bug Microsoft support site article #Q184647)
85       
86       private short             field_1_row;
87       private short             field_2_column;
88       private short             field_3_xf;
89       private double            field_4_value;
90       private short             field_5_options;
91       private int               field_6_zero;
92       private short             field_7_expression_len;
93       private Stack             field_8_parsed_expr;
94       
95       private byte[]            all_data; //if formula support is not enabled then
96                                           //we'll just store/reserialize
97   
98       /** Creates new FormulaRecord */
99   
100      public FormulaRecord()
101      {
102          field_8_parsed_expr = new Stack();
103      }
104  
105      /**
106       * Constructs a Formula record and sets its fields appropriately.
107       *
108       * @param id     id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an "explanation of
109       * this bug in the documentation) or an exception will be throw upon validation
110       * @param size  the size of the data area of the record
111       * @param data  data of the record (should not contain sid/len)
112       */
113  
114      public FormulaRecord(short id, short size, byte [] data)
115      {
116          super(id, size, data);
117      }
118  
119      /**
120       * Constructs a Formula record and sets its fields appropriately.
121       *
122       * @param id     id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an "explanation of
123       * this bug in the documentation) or an exception will be throw upon validation
124       * @param size  the size of the data area of the record
125       * @param data  data of the record (should not contain sid/len)
126       * @param offset of the record's data
127       */
128  
129      public FormulaRecord(short id, short size, byte [] data, int offset)
130      {
131          super(id, size, data, offset);
132      }
133  
134      protected void fillFields(byte [] data, short size, int offset)
135      {
136          if (EXPERIMENTAL_FORMULA_SUPPORT_ENABLED) {
137          field_1_row            = LittleEndian.getShort(data, 0 + offset);
138          field_2_column         = LittleEndian.getShort(data, 2 + offset);
139          field_3_xf             = LittleEndian.getShort(data, 4 + offset);
140          field_4_value          = LittleEndian.getDouble(data, 6 + offset);
141          field_5_options        = LittleEndian.getShort(data, 14 + offset);
142          field_6_zero           = LittleEndian.getInt(data, 16 + offset);
143          field_7_expression_len = LittleEndian.getShort(data, 20 + offset);
144          field_8_parsed_expr    = getParsedExpressionTokens(data, size,
145                                   offset);
146          
147          } else {
148              all_data = new byte[size+4];
149              LittleEndian.putShort(all_data,0,sid);
150              LittleEndian.putShort(all_data,2,size);
151              System.arraycopy(data,offset,all_data,4,size);
152          }
153  
154      }
155  
156      private Stack getParsedExpressionTokens(byte [] data, short size,
157                                              int offset)
158      {
159          Stack stack = new Stack();
160          /*int   pos   = 22 + offset;
161  
162          while (pos < size)
163          {
164              Ptg ptg = Ptg.createPtg(data, pos);
165  
166              pos += ptg.getSize();
167              stack.push(ptg);
168          }*/
169          return stack;
170      }
171  
172      public void setRow(short row)
173      {
174          field_1_row = row;
175      }
176  
177      public void setColumn(short column)
178      {
179          field_2_column = column;
180      }
181  
182      public void setXFIndex(short xf)
183      {
184          field_3_xf = xf;
185      }
186  
187      /**
188       * set the calculated value of the formula
189       *
190       * @param value  calculated value
191       */
192  
193      public void setValue(double value)
194      {
195          field_4_value = value;
196      }
197  
198      /**
199       * set the option flags
200       *
201       * @param options  bitmask
202       */
203  
204      public void setOptions(short options)
205      {
206          field_5_options = options;
207      }
208  
209      /**
210       * set the length (in number of tokens) of the expression
211       * @param len  length
212       */
213  
214      public void setExpressionLength(short len)
215      {
216          field_7_expression_len = len;
217      }
218  
219      public short getRow()
220      {
221          return field_1_row;
222      }
223  
224      public short getColumn()
225      {
226          return field_2_column;
227      }
228  
229      public short getXFIndex()
230      {
231          return field_3_xf;
232      }
233  
234      /**
235       * get the calculated value of the formula
236       *
237       * @return calculated value
238       */
239  
240      public double getValue()
241      {
242          return field_4_value;
243      }
244  
245      /**
246       * get the option flags
247       *
248       * @return bitmask
249       */
250  
251      public short getOptions()
252      {
253          return field_5_options;
254      }
255  
256      /**
257       * get the length (in number of tokens) of the expression
258       * @return  expression length
259       */
260  
261      public short getExpressionLength()
262      {
263          return field_7_expression_len;
264      }
265  
266      /**
267       * push a token onto the stack
268       *
269       * @param ptg  the token
270       */
271  
272      public void pushExpressionToken(Ptg ptg)
273      {
274          field_8_parsed_expr.push(ptg);
275      }
276  
277      /**
278       * pop a token off of the stack
279       *
280       * @return Ptg - the token
281       */
282  
283      public Ptg popExpressionToken()
284      {
285          return ( Ptg ) field_8_parsed_expr.pop();
286      }
287  
288      /**
289       * peek at the token on the top of stack
290       *
291       * @return Ptg - the token
292       */
293  
294      public Ptg peekExpressionToken()
295      {
296          return ( Ptg ) field_8_parsed_expr.peek();
297      }
298  
299      /**
300       * get the size of the stack
301       * @return size of the stack
302       */
303  
304      public int getNumberOfExpressionTokens()
305      {
306          return field_8_parsed_expr.size();
307      }
308  
309      /**
310       * get the stack as a list
311       *
312       * @return list of tokens (casts stack to a list and returns it!)
313       */
314  
315      public List getParsedExpression()
316      {
317          return ( List ) field_8_parsed_expr;
318      }
319  
320      /**
321       * called by constructor, should throw runtime exception in the event of a
322       * record passed with a differing ID.
323       *
324       * @param id alleged id for this record
325       */
326  
327      protected void validateSid(short id)
328      {
329          if (id != sid)
330          {
331              throw new RecordFormatException("NOT A FORMULA RECORD");
332          }
333      }
334  
335      public short getSid()
336      {
337          return sid;
338      }
339  
340      /**
341       * called by the class that is responsible for writing this sucker.
342       * Subclasses should implement this so that their data is passed back in a
343       * byte array.
344       *
345       * @return byte array containing instance data
346       */
347  
348      public int serialize(int offset, byte [] data)
349      {
350          if (EXPERIMENTAL_FORMULA_SUPPORT_ENABLED) {
351          int ptgSize = getTotalPtgSize();
352  
353          LittleEndian.putShort(data, 0 + offset, sid);
354          LittleEndian.putShort(data, 2 + offset, ( short ) (24 + ptgSize));
355          LittleEndian.putShort(data, 4 + offset, getRow());
356          LittleEndian.putShort(data, 6 + offset, getColumn());
357          LittleEndian.putShort(data, 8 + offset, getXFIndex());
358          LittleEndian.putDouble(data, 10 + offset, getValue());
359          LittleEndian.putShort(data, 18 + offset, getOptions());
360          LittleEndian.putInt(data, 20 + offset, field_6_zero);
361          LittleEndian.putShort(data, 24 + offset, getExpressionLength());
362          } else {
363              System.arraycopy(all_data,0,data,offset,all_data.length);
364          }
365  
366          // serializePtgs(data, 26+offset);
367          return getRecordSize();
368      }
369  
370      public int getRecordSize()
371      {
372          int retval =0;
373          
374          if (EXPERIMENTAL_FORMULA_SUPPORT_ENABLED) {
375              retval = getTotalPtgSize() + 28;
376          } else {
377              retval =all_data.length;
378          }
379          return retval;
380  
381          // return getTotalPtgSize() + 28;
382      }
383  
384      private int getTotalPtgSize()
385      {
386          List list   = getParsedExpression();
387          int  retval = 0;
388  
389          for (int k = 0; k < list.size(); k++)
390          {
391              Ptg ptg = ( Ptg ) list.get(k);
392  
393              retval += ptg.getSize();
394          }
395          return retval;
396      }
397  
398      private void serializePtgs(byte [] data, int offset)
399      {
400          int pos = offset;
401  
402          for (int k = 0; k < field_8_parsed_expr.size(); k++)
403          {
404              Ptg ptg = ( Ptg ) field_8_parsed_expr.get(k);
405  
406              ptg.writeBytes(data, pos);
407              pos += ptg.getSize();
408          }
409      }
410  
411      public boolean isBefore(CellValueRecordInterface i)
412      {
413          if (this.getRow() > i.getRow())
414          {
415              return false;
416          }
417          if ((this.getRow() == i.getRow())
418                  && (this.getColumn() > i.getColumn()))
419          {
420              return false;
421          }
422          if ((this.getRow() == i.getRow())
423                  && (this.getColumn() == i.getColumn()))
424          {
425              return false;
426          }
427          return true;
428      }
429  
430      public boolean isAfter(CellValueRecordInterface i)
431      {
432          if (this.getRow() < i.getRow())
433          {
434              return false;
435          }
436          if ((this.getRow() == i.getRow())
437                  && (this.getColumn() < i.getColumn()))
438          {
439              return false;
440          }
441          if ((this.getRow() == i.getRow())
442                  && (this.getColumn() == i.getColumn()))
443          {
444              return false;
445          }
446          return true;
447      }
448  
449      public boolean isEqual(CellValueRecordInterface i)
450      {
451          return ((this.getRow() == i.getRow())
452                  && (this.getColumn() == i.getColumn()));
453      }
454  
455      public boolean isInValueSection()
456      {
457          return true;
458      }
459  
460      public boolean isValue()
461      {
462          return true;
463      }
464  
465      public int compareTo(Object obj)
466      {
467          CellValueRecordInterface loc = ( CellValueRecordInterface ) obj;
468  
469          if ((this.getRow() == loc.getRow())
470                  && (this.getColumn() == loc.getColumn()))
471          {
472              return 0;
473          }
474          if (this.getRow() < loc.getRow())
475          {
476              return -1;
477          }
478          if (this.getRow() > loc.getRow())
479          {
480              return 1;
481          }
482          if (this.getColumn() < loc.getColumn())
483          {
484              return -1;
485          }
486          if (this.getColumn() > loc.getColumn())
487          {
488              return 1;
489          }
490          return -1;
491      }
492  
493      public boolean equals(Object obj)
494      {
495          if (!(obj instanceof CellValueRecordInterface))
496          {
497              return false;
498          }
499          CellValueRecordInterface loc = ( CellValueRecordInterface ) obj;
500  
501          if ((this.getRow() == loc.getRow())
502                  && (this.getColumn() == loc.getColumn()))
503          {
504              return true;
505          }
506          return false;
507      }
508  }
509