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