1 ndation, please see
52 * <http://www.apache.org/>.
53 */
54
55 package org.apache.poi.hssf.record;
56
57 import java.io.UnsupportedEncodingException;
58 import org.apache.poi.util.LittleEndian;
59 import org.apache.poi.util.StringUtil;
60
61 /**
62 * Title: Unicode String<P>
63 * Description: Unicode String record. We implement these as a record, although
64 * they are really just standard fields that are in several records.
65 * It is considered more desirable then repeating it in all of them.<P>
66 * REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
67 * @author Andrew C. Oliver
68 * @author Marc Johnson (mjohnson at apache dot org)
69 * @author Glen Stampoultzis (glens at apache.org)
70 * @version 2.0-pre
71 */
72
73 public class UnicodeString
74 extends Record
75 implements Comparable
76 {
77 public final static short sid = 0xFFF;
78 private short field_1_charCount; // = 0;
79 private byte field_2_optionflags; // = 0;
80 private String field_3_string; // = null;
81 private final int RICH_TEXT_BIT = 8;
82 private final int EXT_BIT = 4;
83
84 public UnicodeString()
85 {
86
87 }
88
89 public int hashCode()
90 {
91 int stringHash = 0;
92 if (field_3_string != null)
93 stringHash = field_3_string.hashCode();
94 return field_1_charCount + stringHash;
95 }
96
97
98 /**
99 * Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely understand
100 * rich text fields yet it's difficult to make a sound comparison.
101 *
102 * @param o The object to compare.
103 * @return true if the object is actually equal.
104 */
105 public boolean equals(Object o)
106 {
107 if ((o == null) || (o.getClass() != this.getClass()))
108 {
109 return false;
110 }
111 UnicodeString other = ( UnicodeString ) o;
112
113 return ((field_1_charCount == other.field_1_charCount)
114 && (field_2_optionflags == other.field_2_optionflags)
115 && field_3_string.equals(other.field_3_string));
116 }
117
118 /**
119 * construct a unicode string record and fill its fields, ID is ignored
120 * @param id - ignored
121 * @param size - size of the data
122 * @param data - the bytes of the string/fields
123 */
124 public UnicodeString(short id, short size, byte [] data)
125 {
126 super(id, size, data);
127 }
128
129 /**
130 * construct a unicode string from a string fragment + data
131 */
132 public UnicodeString(short id, short size, byte [] data, String prefix)
133 {
134 this(id, size, data);
135 field_3_string = prefix + field_3_string;
136 setCharCount();
137 }
138
139 /**
140 * NO OP
141 */
142 protected void validateSid(short id)
143 {
144 // included only for interface compliance
145 }
146
147 protected void fillFields(byte [] data, short size)
148 {
149 field_1_charCount = LittleEndian.getShort(data, 0);
150 field_2_optionflags = data[ 2 ];
151 if ((field_2_optionflags & 1) == 0)
152 {
153 try {
154 field_3_string = new String(data, 3, getCharCount(),
155 StringUtil.getPreferredEncoding());
156 } catch (UnsupportedEncodingException e) {
157 //commented out by glen. this is a 1.4 construct and we
158 //are supposed to compile on 1.3.
159 //throw new RuntimeException(e);
160 throw new RuntimeException(e.toString());
161 }
162 }
163 else
164 {
165 char[] array = new char[ getCharCount() ];
166
167 for (int j = 0; j < array.length; j++)
168 {
169 array[ j ] = ( char ) LittleEndian.getShort(data,
170 3 + (j * 2));
171 }
172 field_3_string = new String(array);
173 }
174 }
175
176 /**
177 * get the number of characters in the string
178 *
179 * @return number of characters
180 *
181 */
182 public short getCharCount()
183 {
184 return field_1_charCount;
185 }
186
187 /**
188 * set the number of characters in the string
189 * @param cc - number of characters
190 */
191 public void setCharCount(short cc)
192 {
193 field_1_charCount = cc;
194 }
195
196 /**
197 * sets the number of characters to whaterver number of characters is in the string
198 * currently. effectively setCharCount(getString.length()).
199 * @see #setString(String)
200 * @see #getString()
201 */
202 public void setCharCount()
203 {
204 field_1_charCount = ( short ) field_3_string.length();
205 }
206
207 /**
208 * get the option flags which among other things return if this is a 16-bit or
209 * 8 bit string
210 *
211 * @return optionflags bitmask
212 *
213 */
214 public byte getOptionFlags()
215 {
216 return field_2_optionflags;
217 }
218
219 /**
220 * set the option flags which among other things return if this is a 16-bit or
221 * 8 bit string
222 *
223 * @param of optionflags bitmask
224 *
225 */
226 public void setOptionFlags(byte of)
227 {
228 field_2_optionflags = of;
229 }
230
231 /**
232 * get the actual string this contains as a java String object
233 *
234 *
235 * @return String
236 *
237 */
238 public String getString()
239 {
240 return field_3_string;
241 }
242
243 /**
244 * set the actual string this contains
245 * @param string the text
246 */
247 public void setString(String string)
248 {
249 field_3_string = string;
250 if (getCharCount() < field_3_string.length())
251 {
252 setCharCount();
253 }
254 }
255
256 /**
257 * unlike the real records we return the same as "getString()" rather than debug info
258 * @see #getDebugInfo()
259 * @return String value of the record
260 */
261 public String toString()
262 {
263 return getString();
264 }
265
266 /**
267 * return a character representation of the fields of this record
268 *
269 *
270 * @return String of output for biffviewer etc.
271 *
272 */
273 public String getDebugInfo()
274 {
275 StringBuffer buffer = new StringBuffer();
276
277 buffer.append("[UNICODESTRING]\n");
278 buffer.append(" .charcount = ")
279 .append(Integer.toHexString(getCharCount())).append("\n");
280 buffer.append(" .optionflags = ")
281 .append(Integer.toHexString(getOptionFlags())).append("\n");
282 buffer.append(" .string = ").append(getString())
283 .append("\n");
284 buffer.append("[/UNICODESTRING]\n");
285
286 return buffer.toString();
287 }
288
289 public int serialize(int offset, byte [] data)
290 {
291 int charsize = 1;
292
293 if (getOptionFlags() == 1)
294 {
295 charsize = 2;
296 }
297
298 // byte[] retval = new byte[ 3 + (getString().length() * charsize)];
299 LittleEndian.putShort(data, 0 + offset, getCharCount());
300 data[ 2 + offset ] = getOptionFlags();
301 // System.out.println("Unicode: We've got "+retval[2]+" for our option flag");
302 try {
303 String unicodeString = new String(getString().getBytes("Unicode"),"Unicode");
304 if (getOptionFlags() == 0)
305 {
306 StringUtil.putCompressedUnicode(unicodeString, data, 0x3 + offset);
307 }
308 else
309 {
310 StringUtil.putUncompressedUnicode(unicodeString, data,
311 0x3 + offset);
312 }
313 }
314 catch (Exception e) {
315 if (getOptionFlags() == 0)
316 {
317 StringUtil.putCompressedUnicode(getString(), data, 0x3 +
318 offset);
319 }
320 else
321 {
322 StringUtil.putUncompressedUnicode(getString(), data,
323 0x3 + offset);
324 }
325 }
326
327 return getRecordSize();
328
329 }
330
331 private boolean isUncompressedUnicode()
332 {
333 return (getOptionFlags() & 0x01) == 1;
334 }
335
336 public int getRecordSize()
337 {
338 int charsize = isUncompressedUnicode() ? 2 : 1;
339 return 3 + (getString().length() * charsize);
340 }
341
342 public short getSid()
343 {
344 return this.sid;
345 }
346
347 /**
348 * called by the constructor, should set class level fields. Should throw
349 * runtime exception for bad/icomplete data.
350 *
351 * @param data raw data
352 * @param size size of data
353 * @param offset of the records data (provided a big array of the file)
354 */
355 protected void fillFields(byte [] data, short size, int offset)
356 {
357
358 }
359
360 public int compareTo(Object obj)
361 {
362 UnicodeString str = ( UnicodeString ) obj;
363
364 return this.getString().compareTo(str.getString());
365 }
366
367 public boolean isRichText()
368 {
369 return (getOptionFlags() & RICH_TEXT_BIT) != 0;
370 }
371
372 int maxBrokenLength(final int proposedBrokenLength)
373 {
374 int rval = proposedBrokenLength;
375
376 if (isUncompressedUnicode())
377 {
378 int proposedStringLength = proposedBrokenLength - 3;
379
380 if ((proposedStringLength % 2) == 1)
381 {
382 proposedStringLength--;
383 }
384 rval = proposedStringLength + 3;
385 }
386 return rval;
387 }
388
389 public boolean isExtendedText()
390 {
391 return (getOptionFlags() & EXT_BIT) != 0;
392 }
393
394 }
395
396 UnicodeStringRecordsidfield_1_charCountUnicodeStringhashCodestringHashfield_1_charCountstringHashequalsooUnicodeStringUnicodeStringofield_1_charCountotherfield_1_charCountotherotherUnicodeStringidsizedataUnicodeStringidsizedataprefixvalidateSid