]> source.dussan.org Git - poi.git/commitdiff
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArr...
authorJosh Micich <josh@apache.org>
Sun, 5 Oct 2008 04:43:48 +0000 (04:43 +0000)
committerJosh Micich <josh@apache.org>
Sun, 5 Oct 2008 04:43:48 +0000 (04:43 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@701747 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/record/RecordInputStream.java
src/java/org/apache/poi/hssf/record/SeriesListRecord.java
src/java/org/apache/poi/util/LittleEndian.java
src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java

index fe6a4b2ea3b70d01b02901bb3d43acae2276fa78..c23868c711d934bbb084edd7126a9216356d1f80 100755 (executable)
@@ -33,7 +33,7 @@ public class RecordInputStream extends InputStream {
   /** Maximum size of a single record (minus the 4 byte header) without a continue*/
   public final static short MAX_RECORD_DATA_SIZE = 8224;
   private static final int INVALID_SID_VALUE = -1;
-  
+
   private InputStream in;
   protected short currentSid;
   protected short currentLength = -1;
@@ -42,34 +42,34 @@ public class RecordInputStream extends InputStream {
   protected byte[] data = new byte[MAX_RECORD_DATA_SIZE];
   protected short recordOffset;
   protected long pos;
-  
+
   private boolean autoContinue = true;
 
-  public RecordInputStream(InputStream in) throws RecordFormatException  {
+  public RecordInputStream(InputStream in) throws RecordFormatException {
     this.in = in;
     try {
       nextSid = LittleEndian.readShort(in);
-      //Dont increment the pos just yet (technically we are at the start of
-      //the record stream until nextRecord is called).      
+      //Don't increment the pos just yet (technically we are at the start of
+      //the record stream until nextRecord is called).
     } catch (IOException ex) {
       throw new RecordFormatException("Error reading bytes", ex);
     }
   }
-  
-  /** This method will read a byte from the current record*/
-  public int read() {
-    checkRecordPosition();
-
-    byte result = data[recordOffset];
-    recordOffset += 1;
-    pos += 1;
-    return result;    
-  }
-  
+
+       /** This method will read a byte from the current record*/
+       public int read() {
+               checkRecordPosition(LittleEndian.BYTE_SIZE);
+
+               byte result = data[recordOffset];
+               recordOffset += LittleEndian.BYTE_SIZE;
+               pos += LittleEndian.BYTE_SIZE;
+               return result;
+       }
+
   public short getSid() {
     return currentSid;
   }
-  
+
   public short getLength() {
     return currentLength;
   }
@@ -85,12 +85,11 @@ public class RecordInputStream extends InputStream {
   public boolean hasNextRecord() {
     return nextSid != INVALID_SID_VALUE;
   }
-  
+
   /** Moves to the next record in the stream.
-   * 
+   *
    * <i>Note: The auto continue flag is reset to true</i>
    */
-  
   public void nextRecord() throws RecordFormatException {
     if ((currentLength != -1) && (currentLength != recordOffset)) {
       System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid));
@@ -100,7 +99,7 @@ public class RecordInputStream extends InputStream {
     autoContinue = true;
     try {
       recordOffset = 0;
-      currentLength = LittleEndian.readShort(in);     
+      currentLength = LittleEndian.readShort(in);
       if (currentLength > MAX_RECORD_DATA_SIZE)
         throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes");
       pos += LittleEndian.SHORT_SIZE;
@@ -113,138 +112,124 @@ public class RecordInputStream extends InputStream {
               // ex45582-22397.xls has one extra byte after the last record
               // Excel reads that file OK
           }
-          nextSid = INVALID_SID_VALUE;  
+          nextSid = INVALID_SID_VALUE;
       } else {
           nextSid = LittleEndian.readShort(in);
           if (nextSid == INVALID_SID_VALUE) {
               throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x"
                       + Integer.toHexString(currentSid).toUpperCase());
           }
-      }      
+      }
     } catch (IOException ex) {
       throw new RecordFormatException("Error reading bytes", ex);
     }
   }
-  
+
   public void setAutoContinue(boolean enable) {
-    this.autoContinue = enable;    
+    this.autoContinue = enable;
   }
-  
+
   public boolean getAutoContinue() {
     return autoContinue;
   }
-  
-  protected void checkRecordPosition() {
-    if (remaining() <= 0) {
-      if (isContinueNext() && autoContinue) {
-        nextRecord();
-      }
-      else throw new ArrayIndexOutOfBoundsException();
-    }    
-  }
-  
-  /**
-   * Reads an 8 bit, signed value
-   */
-  public byte readByte() {
-    checkRecordPosition();
-    
-    byte result = data[recordOffset];
-    recordOffset += 1;
-    pos += 1;
-    return result;
-  }
-  
-  /**
-   * Reads a 16 bit, signed value
-   */
-  public short readShort() {
-    checkRecordPosition();
-    
-    short result = LittleEndian.getShort(data, recordOffset);
-    recordOffset += LittleEndian.SHORT_SIZE;
-    pos += LittleEndian.SHORT_SIZE;
-    return result;
-  }
 
-  public int readInt() {
-    checkRecordPosition();
-    
-    int result = LittleEndian.getInt(data, recordOffset);
-    recordOffset += LittleEndian.INT_SIZE;
-    pos += LittleEndian.INT_SIZE;
-    return result;
-  }
+       private void checkRecordPosition(int requiredByteCount) {
 
-  public long readLong() {
-    checkRecordPosition();    
-    
-    long result = LittleEndian.getLong(data, recordOffset);
-    recordOffset += LittleEndian.LONG_SIZE;
-    pos += LittleEndian.LONG_SIZE;
-    return result;
-  }
+               if (remaining() < requiredByteCount) {
+                       if (isContinueNext() && autoContinue) {
+                               nextRecord();
+                       } else {
+                          throw new ArrayIndexOutOfBoundsException();
+                       }
+               }
+       }
 
-  /**
-   * Reads an 8 bit, unsigned value
-   */
-  public short readUByte() {
-      short s = readByte();
-      if(s < 0) {
-          s += 256;
-      }
-      return s;
-  }
+       /**
+        * Reads an 8 bit, signed value
+        */
+       public byte readByte() {
+               checkRecordPosition(LittleEndian.BYTE_SIZE);
 
-  /**
-   * Reads a 16 bit,un- signed value.
-   * @return
-   */
-  public int readUShort() {
-    checkRecordPosition();    
-    
-    int result = LittleEndian.getUShort(data, recordOffset);
-    recordOffset += LittleEndian.SHORT_SIZE;
-    pos += LittleEndian.SHORT_SIZE;
-    return result;
-  }
+               byte result = data[recordOffset];
+               recordOffset += LittleEndian.BYTE_SIZE;
+               pos += LittleEndian.BYTE_SIZE;
+               return result;
+       }
 
-  public double readDouble() {
-    checkRecordPosition();
-    long valueLongBits = LittleEndian.getLong(data, recordOffset);
-    double result = Double.longBitsToDouble(valueLongBits);
-    if (Double.isNaN(result)) {
-      throw new RuntimeException("Did not expect to read NaN");
-    }
-    recordOffset += LittleEndian.DOUBLE_SIZE;
-    pos += LittleEndian.DOUBLE_SIZE;
-    return result;
-  }
+       /**
+        * Reads a 16 bit, signed value
+        */
+       public short readShort() {
+               checkRecordPosition(LittleEndian.SHORT_SIZE);
 
-  
-  public short[] readShortArray() {
-    checkRecordPosition();
-    
-    short[] arr = LittleEndian.getShortArray(data, recordOffset);
-    final int size = (2 * (arr.length +1));
-    recordOffset += size;
-    pos += size;
-    
-    return arr;
-  }
-  
-  /**     
-   *  given a byte array of 16-bit unicode characters, compress to 8-bit and     
-   *  return a string     
-   *     
-   * { 0x16, 0x00 } -0x16     
-   *      
+               short result = LittleEndian.getShort(data, recordOffset);
+               recordOffset += LittleEndian.SHORT_SIZE;
+               pos += LittleEndian.SHORT_SIZE;
+               return result;
+       }
+
+       public int readInt() {
+               checkRecordPosition(LittleEndian.INT_SIZE);
+
+               int result = LittleEndian.getInt(data, recordOffset);
+               recordOffset += LittleEndian.INT_SIZE;
+               pos += LittleEndian.INT_SIZE;
+               return result;
+       }
+
+       public long readLong() {
+               checkRecordPosition(LittleEndian.LONG_SIZE);
+
+               long result = LittleEndian.getLong(data, recordOffset);
+               recordOffset += LittleEndian.LONG_SIZE;
+               pos += LittleEndian.LONG_SIZE;
+               return result;
+       }
+
+       /**
+        * Reads an 8 bit, unsigned value
+        */
+       public short readUByte() {
+               return (short) (readByte() & 0x00FF);
+       }
+
+       /**
+        * Reads a 16 bit, unsigned value.
+        * @return
+        */
+       public int readUShort() {
+               checkRecordPosition(LittleEndian.SHORT_SIZE);
+
+               int result = LittleEndian.getUShort(data, recordOffset);
+               recordOffset += LittleEndian.SHORT_SIZE;
+               pos += LittleEndian.SHORT_SIZE;
+               return result;
+       }
+
+       public double readDouble() {
+               checkRecordPosition(LittleEndian.DOUBLE_SIZE);
+               long valueLongBits = LittleEndian.getLong(data, recordOffset);
+               double result = Double.longBitsToDouble(valueLongBits);
+               if (Double.isNaN(result)) {
+                       throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN
+               }
+               recordOffset += LittleEndian.DOUBLE_SIZE;
+               pos += LittleEndian.DOUBLE_SIZE;
+               return result;
+       }
+
+  /**
+   *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+   *  return a string
+   *
+   * { 0x16, 0x00 } -0x16
+   *
    * @param length the length of the final string
    * @return                                     the converted string
    * @exception  IllegalArgumentException        if len is too large (i.e.,
-   *      there is not enough data in string to create a String of that     
-   *      length)     
-   */  
+   *      there is not enough data in string to create a String of that
+   *      length)
+   */
   public String readUnicodeLEString(int length) {
     if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
@@ -258,11 +243,11 @@ public class RecordInputStream extends InputStream {
         if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
       }
       char ch = (char)readShort();
-      buf.append(ch); 
+      buf.append(ch);
     }
     return buf.toString();
   }
-    
+
   public String readCompressedUnicode(int length) {
     if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length " + length);
@@ -277,23 +262,23 @@ public class RecordInputStream extends InputStream {
       }
       byte b = readByte();
       char ch = (char)(0x00FF & b); // avoid sex
-      buf.append(ch); 
+      buf.append(ch);
     }
-    return buf.toString();    
+    return buf.toString();
   }
-  
+
   /** Returns an excel style unicode string from the bytes reminaing in the record.
    * <i>Note:</i> Unicode strings differ from <b>normal</b> strings due to the addition of
    * formatting information.
-   * 
+   *
    * @return The unicode string representation of the remaining bytes.
    */
   public UnicodeString readUnicodeString() {
     return new UnicodeString(this);
   }
-  
+
   /** Returns the remaining bytes for the current record.
-   * 
+   *
    * @return The remaining bytes of the current record.
    */
   public byte[] readRemainder() {
@@ -304,39 +289,39 @@ public class RecordInputStream extends InputStream {
     pos += size;
     return result;
   }
-  
+
   /** Reads all byte data for the current record, including any
    *  that overlaps into any following continue records.
-   * 
+   *
    *  @deprecated Best to write a input stream that wraps this one where there is
    *  special sub record that may overlap continue records.
-   */  
+   */
   public byte[] readAllContinuedRemainder() {
     //Using a ByteArrayOutputStream is just an easy way to get a
     //growable array of the data.
     ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE);
 
     while (isContinueNext()) {
-      byte[] b = readRemainder();      
+      byte[] b = readRemainder();
       out.write(b, 0, b.length);
       nextRecord();
     }
-    byte[] b = readRemainder();      
-    out.write(b, 0, b.length);    
-    
+    byte[] b = readRemainder();
+    out.write(b, 0, b.length);
+
     return out.toByteArray();
   }
 
   /** The remaining number of bytes in the <i>current</i> record.
-   * 
+   *
    * @return The number of bytes remaining in the current record
    */
   public int remaining() {
     return (currentLength - recordOffset);
   }
 
-  /** Returns true iif a Continue record is next in the excel stream 
-   * 
+  /** Returns true iif a Continue record is next in the excel stream
+   *
    * @return True when a ContinueRecord is next.
    */
   public boolean isContinueNext() {
index 2894f158777a77d0108902aac251bcda82a9942f..4753eae05f3dbd4232c9820fa8c4fbe7cba44e36 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.record;
 
-
-
-import org.apache.poi.util.*;
+import org.apache.poi.util.LittleEndian;
 
 /**
- * The series list record defines the series displayed as an overlay to the main chart record.
- * NOTE: This source is automatically generated please do not modify this file.  Either subclass or
- *       remove the record in src/records/definitions.
-
+ * 
+ * The series list record defines the series displayed as an overlay to the main chart record.<br/>
+ * TODO - does this record (0x1016) really exist.  It doesn't seem to be referenced in either the OOO or MS doc
+ * 
  * @author Glen Stampoultzis (glens at apache.org)
  */
-public class SeriesListRecord
-    extends Record
-{
-    public final static short      sid                             = 0x1016;
+public final class SeriesListRecord extends Record {
+    public final static short sid = 0x1016;
     private  short[]    field_1_seriesNumbers;
 
-
-    public SeriesListRecord()
-    {
-
+    public SeriesListRecord(short[] seriesNumbers) {
+       field_1_seriesNumbers = seriesNumbers;
     }
 
-    public SeriesListRecord(RecordInputStream in)
-    {
-        field_1_seriesNumbers          = in.readShortArray();
+    public SeriesListRecord(RecordInputStream in) {
+       int nItems = in.readUShort();
+       short[] ss = new short[nItems];
+       for (int i = 0; i < nItems; i++) {
+                       ss[i] = in.readShort();
+                       
+               }
+        field_1_seriesNumbers = ss;
     }
 
-    public String toString()
-    {
+    public String toString() {
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[SERIESLIST]\n");
-        buffer.append("    .seriesNumbers        = ")
-            .append(" (").append( getSeriesNumbers() ).append(" )");
-        buffer.append(System.getProperty("line.separator")); 
+        buffer.append("    .seriesNumbers= ").append(" (").append( getSeriesNumbers() ).append(" )");
+        buffer.append("\n"); 
 
         buffer.append("[/SERIESLIST]\n");
         return buffer.toString();
     }
 
-    public int serialize(int offset, byte[] data)
-    {
-        int pos = 0;
+    public int serialize(int offset, byte[] data) {
 
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+        int nItems = field_1_seriesNumbers.length;
+        int dataSize = 2 + 2 * nItems;
+       
+        LittleEndian.putUShort(data, 0 + offset, sid);
+        LittleEndian.putUShort(data, 2 + offset, dataSize);
 
-        LittleEndian.putShortArray(data, 4 + offset + pos, field_1_seriesNumbers);
+        LittleEndian.putUShort(data, 4 + offset, nItems);
+        
+        int pos = offset + 6;
+       for (int i = 0; i < nItems; i++) {
+               LittleEndian.putUShort(data, pos, field_1_seriesNumbers[i]);
+               pos += 2;
+       }
 
-        return getRecordSize();
+        return 4 + dataSize;
     }
 
-    /**
-     * Size of record (exluding 4 byte header)
-     */
     public int getRecordSize()
     {
         return 4  + field_1_seriesNumbers.length * 2 + 2;
@@ -86,34 +85,23 @@ public class SeriesListRecord
     }
 
     public Object clone() {
-        SeriesListRecord rec = new SeriesListRecord();
-    
-        rec.field_1_seriesNumbers = field_1_seriesNumbers;
-        return rec;
+        return new SeriesListRecord((short[]) field_1_seriesNumbers.clone());
     }
 
-
-
-
     /**
      * Get the series numbers field for the SeriesList record.
      */
-    public short[] getSeriesNumbers()
-    {
+    public short[] getSeriesNumbers() {
         return field_1_seriesNumbers;
     }
 
     /**
      * Set the series numbers field for the SeriesList record.
      */
-    public void setSeriesNumbers(short[] field_1_seriesNumbers)
-    {
+    public void setSeriesNumbers(short[] field_1_seriesNumbers) {
         this.field_1_seriesNumbers = field_1_seriesNumbers;
     }
-
-
-}  // END OF CLASS
-
+}
 
 
 
index 6838eb2aa45ccbd689c173414dde920cadd975fd..373710b47a6609ed49243186fe789ee66225b108 100644 (file)
@@ -250,20 +250,6 @@ public final class LittleEndian implements LittleEndianConsts {
         putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
     }
 
-    /**
-     *  put a array of shorts into a byte array
-     *
-     *@param  data    the byte array
-     *@param  offset  a starting offset into the byte array
-     *@param  value   the short array
-     */
-    public static void putShortArray(final byte[] data, final int offset, final short[] value) {
-        putNumber(data, offset, value.length, SHORT_SIZE);
-        for (int i = 0; i < value.length; i++) {
-            putNumber(data, offset + 2 + (i * 2), value[i], SHORT_SIZE);
-        }
-    }
-
     /**
      * put an unsigned short value into a byte array
      *
index 7e81f2928061e3e854406ebf4c109e3295aa331f..027b6c3867d49c37835def05d6e705f5b93d9a34 100644 (file)
@@ -29,7 +29,7 @@ import junit.framework.TestCase;
  * @author Glen Stampoultzis (glens at apache.org)
  */
 public final class TestSeriesListRecord extends TestCase {
-    byte[] data = new byte[] {
+    private static final byte[] data = {
         (byte)0x02,(byte)0x00,(byte)0x01,(byte)0x20,(byte)0xff,(byte)0xf0
     };
 
@@ -43,10 +43,8 @@ public final class TestSeriesListRecord extends TestCase {
         assertEquals( 4 + 6, record.getRecordSize() );
     }
 
-    public void testStore()
-    {
-        SeriesListRecord record = new SeriesListRecord();
-        record.setSeriesNumbers( new short[] { (short)0x2001, (short)0xf0ff } );
+    public void testStore() {
+        SeriesListRecord record = new SeriesListRecord(new short[] { (short)0x2001, (short)0xf0ff } );
 
         byte [] recordBytes = record.serialize();
         assertEquals(recordBytes.length - 4, data.length);