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