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   package org.apache.poi.hssf.record;
57   
58   import org.apache.poi.util.LittleEndian;
59   import org.apache.poi.hssf.util.RKUtil;
60   
61   /**
62    * Title:        RK Record
63    * Description:  An internal 32 bit number with the two most significant bits
64    *               storing the type.  This is part of a bizarre scheme to save disk
65    *               space and memory (gee look at all the other whole records that
66    *               are in the file just "cause"..,far better to waste processor
67    *               cycles on this then leave on of those "valuable" records out).<P>
68    * We support this in READ-ONLY mode.  HSSF converts these to NUMBER records<P>
69    *
70    *
71    *
72    * REFERENCE:  PG 376 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
73    * @author Andrew C. Oliver (acoliver at apache dot org)
74    * @author Jason Height (jheight at chariot dot net dot au)
75    * @version 2.0-pre
76    * @see org.apache.poi.hssf.record.NumberRecord
77    */
78   
79   public class RKRecord
80       extends Record
81       implements CellValueRecordInterface
82   {
83       public final static short sid                      = 0x27e;
84       public final static short RK_IEEE_NUMBER           = 0;
85       public final static short RK_IEEE_NUMBER_TIMES_100 = 1;
86       public final static short RK_INTEGER               = 2;
87       public final static short RK_INTEGER_TIMES_100     = 3;
88       //private short             field_1_row;
89       private int             field_1_row;
90       private short             field_2_col;
91       private short             field_3_xf_index;
92       private int               field_4_rk_number;
93   
94       public RKRecord()
95       {
96       }
97   
98       /**
99        * Constructs a RK record and sets its fields appropriately.
100       *
101       * @param id     id must be 0x27e or an exception will be throw upon validation
102       * @param size  the size of the data area of the record
103       * @param data  data of the record (should not contain sid/len)
104       */
105  
106      public RKRecord(short id, short size, byte [] data)
107      {
108          super(id, size, data);
109      }
110  
111      /**
112       * Constructs a RK record and sets its fields appropriately.
113       *
114       * @param id     id must be 0x27e or an exception will be throw upon validation
115       * @param size  the size of the data area of the record
116       * @param data  data of the record (should not contain sid/len)
117       * @param offset of the data
118       */
119  
120      public RKRecord(short id, short size, byte [] data, int offset)
121      {
122          super(id, size, data, offset);
123      }
124  
125      protected void validateSid(short id)
126      {
127          if (id != sid)
128          {
129              throw new RecordFormatException("NOT A valid RK RECORD");
130          }
131      }
132  
133      protected void fillFields(byte [] data, short size, int offset)
134      {
135          //field_1_row       = LittleEndian.getShort(data, 0 + offset);
136          field_1_row       = LittleEndian.getUShort(data, 0 + offset);
137          field_2_col       = LittleEndian.getShort(data, 2 + offset);
138          field_3_xf_index  = LittleEndian.getShort(data, 4 + offset);
139          field_4_rk_number = LittleEndian.getInt(data, 6 + offset);
140      }
141  
142      //public short getRow()
143      public int getRow()
144      {
145          return field_1_row;
146      }
147  
148      public short getColumn()
149      {
150          return field_2_col;
151      }
152  
153      public short getXFIndex()
154      {
155          return field_3_xf_index;
156      }
157  
158      public int getRKField()
159      {
160          return field_4_rk_number;
161      }
162  
163      /**
164       * Get the type of the number
165       *
166       * @return one of these values:
167       *         <OL START="0">
168       *             <LI>RK_IEEE_NUMBER</LI>
169       *             <LI>RK_IEEE_NUMBER_TIMES_100</LI>
170       *             <LI>RK_INTEGER</LI>
171       *             <LI>RK_INTEGER_TIMES_100</LI>
172       *         </OL>
173       */
174  
175      public short getRKType()
176      {
177          return ( short ) (field_4_rk_number & 3);
178      }
179  
180      /**
181       * Extract the value of the number
182       * <P>
183       * The mechanism for determining the value is dependent on the two
184       * low order bits of the raw number. If bit 1 is set, the number
185       * is an integer and can be cast directly as a double, otherwise,
186       * it's apparently the exponent and mantissa of a double (and the
187       * remaining low-order bits of the double's mantissa are 0's).
188       * <P>
189       * If bit 0 is set, the result of the conversion to a double is
190       * divided by 100; otherwise, the value is left alone.
191       * <P>
192       * [insert picture of Screwy Squirrel in full Napoleonic regalia]
193       *
194       * @return the value as a proper double (hey, it <B>could</B>
195       *         happen)
196       */
197  
198      public double getRKNumber()
199      {
200          return RKUtil.decodeNumber(field_4_rk_number);
201      }
202  
203  
204      public String toString()
205      {
206          StringBuffer buffer = new StringBuffer();
207  
208          buffer.append("[RK]\n");
209          buffer.append("    .row            = ")
210              .append(Integer.toHexString(getRow())).append("\n");
211          buffer.append("    .col            = ")
212              .append(Integer.toHexString(getColumn())).append("\n");
213          buffer.append("    .xfindex        = ")
214              .append(Integer.toHexString(getXFIndex())).append("\n");
215          buffer.append("    .rknumber       = ")
216              .append(Integer.toHexString(getRKField())).append("\n");
217          buffer.append("        .rktype     = ")
218              .append(Integer.toHexString(getRKType())).append("\n");
219          buffer.append("        .rknumber   = ").append(getRKNumber())
220              .append("\n");
221          buffer.append("[/RK]\n");
222          return buffer.toString();
223      }
224  
225  //temporarily just constructs a new number record and returns its value
226      public int serialize(int offset, byte [] data)
227      {
228          NumberRecord rec = new NumberRecord();
229  
230          rec.setColumn(getColumn());
231          rec.setRow(getRow());
232          rec.setValue(getRKNumber());
233          rec.setXFIndex(getXFIndex());
234          return rec.serialize(offset, data);
235      }
236  
237      /**
238       * Debugging main()
239       * <P>
240       * Normally I'd do this in a junit test, but let's face it -- once
241       * this algorithm has been tested and it works, we are never ever
242       * going to change it. This is driven by the Faceless Enemy's
243       * minions, who dare not change the algorithm out from under us.
244       *
245       * @param ignored_args command line arguments, which we blithely
246       *                     ignore
247       */
248  
249      public static void main(String ignored_args[])
250      {
251          int[]    values  =
252          {
253              0x3FF00000, 0x405EC001, 0x02F1853A, 0x02F1853B, 0xFCDD699A
254          };
255          double[] rvalues =
256          {
257              1, 1.23, 12345678, 123456.78, -13149594
258          };
259  
260          for (int j = 0; j < values.length; j++)
261          {
262              System.out.println("input = " + Integer.toHexString(values[ j ])
263                                 + " -> " + rvalues[ j ] + ": "
264                                 + RKUtil.decodeNumber(values[ j ]));
265          }
266      }
267  
268      public short getSid()
269      {
270          return this.sid;
271      }
272  
273      public boolean isBefore(CellValueRecordInterface i)
274      {
275          if (this.getRow() > i.getRow())
276          {
277              return false;
278          }
279          if ((this.getRow() == i.getRow())
280                  && (this.getColumn() > i.getColumn()))
281          {
282              return false;
283          }
284          if ((this.getRow() == i.getRow())
285                  && (this.getColumn() == i.getColumn()))
286          {
287              return false;
288          }
289          return true;
290      }
291  
292      public boolean isAfter(CellValueRecordInterface i)
293      {
294          if (this.getRow() < i.getRow())
295          {
296              return false;
297          }
298          if ((this.getRow() == i.getRow())
299                  && (this.getColumn() < i.getColumn()))
300          {
301              return false;
302          }
303          if ((this.getRow() == i.getRow())
304                  && (this.getColumn() == i.getColumn()))
305          {
306              return false;
307          }
308          return true;
309      }
310  
311      public boolean isEqual(CellValueRecordInterface i)
312      {
313          return ((this.getRow() == i.getRow())
314                  && (this.getColumn() == i.getColumn()));
315      }
316  
317      public boolean isInValueSection()
318      {
319          return true;
320      }
321  
322      public boolean isValue()
323      {
324          return true;
325      }
326  
327      public void setColumn(short col)
328      {
329      }
330  
331      //public void setRow(short row)
332      public void setRow(int row)
333      {
334      }
335  
336      /**
337       * NO OP!
338       */
339  
340      public void setXFIndex(short xf)
341      {
342      }
343  
344      public Object clone() {
345        RKRecord rec = new RKRecord();
346        rec.field_1_row = field_1_row;
347        rec.field_2_col = field_2_col;
348        rec.field_3_xf_index = field_3_xf_index;
349        rec.field_4_rk_number = field_4_rk_number;
350        return rec;
351      }
352  }
353