package jxl.read.biff; import common.Assert; import jxl.WorkbookSettings; import jxl.biff.IntegerHelper; import jxl.biff.RecordData; import jxl.biff.StringHelper; class SSTRecord extends RecordData { private int totalStrings; private int uniqueStrings; private String[] strings; private int[] continuationBreaks; private static class ByteArrayHolder { public byte[] bytes; private ByteArrayHolder() {} } private static class BooleanHolder { public boolean value; private BooleanHolder() {} } public SSTRecord(Record t, Record[] continuations, WorkbookSettings ws) { super(t); int totalRecordLength = 0; for (int i = 0; i < continuations.length; i++) totalRecordLength += continuations[i].getLength(); totalRecordLength += getRecord().getLength(); byte[] data = new byte[totalRecordLength]; int pos = 0; System.arraycopy(getRecord().getData(), 0, data, 0, getRecord().getLength()); pos += getRecord().getLength(); this.continuationBreaks = new int[continuations.length]; Record r = null; for (int j = 0; j < continuations.length; j++) { r = continuations[j]; System.arraycopy(r.getData(), 0, data, pos, r.getLength()); this.continuationBreaks[j] = pos; pos += r.getLength(); } this.totalStrings = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); this.uniqueStrings = IntegerHelper.getInt(data[4], data[5], data[6], data[7]); this.strings = new String[this.uniqueStrings]; readStrings(data, 8, ws); } private void readStrings(byte[] data, int offset, WorkbookSettings ws) { int pos = offset; String s = null; boolean asciiEncoding = false; boolean richString = false; boolean extendedString = false; int formattingRuns = 0; int extendedRunLength = 0; for (int i = 0; i < this.uniqueStrings; i++) { int numChars = IntegerHelper.getInt(data[pos], data[pos + 1]); pos += 2; byte optionFlags = data[pos]; pos++; extendedString = ((optionFlags & 0x4) != 0); richString = ((optionFlags & 0x8) != 0); if (richString) { formattingRuns = IntegerHelper.getInt(data[pos], data[pos + 1]); pos += 2; } if (extendedString) { extendedRunLength = IntegerHelper.getInt(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); pos += 4; } asciiEncoding = ((optionFlags & 0x1) == 0); ByteArrayHolder bah = new ByteArrayHolder(); BooleanHolder bh = new BooleanHolder(); bh.value = asciiEncoding; pos += getChars(data, bah, pos, bh, numChars); asciiEncoding = bh.value; if (asciiEncoding) { s = StringHelper.getString(bah.bytes, numChars, 0, ws); } else { s = StringHelper.getUnicodeString(bah.bytes, numChars, 0); } this.strings[i] = s; if (richString) pos += 4 * formattingRuns; if (extendedString) pos += extendedRunLength; if (pos > data.length) Assert.verify(false, "pos exceeds record length"); } } private int getChars(byte[] source, ByteArrayHolder bah, int pos, BooleanHolder ascii, int numChars) { int charsRead, i = 0; boolean spansBreak = false; if (ascii.value) { bah.bytes = new byte[numChars]; } else { bah.bytes = new byte[numChars * 2]; } while (i < this.continuationBreaks.length && !spansBreak) { spansBreak = (pos <= this.continuationBreaks[i] && pos + bah.bytes.length > this.continuationBreaks[i]); if (!spansBreak) i++; } if (!spansBreak) { System.arraycopy(source, pos, bah.bytes, 0, bah.bytes.length); return bah.bytes.length; } int breakpos = this.continuationBreaks[i]; System.arraycopy(source, pos, bah.bytes, 0, breakpos - pos); int bytesRead = breakpos - pos; if (ascii.value) { charsRead = bytesRead; } else { charsRead = bytesRead / 2; } bytesRead += getContinuedString(source, bah, bytesRead, i, ascii, numChars - charsRead); return bytesRead; } private int getContinuedString(byte[] source, ByteArrayHolder bah, int destPos, int contBreakIndex, BooleanHolder ascii, int charsLeft) { int breakpos = this.continuationBreaks[contBreakIndex]; int bytesRead = 0; while (charsLeft > 0) { Assert.verify((contBreakIndex < this.continuationBreaks.length), "continuation break index"); if (ascii.value && source[breakpos] == 0) { int length = (contBreakIndex == this.continuationBreaks.length - 1) ? charsLeft : Math.min(charsLeft, this.continuationBreaks[contBreakIndex + 1] - breakpos - 1); System.arraycopy(source, breakpos + 1, bah.bytes, destPos, length); destPos += length; bytesRead += length + 1; charsLeft -= length; ascii.value = true; } else if (!ascii.value && source[breakpos] != 0) { int length = (contBreakIndex == this.continuationBreaks.length - 1) ? (charsLeft * 2) : Math.min(charsLeft * 2, this.continuationBreaks[contBreakIndex + 1] - breakpos - 1); System.arraycopy(source, breakpos + 1, bah.bytes, destPos, length); destPos += length; bytesRead += length + 1; charsLeft -= length / 2; ascii.value = false; } else if (!ascii.value && source[breakpos] == 0) { int chars = (contBreakIndex == this.continuationBreaks.length - 1) ? charsLeft : Math.min(charsLeft, this.continuationBreaks[contBreakIndex + 1] - breakpos - 1); for (int j = 0; j < chars; j++) { bah.bytes[destPos] = source[breakpos + j + 1]; destPos += 2; } bytesRead += chars + 1; charsLeft -= chars; ascii.value = false; } else { byte[] oldBytes = bah.bytes; bah.bytes = new byte[destPos * 2 + charsLeft * 2]; for (int j = 0; j < destPos; j++) bah.bytes[j * 2] = oldBytes[j]; destPos *= 2; int length = (contBreakIndex == this.continuationBreaks.length - 1) ? (charsLeft * 2) : Math.min(charsLeft * 2, this.continuationBreaks[contBreakIndex + 1] - breakpos - 1); System.arraycopy(source, breakpos + 1, bah.bytes, destPos, length); destPos += length; bytesRead += length + 1; charsLeft -= length / 2; ascii.value = false; } contBreakIndex++; if (contBreakIndex < this.continuationBreaks.length) breakpos = this.continuationBreaks[contBreakIndex]; } return bytesRead; } public String getString(int index) { Assert.verify((index < this.uniqueStrings)); return this.strings[index]; } }