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.LittleEndian;
58   import org.apache.poi.util.StringUtil;
59   
60   /**
61    * Title:        Bound Sheet Record (aka BundleSheet) <P>
62    * Description:  Defines a sheet within a workbook.  Basically stores the sheetname
63    *               and tells where the Beginning of file record is within the HSSF
64    *               file. <P>
65    * REFERENCE:  PG 291 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
66    * @author Andrew C. Oliver (acoliver at apache dot org)
67    * @author Sergei Kozello (sergeikozello at mail.ru)
68    * @version 2.0-pre
69    */
70   
71   public class BoundSheetRecord
72           extends Record
73   {
74       public final static short sid = 0x85;
75       private int field_1_position_of_BOF;
76       private short field_2_option_flags;
77       private byte field_3_sheetname_length;
78       private byte field_4_compressed_unicode_flag;   // not documented
79       private String field_5_sheetname;
80   
81       public BoundSheetRecord()
82       {
83       }
84   
85       /**
86        * Constructs a BoundSheetRecord and sets its fields appropriately
87        *
88        * @param id     id must be 0x85 or an exception will be throw upon validation
89        * @param size  the size of the data area of the record
90        * @param data  data of the record (should not contain sid/len)
91        */
92   
93       public BoundSheetRecord( short id, short size, byte[] data )
94       {
95           super( id, size, data );
96       }
97   
98       /**
99        * Constructs a BoundSheetRecord and sets its fields appropriately
100       *
101       * @param id     id must be 0x85 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       * @param offset of the record's data
105       */
106  
107      public BoundSheetRecord( short id, short size, byte[] data, int offset )
108      {
109          super( id, size, data, offset );
110      }
111  
112      protected void validateSid( short id )
113      {
114          if ( id != sid )
115          {
116              throw new RecordFormatException( "NOT A Bound Sheet RECORD" );
117          }
118      }
119  
120      /**
121       *  UTF8:
122       *	sid + len + bof + flags + len(str) + unicode +   str
123       *	 2  +  2  +  4  +   2   +    1     +    1    + len(str)
124       *
125       * 	UNICODE:
126       *	sid + len + bof + flags + len(str) + unicode +   str
127       *	 2  +  2  +  4  +   2   +    1     +    1    + 2 * len(str)
128       *
129       */
130  
131      protected void fillFields( byte[] data, short size, int offset )
132      {
133          field_1_position_of_BOF = LittleEndian.getInt( data, 0 + offset );	// bof
134          field_2_option_flags = LittleEndian.getShort( data, 4 + offset );	// flags
135          field_3_sheetname_length = data[6 + offset];						// len(str)
136          field_4_compressed_unicode_flag = data[7 + offset];						// unicode
137  
138          int nameLength = LittleEndian.ubyteToInt( field_3_sheetname_length );
139          if ( ( field_4_compressed_unicode_flag & 0x01 ) == 1 )
140          {
141              field_5_sheetname = StringUtil.getFromUnicodeHigh( data, 8 + offset, nameLength );
142          }
143          else
144          {
145              field_5_sheetname = new String( data, 8 + offset, nameLength );
146          }
147      }
148  
149      /**
150       * set the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
151       *
152       * @param pos  offset in bytes
153       */
154  
155      public void setPositionOfBof( int pos )
156      {
157          field_1_position_of_BOF = pos;
158      }
159  
160      /**
161       * set the option flags (unimportant for HSSF supported sheets)
162       *
163       * @param flags to set
164       */
165  
166      public void setOptionFlags( short flags )
167      {
168          field_2_option_flags = flags;
169      }
170  
171      /**
172       * Set the length of the sheetname in characters
173       *
174       * @param len  number of characters in the sheet name
175       * @see #setSheetname(String)
176       */
177  
178      public void setSheetnameLength( byte len )
179      {
180          field_3_sheetname_length = len;
181      }
182  
183      /**
184       * set whether or not to interperate the Sheetname as compressed unicode (8/16 bit)
185       * (This is undocumented but can be found as Q187919 on the Microsoft(tm) Support site)
186       * @param flag (0/1) 0- compressed, 1 - uncompressed (16-bit)
187       */
188  
189      public void setCompressedUnicodeFlag( byte flag )
190      {
191          field_4_compressed_unicode_flag = flag;
192      }
193  
194      /**
195       * Set the sheetname for this sheet.  (this appears in the tabs at the bottom)
196       * @param sheetname the name of the sheet
197       */
198  
199      public void setSheetname( String sheetname )
200      {
201          field_5_sheetname = sheetname;
202      }
203  
204      /**
205       * get the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
206       *
207       * @return offset in bytes
208       */
209  
210      public int getPositionOfBof()
211      {
212          return field_1_position_of_BOF;
213      }
214  
215      /**
216       * get the option flags (unimportant for HSSF supported sheets)
217       *
218       * @return flags to set
219       */
220  
221      public short getOptionFlags()
222      {
223          return field_2_option_flags;
224      }
225  
226      /**
227       * get the length of the sheetname in characters
228       *
229       * @return number of characters in the sheet name
230       * @see #getSheetname()
231       */
232  
233      public byte getSheetnameLength()
234      {
235          return field_3_sheetname_length;
236      }
237  
238      /**
239       * get the length of the raw sheetname in characters
240       * the length depends on the unicode flag
241       *
242       * @return number of characters in the raw sheet name
243       */
244  
245      public byte getRawSheetnameLength()
246      {
247          return (byte) ( ( ( field_4_compressed_unicode_flag & 0x01 ) == 1 )
248                  ? 2 * field_3_sheetname_length
249                  : field_3_sheetname_length );
250      }
251  
252      /**
253       * get whether or not to interperate the Sheetname as compressed unicode (8/16 bit)
254       * (This is undocumented but can be found as Q187919 on the Microsoft(tm) Support site)
255       * @return flag (0/1) 0- compressed, 1 - uncompressed (16-bit)
256       */
257  
258      public byte getCompressedUnicodeFlag()
259      {
260          return field_4_compressed_unicode_flag;
261      }
262  
263      /**
264       * get the sheetname for this sheet.  (this appears in the tabs at the bottom)
265       * @return sheetname the name of the sheet
266       */
267  
268      public String getSheetname()
269      {
270          return field_5_sheetname;
271      }
272  
273      public String toString()
274      {
275          StringBuffer buffer = new StringBuffer();
276  
277          buffer.append( "[BOUNDSHEET]\n" );
278          buffer.append( "    .bof             = " )
279                  .append( Integer.toHexString( getPositionOfBof() ) ).append( "\n" );
280          buffer.append( "    .optionflags     = " )
281                  .append( Integer.toHexString( getOptionFlags() ) ).append( "\n" );
282          buffer.append( "    .sheetname length= " )
283                  .append( Integer.toHexString( getSheetnameLength() ) ).append( "\n" );
284          buffer.append( "    .unicodeflag     = " )
285                  .append( Integer.toHexString( getCompressedUnicodeFlag() ) )
286                  .append( "\n" );
287          buffer.append( "    .sheetname       = " ).append( getSheetname() )
288                  .append( "\n" );
289          buffer.append( "[/BOUNDSHEET]\n" );
290          return buffer.toString();
291      }
292  
293      public int serialize( int offset, byte[] data )
294      {
295          LittleEndian.putShort( data, 0 + offset, sid );
296          LittleEndian.putShort( data, 2 + offset, (short) ( 8 + getRawSheetnameLength() ) );
297          LittleEndian.putInt( data, 4 + offset, getPositionOfBof() );
298          LittleEndian.putShort( data, 8 + offset, getOptionFlags() );
299          data[10 + offset] = (byte) ( getSheetnameLength() );
300          data[11 + offset] = getCompressedUnicodeFlag();
301  
302          if ( ( field_4_compressed_unicode_flag & 0x01 ) == 1 )
303              StringUtil.putUncompressedUnicode( getSheetname(), data, 12 + offset );
304          else
305              StringUtil.putCompressedUnicode( getSheetname(), data, 12 + offset );
306  
307  
308          return getRecordSize();
309  
310          /*
311  		byte[] fake = new byte[] {	(byte)0x85, 0x00, 			// sid
312  		    							0x1a, 0x00, 			// length
313  		    							0x3C, 0x09, 0x00, 0x00, // bof
314  		    							0x00, 0x00, 			// flags
315  		    							0x09, 					// len( str )
316  		    							0x01, 					// unicode
317  		    							// <str>
318  		    							0x21, 0x04, 0x42, 0x04, 0x40, 0x04, 0x30, 0x04, 0x3D,
319  		    							0x04, 0x38, 0x04, 0x47, 0x04, 0x3A, 0x04, 0x30, 0x04
320  		    							// </str>
321  		    						};
322  
323  		    						sid + len + bof + flags + len(str) + unicode +   str
324  		    						 2  +  2  +  4  +   2   +    1     +    1    + len(str)
325  
326  		System.arraycopy( fake, 0, data, offset, fake.length );
327  
328  		return fake.length;
329  		*/
330      }
331  
332      public int getRecordSize()
333      {
334          // Includes sid length + size length
335          return 12 + getRawSheetnameLength();
336      }
337  
338      public short getSid()
339      {
340          return this.sid;
341      }
342  }
343