1    /* ====================================================================
2     * The Apache Software License, Version 1.1
3     *
4     * Copyright (c) 2002 The Apache Software Foundation.  All rights
5     * reserved.
6     *
7     * Redistribution and use in source and binary forms, with or without
8     * modification, are permitted provided that the following conditions
9     * are met:
10    *
11    * 1. Redistributions of source code must retain the above copyright
12    *    notice, this list of conditions and the following disclaimer.
13    *
14    * 2. Redistributions in binary form must reproduce the above copyright
15    *    notice, this list of conditions and the following disclaimer in
16    *    the documentation and/or other materials provided with the
17    *    distribution.
18    *
19    * 3. The end-user documentation included with the redistribution,
20    *    if any, must include the following acknowledgment:
21    *       "This product includes software developed by the
22    *        Apache Software Foundation (http://www.apache.org/)."
23    *    Alternately, this acknowledgment may appear in the software itself,
24    *    if and wherever such third-party acknowledgments normally appear.
25    *
26    * 4. The names "Apache" and "Apache Software Foundation" and
27    *    "Apache POI" must not be used to endorse or promote products
28    *    derived from this software without prior written permission. For
29    *    written permission, please contact apache@apache.org.
30    *
31    * 5. Products derived from this software may not be called "Apache",
32    *    "Apache POI", nor may "Apache" appear in their name, without
33    *    prior written permission of the Apache Software Foundation.
34    *
35    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46    * SUCH DAMAGE.
47    * ====================================================================
48    *
49    * This software consists of voluntary contributions made by many
50    * individuals on behalf of the Apache Software Foundation.  For more
51    * information on the Apache Software Foundation, please see
52    * <http://www.apache.org/>.
53    */
54   
55   package org.apache.poi.hssf.record;
56   
57   import org.apache.poi.util.*;
58   
59   import java.io.IOException;
60   
61   /**
62    * Supports the STRING record structure.
63    *
64    * @author Glen Stampoultzis (glens at apache.org)
65    */
66   public class StringRecord
67           extends Record
68   {
69       public final static short   sid = 0x207;
70       private int                 field_1_string_length;
71       private byte                field_2_unicode_flag;
72       private String              field_3_string;
73   
74   
75       public StringRecord()
76       {
77       }
78   
79       /**
80        * Constructs a String record and sets its fields appropriately.
81        *
82        * @param id     id must be 0x204 or an exception will be throw upon validation
83        * @param size  the size of the data area of the record
84        * @param data  data of the record (should not contain sid/len)
85        */
86       public StringRecord( short id, short size, byte[] data )
87       {
88           super( id, size, data );
89       }
90   
91       /**
92        * Constructs an String record and sets its fields appropriately.
93        *
94        * @param id     id must be 0x204 or an exception will be throw upon validation
95        * @param size  the size of the data area of the record
96        * @param data  data of the record (should not contain sid/len)
97        * @param offset of the record
98        */
99       public StringRecord( short id, short size, byte[] data, int offset )
100      {
101          super( id, size, data, offset );
102      }
103  
104  
105      /**
106       * Throw a runtime exception in the event of a
107       * record passed with a differing ID.
108       *
109       * @param id alleged id for this record
110       */
111      protected void validateSid( short id )
112      {
113          if (id != this.sid)
114          {
115              throw new RecordFormatException("Not a valid StringRecord");
116          }
117      }
118  
119      /**
120       * called by the constructor, should set class level fields.  Should throw
121       * runtime exception for bad/icomplete data.
122       *
123       * @param data raw data
124       * @param size size of data
125       * @param offset of the record's data (provided a big array of the file)
126       */
127      protected void fillFields( byte[] data, short size, int offset )
128      {
129          field_1_string_length           = LittleEndian.getUShort(data, 0 + offset);
130          field_2_unicode_flag            = data[ 2 + offset ];
131          if (isUnCompressedUnicode())
132          {
133              field_3_string = StringUtil.getFromUnicode(data, 3 + offset, field_1_string_length );
134          }
135          else
136          {
137              field_3_string = new String(data, 3 + offset, field_1_string_length);
138          }
139      }
140  
141      private int getStringLength()
142      {
143          return field_1_string_length;
144      }
145  
146      private int getStringByteLength()
147      {
148          return isUnCompressedUnicode() ? field_1_string_length * 2 : field_1_string_length;
149      }
150  
151      /**
152       * gives the current serialized size of the record. Should include the sid and reclength (4 bytes).
153       */
154      public int getRecordSize()
155      {
156          return 4 + 2 + 1 + getStringByteLength();
157      }
158  
159      /**
160       * is this uncompressed unicode (16bit)?  Or just 8-bit compressed?
161       * @return isUnicode - True for 16bit- false for 8bit
162       */
163      public boolean isUnCompressedUnicode()
164      {
165          return (field_2_unicode_flag == 1);
166      }
167  
168      /**
169       * called by the class that is responsible for writing this sucker.
170       * Subclasses should implement this so that their data is passed back in a
171       * byte array.
172       *
173       * @param offset to begin writing at
174       * @param data byte array containing instance data
175       * @return number of bytes written
176       */
177      public int serialize( int offset, byte[] data )
178      {
179          LittleEndian.putShort(data, 0 + offset, sid);
180          LittleEndian.putShort(data, 2 + offset, ( short ) (3 + getStringByteLength()));
181          LittleEndian.putUShort(data, 4 + offset, field_1_string_length);
182          data[6 + offset] = field_2_unicode_flag;
183          if (isUnCompressedUnicode())
184          {
185              StringUtil.putUncompressedUnicode(field_3_string, data, 7 + offset);
186          }
187          else
188          {
189              StringUtil.putCompressedUnicode(field_3_string, data, 7 + offset);
190          }
191          return getRecordSize();
192      }
193  
194      /**
195       * return the non static version of the id for this record.
196       */
197      public short getSid()
198      {
199          return sid;
200      }
201  
202      /**
203       * @return The string represented by this record.
204       */
205      public String getString()
206      {
207          return field_3_string;
208      }
209  
210      /**
211       * Sets whether the string is compressed or not
212       * @param unicode_flag   1 = uncompressed, 0 = compressed
213       */
214      public void setCompressedFlag( byte unicode_flag )
215      {
216          this.field_2_unicode_flag = unicode_flag;
217      }
218  
219      /**
220       * Sets the string represented by this record.
221       */
222      public void setString( String string )
223      {
224          this.field_1_string_length = string.length();
225          this.field_3_string = string;
226      }
227  
228  
229  
230      public String toString()
231      {
232          StringBuffer buffer = new StringBuffer();
233  
234          buffer.append("[STRING]\n");
235          buffer.append("    .string            = ")
236              .append(field_3_string).append("\n");
237          buffer.append("[/STRING]\n");
238          return buffer.toString();
239      }
240  
241  }
242