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
This commit is contained in:
Josh Micich 2008-09-03 19:22:53 +00:00
parent 50852e25ee
commit 409941a8be
8 changed files with 400 additions and 527 deletions

View File

@ -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 <code>null</code>
*/
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<refs.length; i++) {
ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
field_13_name_definition.push(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
Ptg ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
temp.add(ptg);
}
// And then a union if we had more than one area
if(refs.length > 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";
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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<names.size(); defNameIndex++) {
NameRecord r = workbook.getNameRecord(defNameIndex);
if (r == null) {
throw new RuntimeException("Unable to find all defined names to iterate over");
}
if (!isRowColHeaderRecord( r )) {
if (!r.isBuiltInName() || r.getBuiltInName() != builtinCode) {
continue;
}
if(r.getSheetNumber() == 0) {
@ -979,10 +1026,6 @@ public class HSSFWorkbook extends POIDocument
return -1;
}
private static boolean isRowColHeaderRecord(NameRecord r) {
return r.isBuiltInName() && r.getBuiltInName() == NameRecord.BUILTIN_PRINT_TITLE;
}
/**
* create a new Font and add it to the workbook's font table
* @return new font object

View File

@ -34,6 +34,7 @@ import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.util.CellRangeAddress;
import org.apache.poi.util.TempFile;
@ -1018,9 +1019,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition();
assertEquals(1, nd.size());
assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.length);
assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
@ -1036,9 +1037,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition();
assertEquals(1, nd.size());
assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.length);
assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
@ -1053,9 +1054,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition();
assertEquals(1, nd.size());
assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.length);
assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
}

View File

@ -223,8 +223,16 @@ public final class TestHSSFSheet extends TestCase {
workbook.cloneSheet(0);
assertNotNull(workbook.getSheet("Test Clone"));
assertNotNull(workbook.getSheet("Test Clone(1)"));
assertNotNull(workbook.getSheet("Test Clone(2)"));
assertNotNull(workbook.getSheet("Test Clone (2)"));
assertEquals("Test Clone (3)", workbook.getSheetName(2));
assertNotNull(workbook.getSheet("Test Clone (3)"));
workbook.removeSheetAt(0);
workbook.removeSheetAt(0);
workbook.removeSheetAt(0);
workbook.createSheet("abc ( 123)");
workbook.cloneSheet(0);
assertEquals("abc (124)", workbook.getSheetName(1));
}
/**

View File

@ -429,9 +429,9 @@ public final class TestHSSFWorkbook extends TestCase {
assertEquals("On2", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(1, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size());
assertEquals(1, nr.getNameDefinition().length);
ptg = (Area3DPtg)nr.getNameDefinition().get(0);
ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(1, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow());
@ -452,9 +452,9 @@ public final class TestHSSFWorkbook extends TestCase {
assertEquals("OnOne", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(0, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size());
assertEquals(1, nr.getNameDefinition().length);
ptg = (Area3DPtg)nr.getNameDefinition().get(0);
ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(0, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(2, ptg.getFirstRow());
@ -475,9 +475,9 @@ public final class TestHSSFWorkbook extends TestCase {
assertEquals("OnSheet3", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(2, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size());
assertEquals(1, nr.getNameDefinition().length);
ptg = (Area3DPtg)nr.getNameDefinition().get(0);
ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(2, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow());

View File

@ -19,7 +19,6 @@ package org.apache.poi.hssf.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import junit.framework.TestCase;
@ -28,6 +27,7 @@ import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFName;
@ -204,13 +204,13 @@ public final class TestAreaReference extends TestCase {
assertNotNull(nr);
assertEquals("test", nr.getNameText());
List def =nr.getNameDefinition();
assertEquals(4, def.size());
Ptg[] def =nr.getNameDefinition();
assertEquals(4, def.length);
MemFuncPtg ptgA = (MemFuncPtg)def.get(0);
Area3DPtg ptgB = (Area3DPtg)def.get(1);
Area3DPtg ptgC = (Area3DPtg)def.get(2);
UnionPtg ptgD = (UnionPtg)def.get(3);
MemFuncPtg ptgA = (MemFuncPtg)def[0];
Area3DPtg ptgB = (Area3DPtg)def[1];
Area3DPtg ptgC = (Area3DPtg)def[2];
UnionPtg ptgD = (UnionPtg)def[3];
assertEquals("", ptgA.toFormulaString(wb));
assertEquals(refA, ptgB.toFormulaString(wb));
assertEquals(refB, ptgC.toFormulaString(wb));