From: Nick Burch Date: Mon, 18 Jan 2010 12:18:00 +0000 (+0000) Subject: records.UnicodeString isn't actually a Record, just a common part that exists in... X-Git-Tag: REL_3_7_BETA1~133 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=62dc99b25f9e99cd53b051d5d99f58aecb623ff6;p=poi.git records.UnicodeString isn't actually a Record, just a common part that exists in many other records. As such, move it to records.common, which is where all the other record components live git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@900362 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java index 3c2a210acb..868d2a0a62 100644 --- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java +++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java @@ -71,12 +71,12 @@ import org.apache.poi.hssf.record.SSTRecord; import org.apache.poi.hssf.record.StyleRecord; import org.apache.poi.hssf.record.SupBookRecord; import org.apache.poi.hssf.record.TabIdRecord; -import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.UseSelFSRecord; import org.apache.poi.hssf.record.WindowOneRecord; import org.apache.poi.hssf.record.WindowProtectRecord; import org.apache.poi.hssf.record.WriteAccessRecord; import org.apache.poi.hssf.record.WriteProtectRecord; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.FormulaShifter; import org.apache.poi.hssf.record.formula.Ptg; diff --git a/src/java/org/apache/poi/hssf/record/DVRecord.java b/src/java/org/apache/poi/hssf/record/DVRecord.java index a6d9f11b61..67e873691f 100644 --- a/src/java/org/apache/poi/hssf/record/DVRecord.java +++ b/src/java/org/apache/poi/hssf/record/DVRecord.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.record; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.usermodel.HSSFDataValidation; import org.apache.poi.ss.formula.Formula; diff --git a/src/java/org/apache/poi/hssf/record/SSTDeserializer.java b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java index 304dc3bfeb..c9f6569b4a 100644 --- a/src/java/org/apache/poi/hssf/record/SSTDeserializer.java +++ b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.record; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.util.IntMapper; /** diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java index 481fa84d3c..e95f155eb5 100644 --- a/src/java/org/apache/poi/hssf/record/SSTRecord.java +++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.record; import java.util.Iterator; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.cont.ContinuableRecord; import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; import org.apache.poi.util.IntMapper; diff --git a/src/java/org/apache/poi/hssf/record/SSTSerializer.java b/src/java/org/apache/poi/hssf/record/SSTSerializer.java index 78844deb30..f1022642e4 100644 --- a/src/java/org/apache/poi/hssf/record/SSTSerializer.java +++ b/src/java/org/apache/poi/hssf/record/SSTSerializer.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.record; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; import org.apache.poi.util.IntMapper; diff --git a/src/java/org/apache/poi/hssf/record/UnicodeString.java b/src/java/org/apache/poi/hssf/record/UnicodeString.java deleted file mode 100644 index 9198e822b3..0000000000 --- a/src/java/org/apache/poi/hssf/record/UnicodeString.java +++ /dev/null @@ -1,587 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -package org.apache.poi.hssf.record; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; -import org.apache.poi.util.HexDump; -import org.apache.poi.util.LittleEndianInput; -import org.apache.poi.util.LittleEndianOutput; - -/** - * Title: Unicode String

- * Description: Unicode String - just standard fields that are in several records. - * It is considered more desirable then repeating it in all of them.

- * REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

- * @author Andrew C. Oliver - * @author Marc Johnson (mjohnson at apache dot org) - * @author Glen Stampoultzis (glens at apache.org) - */ -public final class UnicodeString implements Comparable { - private short field_1_charCount; - private byte field_2_optionflags; - private String field_3_string; - private List field_4_format_runs; - private byte[] field_5_ext_rst; - private static final BitField highByte = BitFieldFactory.getInstance(0x1); - private static final BitField extBit = BitFieldFactory.getInstance(0x4); - private static final BitField richText = BitFieldFactory.getInstance(0x8); - - public static class FormatRun implements Comparable { - final short _character; - short _fontIndex; - - public FormatRun(short character, short fontIndex) { - this._character = character; - this._fontIndex = fontIndex; - } - - public FormatRun(LittleEndianInput in) { - this(in.readShort(), in.readShort()); - } - - public short getCharacterPos() { - return _character; - } - - public short getFontIndex() { - return _fontIndex; - } - - public boolean equals(Object o) { - if (!(o instanceof FormatRun)) { - return false; - } - FormatRun other = ( FormatRun ) o; - - return _character == other._character && _fontIndex == other._fontIndex; - } - - public int compareTo(FormatRun r) { - if (_character == r._character && _fontIndex == r._fontIndex) { - return 0; - } - if (_character == r._character) { - return _fontIndex - r._fontIndex; - } - return _character - r._character; - } - - public String toString() { - return "character="+_character+",fontIndex="+_fontIndex; - } - - public void serialize(LittleEndianOutput out) { - out.writeShort(_character); - out.writeShort(_fontIndex); - } - } - - private UnicodeString() { - //Used for clone method. - } - - public UnicodeString(String str) - { - setString(str); - } - - - - public int hashCode() - { - int stringHash = 0; - if (field_3_string != null) - stringHash = field_3_string.hashCode(); - return field_1_charCount + stringHash; - } - - /** - * Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely understand - * rich text fields yet it's difficult to make a sound comparison. - * - * @param o The object to compare. - * @return true if the object is actually equal. - */ - public boolean equals(Object o) - { - if (!(o instanceof UnicodeString)) { - return false; - } - UnicodeString other = (UnicodeString) o; - - //OK lets do this in stages to return a quickly, first check the actual string - boolean eq = ((field_1_charCount == other.field_1_charCount) - && (field_2_optionflags == other.field_2_optionflags) - && field_3_string.equals(other.field_3_string)); - if (!eq) return false; - - //OK string appears to be equal but now lets compare formatting runs - if ((field_4_format_runs == null) && (other.field_4_format_runs == null)) - //Strings are equal, and there are not formatting runs. - return true; - if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) || - (field_4_format_runs != null) && (other.field_4_format_runs == null)) - //Strings are equal, but one or the other has formatting runs - return false; - - //Strings are equal, so now compare formatting runs. - int size = field_4_format_runs.size(); - if (size != other.field_4_format_runs.size()) - return false; - - for (int i=0;i 0)) { - field_4_format_runs = new ArrayList(runCount); - for (int i=0;i 0)) { - field_5_ext_rst = new byte[extensionLength]; - for (int i=0;i 255 ) - { - useUTF16 = true; - break; - } - } - if (useUTF16) - //Set the uncompressed bit - field_2_optionflags = highByte.setByte(field_2_optionflags); - else field_2_optionflags = highByte.clearByte(field_2_optionflags); - } - - public int getFormatRunCount() { - if (field_4_format_runs == null) - return 0; - return field_4_format_runs.size(); - } - - public FormatRun getFormatRun(int index) { - if (field_4_format_runs == null) { - return null; - } - if (index < 0 || index >= field_4_format_runs.size()) { - return null; - } - return field_4_format_runs.get(index); - } - - private int findFormatRunAt(int characterPos) { - int size = field_4_format_runs.size(); - for (int i=0;i characterPos) - return -1; - } - return -1; - } - - /** Adds a font run to the formatted string. - * - * If a font run exists at the current charcter location, then it is - * replaced with the font run to be added. - */ - public void addFormatRun(FormatRun r) { - if (field_4_format_runs == null) { - field_4_format_runs = new ArrayList(); - } - - int index = findFormatRunAt(r._character); - if (index != -1) - field_4_format_runs.remove(index); - - field_4_format_runs.add(r); - //Need to sort the font runs to ensure that the font runs appear in - //character order - Collections.sort(field_4_format_runs); - - //Make sure that we now say that we are a rich string - field_2_optionflags = richText.setByte(field_2_optionflags); - } - - public Iterator formatIterator() { - if (field_4_format_runs != null) { - return field_4_format_runs.iterator(); - } - return null; - } - - public void removeFormatRun(FormatRun r) { - field_4_format_runs.remove(r); - if (field_4_format_runs.size() == 0) { - field_4_format_runs = null; - field_2_optionflags = richText.clearByte(field_2_optionflags); - } - } - - public void clearFormatting() { - field_4_format_runs = null; - field_2_optionflags = richText.clearByte(field_2_optionflags); - } - - - void setExtendedRst(byte[] ext_rst) { - if (ext_rst != null) - field_2_optionflags = extBit.setByte(field_2_optionflags); - else field_2_optionflags = extBit.clearByte(field_2_optionflags); - this.field_5_ext_rst = ext_rst; - } - - - /** - * Swaps all use in the string of one font index - * for use of a different font index. - * Normally only called when fonts have been - * removed / re-ordered - */ - public void swapFontUse(short oldFontIndex, short newFontIndex) { - for (FormatRun run : field_4_format_runs) { - if(run._fontIndex == oldFontIndex) { - run._fontIndex = newFontIndex; - } - } - } - - /** - * unlike the real records we return the same as "getString()" rather than debug info - * @see #getDebugInfo() - * @return String value of the record - */ - - public String toString() - { - return getString(); - } - - /** - * return a character representation of the fields of this record - * - * - * @return String of output for biffviewer etc. - * - */ - public String getDebugInfo() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[UNICODESTRING]\n"); - buffer.append(" .charcount = ") - .append(Integer.toHexString(getCharCount())).append("\n"); - buffer.append(" .optionflags = ") - .append(Integer.toHexString(getOptionFlags())).append("\n"); - buffer.append(" .string = ").append(getString()).append("\n"); - if (field_4_format_runs != null) { - for (int i = 0; i < field_4_format_runs.size();i++) { - FormatRun r = field_4_format_runs.get(i); - buffer.append(" .format_run"+i+" = ").append(r.toString()).append("\n"); - } - } - if (field_5_ext_rst != null) { - buffer.append(" .field_5_ext_rst = ").append("\n").append(HexDump.toHex(field_5_ext_rst)).append("\n"); - } - buffer.append("[/UNICODESTRING]\n"); - return buffer.toString(); - } - - public void serialize(ContinuableRecordOutput out) { - int numberOfRichTextRuns = 0; - int extendedDataSize = 0; - if (isRichText() && field_4_format_runs != null) { - numberOfRichTextRuns = field_4_format_runs.size(); - } - if (isExtendedText() && field_5_ext_rst != null) { - extendedDataSize = field_5_ext_rst.length; - } - - out.writeString(field_3_string, numberOfRichTextRuns, extendedDataSize); - - if (numberOfRichTextRuns > 0) { - - //This will ensure that a run does not split a continue - for (int i=0;i 0) { - // OK ExtRst is actually not documented, so i am going to hope - // that we can actually continue on byte boundaries - - int extPos = 0; - while (true) { - int nBytesToWrite = Math.min(extendedDataSize - extPos, out.getAvailableSpace()); - out.write(field_5_ext_rst, extPos, nBytesToWrite); - extPos += nBytesToWrite; - if (extPos >= extendedDataSize) { - break; - } - out.writeContinue(); - } - } - } - - public int compareTo(UnicodeString str) { - - int result = getString().compareTo(str.getString()); - - //As per the equals method lets do this in stages - if (result != 0) - return result; - - //OK string appears to be equal but now lets compare formatting runs - if ((field_4_format_runs == null) && (str.field_4_format_runs == null)) - //Strings are equal, and there are no formatting runs. - return 0; - - if ((field_4_format_runs == null) && (str.field_4_format_runs != null)) - //Strings are equal, but one or the other has formatting runs - return 1; - if ((field_4_format_runs != null) && (str.field_4_format_runs == null)) - //Strings are equal, but one or the other has formatting runs - return -1; - - //Strings are equal, so now compare formatting runs. - int size = field_4_format_runs.size(); - if (size != str.field_4_format_runs.size()) - return size - str.field_4_format_runs.size(); - - for (int i=0;i(); - for (FormatRun r : field_4_format_runs) { - str.field_4_format_runs.add(new FormatRun(r._character, r._fontIndex)); - } - } - if (field_5_ext_rst != null) { - str.field_5_ext_rst = new byte[field_5_ext_rst.length]; - System.arraycopy(field_5_ext_rst, 0, str.field_5_ext_rst, 0, - field_5_ext_rst.length); - } - - return str; - } -} diff --git a/src/java/org/apache/poi/hssf/record/common/UnicodeString.java b/src/java/org/apache/poi/hssf/record/common/UnicodeString.java new file mode 100644 index 0000000000..842a7341c5 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/common/UnicodeString.java @@ -0,0 +1,588 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; + +/** + * Title: Unicode String

+ * Description: Unicode String - just standard fields that are in several records. + * It is considered more desirable then repeating it in all of them.

+ * REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

+ * @author Andrew C. Oliver + * @author Marc Johnson (mjohnson at apache dot org) + * @author Glen Stampoultzis (glens at apache.org) + */ +public final class UnicodeString implements Comparable { + private short field_1_charCount; + private byte field_2_optionflags; + private String field_3_string; + private List field_4_format_runs; + private byte[] field_5_ext_rst; + private static final BitField highByte = BitFieldFactory.getInstance(0x1); + private static final BitField extBit = BitFieldFactory.getInstance(0x4); + private static final BitField richText = BitFieldFactory.getInstance(0x8); + + public static class FormatRun implements Comparable { + final short _character; + short _fontIndex; + + public FormatRun(short character, short fontIndex) { + this._character = character; + this._fontIndex = fontIndex; + } + + public FormatRun(LittleEndianInput in) { + this(in.readShort(), in.readShort()); + } + + public short getCharacterPos() { + return _character; + } + + public short getFontIndex() { + return _fontIndex; + } + + public boolean equals(Object o) { + if (!(o instanceof FormatRun)) { + return false; + } + FormatRun other = ( FormatRun ) o; + + return _character == other._character && _fontIndex == other._fontIndex; + } + + public int compareTo(FormatRun r) { + if (_character == r._character && _fontIndex == r._fontIndex) { + return 0; + } + if (_character == r._character) { + return _fontIndex - r._fontIndex; + } + return _character - r._character; + } + + public String toString() { + return "character="+_character+",fontIndex="+_fontIndex; + } + + public void serialize(LittleEndianOutput out) { + out.writeShort(_character); + out.writeShort(_fontIndex); + } + } + + private UnicodeString() { + //Used for clone method. + } + + public UnicodeString(String str) + { + setString(str); + } + + + + public int hashCode() + { + int stringHash = 0; + if (field_3_string != null) + stringHash = field_3_string.hashCode(); + return field_1_charCount + stringHash; + } + + /** + * Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely understand + * rich text fields yet it's difficult to make a sound comparison. + * + * @param o The object to compare. + * @return true if the object is actually equal. + */ + public boolean equals(Object o) + { + if (!(o instanceof UnicodeString)) { + return false; + } + UnicodeString other = (UnicodeString) o; + + //OK lets do this in stages to return a quickly, first check the actual string + boolean eq = ((field_1_charCount == other.field_1_charCount) + && (field_2_optionflags == other.field_2_optionflags) + && field_3_string.equals(other.field_3_string)); + if (!eq) return false; + + //OK string appears to be equal but now lets compare formatting runs + if ((field_4_format_runs == null) && (other.field_4_format_runs == null)) + //Strings are equal, and there are not formatting runs. + return true; + if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) || + (field_4_format_runs != null) && (other.field_4_format_runs == null)) + //Strings are equal, but one or the other has formatting runs + return false; + + //Strings are equal, so now compare formatting runs. + int size = field_4_format_runs.size(); + if (size != other.field_4_format_runs.size()) + return false; + + for (int i=0;i 0)) { + field_4_format_runs = new ArrayList(runCount); + for (int i=0;i 0)) { + field_5_ext_rst = new byte[extensionLength]; + for (int i=0;i 255 ) + { + useUTF16 = true; + break; + } + } + if (useUTF16) + //Set the uncompressed bit + field_2_optionflags = highByte.setByte(field_2_optionflags); + else field_2_optionflags = highByte.clearByte(field_2_optionflags); + } + + public int getFormatRunCount() { + if (field_4_format_runs == null) + return 0; + return field_4_format_runs.size(); + } + + public FormatRun getFormatRun(int index) { + if (field_4_format_runs == null) { + return null; + } + if (index < 0 || index >= field_4_format_runs.size()) { + return null; + } + return field_4_format_runs.get(index); + } + + private int findFormatRunAt(int characterPos) { + int size = field_4_format_runs.size(); + for (int i=0;i characterPos) + return -1; + } + return -1; + } + + /** Adds a font run to the formatted string. + * + * If a font run exists at the current charcter location, then it is + * replaced with the font run to be added. + */ + public void addFormatRun(FormatRun r) { + if (field_4_format_runs == null) { + field_4_format_runs = new ArrayList(); + } + + int index = findFormatRunAt(r._character); + if (index != -1) + field_4_format_runs.remove(index); + + field_4_format_runs.add(r); + //Need to sort the font runs to ensure that the font runs appear in + //character order + Collections.sort(field_4_format_runs); + + //Make sure that we now say that we are a rich string + field_2_optionflags = richText.setByte(field_2_optionflags); + } + + public Iterator formatIterator() { + if (field_4_format_runs != null) { + return field_4_format_runs.iterator(); + } + return null; + } + + public void removeFormatRun(FormatRun r) { + field_4_format_runs.remove(r); + if (field_4_format_runs.size() == 0) { + field_4_format_runs = null; + field_2_optionflags = richText.clearByte(field_2_optionflags); + } + } + + public void clearFormatting() { + field_4_format_runs = null; + field_2_optionflags = richText.clearByte(field_2_optionflags); + } + + + void setExtendedRst(byte[] ext_rst) { + if (ext_rst != null) + field_2_optionflags = extBit.setByte(field_2_optionflags); + else field_2_optionflags = extBit.clearByte(field_2_optionflags); + this.field_5_ext_rst = ext_rst; + } + + + /** + * Swaps all use in the string of one font index + * for use of a different font index. + * Normally only called when fonts have been + * removed / re-ordered + */ + public void swapFontUse(short oldFontIndex, short newFontIndex) { + for (FormatRun run : field_4_format_runs) { + if(run._fontIndex == oldFontIndex) { + run._fontIndex = newFontIndex; + } + } + } + + /** + * unlike the real records we return the same as "getString()" rather than debug info + * @see #getDebugInfo() + * @return String value of the record + */ + + public String toString() + { + return getString(); + } + + /** + * return a character representation of the fields of this record + * + * + * @return String of output for biffviewer etc. + * + */ + public String getDebugInfo() + { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[UNICODESTRING]\n"); + buffer.append(" .charcount = ") + .append(Integer.toHexString(getCharCount())).append("\n"); + buffer.append(" .optionflags = ") + .append(Integer.toHexString(getOptionFlags())).append("\n"); + buffer.append(" .string = ").append(getString()).append("\n"); + if (field_4_format_runs != null) { + for (int i = 0; i < field_4_format_runs.size();i++) { + FormatRun r = field_4_format_runs.get(i); + buffer.append(" .format_run"+i+" = ").append(r.toString()).append("\n"); + } + } + if (field_5_ext_rst != null) { + buffer.append(" .field_5_ext_rst = ").append("\n").append(HexDump.toHex(field_5_ext_rst)).append("\n"); + } + buffer.append("[/UNICODESTRING]\n"); + return buffer.toString(); + } + + public void serialize(ContinuableRecordOutput out) { + int numberOfRichTextRuns = 0; + int extendedDataSize = 0; + if (isRichText() && field_4_format_runs != null) { + numberOfRichTextRuns = field_4_format_runs.size(); + } + if (isExtendedText() && field_5_ext_rst != null) { + extendedDataSize = field_5_ext_rst.length; + } + + out.writeString(field_3_string, numberOfRichTextRuns, extendedDataSize); + + if (numberOfRichTextRuns > 0) { + + //This will ensure that a run does not split a continue + for (int i=0;i 0) { + // OK ExtRst is actually not documented, so i am going to hope + // that we can actually continue on byte boundaries + + int extPos = 0; + while (true) { + int nBytesToWrite = Math.min(extendedDataSize - extPos, out.getAvailableSpace()); + out.write(field_5_ext_rst, extPos, nBytesToWrite); + extPos += nBytesToWrite; + if (extPos >= extendedDataSize) { + break; + } + out.writeContinue(); + } + } + } + + public int compareTo(UnicodeString str) { + + int result = getString().compareTo(str.getString()); + + //As per the equals method lets do this in stages + if (result != 0) + return result; + + //OK string appears to be equal but now lets compare formatting runs + if ((field_4_format_runs == null) && (str.field_4_format_runs == null)) + //Strings are equal, and there are no formatting runs. + return 0; + + if ((field_4_format_runs == null) && (str.field_4_format_runs != null)) + //Strings are equal, but one or the other has formatting runs + return 1; + if ((field_4_format_runs != null) && (str.field_4_format_runs == null)) + //Strings are equal, but one or the other has formatting runs + return -1; + + //Strings are equal, so now compare formatting runs. + int size = field_4_format_runs.size(); + if (size != str.field_4_format_runs.size()) + return size - str.field_4_format_runs.size(); + + for (int i=0;i(); + for (FormatRun r : field_4_format_runs) { + str.field_4_format_runs.add(new FormatRun(r._character, r._fontIndex)); + } + } + if (field_5_ext_rst != null) { + str.field_5_ext_rst = new byte[field_5_ext_rst.length]; + System.arraycopy(field_5_ext_rst, 0, str.field_5_ext_rst, 0, + field_5_ext_rst.length); + } + + return str; + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 071f1b4313..ac7ab6f911 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -41,8 +41,8 @@ import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RecordBase; import org.apache.poi.hssf.record.SubRecord; import org.apache.poi.hssf.record.TextObjectRecord; -import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.formula.ExpPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.eval.ErrorEval; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java b/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java index 0de860214a..66327d9417 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFOptimiser.java @@ -21,7 +21,7 @@ import java.util.Iterator; import org.apache.poi.hssf.record.ExtendedFormatRecord; import org.apache.poi.hssf.record.FontRecord; -import org.apache.poi.hssf.record.UnicodeString; +import org.apache.poi.hssf.record.common.UnicodeString; /** * Excel can get cranky if you give it files containing too diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java index e69a203b9f..f8e3871bcf 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java @@ -21,7 +21,7 @@ import java.util.Iterator; import org.apache.poi.hssf.model.InternalWorkbook; import org.apache.poi.hssf.record.LabelSSTRecord; -import org.apache.poi.hssf.record.UnicodeString; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.RichTextString; /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 5c8c30eed7..97e534be2a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -52,9 +52,9 @@ import org.apache.poi.hssf.record.ObjRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RecordFactory; import org.apache.poi.hssf.record.SSTRecord; -import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.UnknownRecord; import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg; import org.apache.poi.hssf.record.formula.NameXPtg; diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 2d2784c2da..72e055fe70 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -24,7 +24,7 @@ import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.record.NameRecord; -import org.apache.poi.hssf.record.UnicodeString; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.constant.ErrorConstant; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.AddPtg; diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index 6d913bb5df..d49475f5cb 100644 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -23,6 +23,7 @@ import junit.framework.TestSuite; import org.apache.poi.hssf.record.aggregates.AllRecordAggregateTests; import org.apache.poi.hssf.record.cf.TestCellRange; import org.apache.poi.hssf.record.chart.AllChartRecordTests; +import org.apache.poi.hssf.record.common.TestUnicodeString; import org.apache.poi.hssf.record.constant.TestConstantValueParser; import org.apache.poi.hssf.record.crypto.AllHSSFEncryptionTests; import org.apache.poi.hssf.record.formula.AllFormulaTests; diff --git a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java index 4a9b7c0b8c..65d56cf318 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java @@ -30,6 +30,7 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.util.HexRead; diff --git a/src/testcases/org/apache/poi/hssf/record/TestSSTRecordSizeCalculator.java b/src/testcases/org/apache/poi/hssf/record/TestSSTRecordSizeCalculator.java index 2a0830ac79..b171a77a13 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSSTRecordSizeCalculator.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSSTRecordSizeCalculator.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.record; import junit.framework.TestCase; +import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; import org.apache.poi.util.IntMapper; diff --git a/src/testcases/org/apache/poi/hssf/record/TestUnicodeString.java b/src/testcases/org/apache/poi/hssf/record/TestUnicodeString.java deleted file mode 100644 index 1a80f9e921..0000000000 --- a/src/testcases/org/apache/poi/hssf/record/TestUnicodeString.java +++ /dev/null @@ -1,168 +0,0 @@ -/* ==================================================================== - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==================================================================== */ - -package org.apache.poi.hssf.record; - -import junit.framework.TestCase; - -import org.apache.poi.hssf.record.cont.ContinuableRecordOutput; - -/** - * Tests that {@link UnicodeString} record size calculates correctly. The record size - * is used when serializing {@link SSTRecord}s. - * - * @author Jason Height (jheight at apache.org) - */ -public final class TestUnicodeString extends TestCase { - private static final int MAX_DATA_SIZE = RecordInputStream.MAX_RECORD_DATA_SIZE; - - /** a 4 character string requiring 16 bit encoding */ - private static final String STR_16_BIT = "A\u591A\u8A00\u8A9E"; - - private static void confirmSize(int expectedSize, UnicodeString s) { - confirmSize(expectedSize, s, 0); - } - /** - * Note - a value of zero for amountUsedInCurrentRecord would only ever occur just - * after a {@link ContinueRecord} had been started. In the initial {@link SSTRecord} this - * value starts at 8 (for the first {@link UnicodeString} written). In general, it can be - * any value between 0 and {@link #MAX_DATA_SIZE} - */ - private static void confirmSize(int expectedSize, UnicodeString s, int amountUsedInCurrentRecord) { - ContinuableRecordOutput out = ContinuableRecordOutput.createForCountingOnly(); - out.writeContinue(); - for(int i=amountUsedInCurrentRecord; i>0; i--) { - out.writeByte(0); - } - int size0 = out.getTotalSize(); - s.serialize(out); - int size1 = out.getTotalSize(); - int actualSize = size1-size0; - assertEquals(expectedSize, actualSize); - } - - public void testSmallStringSize() { - //Test a basic string - UnicodeString s = makeUnicodeString("Test"); - confirmSize(7, s); - - //Test a small string that is uncompressed - s = makeUnicodeString(STR_16_BIT); - s.setOptionFlags((byte)0x01); - confirmSize(11, s); - - //Test a compressed small string that has rich text formatting - s.setString("Test"); - s.setOptionFlags((byte)0x8); - UnicodeString.FormatRun r = new UnicodeString.FormatRun((short)0,(short)1); - s.addFormatRun(r); - UnicodeString.FormatRun r2 = new UnicodeString.FormatRun((short)2,(short)2); - s.addFormatRun(r2); - confirmSize(17, s); - - //Test a uncompressed small string that has rich text formatting - s.setString(STR_16_BIT); - s.setOptionFlags((byte)0x9); - confirmSize(21, s); - - //Test a compressed small string that has rich text and extended text - s.setString("Test"); - s.setOptionFlags((byte)0xC); - s.setExtendedRst(new byte[]{(byte)0x1,(byte)0x2,(byte)0x3,(byte)0x4,(byte)0x5}); - confirmSize(26, s); - - //Test a uncompressed small string that has rich text and extended text - s.setString(STR_16_BIT); - s.setOptionFlags((byte)0xD); - confirmSize(30, s); - } - - public void testPerfectStringSize() { - //Test a basic string - UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1); - confirmSize(MAX_DATA_SIZE, s); - - //Test an uncompressed string - //Note that we can only ever get to a maximim size of 8227 since an uncompressed - //string is writing double bytes. - s = makeUnicodeString((MAX_DATA_SIZE-2-1)/2, true); - s.setOptionFlags((byte)0x1); - confirmSize(MAX_DATA_SIZE-1, s); - } - - public void testPerfectRichStringSize() { - //Test a rich text string - UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1-8-2); - s.addFormatRun(new UnicodeString.FormatRun((short)1,(short)0)); - s.addFormatRun(new UnicodeString.FormatRun((short)2,(short)1)); - s.setOptionFlags((byte)0x8); - confirmSize(MAX_DATA_SIZE, s); - - //Test an uncompressed rich text string - //Note that we can only ever get to a maximum size of 8227 since an uncompressed - //string is writing double bytes. - s = makeUnicodeString((MAX_DATA_SIZE-2-1-8-2)/2, true); - s.addFormatRun(new UnicodeString.FormatRun((short)1,(short)0)); - s.addFormatRun(new UnicodeString.FormatRun((short)2,(short)1)); - s.setOptionFlags((byte)0x9); - confirmSize(MAX_DATA_SIZE-1, s); - } - - public void testContinuedStringSize() { - //Test a basic string - UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1+20); - confirmSize(MAX_DATA_SIZE+4+1+20, s); - } - - /** Tests that a string size calculation that fits neatly in two records, the second being a continue*/ - public void testPerfectContinuedStringSize() { - //Test a basic string - int strSize = MAX_DATA_SIZE*2; - //String overhead - strSize -= 3; - //Continue Record overhead - strSize -= 4; - //Continue Record additional byte overhead - strSize -= 1; - UnicodeString s = makeUnicodeString(strSize); - confirmSize(MAX_DATA_SIZE*2, s); - } - - - private static UnicodeString makeUnicodeString(String s) { - UnicodeString st = new UnicodeString(s); - st.setOptionFlags((byte)0); - return st; - } - - private static UnicodeString makeUnicodeString(int numChars) { - return makeUnicodeString(numChars, false); - } - /** - * @param is16Bit if true the created string will have characters > 0x00FF - * @return a string of the specified number of characters - */ - private static UnicodeString makeUnicodeString(int numChars, boolean is16Bit) { - StringBuffer b = new StringBuffer(numChars); - int charBase = is16Bit ? 0x8A00 : 'A'; - for (int i=0;iamountUsedInCurrentRecord would only ever occur just + * after a {@link ContinueRecord} had been started. In the initial {@link SSTRecord} this + * value starts at 8 (for the first {@link UnicodeString} written). In general, it can be + * any value between 0 and {@link #MAX_DATA_SIZE} + */ + private static void confirmSize(int expectedSize, UnicodeString s, int amountUsedInCurrentRecord) { + ContinuableRecordOutput out = ContinuableRecordOutput.createForCountingOnly(); + out.writeContinue(); + for(int i=amountUsedInCurrentRecord; i>0; i--) { + out.writeByte(0); + } + int size0 = out.getTotalSize(); + s.serialize(out); + int size1 = out.getTotalSize(); + int actualSize = size1-size0; + assertEquals(expectedSize, actualSize); + } + + public void testSmallStringSize() { + //Test a basic string + UnicodeString s = makeUnicodeString("Test"); + confirmSize(7, s); + + //Test a small string that is uncompressed + s = makeUnicodeString(STR_16_BIT); + s.setOptionFlags((byte)0x01); + confirmSize(11, s); + + //Test a compressed small string that has rich text formatting + s.setString("Test"); + s.setOptionFlags((byte)0x8); + UnicodeString.FormatRun r = new UnicodeString.FormatRun((short)0,(short)1); + s.addFormatRun(r); + UnicodeString.FormatRun r2 = new UnicodeString.FormatRun((short)2,(short)2); + s.addFormatRun(r2); + confirmSize(17, s); + + //Test a uncompressed small string that has rich text formatting + s.setString(STR_16_BIT); + s.setOptionFlags((byte)0x9); + confirmSize(21, s); + + //Test a compressed small string that has rich text and extended text + s.setString("Test"); + s.setOptionFlags((byte)0xC); + s.setExtendedRst(new byte[]{(byte)0x1,(byte)0x2,(byte)0x3,(byte)0x4,(byte)0x5}); + confirmSize(26, s); + + //Test a uncompressed small string that has rich text and extended text + s.setString(STR_16_BIT); + s.setOptionFlags((byte)0xD); + confirmSize(30, s); + } + + public void testPerfectStringSize() { + //Test a basic string + UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1); + confirmSize(MAX_DATA_SIZE, s); + + //Test an uncompressed string + //Note that we can only ever get to a maximim size of 8227 since an uncompressed + //string is writing double bytes. + s = makeUnicodeString((MAX_DATA_SIZE-2-1)/2, true); + s.setOptionFlags((byte)0x1); + confirmSize(MAX_DATA_SIZE-1, s); + } + + public void testPerfectRichStringSize() { + //Test a rich text string + UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1-8-2); + s.addFormatRun(new UnicodeString.FormatRun((short)1,(short)0)); + s.addFormatRun(new UnicodeString.FormatRun((short)2,(short)1)); + s.setOptionFlags((byte)0x8); + confirmSize(MAX_DATA_SIZE, s); + + //Test an uncompressed rich text string + //Note that we can only ever get to a maximum size of 8227 since an uncompressed + //string is writing double bytes. + s = makeUnicodeString((MAX_DATA_SIZE-2-1-8-2)/2, true); + s.addFormatRun(new UnicodeString.FormatRun((short)1,(short)0)); + s.addFormatRun(new UnicodeString.FormatRun((short)2,(short)1)); + s.setOptionFlags((byte)0x9); + confirmSize(MAX_DATA_SIZE-1, s); + } + + public void testContinuedStringSize() { + //Test a basic string + UnicodeString s = makeUnicodeString(MAX_DATA_SIZE-2-1+20); + confirmSize(MAX_DATA_SIZE+4+1+20, s); + } + + /** Tests that a string size calculation that fits neatly in two records, the second being a continue*/ + public void testPerfectContinuedStringSize() { + //Test a basic string + int strSize = MAX_DATA_SIZE*2; + //String overhead + strSize -= 3; + //Continue Record overhead + strSize -= 4; + //Continue Record additional byte overhead + strSize -= 1; + UnicodeString s = makeUnicodeString(strSize); + confirmSize(MAX_DATA_SIZE*2, s); + } + + + private static UnicodeString makeUnicodeString(String s) { + UnicodeString st = new UnicodeString(s); + st.setOptionFlags((byte)0); + return st; + } + + private static UnicodeString makeUnicodeString(int numChars) { + return makeUnicodeString(numChars, false); + } + /** + * @param is16Bit if true the created string will have characters > 0x00FF + * @return a string of the specified number of characters + */ + private static UnicodeString makeUnicodeString(int numChars, boolean is16Bit) { + StringBuffer b = new StringBuffer(numChars); + int charBase = is16Bit ? 0x8A00 : 'A'; + for (int i=0;i