From 409941a8be790f7c33f5d19cc68c80a85d05a98d Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Wed, 3 Sep 2008 19:22:53 +0000 Subject: [PATCH] Initial work on bug 45720 - copy 'FilterDatabase' named record when cloning sheets. Some clean-up in NameRecord. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@691740 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/record/NameRecord.java | 698 +++++++----------- .../poi/hssf/record/formula/Ref3DPtg.java | 6 +- .../apache/poi/hssf/usermodel/HSSFName.java | 61 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 105 ++- .../apache/poi/hssf/usermodel/TestBugs.java | 19 +- .../poi/hssf/usermodel/TestHSSFSheet.java | 12 +- .../poi/hssf/usermodel/TestHSSFWorkbook.java | 12 +- .../poi/hssf/util/TestAreaReference.java | 14 +- 8 files changed, 400 insertions(+), 527 deletions(-) diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 729f9246b..bc2c53586 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -17,9 +17,8 @@ package org.apache.poi.hssf.record; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import java.util.Stack; import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.record.formula.Area3DPtg; @@ -44,51 +43,33 @@ import org.apache.poi.util.StringUtil; */ public final class NameRecord extends Record { public final static short sid = 0x0018; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_CONSOLIDATE_AREA = (byte)1; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_AUTO_OPEN = (byte)2; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_CONSOLIDATE_AREA = 1; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_OPEN = 2; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_CLOSE = 3; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_DATABASE = 4; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_CRITERIA = 5; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_AUTO_CLOSE = (byte)3; + public final static byte BUILTIN_PRINT_AREA = 6; + public final static byte BUILTIN_PRINT_TITLE = 7; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_DATABASE = (byte)4; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_RECORDER = 8; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_DATA_FORM = 9; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_ACTIVATE = 10; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_DEACTIVATE = 11; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_SHEET_TITLE = 12; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_CRITERIA = (byte)5; - - public final static byte BUILTIN_PRINT_AREA = (byte)6; - public final static byte BUILTIN_PRINT_TITLE = (byte)7; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_RECORDER = (byte)8; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_DATA_FORM = (byte)9; - - /**Included for completeness sake, not implemented - */ + public final static byte BUILTIN_FILTER_DB = 13; - public final static byte BUILTIN_AUTO_ACTIVATE = (byte)10; - - /**Included for completeness sake, not implemented - */ - - public final static byte BUILTIN_AUTO_DEACTIVATE = (byte)11; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_SHEET_TITLE = (byte)12; - private static final class Option { public static final int OPT_HIDDEN_NAME = 0x0001; public static final int OPT_FUNCTION_NAME = 0x0002; @@ -98,22 +79,16 @@ public final class NameRecord extends Record { public static final int OPT_BUILTIN = 0x0020; public static final int OPT_BINDATA = 0x1000; } - + private short field_1_option_flag; private byte field_2_keyboard_shortcut; - private byte field_3_length_name_text; - private short field_4_length_name_definition; private short field_5_index_to_sheet; // unused: see field_6 /** the one based sheet number. Zero if this is a global name */ private int field_6_sheetNumber; - private byte field_7_length_custom_menu; - private byte field_8_length_description_text; - private byte field_9_length_help_topic_text; - private byte field_10_length_status_bar_text; - private byte field_11_compressed_unicode_flag; // not documented - private byte field_12_builtIn_name; + private boolean field_11_nameIsMultibyte; + private byte field_12_built_in_code; private String field_12_name_text; - private Stack field_13_name_definition; + private Ptg[] field_13_name_definition; private String field_14_custom_menu_text; private String field_15_description_text; private String field_16_help_topic_text; @@ -122,13 +97,13 @@ public final class NameRecord extends Record { /** Creates new NameRecord */ public NameRecord() { - field_13_name_definition = new Stack(); + field_13_name_definition = Ptg.EMPTY_PTG_ARRAY; - field_12_name_text = new String(); - field_14_custom_menu_text = new String(); - field_15_description_text = new String(); - field_16_help_topic_text = new String(); - field_17_status_bar_text = new String(); + field_12_name_text = ""; + field_14_custom_menu_text = ""; + field_15_description_text = ""; + field_16_help_topic_text = ""; + field_17_status_bar_text = ""; } /** @@ -146,19 +121,10 @@ public final class NameRecord extends Record { */ public NameRecord(byte builtin, int sheetNumber) { - this(); - this.field_12_builtIn_name = builtin; - this.setOptionFlag((short)(this.field_1_option_flag | Option.OPT_BUILTIN)); - this.setNameTextLength((byte)1); + this(); + field_12_built_in_code = builtin; + setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN)); field_6_sheetNumber = sheetNumber; //the extern sheets are set through references - - //clearing these because they are not used with builtin records - this.setCustomMenuLength((byte)0); - this.setDescriptionTextLength((byte)0); - this.setHelpTopicLength((byte)0); - this.setStatusBarLength((byte)0); - - } /** sets the option flag for the named range @@ -176,34 +142,9 @@ public final class NameRecord extends Record { field_2_keyboard_shortcut = shortcut; } - /** sets the name of the named range length - * @param length name length - */ - public void setNameTextLength(byte length){ - field_3_length_name_text = length; - } - - /** sets the definition (reference - formula) length - * @param length defenition length - */ - public void setDefinitionTextLength(short length){ - field_4_length_name_definition = length; - } - - /** sets the index number to the extern sheet (thats is what writen in documentation - * but as i saw , it works differently) - * @param index extern sheet index - */ - public void setUnused(short index){ - field_5_index_to_sheet = index; - - // field_6_equals_to_index_to_sheet is equal to field_5_index_to_sheet -// field_6_equals_to_index_to_sheet = index; - } - /** * For named ranges, and built-in names - * @return the 1-based sheet number. Zero if this is a global name + * @return the 1-based sheet number. Zero if this is a global name */ public int getSheetNumber() { @@ -226,49 +167,12 @@ public final class NameRecord extends Record { } - /** sets the custom menu length - * @param length custom menu length - */ - public void setCustomMenuLength(byte length){ - field_7_length_custom_menu = length; - } - - /** sets the length of named range description - * @param length description length - */ - public void setDescriptionTextLength(byte length){ - field_8_length_description_text = length; - } - - /** sets the help topic length - * @param length help topic length - */ - public void setHelpTopicLength(byte length){ - field_9_length_help_topic_text = length; - } - - /** sets the length of the status bar text - * @param length status bar text length - */ - public void setStatusBarLength(byte length){ - field_10_length_status_bar_text = length; - } - - /** sets the compressed unicode flag - * @param flag unicode flag - */ - public void setCompressedUnicodeFlag(byte flag) { - field_11_compressed_unicode_flag = flag; - } - /** sets the name of the named range * @param name named range name */ public void setNameText(String name){ field_12_name_text = name; - setCompressedUnicodeFlag( - StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0 - ); + field_11_nameIsMultibyte = StringUtil.hasMultibyte(name); } /** sets the custom menu text @@ -313,72 +217,15 @@ public final class NameRecord extends Record { return field_2_keyboard_shortcut ; } - /** + /** * gets the name length, in characters * @return name length */ - public byte getNameTextLength(){ - return field_3_length_name_text; - } - - /** - * gets the name length, in bytes - * @return raw name length - */ - public byte getRawNameTextLength(){ - if( (field_11_compressed_unicode_flag & 0x01) == 1 ) { - return (byte)(2 * field_3_length_name_text); + private int getNameTextLength(){ + if (isBuiltInName()) { + return 1; } - return field_3_length_name_text; - } - - /** get the definition length - * @return definition length - */ - public short getDefinitionLength(){ - return field_4_length_name_definition; - } - - /** gets the index to extern sheet - * @return index to extern sheet - */ - public short getUnused(){ - return field_5_index_to_sheet; - } - - /** gets the custom menu length - * @return custom menu length - */ - public byte getCustomMenuLength(){ - return field_7_length_custom_menu; - } - - /** gets the description text length - * @return description text length - */ - public byte getDescriptionTextLength(){ - return field_8_length_description_text; - } - - /** gets the help topic length - * @return help topic length - */ - public byte getHelpTopicLength(){ - return field_9_length_help_topic_text; - } - - /** get the status bar text length - * @return satus bar length - */ - public byte getStatusBarLength(){ - return field_10_length_status_bar_text; - } - - /** gets the name compressed Unicode flag - * @return compressed unicode flag - */ - public byte getCompressedUnicodeFlag() { - return field_11_compressed_unicode_flag; + return field_12_name_text.length(); } @@ -388,6 +235,13 @@ public final class NameRecord extends Record { public boolean isHiddenName() { return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0; } + public void setHidden(boolean b) { + if (b) { + field_1_option_flag |= Option.OPT_HIDDEN_NAME; + } else { + field_1_option_flag &= (~Option.OPT_HIDDEN_NAME); + } + } /** * @return true if name is a function */ @@ -419,7 +273,7 @@ public final class NameRecord extends Record { */ public boolean isBuiltInName() { - return ((this.field_1_option_flag & Option.OPT_BUILTIN) != 0); + return ((field_1_option_flag & Option.OPT_BUILTIN) != 0); } @@ -428,7 +282,7 @@ public final class NameRecord extends Record { */ public String getNameText(){ - return this.isBuiltInName() ? this.translateBuiltInName(this.getBuiltInName()) : field_12_name_text; + return isBuiltInName() ? translateBuiltInName(getBuiltInName()) : field_12_name_text; } /** Gets the Built In Name @@ -436,19 +290,19 @@ public final class NameRecord extends Record { */ public byte getBuiltInName() { - return this.field_12_builtIn_name; + return field_12_built_in_code; } /** gets the definition, reference (Formula) - * @return definition -- can be null if we cant parse ptgs + * @return the name formula. never null */ - public List getNameDefinition() { - return field_13_name_definition; + public Ptg[] getNameDefinition() { + return (Ptg[]) field_13_name_definition.clone(); } - public void setNameDefinition(Stack nameDefinition) { - field_13_name_definition = nameDefinition; + public void setNameDefinition(Ptg[] ptgs) { + field_13_name_definition = (Ptg[]) ptgs.clone(); } /** get the custom menu text @@ -490,7 +344,8 @@ public final class NameRecord extends Record { throw new RecordFormatException("NOT A valid Name RECORD"); } } - + + /** * called by the class that is responsible for writing this sucker. * Subclasses should implement this so that their data is passed back in a @@ -498,109 +353,107 @@ public final class NameRecord extends Record { * @param data byte array containing instance data * @return number of bytes written */ - public int serialize( int offset, byte[] data ) - { - LittleEndian.putShort( data, 0 + offset, sid ); - short size = (short)( 15 + getTextsLength() + getNameDefinitionSize()); - LittleEndian.putShort( data, 2 + offset, size ); - // size defined below - LittleEndian.putShort( data, 4 + offset, getOptionFlag() ); - data[6 + offset] = getKeyboardShortcut(); - data[7 + offset] = getNameTextLength(); - LittleEndian.putShort( data, 8 + offset, getDefinitionLength() ); - LittleEndian.putShort( data, 10 + offset, getUnused() ); - LittleEndian.putUShort( data, 12 + offset, field_6_sheetNumber); - data[14 + offset] = getCustomMenuLength(); - data[15 + offset] = getDescriptionTextLength(); - data[16 + offset] = getHelpTopicLength(); - data[17 + offset] = getStatusBarLength(); - data[18 + offset] = getCompressedUnicodeFlag(); + public int serialize( int offset, byte[] data ) { - int start_of_name_definition = 19 + field_3_length_name_text; - - if (this.isBuiltInName()) { - //can send the builtin name directly in - data [19 + offset] = this.getBuiltInName(); - } else if ((this.getCompressedUnicodeFlag() & 0x01) == 1) { - StringUtil.putUnicodeLE( getNameText(), data, 19 + offset ); - start_of_name_definition = 19 + (2 * field_3_length_name_text); - } else { - StringUtil.putCompressedUnicode( getNameText(), data, 19 + offset ); - } - - - Ptg.serializePtgStack(field_13_name_definition, data, start_of_name_definition + offset ); - - - int start_of_custom_menu_text = start_of_name_definition + field_4_length_name_definition; - StringUtil.putCompressedUnicode( getCustomMenuText(), data, start_of_custom_menu_text + offset ); - - int start_of_description_text = start_of_custom_menu_text + field_7_length_custom_menu; - StringUtil.putCompressedUnicode( getDescriptionText(), data, start_of_description_text + offset ); - - int start_of_help_topic_text = start_of_description_text + field_8_length_description_text; - StringUtil.putCompressedUnicode( getHelpTopicText(), data, start_of_help_topic_text + offset ); - - int start_of_status_bar_text = start_of_help_topic_text + field_9_length_help_topic_text; - StringUtil.putCompressedUnicode( getStatusBarText(), data, start_of_status_bar_text + offset ); - - return getRecordSize(); - /* } */ - } - - /** - * Gets the length of all texts, in bytes - * @return total length - */ - public int getTextsLength(){ - int result; - - result = getRawNameTextLength() + getDescriptionTextLength() + - getHelpTopicLength() + getStatusBarLength(); - - return result; - } - - private int getNameDefinitionSize() { - int result = 0; - List list = field_13_name_definition; + int field_7_length_custom_menu = field_14_custom_menu_text.length(); + int field_8_length_description_text = field_15_description_text.length(); + int field_9_length_help_topic_text = field_16_help_topic_text.length(); + int field_10_length_status_bar_text = field_17_status_bar_text.length(); + int rawNameSize = getNameRawSize(); - for (int k = 0; k < list.size(); k++) - { - Ptg ptg = ( Ptg ) list.get(k); - - result += ptg.getSize(); + int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition); + int dataSize = 15 // 4 shorts + 7 bytes + + rawNameSize + + field_7_length_custom_menu + + field_8_length_description_text + + field_9_length_help_topic_text + + field_10_length_status_bar_text + + formulaTotalSize; + + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); + // size defined below + LittleEndian.putShort(data, 4 + offset, getOptionFlag()); + LittleEndian.putByte(data, 6 + offset, getKeyboardShortcut()); + LittleEndian.putByte(data, 7 + offset, getNameTextLength()); + // Note - + LittleEndian.putUShort(data, 8 + offset, Ptg.getEncodedSizeWithoutArrayData(field_13_name_definition)); + LittleEndian.putUShort(data, 10 + offset, field_5_index_to_sheet); + LittleEndian.putUShort(data, 12 + offset, field_6_sheetNumber); + LittleEndian.putByte(data, 14 + offset, field_7_length_custom_menu); + LittleEndian.putByte(data, 15 + offset, field_8_length_description_text); + LittleEndian.putByte(data, 16 + offset, field_9_length_help_topic_text); + LittleEndian.putByte(data, 17 + offset, field_10_length_status_bar_text); + LittleEndian.putByte(data, 18 + offset, field_11_nameIsMultibyte ? 1 : 0); + int pos = 19 + offset; + + if (isBuiltInName()) { + //can send the builtin name directly in + LittleEndian.putByte(data, pos, field_12_built_in_code); + } else { + String nameText = field_12_name_text; + if (field_11_nameIsMultibyte) { + StringUtil.putUnicodeLE(nameText, data, pos); + } else { + StringUtil.putCompressedUnicode(nameText, data, pos); + } } - return result; + pos += rawNameSize; + + Ptg.serializePtgs(field_13_name_definition, data, pos); + pos += formulaTotalSize; + + StringUtil.putCompressedUnicode( getCustomMenuText(), data, pos); + pos += field_7_length_custom_menu; + StringUtil.putCompressedUnicode( getDescriptionText(), data, pos); + pos += field_8_length_description_text; + StringUtil.putCompressedUnicode( getHelpTopicText(), data, pos); + pos += field_9_length_help_topic_text; + StringUtil.putCompressedUnicode( getStatusBarText(), data, pos); + + return 4 + dataSize; + } + private int getNameRawSize() { + if (isBuiltInName()) { + return 1; + } + int nChars = field_12_name_text.length(); + if(field_11_nameIsMultibyte) { + return 2 * nChars; + } + return nChars; } /** returns the record size */ public int getRecordSize(){ - int result; - - result = 19 + getTextsLength() + getNameDefinitionSize(); - - - return result; + return 4 // sid + size + + 15 // 4 shorts + 7 bytes + + getNameRawSize() + + field_14_custom_menu_text.length() + + field_15_description_text.length() + + field_16_help_topic_text.length() + + field_17_status_bar_text.length() + + Ptg.getEncodedSize(field_13_name_definition); } /** gets the extern sheet number * @return extern sheet index */ public short getExternSheetNumber(){ - if (field_13_name_definition == null || field_13_name_definition.isEmpty()) return 0; - Ptg ptg = (Ptg) field_13_name_definition.peek(); - short result = 0; + if (field_13_name_definition.length < 1) { + return 0; + } + Ptg ptg = field_13_name_definition[0]; if (ptg.getClass() == Area3DPtg.class){ - result = ((Area3DPtg) ptg).getExternSheetIndex(); + return ((Area3DPtg) ptg).getExternSheetIndex(); - } else if (ptg.getClass() == Ref3DPtg.class){ - result = ((Ref3DPtg) ptg).getExternSheetIndex(); } - - return result; + if (ptg.getClass() == Ref3DPtg.class){ + return ((Ref3DPtg) ptg).getExternSheetIndex(); + } + return 0; } /** sets the extern sheet number @@ -609,11 +462,13 @@ public final class NameRecord extends Record { public void setExternSheetNumber(short externSheetNumber){ Ptg ptg; - if (field_13_name_definition == null || field_13_name_definition.isEmpty()){ - field_13_name_definition = new Stack(); + if (field_13_name_definition.length < 1){ ptg = createNewPtg(); + field_13_name_definition = new Ptg[] { + ptg, + }; } else { - ptg = (Ptg) field_13_name_definition.peek(); + ptg = field_13_name_definition[0]; } if (ptg.getClass() == Area3DPtg.class){ @@ -625,11 +480,8 @@ public final class NameRecord extends Record { } - private Ptg createNewPtg(){ - Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised - field_13_name_definition.push(ptg); - - return ptg; + private static Ptg createNewPtg(){ + return new Area3DPtg("A1", 0); // TODO - change to not be partially initialised } /** gets the reference , the area only (range) @@ -646,16 +498,14 @@ public final class NameRecord extends Record { //Trying to find if what ptg do we need RangeAddress ra = new RangeAddress(ref); Ptg oldPtg; - Ptg ptg; - if (field_13_name_definition==null ||field_13_name_definition.isEmpty()){ - field_13_name_definition = new Stack(); + if (field_13_name_definition.length < 1){ oldPtg = createNewPtg(); } else { //Trying to find extern sheet index - oldPtg = (Ptg) field_13_name_definition.pop(); + oldPtg = field_13_name_definition[0]; } - + List temp = new ArrayList(); short externSheetIndex = 0; if (oldPtg.getClass() == Area3DPtg.class){ @@ -667,29 +517,27 @@ public final class NameRecord extends Record { if (ra.hasRange()) { // Is it contiguous or not? - AreaReference[] refs = - AreaReference.generateContiguous(ref); - this.setDefinitionTextLength((short)0); + AreaReference[] refs = AreaReference.generateContiguous(ref); - // Add the area reference(s) + // Add the area reference(s) for(int i=0; i 1) { - ptg = UnionPtg.instance; - field_13_name_definition.push(ptg); - this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) ); + Ptg ptg = UnionPtg.instance; + temp.add(ptg); } } else { - ptg = new Ref3DPtg(); + Ptg ptg = new Ref3DPtg(); ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex); ((Ref3DPtg) ptg).setArea(ref); - field_13_name_definition.push(ptg); - this.setDefinitionTextLength((short)ptg.getSize()); + temp.add(ptg); } + Ptg[] ptgs = new Ptg[temp.size()]; + temp.toArray(ptgs); + field_13_name_definition = ptgs; } /** @@ -699,40 +547,36 @@ public final class NameRecord extends Record { * @param in the RecordInputstream to read the record from */ protected void fillFields(RecordInputStream in) { - field_1_option_flag = in.readShort(); - field_2_keyboard_shortcut = in.readByte(); - field_3_length_name_text = in.readByte(); - field_4_length_name_definition = in.readShort(); - field_5_index_to_sheet = in.readShort(); - field_6_sheetNumber = in.readUShort(); - field_7_length_custom_menu = in.readByte(); - field_8_length_description_text = in.readByte(); - field_9_length_help_topic_text = in.readByte(); - field_10_length_status_bar_text = in.readByte(); - - //store the name in byte form if it's a builtin name - field_11_compressed_unicode_flag= in.readByte(); - if (this.isBuiltInName()) { - field_12_builtIn_name = in.readByte(); - } else { - if (field_11_compressed_unicode_flag == 1) { - field_12_name_text = in.readUnicodeLEString(field_3_length_name_text); - } else { - field_12_name_text = in.readCompressedUnicode(field_3_length_name_text); - } + field_1_option_flag = in.readShort(); + field_2_keyboard_shortcut = in.readByte(); + int field_3_length_name_text = in.readByte(); + int field_4_length_name_definition = in.readShort(); + field_5_index_to_sheet = in.readShort(); + field_6_sheetNumber = in.readUShort(); + int field_7_length_custom_menu = in.readUByte(); + int field_8_length_description_text = in.readUByte(); + int field_9_length_help_topic_text = in.readUByte(); + int field_10_length_status_bar_text = in.readUByte(); + + //store the name in byte form if it's a built-in name + field_11_nameIsMultibyte = (in.readByte() != 0); + if (isBuiltInName()) { + field_12_built_in_code = in.readByte(); + } else { + if (field_11_nameIsMultibyte) { + field_12_name_text = in.readUnicodeLEString(field_3_length_name_text); + } else { + field_12_name_text = in.readCompressedUnicode(field_3_length_name_text); + } } - - field_13_name_definition = Ptg.createParsedExpressionTokens(field_4_length_name_definition, in); - + + field_13_name_definition = Ptg.readTokens(field_4_length_name_definition, in); + //Who says that this can only ever be compressed unicode??? - field_14_custom_menu_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_7_length_custom_menu)); - - field_15_description_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_8_length_description_text)); - - field_16_help_topic_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_9_length_help_topic_text)); - - field_17_status_bar_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_10_length_status_bar_text)); - /*} */ + field_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu); + field_15_description_text = in.readCompressedUnicode(field_8_length_description_text); + field_16_help_topic_text = in.readCompressedUnicode(field_9_length_help_topic_text); + field_17_status_bar_text = in.readCompressedUnicode(field_10_length_status_bar_text); } /** @@ -742,113 +586,90 @@ public final class NameRecord extends Record { return sid; } /* - 20 00 - 00 - 01 + 20 00 + 00 + 01 1A 00 // sz = 0x1A = 26 - 00 00 - 01 00 - 00 - 00 - 00 - 00 + 00 00 + 01 00 + 00 + 00 + 00 + 00 00 // unicode flag 07 // name - + 29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26 00 07 00 07 00 00 00 FF 00 10 // } - - - - 20 00 - 00 - 01 + + + + 20 00 + 00 + 01 0B 00 // sz = 0xB = 11 - 00 00 - 01 00 - 00 - 00 - 00 - 00 + 00 00 + 01 00 + 00 + 00 + 00 + 00 00 // unicode flag 07 // name - + 3B 00 00 07 00 07 00 00 00 FF 00 // { 11 } */ /* - 18, 00, - 1B, 00, - - 20, 00, - 00, - 01, - 0B, 00, - 00, - 00, - 00, - 00, - 00, - 07, - 3B 00 00 07 00 07 00 00 00 FF 00 ] + 18, 00, + 1B, 00, + + 20, 00, + 00, + 01, + 0B, 00, + 00, + 00, + 00, + 00, + 00, + 07, + 3B 00 00 07 00 07 00 00 00 FF 00 ] */ - /** - * @see Object#toString() - */ public String toString() { - StringBuffer buffer = new StringBuffer(); + StringBuffer sb = new StringBuffer(); - buffer.append("[NAME]\n"); - buffer.append(" .option flags = ").append( HexDump.toHex( field_1_option_flag ) ) - .append("\n"); - buffer.append(" .keyboard shortcut = ").append( HexDump.toHex( field_2_keyboard_shortcut ) ) - .append("\n"); - buffer.append(" .length of the name = ").append( field_3_length_name_text ) - .append("\n"); - buffer.append(" .size of the formula data = ").append( field_4_length_name_definition ) - .append("\n"); - buffer.append(" .unused = ").append( field_5_index_to_sheet ) - .append("\n"); - buffer.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ) - .append("\n"); - buffer.append(" .Length of menu text (character count) = ").append( field_7_length_custom_menu ) - .append("\n"); - buffer.append(" .Length of description text (character count) = ").append( field_8_length_description_text ) - .append("\n"); - buffer.append(" .Length of help topic text (character count) = ").append( field_9_length_help_topic_text ) - .append("\n"); - buffer.append(" .Length of status bar text (character count) = ").append( field_10_length_status_bar_text ) - .append("\n"); - buffer.append(" .Name (Unicode flag) = ").append( field_11_compressed_unicode_flag ) - .append("\n"); - buffer.append(" .Name (Unicode text) = ").append( getNameText() ) - .append("\n"); - - buffer.append(" .Parts (" + field_13_name_definition.size() +"):") - .append("\n"); - Iterator it = field_13_name_definition.iterator(); - while(it.hasNext()) { - Ptg ptg = (Ptg)it.next(); - buffer.append(" " + ptg.toString()).append("\n"); + sb.append("[NAME]\n"); + sb.append(" .option flags = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n"); + sb.append(" .keyboard shortcut = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n"); + sb.append(" .length of the name = ").append(getNameTextLength()).append("\n"); + sb.append(" .unused = ").append( field_5_index_to_sheet ).append("\n"); + sb.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ).append("\n"); + sb.append(" .Menu text length = ").append(field_14_custom_menu_text.length()).append("\n"); + sb.append(" .Description text length= ").append(field_15_description_text.length()).append("\n"); + sb.append(" .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n"); + sb.append(" .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n"); + sb.append(" .NameIsMultibyte = ").append(field_11_nameIsMultibyte).append("\n"); + sb.append(" .Name (Unicode text) = ").append( getNameText() ).append("\n"); + sb.append(" .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n"); + for (int i = 0; i < field_13_name_definition.length; i++) { + Ptg ptg = field_13_name_definition[i]; + sb.append(" " + ptg.toString()).append(ptg.getRVAType()).append("\n"); } - - buffer.append(" .Menu text (Unicode string without length field) = ").append( field_14_custom_menu_text ) - .append("\n"); - buffer.append(" .Description text (Unicode string without length field) = ").append( field_15_description_text ) - .append("\n"); - buffer.append(" .Help topic text (Unicode string without length field) = ").append( field_16_help_topic_text ) - .append("\n"); - buffer.append(" .Status bar text (Unicode string without length field) = ").append( field_17_status_bar_text ) - .append("\n"); - buffer.append("[/NAME]\n"); - - return buffer.toString(); + + sb.append(" .Menu text = ").append(field_14_custom_menu_text).append("\n"); + sb.append(" .Description text= ").append(field_15_description_text).append("\n"); + sb.append(" .Help topic text = ").append(field_16_help_topic_text).append("\n"); + sb.append(" .Status bar text = ").append(field_17_status_bar_text).append("\n"); + sb.append("[/NAME]\n"); + + return sb.toString(); } /**Creates a human readable name for built in types * @return Unknown if the built-in name cannot be translated */ - protected String translateBuiltInName(byte name) + private static String translateBuiltInName(byte name) { switch (name) { @@ -859,14 +680,15 @@ public final class NameRecord extends Record { case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area"; case NameRecord.BUILTIN_CRITERIA : return "Criteria"; case NameRecord.BUILTIN_DATABASE : return "Database"; - case NameRecord.BUILTIN_DATA_FORM : return "Data_Form"; + case NameRecord.BUILTIN_DATA_FORM : return "Data_Form"; case NameRecord.BUILTIN_PRINT_AREA : return "Print_Area"; case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles"; case NameRecord.BUILTIN_RECORDER : return "Recorder"; case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title"; - + case NameRecord.BUILTIN_FILTER_DB : return "_FilterDatabase"; + } - + return "Unknown"; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java index 69c7f1830..d1133d980 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java @@ -41,7 +41,7 @@ public final class Ref3DPtg extends OperandPtg { private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); private final static int SIZE = 7; // 6 + 1 for Ptg - private short field_1_index_extern_sheet; + private int field_1_index_extern_sheet; /** The row index - zero based unsigned 16 bit value */ private int field_2_row; /** Field 2 @@ -93,10 +93,10 @@ public final class Ref3DPtg extends OperandPtg { } public short getExternSheetIndex(){ - return field_1_index_extern_sheet; + return (short)field_1_index_extern_sheet; } - public void setExternSheetIndex(short index){ + public void setExternSheetIndex(int index){ field_1_index_extern_sheet = index; } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java index 18aeaed8f..1f0dffda9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java @@ -22,15 +22,15 @@ import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.util.RangeAddress; /** - * High Level Representation of a 'defined name' which could be a 'built-in' name, + * High Level Representation of a 'defined name' which could be a 'built-in' name, * 'named range' or name of a user defined function. - * + * * @author Libin Roman (Vista Portal LDT. Developer) */ public final class HSSFName { private HSSFWorkbook _book; private NameRecord _definedNameRec; - + /** Creates new HSSFName - called by HSSFWorkbook to create a sheet from * scratch. * @@ -42,69 +42,68 @@ public final class HSSFName { _book = book; _definedNameRec = name; } - + /** Get the sheets name which this named range is referenced to * @return sheet name, which this named range referred to - */ + */ public String getSheetName() { short indexToExternSheet = _definedNameRec.getExternSheetNumber(); - + return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); } - - /** + + /** * @return text name of this defined name - */ + */ public String getNameName(){ return _definedNameRec.getNameText(); } - - /** + + /** * sets the name of the named range * @param nameName named range name to set - */ + */ public void setNameName(String nameName){ _definedNameRec.setNameText(nameName); - _definedNameRec.setNameTextLength((byte)nameName.length()); Workbook wb = _book.getWorkbook(); - + //Check to ensure no other names have the same case-insensitive name for ( int i = wb.getNumNames()-1; i >=0; i-- ) { - NameRecord rec = wb.getNameRecord(i); - if (rec != _definedNameRec) { - if (rec.getNameText().equalsIgnoreCase(getNameName())) - throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); - } + NameRecord rec = wb.getNameRecord(i); + if (rec != _definedNameRec) { + if (rec.getNameText().equalsIgnoreCase(getNameName())) + throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); + } } } - /** + /** * Note - this method only applies to named ranges * @return the formula text defining the named range - */ + */ public String getReference() { - if (_definedNameRec.isFunctionName()) { - throw new IllegalStateException("Only applicable to named ranges"); - } + if (_definedNameRec.isFunctionName()) { + throw new IllegalStateException("Only applicable to named ranges"); + } return _definedNameRec.getAreaReference(_book); } - /** + /** * sets the sheet name which this named range referenced to * @param sheetName the sheet name of the reference - */ + */ private void setSheetName(String sheetName){ int sheetNumber = _book.getSheetIndex(sheetName); short externSheetNumber = _book.getExternalSheetIndex(sheetNumber); _definedNameRec.setExternSheetNumber(externSheetNumber); } - - /** + + /** * sets the reference of this named range * @param ref the reference to set - */ + */ public void setReference(String ref){ RangeAddress ra = new RangeAddress(ref); @@ -115,7 +114,7 @@ public final class HSSFName { setSheetName(sheetName); } - //allow the poi utilities to parse it out + //allow the poi utilities to parse it out _definedNameRec.setAreaReference(ref); } @@ -129,7 +128,7 @@ public final class HSSFName { return "#REF!".endsWith(ref); } public boolean isFunctionName() { - return _definedNameRec.isFunctionName(); + return _definedNameRec.isFunctionName(); } public String toString() { StringBuffer sb = new StringBuffer(64); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 3ea495268..0c3d27182 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; -import java.util.Stack; import org.apache.poi.POIDocument; import org.apache.poi.ddf.EscherBSERecord; @@ -55,6 +54,8 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg; import org.apache.poi.hssf.record.formula.NameXPtg; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.usermodel.HSSFRow.MissingCellPolicy; import org.apache.poi.hssf.util.CellReference; @@ -666,33 +667,81 @@ public class HSSFWorkbook extends POIDocument * @return HSSFSheet representing the cloned sheet. */ - public HSSFSheet cloneSheet(int sheetNum) { - validateSheetIndex(sheetNum); - HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum); - String srcName = workbook.getSheetName(sheetNum); + public HSSFSheet cloneSheet(int sheetIndex) { + validateSheetIndex(sheetIndex); + HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetIndex); + String srcName = workbook.getSheetName(sheetIndex); HSSFSheet clonedSheet = srcSheet.cloneSheet(this); clonedSheet.setSelected(false); clonedSheet.setActive(false); + String name = getUniqueSheetName(srcName); + int newSheetIndex = _sheets.size(); _sheets.add(clonedSheet); - int i = 1; + workbook.setSheetName(newSheetIndex, name); + + // Check this sheet has an autofilter, (which has a built-in NameRecord at workbook level) + int filterDbNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_FILTER_DB); + if (filterDbNameIndex >=0) { + NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex); + // copy original formula but adjust 3D refs to the new external sheet index + int newExtSheetIx = getExternalSheetIndex(newSheetIndex); + Ptg[] ptgs = origNameRecord.getNameDefinition(); + for (int i=0; i< ptgs.length; i++) { + Ptg ptg = ptgs[i]; + ptg = ptg.copy(); + + if (ptg instanceof Area3DPtg) { + Area3DPtg a3p = (Area3DPtg) ptg; + a3p.setExternSheetIndex(newExtSheetIx); + } else if (ptg instanceof Ref3DPtg) { + Ref3DPtg r3p = (Ref3DPtg) ptg; + r3p.setExternSheetIndex(newExtSheetIx); + } + ptgs[i] = ptg; + } + NameRecord newNameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_FILTER_DB, newSheetIndex+1); + newNameRecord.setNameDefinition(ptgs); + newNameRecord.setHidden(true); + HSSFName newName = new HSSFName(this, newNameRecord); + names.add(newName); + // TODO - when an Autofilter is present, there are some DrawingRecords which also need adjusting + // In particular, some IDs of some EscherSpRecords need changing. See bug 45720 + } + // TODO - maybe same logic required for other/all built-in name records + + return clonedSheet; + } + + private String getUniqueSheetName(String srcName) { + int uniqueIndex = 2; + String baseName = srcName; + int bracketPos = srcName.lastIndexOf('('); + if (bracketPos > 0 && srcName.endsWith(")")) { + String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length()); + try { + uniqueIndex = Integer.parseInt(suffix.trim()); + uniqueIndex++; + baseName=srcName.substring(0, bracketPos).trim(); + } catch (NumberFormatException e) { + // contents of brackets not numeric + } + } while (true) { // Try and find the next sheet name that is unique - String name = srcName; - String index = Integer.toString(i++); - if (name.length() + index.length() + 2 < 31) { - name = name + "(" + index + ")"; + String index = Integer.toString(uniqueIndex++); + String name; + if (baseName.length() + index.length() + 2 < 31) { + name = baseName + " (" + index + ")"; } else { - name = name.substring(0, 31 - index.length() - 2) + "(" + index + ")"; + name = baseName.substring(0, 31 - index.length() - 2) + "(" + index + ")"; } //If the sheet name is unique, then set it otherwise move on to the next number. if (workbook.getSheetIndex(name) == -1) { - workbook.setSheetName(_sheets.size()-1, name); - break; + return name; } } - return clonedSheet; } /** @@ -901,7 +950,7 @@ public class HSSFWorkbook extends POIDocument boolean removingRange = startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1; - int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex); + int rowColHeaderNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_PRINT_TITLE); if (removingRange) { if (rowColHeaderNameIndex >= 0) { workbook.removeName(rowColHeaderNameIndex); @@ -919,29 +968,27 @@ public class HSSFWorkbook extends POIDocument isNewRecord = false; } - short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; - nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove - - Stack ptgs = new Stack(); + List temp = new ArrayList(); if (settingRowAndColumn) { final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE - ptgs.add(new MemFuncPtg(exprsSize)); + temp.add(new MemFuncPtg(exprsSize)); } if (startColumn >= 0) { Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, false, false, false, false, externSheetIndex); - ptgs.add(colArea); + temp.add(colArea); } if (startRow >= 0) { Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, false, false, false, false, externSheetIndex); - ptgs.add(rowArea); + temp.add(rowArea); } - if (settingRowAndColumn) - { - ptgs.add(UnionPtg.instance); + if (settingRowAndColumn) { + temp.add(UnionPtg.instance); } + Ptg[] ptgs = new Ptg[temp.size()]; + temp.toArray(ptgs); nameRecord.setNameDefinition(ptgs); if (isNewRecord) @@ -957,13 +1004,13 @@ public class HSSFWorkbook extends POIDocument } - private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) { + private int findExistingBuiltinNameRecordIdx(int sheetIndex, byte builtinCode) { for(int defNameIndex =0; defNameIndex