001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hssf.record;
019:
020: import org.apache.poi.util.LittleEndian;
021: import org.apache.poi.util.StringUtil;
022:
023: /**
024: * Title: Bound Sheet Record (aka BundleSheet) <P>
025: * Description: Defines a sheet within a workbook. Basically stores the sheetname
026: * and tells where the Beginning of file record is within the HSSF
027: * file. <P>
028: * REFERENCE: PG 291 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
029: * @author Andrew C. Oliver (acoliver at apache dot org)
030: * @author Sergei Kozello (sergeikozello at mail.ru)
031: * @version 2.0-pre
032: */
033:
034: public class BoundSheetRecord extends Record {
035: public final static short sid = 0x85;
036: private int field_1_position_of_BOF;
037: private short field_2_option_flags;
038: private byte field_3_sheetname_length;
039: private byte field_4_compressed_unicode_flag; // not documented
040: private String field_5_sheetname;
041:
042: public BoundSheetRecord() {
043: }
044:
045: /**
046: * Constructs a BoundSheetRecord and sets its fields appropriately
047: *
048: * @param in the RecordInputstream to read the record from
049: */
050:
051: public BoundSheetRecord(RecordInputStream in) {
052: super (in);
053: }
054:
055: protected void validateSid(short id) {
056: if (id != sid) {
057: throw new RecordFormatException("NOT A Bound Sheet RECORD");
058: }
059: }
060:
061: /**
062: * UTF8:
063: * sid + len + bof + flags + len(str) + unicode + str
064: * 2 + 2 + 4 + 2 + 1 + 1 + len(str)
065: *
066: * UNICODE:
067: * sid + len + bof + flags + len(str) + unicode + str
068: * 2 + 2 + 4 + 2 + 1 + 1 + 2 * len(str)
069: *
070: */
071:
072: protected void fillFields(RecordInputStream in) {
073: field_1_position_of_BOF = in.readInt(); // bof
074: field_2_option_flags = in.readShort(); // flags
075: field_3_sheetname_length = in.readByte(); // len(str)
076: field_4_compressed_unicode_flag = in.readByte(); // unicode
077:
078: int nameLength = LittleEndian
079: .ubyteToInt(field_3_sheetname_length);
080: if ((field_4_compressed_unicode_flag & 0x01) == 1) {
081: field_5_sheetname = in.readUnicodeLEString(nameLength);
082: } else {
083: field_5_sheetname = in.readCompressedUnicode(nameLength);
084: }
085: }
086:
087: /**
088: * set the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
089: *
090: * @param pos offset in bytes
091: */
092:
093: public void setPositionOfBof(int pos) {
094: field_1_position_of_BOF = pos;
095: }
096:
097: /**
098: * set the option flags (unimportant for HSSF supported sheets)
099: *
100: * @param flags to set
101: */
102:
103: public void setOptionFlags(short flags) {
104: field_2_option_flags = flags;
105: }
106:
107: /**
108: * Set the length of the sheetname in characters
109: *
110: * @param len number of characters in the sheet name
111: * @see #setSheetname(String)
112: */
113:
114: public void setSheetnameLength(byte len) {
115: field_3_sheetname_length = len;
116: }
117:
118: /**
119: * set whether or not to interperate the Sheetname as compressed unicode (8/16 bit)
120: * (This is undocumented but can be found as Q187919 on the Microsoft(tm) Support site)
121: * @param flag (0/1) 0- compressed, 1 - uncompressed (16-bit)
122: */
123:
124: public void setCompressedUnicodeFlag(byte flag) {
125: field_4_compressed_unicode_flag = flag;
126: }
127:
128: /**
129: * Set the sheetname for this sheet. (this appears in the tabs at the bottom)
130: * @param sheetname the name of the sheet
131: * @throws IllegalArgumentException if sheet name will cause excel to crash.
132: */
133:
134: public void setSheetname(String sheetname) {
135:
136: if ((sheetname == null) || (sheetname.length() == 0)
137: || (sheetname.length() > 31)
138: || (sheetname.indexOf("/") > -1)
139: || (sheetname.indexOf("\\") > -1)
140: || (sheetname.indexOf("?") > -1)
141: || (sheetname.indexOf("*") > -1)
142: || (sheetname.indexOf("]") > -1)
143: || (sheetname.indexOf("[") > -1)) {
144: throw new IllegalArgumentException(
145: "Sheet name cannot be blank, greater than 31 chars, or contain any of /\\*?[]");
146: }
147: field_5_sheetname = sheetname;
148: setCompressedUnicodeFlag(StringUtil.hasMultibyte(sheetname) ? (byte) 1
149: : (byte) 0);
150: }
151:
152: /**
153: * get the offset in bytes of the Beginning of File Marker within the HSSF Stream part of the POIFS file
154: *
155: * @return offset in bytes
156: */
157:
158: public int getPositionOfBof() {
159: return field_1_position_of_BOF;
160: }
161:
162: /**
163: * get the option flags (unimportant for HSSF supported sheets)
164: *
165: * @return flags to set
166: */
167:
168: public short getOptionFlags() {
169: return field_2_option_flags;
170: }
171:
172: /**
173: * get the length of the sheetname in characters
174: *
175: * @return number of characters in the sheet name
176: * @see #getSheetname()
177: */
178:
179: public byte getSheetnameLength() {
180: return field_3_sheetname_length;
181: }
182:
183: /**
184: * get the length of the raw sheetname in characters
185: * the length depends on the unicode flag
186: *
187: * @return number of characters in the raw sheet name
188: */
189:
190: public byte getRawSheetnameLength() {
191: return (byte) (((field_4_compressed_unicode_flag & 0x01) == 1) ? 2 * field_3_sheetname_length
192: : field_3_sheetname_length);
193: }
194:
195: /**
196: * get whether or not to interperate the Sheetname as compressed unicode (8/16 bit)
197: * (This is undocumented but can be found as Q187919 on the Microsoft(tm) Support site)
198: * @return flag (0/1) 0- compressed, 1 - uncompressed (16-bit)
199: */
200:
201: public byte getCompressedUnicodeFlag() {
202: return field_4_compressed_unicode_flag;
203: }
204:
205: /**
206: * get the sheetname for this sheet. (this appears in the tabs at the bottom)
207: * @return sheetname the name of the sheet
208: */
209:
210: public String getSheetname() {
211: return field_5_sheetname;
212: }
213:
214: public String toString() {
215: StringBuffer buffer = new StringBuffer();
216:
217: buffer.append("[BOUNDSHEET]\n");
218: buffer.append(" .bof = ").append(
219: Integer.toHexString(getPositionOfBof())).append("\n");
220: buffer.append(" .optionflags = ").append(
221: Integer.toHexString(getOptionFlags())).append("\n");
222: buffer.append(" .sheetname length= ").append(
223: Integer.toHexString(getSheetnameLength())).append("\n");
224: buffer.append(" .unicodeflag = ").append(
225: Integer.toHexString(getCompressedUnicodeFlag()))
226: .append("\n");
227: buffer.append(" .sheetname = ").append(getSheetname())
228: .append("\n");
229: buffer.append("[/BOUNDSHEET]\n");
230: return buffer.toString();
231: }
232:
233: public int serialize(int offset, byte[] data) {
234: LittleEndian.putShort(data, 0 + offset, sid);
235: LittleEndian.putShort(data, 2 + offset,
236: (short) (8 + getRawSheetnameLength()));
237: LittleEndian.putInt(data, 4 + offset, getPositionOfBof());
238: LittleEndian.putShort(data, 8 + offset, getOptionFlags());
239: data[10 + offset] = (byte) (getSheetnameLength());
240: data[11 + offset] = getCompressedUnicodeFlag();
241:
242: if ((field_4_compressed_unicode_flag & 0x01) == 1)
243: StringUtil.putUnicodeLE(getSheetname(), data, 12 + offset);
244: else
245: StringUtil.putCompressedUnicode(getSheetname(), data,
246: 12 + offset);
247:
248: return getRecordSize();
249:
250: /*
251: byte[] fake = new byte[] { (byte)0x85, 0x00, // sid
252: 0x1a, 0x00, // length
253: 0x3C, 0x09, 0x00, 0x00, // bof
254: 0x00, 0x00, // flags
255: 0x09, // len( str )
256: 0x01, // unicode
257: // <str>
258: 0x21, 0x04, 0x42, 0x04, 0x40, 0x04, 0x30, 0x04, 0x3D,
259: 0x04, 0x38, 0x04, 0x47, 0x04, 0x3A, 0x04, 0x30, 0x04
260: // </str>
261: };
262:
263: sid + len + bof + flags + len(str) + unicode + str
264: 2 + 2 + 4 + 2 + 1 + 1 + len(str)
265:
266: System.arraycopy( fake, 0, data, offset, fake.length );
267:
268: return fake.length;
269: */
270: }
271:
272: public int getRecordSize() {
273: // Includes sid length + size length
274: return 12 + getRawSheetnameLength();
275: }
276:
277: public short getSid() {
278: return sid;
279: }
280: }
|