https://svn.apache.org/repos/asf/poi/trunk
........
r684884 | josh | 2008-08-11 20:28:58 +0100 (Mon, 11 Aug 2008) | 1 line
deleted obsolete comment (should have been done in c669809
)
........
r684938 | josh | 2008-08-11 22:24:19 +0100 (Mon, 11 Aug 2008) | 1 line
Refinements to fix for bug 45126. Excel does not produce any records like 'Excel_Name_Record_Titles_*'
........
r684939 | nick | 2008-08-11 22:25:17 +0100 (Mon, 11 Aug 2008) | 1 line
CHPXs and PAPXs are apparently cp based, but are really byte based! Work around this
........
r684959 | nick | 2008-08-11 23:07:37 +0100 (Mon, 11 Aug 2008) | 1 line
Get insert based HWPF tests working fine, delete ones still problematic
........
r684971 | josh | 2008-08-11 23:55:38 +0100 (Mon, 11 Aug 2008) | 1 line
initial work on supporting calls to add-in functions
........
r684986 | nick | 2008-08-12 00:42:39 +0100 (Tue, 12 Aug 2008) | 1 line
Finally get all HWPF tests to pass again, by working around how evil PAPX/CHPX/SEPX byte references are
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@684990 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_5_BETA2
@@ -58,6 +58,7 @@ | |||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> | |||
</release> | |||
<release version="3.1.1-alpha1" date="2008-??-??"> | |||
<action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action> | |||
<action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action> | |||
<action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action> | |||
<action dev="POI-DEVELOPERS" type="fix">Improve how HWPF deals with unicode internally. Should avoid some odd behaviour when manipulating unicode text</action> |
@@ -55,6 +55,7 @@ | |||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> | |||
</release> | |||
<release version="3.1.1-alpha1" date="2008-??-??"> | |||
<action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action> | |||
<action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action> | |||
<action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action> | |||
<action dev="POI-DEVELOPERS" type="fix">Improve how HWPF deals with unicode internally. Should avoid some odd behaviour when manipulating unicode text</action> |
@@ -25,6 +25,7 @@ import java.util.Stack; | |||
import org.apache.poi.hssf.record.formula.*; | |||
import org.apache.poi.hssf.record.formula.function.FunctionMetadata; | |||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; | |||
import org.apache.poi.ss.usermodel.Name; | |||
import org.apache.poi.ss.usermodel.Workbook; | |||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
@@ -370,13 +371,32 @@ public final class FormulaParser { | |||
* @param name case preserved function name (as it was entered/appeared in the formula). | |||
*/ | |||
private ParseNode function(String name) { | |||
NamePtg nameToken = null; | |||
// Note regarding parameter - | |||
if(!AbstractFunctionPtg.isInternalFunctionName(name)) { | |||
// external functions get a Name token which points to a defined name record | |||
nameToken = new NamePtg(name, this.book); | |||
Ptg nameToken = null; | |||
if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) { | |||
// user defined function | |||
// in the token tree, the name is more or less the first argument | |||
int nameIndex = book.getNameIndex(name); | |||
if (nameIndex >= 0) { | |||
Name hName = book.getNameAt(nameIndex); | |||
if (!hName.isFunctionName()) { | |||
throw new FormulaParseException("Attempt to use name '" + name | |||
+ "' as a function, but defined name in workbook does not refer to a function"); | |||
} | |||
// calls to user-defined functions within the workbook | |||
// get a Name token which points to a defined name record | |||
nameToken = new NamePtg(name, this.book); | |||
} else { | |||
if(book instanceof HSSFWorkbook) { | |||
nameToken = ((HSSFWorkbook)book).getNameXPtg(name); | |||
} | |||
if (nameToken == null) { | |||
throw new FormulaParseException("Name '" + name | |||
+ "' is completely unknown in the current workbook"); | |||
} | |||
} | |||
} | |||
Match('('); | |||
@@ -390,11 +410,11 @@ public final class FormulaParser { | |||
* Generates the variable function ptg for the formula. | |||
* <p> | |||
* For IF Formulas, additional PTGs are added to the tokens | |||
* @param name | |||
* @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code> | |||
* @param numArgs | |||
* @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function | |||
*/ | |||
private ParseNode getFunction(String name, NamePtg namePtg, ParseNode[] args) { | |||
private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) { | |||
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase()); | |||
int numArgs = args.length; |
@@ -25,11 +25,11 @@ import org.apache.poi.hssf.record.CRNCountRecord; | |||
import org.apache.poi.hssf.record.CRNRecord; | |||
import org.apache.poi.hssf.record.CountryRecord; | |||
import org.apache.poi.hssf.record.ExternSheetRecord; | |||
import org.apache.poi.hssf.record.ExternSheetSubRecord; | |||
import org.apache.poi.hssf.record.ExternalNameRecord; | |||
import org.apache.poi.hssf.record.NameRecord; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.hssf.record.SupBookRecord; | |||
import org.apache.poi.hssf.record.formula.NameXPtg; | |||
/** | |||
* Link Table (OOO pdf reference: 4.10.3 ) <p/> | |||
@@ -122,6 +122,19 @@ final class LinkTable { | |||
public String getNameText(int definedNameIndex) { | |||
return _externalNameRecords[definedNameIndex].getText(); | |||
} | |||
/** | |||
* Performs case-insensitive search | |||
* @return -1 if not found | |||
*/ | |||
public int getIndexOfName(String name) { | |||
for (int i = 0; i < _externalNameRecords.length; i++) { | |||
if(_externalNameRecords[i].getText().equalsIgnoreCase(name)) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
} | |||
private final ExternalBookBlock[] _externalBookBlocks; | |||
@@ -192,14 +205,18 @@ final class LinkTable { | |||
} | |||
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex) { | |||
/** | |||
* @param builtInCode a BUILTIN_~ constant from {@link NameRecord} | |||
* @param sheetNumber 1-based sheet number | |||
*/ | |||
public NameRecord getSpecificBuiltinRecord(byte builtInCode, int sheetNumber) { | |||
Iterator iterator = _definedNames.iterator(); | |||
while (iterator.hasNext()) { | |||
NameRecord record = ( NameRecord ) iterator.next(); | |||
//print areas are one based | |||
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) { | |||
if (record.getBuiltInName() == builtInCode && record.getSheetNumber() == sheetNumber) { | |||
return record; | |||
} | |||
} | |||
@@ -241,69 +258,56 @@ final class LinkTable { | |||
_definedNames.remove(namenum); | |||
} | |||
/** | |||
* checks if the given name is already included in the linkTable | |||
*/ | |||
public boolean nameAlreadyExists(NameRecord name) | |||
{ | |||
// Check to ensure no other names have the same case-insensitive name | |||
for ( int i = getNumNames()-1; i >=0; i-- ) { | |||
NameRecord rec = getNameRecord(i); | |||
if (rec != name) { | |||
if (isDuplicatedNames(name, rec)) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
private boolean isDuplicatedNames(NameRecord firstName, NameRecord lastName) | |||
{ | |||
return lastName.getNameText().equalsIgnoreCase(firstName.getNameText()) | |||
&& isSameSheetNames(firstName, lastName); | |||
} | |||
private boolean isSameSheetNames(NameRecord firstName, NameRecord lastName) | |||
{ | |||
return lastName.getEqualsToIndexToSheet() == firstName.getEqualsToIndexToSheet(); | |||
} | |||
public short getIndexToSheet(short num) { | |||
return _externSheetRecord.getREFRecordAt(num).getIndexToFirstSupBook(); | |||
/** | |||
* checks if the given name is already included in the linkTable | |||
*/ | |||
public boolean nameAlreadyExists(NameRecord name) | |||
{ | |||
// Check to ensure no other names have the same case-insensitive name | |||
for ( int i = getNumNames()-1; i >=0; i-- ) { | |||
NameRecord rec = getNameRecord(i); | |||
if (rec != name) { | |||
if (isDuplicatedNames(name, rec)) | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
private static boolean isDuplicatedNames(NameRecord firstName, NameRecord lastName) { | |||
return lastName.getNameText().equalsIgnoreCase(firstName.getNameText()) | |||
&& isSameSheetNames(firstName, lastName); | |||
} | |||
private static boolean isSameSheetNames(NameRecord firstName, NameRecord lastName) { | |||
return lastName.getSheetNumber() == firstName.getSheetNumber(); | |||
} | |||
public int getIndexToSheet(int extRefIndex) { | |||
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex); | |||
} | |||
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) { | |||
if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) { | |||
public int getSheetIndexFromExternSheetIndex(int extRefIndex) { | |||
if (extRefIndex >= _externSheetRecord.getNumOfRefs()) { | |||
return -1; | |||
} | |||
return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook(); | |||
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex); | |||
} | |||
public short addSheetIndexToExternSheet(short sheetNumber) { | |||
ExternSheetSubRecord record = new ExternSheetSubRecord(); | |||
record.setIndexToFirstSupBook(sheetNumber); | |||
record.setIndexToLastSupBook(sheetNumber); | |||
_externSheetRecord.addREFRecord(record); | |||
_externSheetRecord.setNumOfREFStructures((short)(_externSheetRecord.getNumOfREFStructures() + 1)); | |||
return (short)(_externSheetRecord.getNumOfREFStructures() - 1); | |||
public int addSheetIndexToExternSheet(int sheetNumber) { | |||
// TODO - what about the first parameter (extBookIndex)? | |||
return _externSheetRecord.addRef(0, sheetNumber, sheetNumber); | |||
} | |||
public short checkExternSheet(int sheetNumber) { | |||
public short checkExternSheet(int sheetIndex) { | |||
//Trying to find reference to this sheet | |||
int nESRs = _externSheetRecord.getNumOfREFStructures(); | |||
for(short i=0; i< nESRs; i++) { | |||
ExternSheetSubRecord esr = _externSheetRecord.getREFRecordAt(i); | |||
if (esr.getIndexToFirstSupBook() == sheetNumber | |||
&& esr.getIndexToLastSupBook() == sheetNumber){ | |||
return i; | |||
} | |||
int i = _externSheetRecord.getRefIxForSheet(sheetIndex); | |||
if (i>=0) { | |||
return (short)i; | |||
} | |||
//We Haven't found reference to this sheet | |||
return addSheetIndexToExternSheet((short) sheetNumber); | |||
return (short)addSheetIndexToExternSheet((short) sheetIndex); | |||
} | |||
@@ -324,11 +328,31 @@ final class LinkTable { | |||
} | |||
public int getNumberOfREFStructures() { | |||
return _externSheetRecord.getNumOfREFStructures(); | |||
return _externSheetRecord.getNumOfRefs(); | |||
} | |||
public String resolveNameXText(int refIndex, int definedNameIndex) { | |||
short extBookIndex = _externSheetRecord.getREFRecordAt(refIndex).getIndexToSupBook(); | |||
int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex); | |||
return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex); | |||
} | |||
public NameXPtg getNameXPtg(String name) { | |||
// first find any external book block that contains the name: | |||
for (int i = 0; i < _externalBookBlocks.length; i++) { | |||
int definedNameIndex = _externalBookBlocks[i].getIndexOfName(name); | |||
if (definedNameIndex < 0) { | |||
continue; | |||
} | |||
// found it. | |||
int sheetRefIndex = findRefIndexFromExtBookIndex(i); | |||
if (sheetRefIndex >= 0) { | |||
return new NameXPtg(sheetRefIndex, definedNameIndex); | |||
} | |||
} | |||
return null; | |||
} | |||
private int findRefIndexFromExtBookIndex(int extBookIndex) { | |||
return _externSheetRecord.findRefIndexFromExtBookIndex(extBookIndex); | |||
} | |||
} |
@@ -15,21 +15,70 @@ | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.hssf.record.*; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import org.apache.poi.hssf.util.SheetReferences; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import org.apache.poi.ddf.EscherBSERecord; | |||
import org.apache.poi.ddf.EscherBoolProperty; | |||
import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherDggRecord; | |||
import org.apache.poi.ddf.EscherOptRecord; | |||
import org.apache.poi.ddf.EscherProperties; | |||
import org.apache.poi.ddf.EscherRGBProperty; | |||
import org.apache.poi.ddf.EscherRecord; | |||
import org.apache.poi.ddf.EscherSplitMenuColorsRecord; | |||
import org.apache.poi.hssf.record.BOFRecord; | |||
import org.apache.poi.hssf.record.BackupRecord; | |||
import org.apache.poi.hssf.record.BookBoolRecord; | |||
import org.apache.poi.hssf.record.BoundSheetRecord; | |||
import org.apache.poi.hssf.record.CodepageRecord; | |||
import org.apache.poi.hssf.record.CountryRecord; | |||
import org.apache.poi.hssf.record.DSFRecord; | |||
import org.apache.poi.hssf.record.DateWindow1904Record; | |||
import org.apache.poi.hssf.record.DrawingGroupRecord; | |||
import org.apache.poi.hssf.record.EOFRecord; | |||
import org.apache.poi.hssf.record.ExtSSTRecord; | |||
import org.apache.poi.hssf.record.ExtendedFormatRecord; | |||
import org.apache.poi.hssf.record.ExternSheetRecord; | |||
import org.apache.poi.hssf.record.FileSharingRecord; | |||
import org.apache.poi.hssf.record.FnGroupCountRecord; | |||
import org.apache.poi.hssf.record.FontRecord; | |||
import org.apache.poi.hssf.record.FormatRecord; | |||
import org.apache.poi.hssf.record.HideObjRecord; | |||
import org.apache.poi.hssf.record.HyperlinkRecord; | |||
import org.apache.poi.hssf.record.InterfaceEndRecord; | |||
import org.apache.poi.hssf.record.InterfaceHdrRecord; | |||
import org.apache.poi.hssf.record.MMSRecord; | |||
import org.apache.poi.hssf.record.NameRecord; | |||
import org.apache.poi.hssf.record.PaletteRecord; | |||
import org.apache.poi.hssf.record.PasswordRecord; | |||
import org.apache.poi.hssf.record.PasswordRev4Record; | |||
import org.apache.poi.hssf.record.PrecisionRecord; | |||
import org.apache.poi.hssf.record.ProtectRecord; | |||
import org.apache.poi.hssf.record.ProtectionRev4Record; | |||
import org.apache.poi.hssf.record.RecalcIdRecord; | |||
import org.apache.poi.hssf.record.Record; | |||
import org.apache.poi.hssf.record.RefreshAllRecord; | |||
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.formula.NameXPtg; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import org.apache.poi.hssf.util.SheetReferences; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Low level model implementation of a Workbook. Provides creational methods | |||
* for settings and objects contained in the workbook object. | |||
@@ -54,18 +103,13 @@ import java.util.Locale; | |||
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook | |||
* @version 1.0-pre | |||
*/ | |||
public class Workbook implements Model | |||
{ | |||
public final class Workbook implements Model { | |||
private static final int DEBUG = POILogger.DEBUG; | |||
// public static Workbook currentBook = null; | |||
/** | |||
* constant used to set the "codepage" wherever "codepage" is set in records | |||
* (which is duplciated in more than one record) | |||
* (which is duplicated in more than one record) | |||
*/ | |||
private final static short CODEPAGE = ( short ) 0x4b0; | |||
/** | |||
@@ -105,8 +149,6 @@ public class Workbook implements Model | |||
private static POILogger log = POILogFactory.getLogger(Workbook.class); | |||
protected static final String EXCEL_REPEATING_NAME_PREFIX_ = "Excel_Name_Record_Titles_"; | |||
/** | |||
* Creates new Workbook with no intitialization --useless right now | |||
* @see #createWorkbook(List) | |||
@@ -250,9 +292,9 @@ public class Workbook implements Model | |||
for ( ; k < recs.size(); k++) { | |||
Record rec = ( Record ) recs.get(k); | |||
switch (rec.getSid()) { | |||
case HyperlinkRecord.sid: | |||
retval.hyperlinks.add(rec); | |||
break; | |||
case HyperlinkRecord.sid: | |||
retval.hyperlinks.add(rec); | |||
break; | |||
} | |||
} | |||
@@ -358,22 +400,22 @@ public class Workbook implements Model | |||
} | |||
/**Retrieves the Builtin NameRecord that matches the name and index | |||
* There shouldn't be too many names to make the sequential search too slow | |||
* @param name byte representation of the builtin name to match | |||
* @param sheetIndex Index to match | |||
* @return null if no builtin NameRecord matches | |||
*/ | |||
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex) | |||
/**Retrieves the Builtin NameRecord that matches the name and index | |||
* There shouldn't be too many names to make the sequential search too slow | |||
* @param name byte representation of the builtin name to match | |||
* @param sheetNumber 1-based sheet number | |||
* @return null if no builtin NameRecord matches | |||
*/ | |||
public NameRecord getSpecificBuiltinRecord(byte name, int sheetNumber) | |||
{ | |||
return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetIndex); | |||
return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetNumber); | |||
} | |||
/** | |||
* Removes the specified Builtin NameRecord that matches the name and index | |||
* @param name byte representation of the builtin to match | |||
* @param sheetIndex zero-based sheet reference | |||
*/ | |||
/** | |||
* Removes the specified Builtin NameRecord that matches the name and index | |||
* @param name byte representation of the builtin to match | |||
* @param sheetIndex zero-based sheet reference | |||
*/ | |||
public void removeBuiltinRecord(byte name, int sheetIndex) { | |||
linkTable.removeBuiltinRecord(name, sheetIndex); | |||
// TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}? | |||
@@ -413,18 +455,18 @@ public class Workbook implements Model | |||
* Retrieves the index of the given font | |||
*/ | |||
public int getFontIndex(FontRecord font) { | |||
for(int i=0; i<=numfonts; i++) { | |||
for(int i=0; i<=numfonts; i++) { | |||
FontRecord thisFont = | |||
( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + i); | |||
if(thisFont == font) { | |||
// There is no 4! | |||
if(i > 3) { | |||
return (i+1); | |||
} | |||
return i; | |||
// There is no 4! | |||
if(i > 3) { | |||
return (i+1); | |||
} | |||
return i; | |||
} | |||
} | |||
throw new IllegalArgumentException("Could not find that font!"); | |||
} | |||
throw new IllegalArgumentException("Could not find that font!"); | |||
} | |||
/** | |||
@@ -451,7 +493,7 @@ public class Workbook implements Model | |||
* so you'll need to update those yourself! | |||
*/ | |||
public void removeFontRecord(FontRecord rec) { | |||
records.remove(rec); // this updates FontPos for us | |||
records.remove(rec); // this updates FontPos for us | |||
numfonts--; | |||
} | |||
@@ -468,19 +510,23 @@ public class Workbook implements Model | |||
/** | |||
* Sets the BOF for a given sheet | |||
* | |||
* @param sheetnum the number of the sheet to set the positing of the bof for | |||
* @param sheetIndex the number of the sheet to set the positing of the bof for | |||
* @param pos the actual bof position | |||
*/ | |||
public void setSheetBof(int sheetnum, int pos) { | |||
public void setSheetBof(int sheetIndex, int pos) { | |||
if (log.check( POILogger.DEBUG )) | |||
log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetnum), | |||
log.log(DEBUG, "setting bof for sheetnum =", new Integer(sheetIndex), | |||
" at pos=", new Integer(pos)); | |||
checkSheets(sheetnum); | |||
(( BoundSheetRecord ) boundsheets.get(sheetnum)) | |||
checkSheets(sheetIndex); | |||
getBoundSheetRec(sheetIndex) | |||
.setPositionOfBof(pos); | |||
} | |||
private BoundSheetRecord getBoundSheetRec(int sheetIndex) { | |||
return ((BoundSheetRecord) boundsheets.get(sheetIndex)); | |||
} | |||
/** | |||
* Returns the position of the backup record. | |||
*/ | |||
@@ -516,7 +562,7 @@ public class Workbook implements Model | |||
{ | |||
for ( int i = 0; i < boundsheets.size(); i++ ) | |||
{ | |||
BoundSheetRecord boundSheetRecord = (BoundSheetRecord) boundsheets.get( i ); | |||
BoundSheetRecord boundSheetRecord = getBoundSheetRec(i); | |||
if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname())) | |||
return true; | |||
} | |||
@@ -533,35 +579,33 @@ public class Workbook implements Model | |||
*/ | |||
public void setSheetName(int sheetnum, String sheetname, short encoding ) { | |||
checkSheets(sheetnum); | |||
BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum ); | |||
BoundSheetRecord sheet = getBoundSheetRec(sheetnum); | |||
sheet.setSheetname(sheetname); | |||
sheet.setSheetnameLength( (byte)sheetname.length() ); | |||
sheet.setCompressedUnicodeFlag( (byte)encoding ); | |||
sheet.setCompressedUnicodeFlag( (byte)encoding ); | |||
} | |||
/** | |||
* sets the order of appearance for a given sheet. | |||
* | |||
* @param sheetname the name of the sheet to reorder | |||
* @param pos the position that we want to insert the sheet into (0 based) | |||
*/ | |||
* sets the order of appearance for a given sheet. | |||
* | |||
* @param sheetname the name of the sheet to reorder | |||
* @param pos the position that we want to insert the sheet into (0 based) | |||
*/ | |||
public void setSheetOrder(String sheetname, int pos ) { | |||
int sheetNumber = getSheetIndex(sheetname); | |||
//remove the sheet that needs to be reordered and place it in the spot we want | |||
boundsheets.add(pos, boundsheets.remove(sheetNumber)); | |||
int sheetNumber = getSheetIndex(sheetname); | |||
//remove the sheet that needs to be reordered and place it in the spot we want | |||
boundsheets.add(pos, boundsheets.remove(sheetNumber)); | |||
} | |||
/** | |||
* gets the name for a given sheet. | |||
* | |||
* @param sheetnum the sheet number (0 based) | |||
* @param sheetIndex the sheet number (0 based) | |||
* @return sheetname the name for the sheet | |||
*/ | |||
public String getSheetName(int sheetnum) { | |||
return (( BoundSheetRecord ) boundsheets.get(sheetnum)) | |||
.getSheetname(); | |||
public String getSheetName(int sheetIndex) { | |||
return getBoundSheetRec(sheetIndex).getSheetname(); | |||
} | |||
/** | |||
@@ -572,8 +616,7 @@ public class Workbook implements Model | |||
*/ | |||
public boolean isSheetHidden(int sheetnum) { | |||
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum); | |||
return bsr.isHidden(); | |||
return getBoundSheetRec(sheetnum).isHidden(); | |||
} | |||
/** | |||
@@ -584,8 +627,7 @@ public class Workbook implements Model | |||
*/ | |||
public void setSheetHidden(int sheetnum, boolean hidden) { | |||
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum); | |||
bsr.setHidden(hidden); | |||
getBoundSheetRec(sheetnum).setHidden(hidden); | |||
} | |||
/** | |||
* get the sheet's index | |||
@@ -627,11 +669,13 @@ public class Workbook implements Model | |||
} | |||
} | |||
public void removeSheet(int sheetnum) { | |||
if (boundsheets.size() > sheetnum) { | |||
records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetnum); | |||
// records.bspos--; | |||
boundsheets.remove(sheetnum); | |||
/** | |||
* @param sheetIndex zero based sheet index | |||
*/ | |||
public void removeSheet(int sheetIndex) { | |||
if (boundsheets.size() > sheetIndex) { | |||
records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetIndex); | |||
boundsheets.remove(sheetIndex); | |||
fixTabIdRecord(); | |||
} | |||
@@ -642,20 +686,18 @@ public class Workbook implements Model | |||
// However, the sheet index must be adjusted, or | |||
// excel will break. (Sheet index is either 0 for | |||
// global, or 1 based index to sheet) | |||
int sheetNum1Based = sheetnum + 1; | |||
int sheetNum1Based = sheetIndex + 1; | |||
for(int i=0; i<getNumNames(); i++) { | |||
NameRecord nr = getNameRecord(i); | |||
if(nr.getIndexToSheet() == sheetNum1Based) { | |||
// Excel re-writes these to point to no sheet | |||
nr.setEqualsToIndexToSheet((short)0); | |||
} else if(nr.getIndexToSheet() > sheetNum1Based) { | |||
// Bump down by one, so still points | |||
// at the same sheet | |||
nr.setEqualsToIndexToSheet((short)( | |||
nr.getEqualsToIndexToSheet()-1 | |||
)); | |||
} | |||
NameRecord nr = getNameRecord(i); | |||
if(nr.getSheetNumber() == sheetNum1Based) { | |||
// Excel re-writes these to point to no sheet | |||
nr.setSheetNumber(0); | |||
} else if(nr.getSheetNumber() > sheetNum1Based) { | |||
// Bump down by one, so still points | |||
// at the same sheet | |||
nr.setSheetNumber(nr.getSheetNumber()-1); | |||
} | |||
} | |||
} | |||
@@ -721,7 +763,7 @@ public class Workbook implements Model | |||
* so you'll need to update those yourself! | |||
*/ | |||
public void removeExFormatRecord(ExtendedFormatRecord rec) { | |||
records.remove(rec); // this updates XfPos for us | |||
records.remove(rec); // this updates XfPos for us | |||
numxfs--; | |||
} | |||
@@ -813,9 +855,9 @@ public class Workbook implements Model | |||
// | |||
// Record record = records.get(k); | |||
//// Let's skip RECALCID records, as they are only use for optimization | |||
// if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) { | |||
// if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) { | |||
// pos += record.serialize(pos, retval); // rec.length; | |||
// } | |||
// } | |||
// } | |||
// log.log(DEBUG, "Exiting serialize workbook"); | |||
// return retval; | |||
@@ -858,7 +900,7 @@ public class Workbook implements Model | |||
if (record instanceof BoundSheetRecord) { | |||
if(!wroteBoundSheets) { | |||
for (int i = 0; i < boundsheets.size(); i++) { | |||
len+= ((BoundSheetRecord)boundsheets.get(i)) | |||
len+= getBoundSheetRec(i) | |||
.serialize(pos+offset+len, data); | |||
} | |||
wroteBoundSheets = true; | |||
@@ -1141,8 +1183,8 @@ public class Workbook implements Model | |||
retval.setWidth(( short ) 0x3a5c); | |||
retval.setHeight(( short ) 0x23be); | |||
retval.setOptions(( short ) 0x38); | |||
retval.setSelectedTab(( short ) 0x0); | |||
retval.setDisplayedTab(( short ) 0x0); | |||
retval.setActiveSheetIndex( 0x0); | |||
retval.setFirstVisibleTab(0x0); | |||
retval.setNumSelectedTabs(( short ) 1); | |||
retval.setTabWidthRatio(( short ) 0x258); | |||
return retval; | |||
@@ -1821,10 +1863,10 @@ public class Workbook implements Model | |||
// from Russia with love ;) | |||
if ( Locale.getDefault().toString().equals( "ru_RU" ) ) { | |||
retval.setCurrentCountry(( short ) 7); | |||
retval.setCurrentCountry(( short ) 7); | |||
} | |||
else { | |||
retval.setCurrentCountry(( short ) 1); | |||
retval.setCurrentCountry(( short ) 1); | |||
} | |||
return retval; | |||
@@ -1889,15 +1931,15 @@ public class Workbook implements Model | |||
* @return sheet name | |||
*/ | |||
public String findSheetNameFromExternSheet(short num){ | |||
String result=""; | |||
short indexToSheet = linkTable.getIndexToSheet(num); | |||
int indexToSheet = linkTable.getIndexToSheet(num); | |||
if (indexToSheet>-1) { //error check, bail out gracefully! | |||
result = getSheetName(indexToSheet); | |||
if (indexToSheet < 0) { | |||
// TODO - what does '-1' mean here? | |||
//error check, bail out gracefully! | |||
return ""; | |||
} | |||
return result; | |||
return getSheetName(indexToSheet); | |||
} | |||
/** | |||
@@ -1950,10 +1992,10 @@ public class Workbook implements Model | |||
*/ | |||
public NameRecord addName(NameRecord name) | |||
{ | |||
LinkTable linkTable = getOrCreateLinkTable(); | |||
if(linkTable.nameAlreadyExists(name)) { | |||
throw new IllegalArgumentException( | |||
throw new IllegalArgumentException( | |||
"You are trying to assign a duplicated name record: " | |||
+ name.getNameText()); | |||
} | |||
@@ -1964,27 +2006,18 @@ public class Workbook implements Model | |||
/** | |||
* Generates a NameRecord to represent a built-in region | |||
* @return a new NameRecord unless the index is invalid | |||
* @return a new NameRecord | |||
*/ | |||
public NameRecord createBuiltInName(byte builtInName, int index) | |||
{ | |||
if (index == -1 || index+1 > Short.MAX_VALUE) | |||
throw new IllegalArgumentException("Index is not valid ["+index+"]"); | |||
public NameRecord createBuiltInName(byte builtInName, int sheetNumber) { | |||
if (sheetNumber < 0 || sheetNumber+1 > Short.MAX_VALUE) { | |||
throw new IllegalArgumentException("Sheet number ["+sheetNumber+"]is not valid "); | |||
} | |||
NameRecord name = new NameRecord(builtInName, (short)(index)); | |||
NameRecord name = new NameRecord(builtInName, sheetNumber); | |||
String prefix = EXCEL_REPEATING_NAME_PREFIX_ + index + "_"; | |||
int cont = 0; | |||
while(linkTable.nameAlreadyExists(name)) { | |||
cont++; | |||
String altNameName = prefix + cont; | |||
// It would be better to set a different builtInName here. | |||
// It does not seem possible, so we create it as a | |||
// non built-in name from this point on | |||
name = new NameRecord(); | |||
name.setNameText(altNameName); | |||
name.setNameTextLength((byte)altNameName.length()); | |||
throw new RuntimeException("Builtin (" + builtInName | |||
+ ") already exists for sheet (" + sheetNumber + ")"); | |||
} | |||
addName(name); | |||
return name; | |||
@@ -1992,16 +2025,15 @@ public class Workbook implements Model | |||
/** removes the name | |||
* @param namenum name index | |||
* @param nameIndex name index | |||
*/ | |||
public void removeName(int namenum){ | |||
public void removeName(int nameIndex){ | |||
if (linkTable.getNumNames() > namenum) { | |||
if (linkTable.getNumNames() > nameIndex) { | |||
int idx = findFirstRecordLocBySid(NameRecord.sid); | |||
records.remove(idx + namenum); | |||
linkTable.removeName(namenum); | |||
records.remove(idx + nameIndex); | |||
linkTable.removeName(nameIndex); | |||
} | |||
} | |||
/** | |||
@@ -2011,19 +2043,19 @@ public class Workbook implements Model | |||
* @return the format id of a format that matches or -1 if none found and createIfNotFound | |||
*/ | |||
public short getFormat(String format, boolean createIfNotFound) { | |||
Iterator iterator; | |||
for (iterator = formats.iterator(); iterator.hasNext();) { | |||
FormatRecord r = (FormatRecord)iterator.next(); | |||
if (r.getFormatString().equals(format)) { | |||
return r.getIndexCode(); | |||
} | |||
} | |||
Iterator iterator; | |||
for (iterator = formats.iterator(); iterator.hasNext();) { | |||
FormatRecord r = (FormatRecord)iterator.next(); | |||
if (r.getFormatString().equals(format)) { | |||
return r.getIndexCode(); | |||
} | |||
} | |||
if (createIfNotFound) { | |||
return createFormat(format); | |||
} | |||
if (createIfNotFound) { | |||
return createFormat(format); | |||
} | |||
return -1; | |||
return -1; | |||
} | |||
/** | |||
@@ -2031,7 +2063,7 @@ public class Workbook implements Model | |||
* @return ArrayList of FormatRecords in the notebook | |||
*/ | |||
public ArrayList getFormats() { | |||
return formats; | |||
return formats; | |||
} | |||
/** | |||
@@ -2043,7 +2075,7 @@ public class Workbook implements Model | |||
*/ | |||
public short createFormat( String format ) | |||
{ | |||
// ++xfpos; //These are to ensure that positions are updated properly | |||
// ++xfpos; //These are to ensure that positions are updated properly | |||
// ++palettepos; | |||
// ++bspos; | |||
FormatRecord rec = new FormatRecord(); | |||
@@ -2113,7 +2145,7 @@ public class Workbook implements Model | |||
public List getHyperlinks() | |||
{ | |||
return hyperlinks; | |||
return hyperlinks; | |||
} | |||
public List getRecords() | |||
@@ -2170,54 +2202,54 @@ public class Workbook implements Model | |||
* Finds the primary drawing group, if one already exists | |||
*/ | |||
public void findDrawingGroup() { | |||
// Need to find a DrawingGroupRecord that | |||
// contains a EscherDggRecord | |||
for(Iterator rit = records.iterator(); rit.hasNext();) { | |||
Record r = (Record)rit.next(); | |||
if(r instanceof DrawingGroupRecord) { | |||
DrawingGroupRecord dg = (DrawingGroupRecord)r; | |||
dg.processChildRecords(); | |||
EscherContainerRecord cr = | |||
dg.getEscherContainer(); | |||
if(cr == null) { | |||
continue; | |||
} | |||
EscherDggRecord dgg = null; | |||
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) { | |||
Object er = it.next(); | |||
if(er instanceof EscherDggRecord) { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
return; | |||
} | |||
} | |||
} | |||
// Look for the DrawingGroup record | |||
// Need to find a DrawingGroupRecord that | |||
// contains a EscherDggRecord | |||
for(Iterator rit = records.iterator(); rit.hasNext();) { | |||
Record r = (Record)rit.next(); | |||
if(r instanceof DrawingGroupRecord) { | |||
DrawingGroupRecord dg = (DrawingGroupRecord)r; | |||
dg.processChildRecords(); | |||
EscherContainerRecord cr = | |||
dg.getEscherContainer(); | |||
if(cr == null) { | |||
continue; | |||
} | |||
EscherDggRecord dgg = null; | |||
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) { | |||
Object er = it.next(); | |||
if(er instanceof EscherDggRecord) { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
return; | |||
} | |||
} | |||
} | |||
// Look for the DrawingGroup record | |||
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); | |||
// If there is one, does it have a EscherDggRecord? | |||
// If there is one, does it have a EscherDggRecord? | |||
if(dgLoc != -1) { | |||
DrawingGroupRecord dg = | |||
(DrawingGroupRecord)records.get(dgLoc); | |||
EscherDggRecord dgg = null; | |||
for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) { | |||
Object er = it.next(); | |||
if(er instanceof EscherDggRecord) { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
} | |||
DrawingGroupRecord dg = | |||
(DrawingGroupRecord)records.get(dgLoc); | |||
EscherDggRecord dgg = null; | |||
for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) { | |||
Object er = it.next(); | |||
if(er instanceof EscherDggRecord) { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
} | |||
} | |||
} | |||
@@ -2379,7 +2411,7 @@ public class Workbook implements Model | |||
*/ | |||
public boolean isWriteProtected() { | |||
if (this.fileShare == null) { | |||
return false; | |||
return false; | |||
} | |||
FileSharingRecord frec = getFileSharing(); | |||
return (frec.getReadOnly() == 1); | |||
@@ -2419,6 +2451,8 @@ public class Workbook implements Model | |||
public String resolveNameXText(int refIndex, int definedNameIndex) { | |||
return linkTable.resolveNameXText(refIndex, definedNameIndex); | |||
} | |||
} | |||
public NameXPtg getNameXPtg(String name) { | |||
return getOrCreateLinkTable().getNameXPtg(name); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,29 +14,85 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import org.apache.poi.util.LittleEndian; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.util.LittleEndian; | |||
/** | |||
* Title: Extern Sheet <P> | |||
* Description: A List of Inndexes to SupBook <P> | |||
* REFERENCE: <P> | |||
* EXTERNSHEET (0x0017)<br/> | |||
* A List of Indexes to EXTERNALBOOK (supplemental book) Records <p/> | |||
* | |||
* @author Libin Roman (Vista Portal LDT. Developer) | |||
* @version 1.0-pre | |||
*/ | |||
public class ExternSheetRecord extends Record { | |||
public final static short sid = 0x17; | |||
private short field_1_number_of_REF_sturcutres; | |||
private ArrayList field_2_REF_structures; | |||
public final static short sid = 0x0017; | |||
private List _list; | |||
private final class RefSubRecord { | |||
public static final int ENCODED_SIZE = 6; | |||
/** index to External Book Block (which starts with a EXTERNALBOOK record) */ | |||
private int _extBookIndex; | |||
private int _firstSheetIndex; // may be -1 (0xFFFF) | |||
private int _lastSheetIndex; // may be -1 (0xFFFF) | |||
/** a Constructor for making new sub record | |||
*/ | |||
public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { | |||
_extBookIndex = extBookIndex; | |||
_firstSheetIndex = firstSheetIndex; | |||
_lastSheetIndex = lastSheetIndex; | |||
} | |||
/** | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
public RefSubRecord(RecordInputStream in) { | |||
this(in.readShort(), in.readShort(), in.readShort()); | |||
} | |||
public int getExtBookIndex(){ | |||
return _extBookIndex; | |||
} | |||
public int getFirstSheetIndex(){ | |||
return _firstSheetIndex; | |||
} | |||
public int getLastSheetIndex(){ | |||
return _lastSheetIndex; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("extBook=").append(_extBookIndex); | |||
buffer.append(" firstSheet=").append(_firstSheetIndex); | |||
buffer.append(" lastSheet=").append(_lastSheetIndex); | |||
return buffer.toString(); | |||
} | |||
/** | |||
* called by the class that is responsible for writing this sucker. | |||
* Subclasses should implement this so that their data is passed back in a | |||
* byte array. | |||
* | |||
* @param offset to begin writing at | |||
* @param data byte array containing instance data | |||
* @return number of bytes written | |||
*/ | |||
public void serialize(int offset, byte [] data) { | |||
LittleEndian.putUShort(data, 0 + offset, _extBookIndex); | |||
LittleEndian.putUShort(data, 2 + offset, _firstSheetIndex); | |||
LittleEndian.putUShort(data, 4 + offset, _lastSheetIndex); | |||
} | |||
} | |||
public ExternSheetRecord() { | |||
field_2_REF_structures = new ArrayList(); | |||
_list = new ArrayList(); | |||
} | |||
/** | |||
@@ -68,72 +123,60 @@ public class ExternSheetRecord extends Record { | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
protected void fillFields(RecordInputStream in) { | |||
field_2_REF_structures = new ArrayList(); | |||
_list = new ArrayList(); | |||
field_1_number_of_REF_sturcutres = in.readShort(); | |||
int nItems = in.readShort(); | |||
for (int i = 0 ; i < field_1_number_of_REF_sturcutres ; ++i) { | |||
ExternSheetSubRecord rec = new ExternSheetSubRecord(in); | |||
for (int i = 0 ; i < nItems ; ++i) { | |||
RefSubRecord rec = new RefSubRecord(in); | |||
field_2_REF_structures.add( rec); | |||
_list.add( rec); | |||
} | |||
} | |||
/** | |||
* sets the number of the REF structors , that is in Excel file | |||
* @param numStruct number of REF structs | |||
*/ | |||
public void setNumOfREFStructures(short numStruct) { | |||
field_1_number_of_REF_sturcutres = numStruct; | |||
} | |||
/** | |||
* return the number of the REF structors , that is in Excel file | |||
* @return number of REF structs | |||
* @return number of REF structures | |||
*/ | |||
public short getNumOfREFStructures() { | |||
return field_1_number_of_REF_sturcutres; | |||
public int getNumOfRefs() { | |||
return _list.size(); | |||
} | |||
/** | |||
* adds REF struct (ExternSheetSubRecord) | |||
* @param rec REF struct | |||
*/ | |||
public void addREFRecord(ExternSheetSubRecord rec) { | |||
field_2_REF_structures.add(rec); | |||
public void addREFRecord(RefSubRecord rec) { | |||
_list.add(rec); | |||
} | |||
/** returns the number of REF Records, which is in model | |||
* @return number of REF records | |||
*/ | |||
public int getNumOfREFRecords() { | |||
return field_2_REF_structures.size(); | |||
return _list.size(); | |||
} | |||
/** returns the REF record (ExternSheetSubRecord) | |||
* @param elem index to place | |||
* @return REF record | |||
*/ | |||
public ExternSheetSubRecord getREFRecordAt(int elem) { | |||
ExternSheetSubRecord result = ( ExternSheetSubRecord ) field_2_REF_structures.get(elem); | |||
return result; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[EXTERNSHEET]\n"); | |||
buffer.append(" numOfRefs = ").append(getNumOfREFStructures()).append("\n"); | |||
for (int k=0; k < this.getNumOfREFRecords(); k++) { | |||
buffer.append("refrec #").append(k).append('\n'); | |||
buffer.append(getREFRecordAt(k).toString()); | |||
buffer.append("----refrec #").append(k).append('\n'); | |||
StringBuffer sb = new StringBuffer(); | |||
int nItems = _list.size(); | |||
sb.append("[EXTERNSHEET]\n"); | |||
sb.append(" numOfRefs = ").append(nItems).append("\n"); | |||
for (int i=0; i < nItems; i++) { | |||
sb.append("refrec #").append(i).append(": "); | |||
sb.append(getRef(i).toString()); | |||
sb.append('\n'); | |||
} | |||
buffer.append("[/EXTERNSHEET]\n"); | |||
sb.append("[/EXTERNSHEET]\n"); | |||
return buffer.toString(); | |||
return sb.toString(); | |||
} | |||
private int getDataSize() { | |||
return 2 + _list.size() * RefSubRecord.ENCODED_SIZE; | |||
} | |||
/** | |||
@@ -146,24 +189,29 @@ public class ExternSheetRecord extends Record { | |||
* @return number of bytes written | |||
*/ | |||
public int serialize(int offset, byte [] data) { | |||
LittleEndian.putShort(data, 0 + offset, sid); | |||
LittleEndian.putShort(data, 2 + offset,(short)(2 + (getNumOfREFRecords() *6))); | |||
int dataSize = getDataSize(); | |||
LittleEndian.putShort(data, 4 + offset, getNumOfREFStructures()); | |||
int nItems = _list.size(); | |||
LittleEndian.putShort(data, 0 + offset, sid); | |||
LittleEndian.putUShort(data, 2 + offset, dataSize); | |||
LittleEndian.putUShort(data, 4 + offset, nItems); | |||
int pos = 6 ; | |||
for (int k = 0; k < getNumOfREFRecords(); k++) { | |||
ExternSheetSubRecord record = getREFRecordAt(k); | |||
System.arraycopy(record.serialize(), 0, data, pos + offset, 6); | |||
for (int i = 0; i < nItems; i++) { | |||
getRef(i).serialize(offset + pos, data); | |||
pos +=6; | |||
} | |||
return getRecordSize(); | |||
return dataSize + 4; | |||
} | |||
private RefSubRecord getRef(int i) { | |||
return (RefSubRecord) _list.get(i); | |||
} | |||
public int getRecordSize() { | |||
return 4 + 2 + getNumOfREFRecords() * 6; | |||
return 4 + getDataSize(); | |||
} | |||
/** | |||
@@ -172,4 +220,44 @@ public class ExternSheetRecord extends Record { | |||
public short getSid() { | |||
return sid; | |||
} | |||
public int getExtbookIndexFromRefIndex(int refIndex) { | |||
return getRef(refIndex).getExtBookIndex(); | |||
} | |||
/** | |||
* @return -1 if not found | |||
*/ | |||
public int findRefIndexFromExtBookIndex(int extBookIndex) { | |||
int nItems = _list.size(); | |||
for (int i = 0; i < nItems; i++) { | |||
if (getRef(i).getExtBookIndex() == extBookIndex) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
public int getFirstSheetIndexFromRefIndex(int extRefIndex) { | |||
return getRef(extRefIndex).getFirstSheetIndex(); | |||
} | |||
/** | |||
* @return index of newly added ref | |||
*/ | |||
public int addRef(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { | |||
_list.add(new RefSubRecord(extBookIndex, firstSheetIndex, lastSheetIndex)); | |||
return _list.size() - 1; | |||
} | |||
public int getRefIxForSheet(int sheetIndex) { | |||
int nItems = _list.size(); | |||
for (int i = 0; i < nItems; i++) { | |||
RefSubRecord ref = getRef(i); | |||
if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
} |
@@ -1,154 +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 org.apache.poi.util.LittleEndian; | |||
/** | |||
* Title: A sub Record for Extern Sheet <P> | |||
* Description: Defines a named range within a workbook. <P> | |||
* REFERENCE: <P> | |||
* @author Libin Roman (Vista Portal LDT. Developer) | |||
* @version 1.0-pre | |||
*/ | |||
public class ExternSheetSubRecord extends Record { | |||
public final static short sid = 0xFFF; // only here for conformance, doesn't really have an sid | |||
private short field_1_index_to_supbook; | |||
private short field_2_index_to_first_supbook_sheet; | |||
private short field_3_index_to_last_supbook_sheet; | |||
/** a Constractor for making new sub record | |||
*/ | |||
public ExternSheetSubRecord() { | |||
} | |||
/** | |||
* Constructs a Extern Sheet Sub Record record and sets its fields appropriately. | |||
* | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
public ExternSheetSubRecord(RecordInputStream in) { | |||
super(in); | |||
} | |||
/** Sets the Index to the sup book | |||
* @param index sup book index | |||
*/ | |||
public void setIndexToSupBook(short index){ | |||
field_1_index_to_supbook = index; | |||
} | |||
/** gets the index to sup book | |||
* @return sup book index | |||
*/ | |||
public short getIndexToSupBook(){ | |||
return field_1_index_to_supbook; | |||
} | |||
/** sets the index to first sheet in supbook | |||
* @param index index to first sheet | |||
*/ | |||
public void setIndexToFirstSupBook(short index){ | |||
field_2_index_to_first_supbook_sheet = index; | |||
} | |||
/** gets the index to first sheet from supbook | |||
* @return index to first supbook | |||
*/ | |||
public short getIndexToFirstSupBook(){ | |||
return field_2_index_to_first_supbook_sheet; | |||
} | |||
/** sets the index to last sheet in supbook | |||
* @param index index to last sheet | |||
*/ | |||
public void setIndexToLastSupBook(short index){ | |||
field_3_index_to_last_supbook_sheet = index; | |||
} | |||
/** gets the index to last sheet in supbook | |||
* @return index to last supbook | |||
*/ | |||
public short getIndexToLastSupBook(){ | |||
return field_3_index_to_last_supbook_sheet; | |||
} | |||
/** | |||
* called by constructor, should throw runtime exception in the event of a | |||
* record passed with a differing ID. | |||
* | |||
* @param id alleged id for this record | |||
*/ | |||
protected void validateSid(short id) { | |||
// do nothing | |||
} | |||
/** | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
protected void fillFields(RecordInputStream in) { | |||
field_1_index_to_supbook = in.readShort(); | |||
field_2_index_to_first_supbook_sheet = in.readShort(); | |||
field_3_index_to_last_supbook_sheet = in.readShort(); | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(" supbookindex =").append(getIndexToSupBook()).append('\n'); | |||
buffer.append(" 1stsbindex =").append(getIndexToFirstSupBook()).append('\n'); | |||
buffer.append(" lastsbindex =").append(getIndexToLastSupBook()).append('\n'); | |||
return buffer.toString(); | |||
} | |||
/** | |||
* called by the class that is responsible for writing this sucker. | |||
* Subclasses should implement this so that their data is passed back in a | |||
* byte array. | |||
* | |||
* @param offset to begin writing at | |||
* @param data byte array containing instance data | |||
* @return number of bytes written | |||
*/ | |||
public int serialize(int offset, byte [] data) { | |||
LittleEndian.putShort(data, 0 + offset, getIndexToSupBook()); | |||
LittleEndian.putShort(data, 2 + offset, getIndexToFirstSupBook()); | |||
LittleEndian.putShort(data, 4 + offset, getIndexToLastSupBook()); | |||
return getRecordSize(); | |||
} | |||
/** returns the record size | |||
*/ | |||
public int getRecordSize() { | |||
return 6; | |||
} | |||
/** | |||
* return the non static version of the id for this record. | |||
*/ | |||
public short getSid() { | |||
return sid; | |||
} | |||
} |
@@ -22,7 +22,7 @@ import org.apache.poi.util.LittleEndian; | |||
/** | |||
* Title: Sup Book (EXTERNALBOOK) <P> | |||
* Description: A External Workbook Description (Suplemental Book) | |||
* Description: A External Workbook Description (Supplemental Book) | |||
* Its only a dummy record for making new ExternSheet Record <P> | |||
* REFERENCE: 5.38<P> | |||
* @author Libin Roman (Vista Portal LDT. Developer) |
@@ -110,7 +110,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg { | |||
* @return <code>true</code> if the name specifies a standard worksheet function, | |||
* <code>false</code> if the name should be assumed to be an external function. | |||
*/ | |||
public static final boolean isInternalFunctionName(String name) { | |||
public static final boolean isBuiltInFunctionName(String name) { | |||
short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase()); | |||
return ix >= 0; | |||
} |
@@ -22,41 +22,55 @@ import org.apache.poi.ss.usermodel.Workbook; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
/** | |||
* | |||
* @author aviks | |||
* | |||
* @author aviks | |||
*/ | |||
public final class NameXPtg extends OperandPtg { | |||
public final static short sid = 0x39; | |||
private final static int SIZE = 7; | |||
private short field_1_ixals; // index to REF entry in externsheet record | |||
private short field_2_ilbl; //index to defined name or externname table(1 based) | |||
private short field_3_reserved; // reserved must be 0 | |||
public NameXPtg(RecordInputStream in) { | |||
field_1_ixals = in.readShort(); | |||
field_2_ilbl = in.readShort(); | |||
field_3_reserved = in.readShort(); | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
array[ offset + 0 ] = (byte)(sid + getPtgClass()); | |||
LittleEndian.putShort(array, offset + 1, field_1_ixals); | |||
LittleEndian.putShort(array,offset+3, field_2_ilbl); | |||
LittleEndian.putShort(array, offset + 5, field_3_reserved); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString(Workbook book) | |||
{ | |||
// -1 to convert definedNameIndex from 1-based to zero-based | |||
return book.resolveNameXText(field_1_ixals, field_2_ilbl-1); | |||
} | |||
public byte getDefaultOperandClass() { | |||
public final static short sid = 0x39; | |||
private final static int SIZE = 7; | |||
/** index to REF entry in externsheet record */ | |||
private int _sheetRefIndex; | |||
/** index to defined name or externname table(1 based) */ | |||
private int _nameNumber; | |||
/** reserved must be 0 */ | |||
private int _reserved; | |||
private NameXPtg(int sheetRefIndex, int nameNumber, int reserved) { | |||
_sheetRefIndex = sheetRefIndex; | |||
_nameNumber = nameNumber; | |||
_reserved = reserved; | |||
} | |||
/** | |||
* @param sheetRefIndex index to REF entry in externsheet record | |||
* @param nameIndex index to defined name or externname table | |||
*/ | |||
public NameXPtg(int sheetRefIndex, int nameIndex) { | |||
this(sheetRefIndex, nameIndex + 1, 0); | |||
} | |||
public NameXPtg(RecordInputStream in) { | |||
this(in.readUShort(), in.readUShort(), in.readUShort()); | |||
} | |||
public void writeBytes(byte[] array, int offset) { | |||
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); | |||
LittleEndian.putUShort(array, offset + 1, _sheetRefIndex); | |||
LittleEndian.putUShort(array, offset + 3, _nameNumber); | |||
LittleEndian.putUShort(array, offset + 5, _reserved); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString(Workbook book) { | |||
// -1 to convert definedNameIndex from 1-based to zero-based | |||
return book.resolveNameXText(_sheetRefIndex, _nameNumber - 1); | |||
} | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_VALUE; | |||
} | |||
} |
@@ -23,67 +23,57 @@ import org.apache.poi.hssf.util.RangeAddress; | |||
import org.apache.poi.ss.usermodel.Name; | |||
/** | |||
* Title: High Level Represantion of Named Range <P> | |||
* REFERENCE: <P> | |||
* 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 class HSSFName implements Name { | |||
private HSSFWorkbook book; | |||
private NameRecord name; | |||
private HSSFWorkbook _book; | |||
private NameRecord _definedNameRec; | |||
/** Creates new HSSFName - called by HSSFWorkbook to create a sheet from | |||
* scratch. | |||
* | |||
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName() | |||
* @param name the Name Record | |||
* @param book lowlevel Workbook object associated with the sheet. | |||
* @param book workbook object associated with the sheet. | |||
*/ | |||
protected HSSFName(HSSFWorkbook book, NameRecord name) { | |||
this.book = book; | |||
this.name = name; | |||
/* package */ HSSFName(HSSFWorkbook book, NameRecord name) { | |||
_book = book; | |||
_definedNameRec = name; | |||
} | |||
/** Get the sheets name which this named range is referenced to | |||
* @return sheet name, which this named range refered to | |||
* @return sheet name, which this named range referred to | |||
*/ | |||
public String getSheetName() { | |||
String result ; | |||
short indexToExternSheet = name.getExternSheetNumber(); | |||
short indexToExternSheet = _definedNameRec.getExternSheetNumber(); | |||
result = book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); | |||
return result; | |||
return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); | |||
} | |||
/** | |||
* gets the name of the named range | |||
* @return named range name | |||
* @return text name of this defined name | |||
*/ | |||
public String getNameName(){ | |||
String result = name.getNameText(); | |||
return result; | |||
return _definedNameRec.getNameText(); | |||
} | |||
/** | |||
* sets the name of the named range | |||
* @param nameName named range name to set | |||
*/ | |||
public void setNameName(String nameName){ | |||
name.setNameText(nameName); | |||
name.setNameTextLength((byte)nameName.length()); | |||
Workbook wb = book.getWorkbook(); | |||
_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 != name) { | |||
if (rec != _definedNameRec) { | |||
if (rec.getNameText().equalsIgnoreCase(getNameName())) | |||
throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); | |||
} | |||
@@ -91,32 +81,25 @@ public class HSSFName implements Name { | |||
} | |||
/** | |||
* gets the reference of the named range | |||
* @return reference of the named range | |||
* Note - this method only applies to named ranges | |||
* @return the formula text defining the named range | |||
*/ | |||
public String getReference() { | |||
String result; | |||
result = name.getAreaReference(book); | |||
return result; | |||
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); | |||
int sheetNumber = _book.getSheetIndex(sheetName); | |||
short externSheetNumber = (short) | |||
book.getExternalSheetIndex(sheetNumber); | |||
name.setExternSheetNumber(externSheetNumber); | |||
// name.setIndexToSheet(externSheetNumber); | |||
_book.getExternalSheetIndex(sheetNumber); | |||
_definedNameRec.setExternSheetNumber(externSheetNumber); | |||
} | |||
@@ -124,7 +107,6 @@ public class HSSFName implements Name { | |||
* sets the reference of this named range | |||
* @param ref the reference to set | |||
*/ | |||
public void setReference(String ref){ | |||
RangeAddress ra = new RangeAddress(ref); | |||
@@ -136,8 +118,7 @@ public class HSSFName implements Name { | |||
} | |||
//allow the poi utilities to parse it out | |||
name.setAreaReference(ref); | |||
_definedNameRec.setAreaReference(ref); | |||
} | |||
/** | |||
@@ -149,4 +130,14 @@ public class HSSFName implements Name { | |||
String ref = getReference(); | |||
return "#REF!".endsWith(ref); | |||
} | |||
public boolean isFunctionName() { | |||
return _definedNameRec.isFunctionName(); | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(_definedNameRec.getNameText()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -53,6 +53,7 @@ import org.apache.poi.hssf.record.UnicodeString; | |||
import org.apache.poi.hssf.record.UnknownRecord; | |||
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.UnionPtg; | |||
import org.apache.poi.hssf.util.CellReference; | |||
import org.apache.poi.hssf.util.SheetReferences; | |||
@@ -77,6 +78,9 @@ import org.apache.poi.util.POILogger; | |||
*/ | |||
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook | |||
{ | |||
private static final int MAX_ROW = 0xFFFF; | |||
private static final short MAX_COLUMN = (short)0x00FF; | |||
private static final int DEBUG = POILogger.DEBUG; | |||
/** | |||
@@ -856,7 +860,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
/** | |||
* Sets the repeating rows and columns for a sheet (as found in | |||
* File->PageSetup->Sheet). This is function is included in the workbook | |||
* 2003:File->PageSetup->Sheet, 2007:Page Layout->Print Titles). | |||
* This is function is included in the workbook | |||
* because it creates/modifies name records which are stored at the | |||
* workbook level. | |||
* <p> | |||
@@ -886,10 +891,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
// Check arguments | |||
if (startColumn == -1 && endColumn != -1) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (startRow == -1 && endRow != -1) throw new IllegalArgumentException("Invalid row range specification"); | |||
if (startColumn < -1 || startColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (endColumn < -1 || endColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (startRow < -1 || startRow > 65535) throw new IllegalArgumentException("Invalid row range specification"); | |||
if (endRow < -1 || endRow > 65535) throw new IllegalArgumentException("Invalid row range specification"); | |||
if (startColumn < -1 || startColumn >= MAX_COLUMN) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (endColumn < -1 || endColumn >= MAX_COLUMN) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (startRow < -1 || startRow > MAX_ROW) throw new IllegalArgumentException("Invalid row range specification"); | |||
if (endRow < -1 || endRow > MAX_ROW) throw new IllegalArgumentException("Invalid row range specification"); | |||
if (startColumn > endColumn) throw new IllegalArgumentException("Invalid column range specification"); | |||
if (startRow > endRow) throw new IllegalArgumentException("Invalid row range specification"); | |||
@@ -901,50 +906,60 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
boolean removingRange = | |||
startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1; | |||
boolean isNewRecord = false; | |||
NameRecord nameRecord; | |||
nameRecord = findExistingRowColHeaderNameRecord(sheetIndex); | |||
if (removingRange ) | |||
{ | |||
if (nameRecord != null) | |||
workbook.removeName(findExistingRowColHeaderNameRecordIdx(sheetIndex+1)); | |||
int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex); | |||
if (removingRange) { | |||
if (rowColHeaderNameIndex >= 0) { | |||
workbook.removeName(rowColHeaderNameIndex); | |||
} | |||
return; | |||
} | |||
if ( nameRecord == null ) | |||
{ | |||
nameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_TITLE, sheetIndex+1); | |||
boolean isNewRecord; | |||
NameRecord nameRecord; | |||
if (rowColHeaderNameIndex < 0) { | |||
//does a lot of the house keeping for builtin records, like setting lengths to zero etc | |||
nameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_TITLE, sheetIndex+1); | |||
isNewRecord = true; | |||
} else { | |||
nameRecord = workbook.getNameRecord(rowColHeaderNameIndex); | |||
isNewRecord = false; | |||
} | |||
short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; | |||
nameRecord.setDefinitionTextLength(definitionTextLength); | |||
nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove | |||
Stack ptgs = new Stack(); | |||
if (settingRowAndColumn) | |||
{ | |||
ptgs.add(new MemFuncPtg(23)); // TODO - where did constant '23' come from? | |||
if (settingRowAndColumn) { | |||
final int exprsSize = 2 * 11 + 1; // Area3DPtg.SIZE + UnionPtg.SIZE | |||
ptgs.add(new MemFuncPtg(exprsSize)); | |||
} | |||
if (startColumn >= 0) | |||
{ | |||
Area3DPtg area3DPtg1 = new Area3DPtg(); | |||
area3DPtg1.setExternSheetIndex(externSheetIndex); | |||
area3DPtg1.setFirstColumn((short)startColumn); | |||
area3DPtg1.setLastColumn((short)endColumn); | |||
area3DPtg1.setFirstRow((short)0); | |||
area3DPtg1.setLastRow((short)0xFFFF); | |||
ptgs.add(area3DPtg1); | |||
Area3DPtg colArea = new Area3DPtg(); | |||
colArea.setExternSheetIndex(externSheetIndex); | |||
colArea.setFirstColumn((short)startColumn); | |||
colArea.setLastColumn((short)endColumn); | |||
colArea.setFirstRow(0); | |||
colArea.setLastRow(MAX_ROW); | |||
colArea.setFirstColRelative(false); | |||
colArea.setLastColRelative(false); | |||
colArea.setFirstRowRelative(false); | |||
colArea.setLastRowRelative(false); | |||
ptgs.add(colArea); | |||
} | |||
if (startRow >= 0) | |||
{ | |||
Area3DPtg area3DPtg2 = new Area3DPtg(); | |||
area3DPtg2.setExternSheetIndex(externSheetIndex); | |||
area3DPtg2.setFirstColumn((short)0); | |||
area3DPtg2.setLastColumn((short)0x00FF); | |||
area3DPtg2.setFirstRow((short)startRow); | |||
area3DPtg2.setLastRow((short)endRow); | |||
ptgs.add(area3DPtg2); | |||
Area3DPtg rowArea = new Area3DPtg(); | |||
rowArea.setExternSheetIndex(externSheetIndex); | |||
rowArea.setFirstColumn((short)0); | |||
rowArea.setLastColumn(MAX_COLUMN); | |||
rowArea.setFirstRow(startRow); | |||
rowArea.setLastRow(endRow); | |||
rowArea.setFirstColRelative(false); | |||
rowArea.setLastColRelative(false); | |||
rowArea.setFirstRowRelative(false); | |||
rowArea.setLastRowRelative(false); | |||
ptgs.add(rowArea); | |||
} | |||
if (settingRowAndColumn) | |||
{ | |||
@@ -964,38 +979,31 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
sheet.setActive(true); | |||
} | |||
private NameRecord findExistingRowColHeaderNameRecord( int sheetIndex ) | |||
{ | |||
int index = findExistingRowColHeaderNameRecordIdx(sheetIndex); | |||
if (index == -1) | |||
return null; | |||
else | |||
return (NameRecord)workbook.findNextRecordBySid(NameRecord.sid, index); | |||
} | |||
private int findExistingRowColHeaderNameRecordIdx( int sheetIndex ) | |||
{ | |||
int index = 0; | |||
NameRecord r = null; | |||
while ((r = (NameRecord) workbook.findNextRecordBySid(NameRecord.sid, index)) != null) | |||
{ | |||
int indexToSheet = r.getEqualsToIndexToSheet() -1; | |||
if(indexToSheet > -1) { //ignore "GLOBAL" name records | |||
int nameRecordSheetIndex = workbook.getSheetIndexFromExternSheetIndex(indexToSheet); | |||
if (isRowColHeaderRecord( r ) && nameRecordSheetIndex == sheetIndex) | |||
{ | |||
return index; | |||
} | |||
private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) { | |||
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 )) { | |||
continue; | |||
} | |||
if(r.getSheetNumber() == 0) { | |||
//ignore "GLOBAL" name records | |||
continue; | |||
} | |||
int externIndex = r.getSheetNumber() -1; | |||
int nameRecordSheetIndex = workbook.getSheetIndexFromExternSheetIndex(externIndex); | |||
if (nameRecordSheetIndex == sheetIndex) { | |||
return defNameIndex; | |||
} | |||
index++; | |||
} | |||
return -1; | |||
} | |||
private boolean isRowColHeaderRecord( NameRecord r ) | |||
{ | |||
return r.getOptionFlag() == 0x20 && ("" + ((char)7)).equals(r.getNameText()); | |||
private static boolean isRowColHeaderRecord(NameRecord r) { | |||
return r.isBuiltInName() && r.getBuiltInName() == NameRecord.BUILTIN_PRINT_TITLE; | |||
} | |||
/** | |||
@@ -1089,7 +1097,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
* and that's not something you should normally do | |||
*/ | |||
protected void resetFontCache() { | |||
fonts = new Hashtable(); | |||
fonts = new Hashtable(); | |||
} | |||
/** | |||
@@ -1658,9 +1666,16 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
return new HSSFCreationHelper(this); | |||
} | |||
private byte[] newUID() | |||
{ | |||
byte[] bytes = new byte[16]; | |||
return bytes; | |||
private static byte[] newUID() { | |||
return new byte[16]; | |||
} | |||
/** | |||
* Note - This method should only used by POI internally. | |||
* It may get deleted or change definition in future POI versions | |||
*/ | |||
public NameXPtg getNameXPtg(String name) { | |||
return workbook.getNameXPtg(name); | |||
} | |||
} |
@@ -19,4 +19,5 @@ package org.apache.poi.ss.usermodel; | |||
public interface Name { | |||
void setNameName(String nameName); | |||
boolean isFunctionName(); | |||
} |
@@ -33,6 +33,7 @@ public interface Workbook { | |||
HSSFName createName(); | |||
HSSFName getNameAt(int index); | |||
int getNameIndex(String name); | |||
String getNameName(int index); | |||
String resolveNameXText(int refIndex, int definedNameIndex); | |||
@@ -53,4 +53,5 @@ public interface Name { | |||
void setReference(String ref); | |||
} | |||
boolean isFunctionName(); | |||
} |
@@ -219,24 +219,25 @@ public class HWPFDocument extends POIDocument | |||
_dataStream = new byte[0]; | |||
} | |||
// get the start of text in the main stream | |||
int fcMin = _fib.getFcMin(); | |||
// Get the cp of the start of text in the main stream | |||
// The latest spec doc says this is always zero! | |||
int fcMin = 0; | |||
//fcMin = _fib.getFcMin() | |||
// load up our standard structures. | |||
// Start to load up our standard structures. | |||
_dop = new DocumentProperties(_tableStream, _fib.getFcDop()); | |||
_cft = new ComplexFileTable(_mainStream, _tableStream, _fib.getFcClx(), fcMin); | |||
_tpt = _cft.getTextPieceTable(); | |||
_cbt = new CHPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbteChpx(), _fib.getLcbPlcfbteChpx(), fcMin); | |||
_pbt = new PAPBinTable(_mainStream, _tableStream, _dataStream, _fib.getFcPlcfbtePapx(), _fib.getLcbPlcfbtePapx(), fcMin); | |||
// Word XP puts in a zero filled buffer in front of the text and it screws | |||
// up my system for offsets. This is an adjustment. | |||
// Word XP and later all put in a zero filled buffer in | |||
// front of the text. This screws up the system for offsets, | |||
// which assume we always start at zero. This is an adjustment. | |||
int cpMin = _tpt.getCpMin(); | |||
if (cpMin > 0) | |||
{ | |||
_cbt.adjustForDelete(0, 0, cpMin); | |||
_pbt.adjustForDelete(0, 0, cpMin); | |||
} | |||
// Now load the rest of the properties, which need to be adjusted | |||
// for where text really begin | |||
_cbt = new CHPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbteChpx(), _fib.getLcbPlcfbteChpx(), cpMin, _tpt); | |||
_pbt = new PAPBinTable(_mainStream, _tableStream, _dataStream, _fib.getFcPlcfbtePapx(), _fib.getLcbPlcfbtePapx(), cpMin, _tpt); | |||
// Read FSPA and Escher information | |||
_fspa = new FSPATable(_tableStream, _fib.getFcPlcspaMom(), _fib.getLcbPlcspaMom(), getTextTable().getTextPieces()); | |||
@@ -252,7 +253,7 @@ public class HWPFDocument extends POIDocument | |||
// read in the pictures stream | |||
_pictures = new PicturesTable(this, _dataStream, _mainStream, _fspa, _dgg); | |||
_st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin, getTextTable().getTextPieces()); | |||
_st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin, _tpt, _cpSplit); | |||
_ss = new StyleSheet(_tableStream, _fib.getFcStshf()); | |||
_ft = new FontTable(_tableStream, _fib.getFcSttbfffn(), _fib.getLcbSttbfffn()); | |||
@@ -316,6 +317,11 @@ public class HWPFDocument extends POIDocument | |||
* document, but excludes any headers and footers. | |||
*/ | |||
public Range getRange() { | |||
// First up, trigger a full-recalculate | |||
// Needed in case of deletes etc | |||
getOverallRange(); | |||
// Now, return the real one | |||
return new Range( | |||
_cpSplit.getMainDocumentStart(), | |||
_cpSplit.getMainDocumentEnd(), |
@@ -0,0 +1,60 @@ | |||
/* ==================================================================== | |||
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.hwpf.model; | |||
/** | |||
* Normally PropertyNodes only ever work in characters, but | |||
* a few cases actually store bytes, and this lets everything | |||
* still work despite that. | |||
* It handles the conversion as required between bytes | |||
* and characters. | |||
*/ | |||
public abstract class BytePropertyNode extends PropertyNode { | |||
private boolean isUnicode; | |||
/** | |||
* @param fcStart The start of the text for this property, in _bytes_ | |||
* @param fcEnd The end of the text for this property, in _bytes_ | |||
*/ | |||
public BytePropertyNode(int fcStart, int fcEnd, Object buf, boolean isUnicode) { | |||
super( | |||
generateCp(fcStart, isUnicode), | |||
generateCp(fcEnd, isUnicode), | |||
buf | |||
); | |||
this.isUnicode = isUnicode; | |||
} | |||
private static int generateCp(int val, boolean isUnicode) { | |||
if(isUnicode) | |||
return val/2; | |||
return val; | |||
} | |||
public boolean isUnicode() { | |||
return isUnicode; | |||
} | |||
public int getStartBytes() { | |||
if(isUnicode) | |||
return getStart()*2; | |||
return getStart(); | |||
} | |||
public int getEndBytes() { | |||
if(isUnicode) | |||
return getEnd()*2; | |||
return getEnd(); | |||
} | |||
} |
@@ -37,6 +37,8 @@ public class CHPBinTable | |||
/** List of character properties.*/ | |||
protected ArrayList _textRuns = new ArrayList(); | |||
/** So we can know if things are unicode or not */ | |||
private TextPieceTable tpt; | |||
public CHPBinTable() | |||
{ | |||
@@ -52,9 +54,10 @@ public class CHPBinTable | |||
* @param fcMin | |||
*/ | |||
public CHPBinTable(byte[] documentStream, byte[] tableStream, int offset, | |||
int size, int fcMin) | |||
int size, int fcMin, TextPieceTable tpt) | |||
{ | |||
PlexOfCps binTable = new PlexOfCps(tableStream, offset, size, 4); | |||
this.tpt = tpt; | |||
int length = binTable.length(); | |||
for (int x = 0; x < length; x++) | |||
@@ -65,7 +68,7 @@ public class CHPBinTable | |||
int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum; | |||
CHPFormattedDiskPage cfkp = new CHPFormattedDiskPage(documentStream, | |||
pageOffset, fcMin); | |||
pageOffset, fcMin, tpt); | |||
int fkpSize = cfkp.size(); | |||
@@ -116,7 +119,14 @@ public class CHPBinTable | |||
public void insert(int listIndex, int cpStart, SprmBuffer buf) | |||
{ | |||
CHPX insertChpx = new CHPX(cpStart, cpStart, buf); | |||
boolean needsToBeUnicode = tpt.isUnicodeAtCharOffset(cpStart); | |||
CHPX insertChpx = new CHPX(0, 0, buf, needsToBeUnicode); | |||
// Ensure character offsets are really characters | |||
insertChpx.setStart(cpStart); | |||
insertChpx.setEnd(cpStart); | |||
if (listIndex == _textRuns.size()) | |||
{ | |||
_textRuns.add(insertChpx); | |||
@@ -126,7 +136,16 @@ public class CHPBinTable | |||
CHPX chpx = (CHPX)_textRuns.get(listIndex); | |||
if (chpx.getStart() < cpStart) | |||
{ | |||
CHPX clone = new CHPX(cpStart, chpx.getEnd(), chpx.getSprmBuf()); | |||
// Copy the properties of the one before to afterwards | |||
// Will go: | |||
// Original, until insert at point | |||
// New one | |||
// Clone of original, on to the old end | |||
CHPX clone = new CHPX(0, 0, chpx.getSprmBuf(), needsToBeUnicode); | |||
// Again ensure contains character based offsets no matter what | |||
clone.setStart(cpStart); | |||
clone.setEnd(chpx.getEnd()); | |||
chpx.setEnd(cpStart); | |||
_textRuns.add(listIndex + 1, insertChpx); |
@@ -55,13 +55,14 @@ public class CHPFormattedDiskPage extends FormattedDiskPage | |||
* This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array | |||
* read from a Word file). | |||
*/ | |||
public CHPFormattedDiskPage(byte[] documentStream, int offset, int fcMin) | |||
public CHPFormattedDiskPage(byte[] documentStream, int offset, int fcMin, TextPieceTable tpt) | |||
{ | |||
super(documentStream, offset); | |||
for (int x = 0; x < _crun; x++) | |||
{ | |||
_chpxList.add(new CHPX(getStart(x) - fcMin, getEnd(x) - fcMin, getGrpprl(x))); | |||
boolean isUnicode = tpt.isUnicodeAtByteOffset( getStart(x) ); | |||
_chpxList.add(new CHPX(getStart(x) - fcMin, getEnd(x) - fcMin, getGrpprl(x), isUnicode)); | |||
} | |||
} | |||
@@ -157,7 +158,7 @@ public class CHPFormattedDiskPage extends FormattedDiskPage | |||
chpx = (CHPX)_chpxList.get(x); | |||
byte[] grpprl = chpx.getGrpprl(); | |||
LittleEndian.putInt(buf, fcOffset, chpx.getStart() + fcMin); | |||
LittleEndian.putInt(buf, fcOffset, chpx.getStartBytes() + fcMin); | |||
grpprlOffset -= (1 + grpprl.length); | |||
grpprlOffset -= (grpprlOffset % 2); | |||
buf[offsetOffset] = (byte)(grpprlOffset/2); | |||
@@ -168,7 +169,7 @@ public class CHPFormattedDiskPage extends FormattedDiskPage | |||
fcOffset += FC_SIZE; | |||
} | |||
// put the last chpx's end in | |||
LittleEndian.putInt(buf, fcOffset, chpx.getEnd() + fcMin); | |||
LittleEndian.putInt(buf, fcOffset, chpx.getEndBytes() + fcMin); | |||
return buf; | |||
} | |||
@@ -25,22 +25,26 @@ import org.apache.poi.hwpf.sprm.SprmBuffer; | |||
import org.apache.poi.hwpf.sprm.CharacterSprmUncompressor; | |||
/** | |||
* Comment me | |||
* DANGER - works in bytes! | |||
* | |||
* Make sure you call getStart() / getEnd() when you want characters | |||
* (normal use), but getStartByte() / getEndByte() when you're | |||
* reading in / writing out! | |||
* | |||
* @author Ryan Ackley | |||
*/ | |||
public class CHPX extends PropertyNode | |||
public class CHPX extends BytePropertyNode | |||
{ | |||
public CHPX(int fcStart, int fcEnd, byte[] grpprl) | |||
public CHPX(int fcStart, int fcEnd, byte[] grpprl, boolean isUnicode) | |||
{ | |||
super(fcStart, fcEnd, new SprmBuffer(grpprl)); | |||
super(fcStart, fcEnd, new SprmBuffer(grpprl), isUnicode); | |||
} | |||
public CHPX(int fcStart, int fcEnd, SprmBuffer buf) | |||
public CHPX(int fcStart, int fcEnd, SprmBuffer buf, boolean isUnicode) | |||
{ | |||
super(fcStart, fcEnd, buf); | |||
super(fcStart, fcEnd, buf, isUnicode); | |||
} | |||
@@ -39,14 +39,18 @@ public class PAPBinTable | |||
protected ArrayList _paragraphs = new ArrayList(); | |||
byte[] _dataStream; | |||
/** So we can know if things are unicode or not */ | |||
private TextPieceTable tpt; | |||
public PAPBinTable() | |||
{ | |||
} | |||
public PAPBinTable(byte[] documentStream, byte[] tableStream, byte[] dataStream, int offset, | |||
int size, int fcMin) | |||
int size, int fcMin, TextPieceTable tpt) | |||
{ | |||
PlexOfCps binTable = new PlexOfCps(tableStream, offset, size, 4); | |||
this.tpt = tpt; | |||
int length = binTable.length(); | |||
for (int x = 0; x < length; x++) | |||
@@ -57,13 +61,14 @@ public class PAPBinTable | |||
int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum; | |||
PAPFormattedDiskPage pfkp = new PAPFormattedDiskPage(documentStream, | |||
dataStream, pageOffset, fcMin); | |||
dataStream, pageOffset, fcMin, tpt); | |||
int fkpSize = pfkp.size(); | |||
for (int y = 0; y < fkpSize; y++) | |||
{ | |||
_paragraphs.add(pfkp.getPAPX(y)); | |||
PAPX papx = pfkp.getPAPX(y); | |||
_paragraphs.add(papx); | |||
} | |||
} | |||
_dataStream = dataStream; | |||
@@ -71,7 +76,14 @@ public class PAPBinTable | |||
public void insert(int listIndex, int cpStart, SprmBuffer buf) | |||
{ | |||
PAPX forInsert = new PAPX(cpStart, cpStart, buf, _dataStream); | |||
boolean needsToBeUnicode = tpt.isUnicodeAtCharOffset(cpStart); | |||
PAPX forInsert = new PAPX(0, 0, buf, _dataStream, needsToBeUnicode); | |||
// Ensure character offsets are really characters | |||
forInsert.setStart(cpStart); | |||
forInsert.setEnd(cpStart); | |||
if (listIndex == _paragraphs.size()) | |||
{ | |||
_paragraphs.add(forInsert); | |||
@@ -90,10 +102,21 @@ public class PAPBinTable | |||
{ | |||
exc.printStackTrace(); | |||
} | |||
// Copy the properties of the one before to afterwards | |||
// Will go: | |||
// Original, until insert at point | |||
// New one | |||
// Clone of original, on to the old end | |||
PAPX clone = new PAPX(0, 0, clonedBuf, _dataStream, needsToBeUnicode); | |||
// Again ensure contains character based offsets no matter what | |||
clone.setStart(cpStart); | |||
clone.setEnd(currentPap.getEnd()); | |||
currentPap.setEnd(cpStart); | |||
PAPX splitPap = new PAPX(cpStart, currentPap.getEnd(), clonedBuf, _dataStream); | |||
_paragraphs.add(++listIndex, forInsert); | |||
_paragraphs.add(++listIndex, splitPap); | |||
_paragraphs.add(listIndex + 1, forInsert); | |||
_paragraphs.add(listIndex + 2, clone); | |||
} | |||
else | |||
{ |
@@ -60,13 +60,17 @@ public class PAPFormattedDiskPage extends FormattedDiskPage | |||
/** | |||
* Creates a PAPFormattedDiskPage from a 512 byte array | |||
*/ | |||
public PAPFormattedDiskPage(byte[] documentStream, byte[] dataStream, int offset, int fcMin) | |||
public PAPFormattedDiskPage(byte[] documentStream, byte[] dataStream, int offset, int fcMin, TextPieceTable tpt) | |||
{ | |||
super(documentStream, offset); | |||
for (int x = 0; x < _crun; x++) | |||
{ | |||
_papxList.add(new PAPX(getStart(x) - fcMin, getEnd(x) - fcMin, getGrpprl(x), getParagraphHeight(x), dataStream)); | |||
for (int x = 0; x < _crun; x++) { | |||
int startAt = getStart(x) - fcMin; | |||
int endAt = getEnd(x) - fcMin; | |||
boolean isUnicode = tpt.isUnicodeAtByteOffset(startAt); | |||
//System.err.println(startAt + " -> " + endAt + " = " + isUnicode); | |||
_papxList.add(new PAPX(startAt, endAt, getGrpprl(x), getParagraphHeight(x), dataStream, isUnicode)); | |||
} | |||
_fkp = null; | |||
_dataStream = dataStream; | |||
@@ -110,7 +114,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage | |||
} | |||
/** | |||
* Gets the papx for the paragraph at index in this fkp. | |||
* Gets the papx grpprl for the paragraph at index in this fkp. | |||
* | |||
* @param index The index of the papx to get. | |||
* @return a papx grpprl. | |||
@@ -259,7 +263,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage | |||
grpprlOffset -= (grpprl.length + (2 - grpprl.length % 2)); | |||
grpprlOffset -= (grpprlOffset % 2); | |||
} | |||
LittleEndian.putInt(buf, fcOffset, papx.getStart() + fcMin); | |||
LittleEndian.putInt(buf, fcOffset, papx.getStartBytes() + fcMin); | |||
buf[bxOffset] = (byte)(grpprlOffset/2); | |||
System.arraycopy(phe, 0, buf, bxOffset + 1, phe.length); | |||
@@ -287,7 +291,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage | |||
} | |||
LittleEndian.putInt(buf, fcOffset, papx.getEnd() + fcMin); | |||
LittleEndian.putInt(buf, fcOffset, papx.getEndBytes() + fcMin); | |||
return buf; | |||
} | |||
@@ -29,29 +29,32 @@ import org.apache.poi.hwpf.sprm.SprmBuffer; | |||
import org.apache.poi.hwpf.sprm.SprmOperation; | |||
/** | |||
* Comment me | |||
* DANGER - works in bytes! | |||
* | |||
* Make sure you call getStart() / getEnd() when you want characters | |||
* (normal use), but getStartByte() / getEndByte() when you're | |||
* reading in / writing out! | |||
* | |||
* @author Ryan Ackley | |||
*/ | |||
public class PAPX extends PropertyNode | |||
{ | |||
public class PAPX extends BytePropertyNode { | |||
private ParagraphHeight _phe; | |||
private int _hugeGrpprlOffset = -1; | |||
public PAPX(int fcStart, int fcEnd, byte[] papx, ParagraphHeight phe, byte[] dataStream) | |||
public PAPX(int fcStart, int fcEnd, byte[] papx, ParagraphHeight phe, byte[] dataStream, boolean isUnicode) | |||
{ | |||
super(fcStart, fcEnd, new SprmBuffer(papx)); | |||
super(fcStart, fcEnd, new SprmBuffer(papx), isUnicode); | |||
_phe = phe; | |||
SprmBuffer buf = findHuge(new SprmBuffer(papx), dataStream); | |||
if(buf != null) | |||
_buf = buf; | |||
} | |||
public PAPX(int fcStart, int fcEnd, SprmBuffer buf, byte[] dataStream) | |||
public PAPX(int fcStart, int fcEnd, SprmBuffer buf, byte[] dataStream, boolean isUnicode) | |||
{ | |||
super(fcStart, fcEnd, buf); | |||
super(fcStart, fcEnd, buf, isUnicode); | |||
_phe = new ParagraphHeight(); | |||
buf = findHuge(buf, dataStream); | |||
if(buf != null) |
@@ -22,7 +22,10 @@ import java.util.Arrays; | |||
/** | |||
* Represents a lightweight node in the Trees used to store content | |||
* properties. Works only in characters. | |||
* properties. | |||
* This only ever works in characters. For the few odd cases when | |||
* the start and end aren't in characters (eg PAPX and CHPX), use | |||
* {@link BytePropertyNode} between you and this. | |||
* | |||
* @author Ryan Ackley | |||
*/ | |||
@@ -45,6 +48,11 @@ public abstract class PropertyNode implements Comparable, Cloneable | |||
_cpStart = fcStart; | |||
_cpEnd = fcEnd; | |||
_buf = buf; | |||
if(_cpStart < 0) { | |||
System.err.println("A property claimed to start before zero, at " + _cpStart + "! Resetting it to zero, and hoping for the best"); | |||
_cpStart = 0; | |||
} | |||
} | |||
/** | |||
@@ -82,18 +90,18 @@ public abstract class PropertyNode implements Comparable, Cloneable | |||
{ | |||
int end = start + length; | |||
if (_cpEnd > start) | |||
{ | |||
if (_cpStart < end) | |||
{ | |||
_cpEnd = end >= _cpEnd ? start : _cpEnd - length; | |||
_cpStart = Math.min(start, _cpStart); | |||
} | |||
else | |||
{ | |||
_cpEnd -= length; | |||
_cpStart -= length; | |||
} | |||
if (_cpEnd > start) { | |||
// The start of the change is before we end | |||
if (_cpStart < end) { | |||
// The delete was somewhere in the middle of us | |||
_cpEnd = end >= _cpEnd ? start : _cpEnd - length; | |||
_cpStart = Math.min(start, _cpStart); | |||
} else { | |||
// The delete was before us | |||
_cpEnd -= length; | |||
_cpStart -= length; | |||
} | |||
} | |||
} | |||
@@ -20,19 +20,20 @@ | |||
package org.apache.poi.hwpf.model; | |||
import org.apache.poi.hwpf.sprm.SprmBuffer; | |||
import org.apache.poi.hwpf.sprm.SectionSprmUncompressor; | |||
import org.apache.poi.hwpf.sprm.SectionSprmCompressor; | |||
import org.apache.poi.hwpf.sprm.SectionSprmUncompressor; | |||
import org.apache.poi.hwpf.usermodel.SectionProperties; | |||
public class SEPX extends PropertyNode | |||
/** | |||
*/ | |||
public class SEPX extends BytePropertyNode | |||
{ | |||
SectionDescriptor _sed; | |||
public SEPX(SectionDescriptor sed, int start, int end, byte[] grpprl) | |||
public SEPX(SectionDescriptor sed, int start, int end, byte[] grpprl, boolean isUnicode) | |||
{ | |||
super(start, end, SectionSprmUncompressor.uncompressSEP(grpprl, 0)); | |||
super(start, end, SectionSprmUncompressor.uncompressSEP(grpprl, 0), isUnicode); | |||
_sed = sed; | |||
} | |||
@@ -34,6 +34,9 @@ public class SectionTable | |||
protected ArrayList _sections = new ArrayList(); | |||
protected List _text; | |||
/** So we can know if things are unicode or not */ | |||
private TextPieceTable tpt; | |||
public SectionTable() | |||
{ | |||
} | |||
@@ -41,10 +44,11 @@ public class SectionTable | |||
public SectionTable(byte[] documentStream, byte[] tableStream, int offset, | |||
int size, int fcMin, | |||
List tpt) | |||
TextPieceTable tpt, CPSplitCalculator cps) | |||
{ | |||
PlexOfCps sedPlex = new PlexOfCps(tableStream, offset, size, SED_SIZE); | |||
_text = tpt; | |||
this.tpt = tpt; | |||
this._text = tpt.getTextPieces(); | |||
int length = sedPlex.length(); | |||
@@ -54,11 +58,16 @@ public class SectionTable | |||
SectionDescriptor sed = new SectionDescriptor(node.getBytes(), 0); | |||
int fileOffset = sed.getFc(); | |||
int startAt = CPtoFC(node.getStart()); | |||
int endAt = CPtoFC(node.getEnd()); | |||
boolean isUnicodeAtStart = tpt.isUnicodeAtByteOffset( startAt ); | |||
// System.err.println(startAt + " -> " + endAt + " = " + isUnicodeAtStart); | |||
// check for the optimization | |||
if (fileOffset == 0xffffffff) | |||
{ | |||
_sections.add(new SEPX(sed, CPtoFC(node.getStart()), CPtoFC(node.getEnd()), new byte[0])); | |||
_sections.add(new SEPX(sed, startAt, endAt, new byte[0], isUnicodeAtStart)); | |||
} | |||
else | |||
{ | |||
@@ -67,9 +76,34 @@ public class SectionTable | |||
byte[] buf = new byte[sepxSize]; | |||
fileOffset += LittleEndian.SHORT_SIZE; | |||
System.arraycopy(documentStream, fileOffset, buf, 0, buf.length); | |||
_sections.add(new SEPX(sed, CPtoFC(node.getStart()), CPtoFC(node.getEnd()), buf)); | |||
_sections.add(new SEPX(sed, startAt, endAt, buf, isUnicodeAtStart)); | |||
} | |||
} | |||
// Some files seem to lie about their unicode status, which | |||
// is very very pesky. Try to work around these, but this | |||
// is getting on for black magic... | |||
int mainEndsAt = cps.getMainDocumentEnd(); | |||
boolean matchAt = false; | |||
boolean matchHalf = false; | |||
for(int i=0; i<_sections.size(); i++) { | |||
SEPX s = (SEPX)_sections.get(i); | |||
if(s.getEnd() == mainEndsAt) { | |||
matchAt = true; | |||
} else if(s.getEndBytes() == mainEndsAt || s.getEndBytes() == mainEndsAt-1) { | |||
matchHalf = true; | |||
} | |||
} | |||
if(! matchAt && matchHalf) { | |||
System.err.println("Your document seemed to be mostly unicode, but the section definition was in bytes! Trying anyway, but things may well go wrong!"); | |||
for(int i=0; i<_sections.size(); i++) { | |||
SEPX s = (SEPX)_sections.get(i); | |||
GenericPropertyNode node = sedPlex.getProperty(i); | |||
s.setStart( CPtoFC(node.getStart()) ); | |||
s.setEnd( CPtoFC(node.getEnd()) ); | |||
} | |||
} | |||
} | |||
public void adjustForInsert(int listIndex, int length) | |||
@@ -171,7 +205,7 @@ public class SectionTable | |||
// Line using Ryan's FCtoCP() conversion method - | |||
// unable to observe any effect on our testcases when using this code - piers | |||
GenericPropertyNode property = new GenericPropertyNode(FCtoCP(sepx.getStart()), FCtoCP(sepx.getEnd()), sed.toByteArray()); | |||
GenericPropertyNode property = new GenericPropertyNode(FCtoCP(sepx.getStartBytes()), FCtoCP(sepx.getEndBytes()), sed.toByteArray()); | |||
plex.addProperty(property); |
@@ -118,6 +118,9 @@ public class TextPiece extends PropertyNode implements Comparable | |||
if(end > buf.length()) { | |||
throw new StringIndexOutOfBoundsException("Index " + end + " out of range 0 -> " + buf.length()); | |||
} | |||
if(end < start) { | |||
throw new StringIndexOutOfBoundsException("Asked for text from " + start + " to " + end + ", which has an end before the start!"); | |||
} | |||
return buf.substring(start, end); | |||
} | |||
@@ -25,6 +25,9 @@ import org.apache.poi.poifs.common.POIFSConstants; | |||
import java.io.IOException; | |||
import java.io.UnsupportedEncodingException; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Hashtable; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
/** | |||
@@ -62,8 +65,17 @@ public class TextPieceTable | |||
pieces[x] = new PieceDescriptor(node.getBytes(), 0); | |||
} | |||
int firstPieceFilePosition = pieces[0].getFilePosition(); | |||
_cpMin = firstPieceFilePosition - fcMin; | |||
// Figure out the cp of the earliest text piece | |||
// Note that text pieces don't have to be stored in order! | |||
_cpMin = pieces[0].getFilePosition() - fcMin; | |||
for (int x = 0; x < pieces.length; x++) { | |||
int start = pieces[x].getFilePosition() - fcMin; | |||
if(start < _cpMin) { | |||
_cpMin = start; | |||
} | |||
} | |||
// using the PieceDescriptors, build our list of TextPieces. | |||
for (int x = 0; x < pieces.length; x++) | |||
@@ -93,6 +105,15 @@ public class TextPieceTable | |||
// And now build the piece | |||
_textPieces.add(new TextPiece(nodeStartChars, nodeEndChars, buf, pieces[x], node.getStart())); | |||
} | |||
// In the interest of our sanity, now sort the text pieces | |||
// into order, if they're not already | |||
TextPiece[] tp = (TextPiece[]) | |||
_textPieces.toArray(new TextPiece[_textPieces.size()]); | |||
Arrays.sort(tp); | |||
for(int i=0; i<tp.length; i++) { | |||
_textPieces.set(i, tp[i]); | |||
} | |||
} | |||
public int getCpMin() | |||
@@ -104,6 +125,62 @@ public class TextPieceTable | |||
{ | |||
return _textPieces; | |||
} | |||
/** | |||
* Is the text at the given Character offset | |||
* unicode, or plain old ascii? | |||
* In a very evil fashion, you have to actually | |||
* know this to make sense of character and | |||
* paragraph properties :( | |||
* @param cp The character offset to check about | |||
*/ | |||
public boolean isUnicodeAtCharOffset(int cp) { | |||
boolean lastWas = false; | |||
Iterator it = _textPieces.iterator(); | |||
while(it.hasNext()) { | |||
TextPiece tp = (TextPiece)it.next(); | |||
// If the text piece covers the character, all good | |||
if(tp.getStart() <= cp && tp.getEnd() >= cp) { | |||
return tp.isUnicode(); | |||
} | |||
// Otherwise keep track for the last one | |||
lastWas = tp.isUnicode(); | |||
} | |||
// If they ask off the end, just go with the last one... | |||
return lastWas; | |||
} | |||
/** | |||
* Is the text at the given byte offset | |||
* unicode, or plain old ascii? | |||
* In a very evil fashion, you have to actually | |||
* know this to make sense of character and | |||
* paragraph properties :( | |||
* @param cp The character offset to check about | |||
*/ | |||
public boolean isUnicodeAtByteOffset(int bytePos) { | |||
boolean lastWas = false; | |||
int curByte = 0; | |||
Iterator it = _textPieces.iterator(); | |||
while(it.hasNext()) { | |||
TextPiece tp = (TextPiece)it.next(); | |||
int nextByte = curByte + tp.bytesLength(); | |||
// If the text piece covers the character, all good | |||
if(curByte <= bytePos && nextByte >= bytePos) { | |||
return tp.isUnicode(); | |||
} | |||
// Otherwise keep track for the last one | |||
lastWas = tp.isUnicode(); | |||
// Move along | |||
curByte = nextByte; | |||
} | |||
// If they ask off the end, just go with the last one... | |||
return lastWas; | |||
} | |||
public byte[] writeTo(HWPFOutputStream docStream) | |||
throws IOException |
@@ -70,10 +70,10 @@ public abstract class FIBAbstractType | |||
private static BitField fFutureSavedUndo = BitFieldFactory.getInstance(0x0008); | |||
private static BitField fWord97Saved = BitFieldFactory.getInstance(0x0010); | |||
private static BitField fSpare0 = BitFieldFactory.getInstance(0x00FE); | |||
protected int field_11_chs; | |||
protected int field_12_chsTables; | |||
protected int field_13_fcMin; | |||
protected int field_14_fcMac; | |||
protected int field_11_chs; /** Latest docs say this is Reserved3! */ | |||
protected int field_12_chsTables; /** Latest docs say this is Reserved4! */ | |||
protected int field_13_fcMin; /** Latest docs say this is Reserved5! */ | |||
protected int field_14_fcMac; /** Latest docs say this is Reserved6! */ | |||
public FIBAbstractType() |
@@ -155,6 +155,8 @@ public class Range | |||
_characters = _doc.getCharacterTable().getTextRuns(); | |||
_text = _doc.getTextTable().getTextPieces(); | |||
_parent = new WeakReference(null); | |||
sanityCheckStartEnd(); | |||
} | |||
@@ -175,6 +177,8 @@ public class Range | |||
_characters = parent._characters; | |||
_text = parent._text; | |||
_parent = new WeakReference(parent); | |||
sanityCheckStartEnd(); | |||
} | |||
/** | |||
@@ -226,6 +230,22 @@ public class Range | |||
_textRangeFound = true; | |||
break; | |||
} | |||
sanityCheckStartEnd(); | |||
} | |||
/** | |||
* Ensures that the start and end were were given | |||
* are actually valid, to avoid issues later on | |||
* if they're not | |||
*/ | |||
private void sanityCheckStartEnd() { | |||
if(_start < 0) { | |||
throw new IllegalArgumentException("Range start must not be negative. Given " + _start); | |||
} | |||
if(_end < _start) { | |||
throw new IllegalArgumentException("The end (" + _end + ") must not be before the start ("+_start+")"); | |||
} | |||
} | |||
/** | |||
@@ -537,13 +557,17 @@ public class Range | |||
for (int x = _parStart; x < numParagraphs; x++) | |||
{ | |||
PAPX papx = (PAPX)_paragraphs.get(x); | |||
//System.err.println("Paragraph " + x + " was " + papx.getStart() + " -> " + papx.getEnd()); | |||
papx.adjustForDelete(_start, _end - _start); | |||
//System.err.println("Paragraph " + x + " is now " + papx.getStart() + " -> " + papx.getEnd()); | |||
} | |||
for (int x = _sectionStart; x < numSections; x++) | |||
{ | |||
SEPX sepx = (SEPX)_sections.get(x); | |||
//System.err.println("Section " + x + " was " + sepx.getStart() + " -> " + sepx.getEnd()); | |||
sepx.adjustForDelete(_start, _end - _start); | |||
//System.err.println("Section " + x + " is now " + sepx.getStart() + " -> " + sepx.getEnd()); | |||
} | |||
for (int x = _textStart; x < numTextPieces; x++) | |||
@@ -650,14 +674,6 @@ public class Range | |||
absPlaceHolderIndex, | |||
(absPlaceHolderIndex + pPlaceHolder.length()), getDocument() | |||
); | |||
if (subRange.usesUnicode()) { | |||
absPlaceHolderIndex = getStartOffset() + (pOffset * 2); | |||
subRange = new Range( | |||
absPlaceHolderIndex, | |||
(absPlaceHolderIndex + (pPlaceHolder.length() * 2)), | |||
getDocument() | |||
); | |||
} | |||
// this Range isn't a proper parent of the subRange() so we'll have to keep | |||
// track of an updated endOffset on our own | |||
@@ -674,12 +690,6 @@ public class Range | |||
(absPlaceHolderIndex + pPlaceHolder.length() + pValue.length()), | |||
getDocument() | |||
); | |||
if (subRange.usesUnicode()) | |||
subRange = new Range( | |||
(absPlaceHolderIndex + (pValue.length() * 2)), | |||
(absPlaceHolderIndex + (pPlaceHolder.length() * 2) + | |||
(pValue.length() * 2)), getDocument() | |||
); | |||
// deletes are automagically propagated | |||
subRange.delete(); | |||
@@ -820,6 +830,10 @@ public class Range | |||
{ | |||
throw new ArrayIndexOutOfBoundsException("The table's bounds fall outside of this Range"); | |||
} | |||
if (tableEnd < 0) | |||
{ | |||
throw new ArrayIndexOutOfBoundsException("The table's end is negative, which isn't allowed!"); | |||
} | |||
return new Table(r._parStart, tableEnd, r._doc.getRange(), paragraph.getTableLevel()); | |||
} | |||
@@ -32,6 +32,8 @@ public class TestCHPBinTable | |||
{ | |||
private CHPBinTable _cHPBinTable = null; | |||
private HWPFDocFixture _hWPFDocFixture; | |||
private TextPieceTable fakeTPT = new TextPieceTable(); | |||
public TestCHPBinTable(String name) | |||
{ | |||
@@ -46,7 +48,7 @@ public class TestCHPBinTable | |||
byte[] tableStream = _hWPFDocFixture._tableStream; | |||
int fcMin = fib.getFcMin(); | |||
_cHPBinTable = new CHPBinTable(mainStream, tableStream, fib.getFcPlcfbteChpx(), fib.getLcbPlcfbteChpx(), fcMin); | |||
_cHPBinTable = new CHPBinTable(mainStream, tableStream, fib.getFcPlcfbteChpx(), fib.getLcbPlcfbteChpx(), fcMin, fakeTPT); | |||
HWPFFileSystem fileSys = new HWPFFileSystem(); | |||
@@ -57,7 +59,7 @@ public class TestCHPBinTable | |||
byte[] newTableStream = tableOut.toByteArray(); | |||
byte[] newMainStream = mainOut.toByteArray(); | |||
CHPBinTable newBinTable = new CHPBinTable(newMainStream, newTableStream, 0, newTableStream.length, 0); | |||
CHPBinTable newBinTable = new CHPBinTable(newMainStream, newTableStream, 0, newTableStream.length, 0, fakeTPT); | |||
ArrayList oldTextRuns = _cHPBinTable._textRuns; | |||
ArrayList newTextRuns = newBinTable._textRuns; |
@@ -32,6 +32,8 @@ public class TestPAPBinTable | |||
private PAPBinTable _pAPBinTable = null; | |||
private HWPFDocFixture _hWPFDocFixture; | |||
private TextPieceTable fakeTPT = new TextPieceTable(); | |||
public TestPAPBinTable(String name) | |||
{ | |||
super(name); | |||
@@ -45,7 +47,7 @@ public class TestPAPBinTable | |||
byte[] tableStream = _hWPFDocFixture._tableStream; | |||
int fcMin = fib.getFcMin(); | |||
_pAPBinTable = new PAPBinTable(mainStream, tableStream, null, fib.getFcPlcfbtePapx(), fib.getLcbPlcfbtePapx(), fcMin); | |||
_pAPBinTable = new PAPBinTable(mainStream, tableStream, null, fib.getFcPlcfbtePapx(), fib.getLcbPlcfbtePapx(), fcMin, fakeTPT); | |||
HWPFFileSystem fileSys = new HWPFFileSystem(); | |||
@@ -56,7 +58,7 @@ public class TestPAPBinTable | |||
byte[] newTableStream = tableOut.toByteArray(); | |||
byte[] newMainStream = mainOut.toByteArray(); | |||
PAPBinTable newBinTable = new PAPBinTable(newMainStream, newTableStream, null,0, newTableStream.length, 0); | |||
PAPBinTable newBinTable = new PAPBinTable(newMainStream, newTableStream, null,0, newTableStream.length, 0, fakeTPT); | |||
ArrayList oldTextRuns = _pAPBinTable.getParagraphs(); | |||
ArrayList newTextRuns = newBinTable.getParagraphs(); |
@@ -45,13 +45,15 @@ public class TestSectionTable | |||
byte[] tableStream = _hWPFDocFixture._tableStream; | |||
int fcMin = fib.getFcMin(); | |||
CPSplitCalculator cps = new CPSplitCalculator(fib); | |||
ComplexFileTable cft = new ComplexFileTable(mainStream, tableStream, fib.getFcClx(), fcMin); | |||
TextPieceTable tpt = cft.getTextPieceTable(); | |||
SectionTable sectionTable = new SectionTable(mainStream, tableStream, | |||
fib.getFcPlcfsed(), | |||
fib.getLcbPlcfsed(), | |||
fcMin, tpt.getTextPieces()); | |||
fcMin, tpt, cps); | |||
HWPFFileSystem fileSys = new HWPFFileSystem(); | |||
sectionTable.writeTo(fileSys, 0); | |||
@@ -61,7 +63,9 @@ public class TestSectionTable | |||
byte[] newTableStream = tableOut.toByteArray(); | |||
byte[] newMainStream = mainOut.toByteArray(); | |||
SectionTable newSectionTable = new SectionTable(newMainStream, newTableStream, 0, newTableStream.length, 0, tpt.getTextPieces()); | |||
SectionTable newSectionTable = new SectionTable( | |||
newMainStream, newTableStream, 0, | |||
newTableStream.length, 0, tpt, cps); | |||
ArrayList oldSections = sectionTable.getSections(); | |||
ArrayList newSections = newSectionTable.getSections(); |
@@ -81,9 +81,16 @@ public class TestProblems extends TestCase { | |||
HWPFDocument doc = new HWPFDocument(new FileInputStream( | |||
new File(dirname, "Bug44292.doc"))); | |||
Range r = doc.getRange(); | |||
assertEquals(6, r.numParagraphs()); | |||
assertEquals(0, r.getStartOffset()); | |||
assertEquals(87, r.getEndOffset()); | |||
//get the table | |||
// Paragraph with table | |||
Paragraph p = r.getParagraph(0); | |||
assertEquals(0, p.getStartOffset()); | |||
assertEquals(20, p.getEndOffset()); | |||
// Get the table | |||
Table t = r.getTable(p); | |||
//get the only row |
@@ -18,27 +18,28 @@ | |||
package org.apache.poi.hwpf.usermodel; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.FileInputStream; | |||
import java.util.List; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.model.PicturesTable; | |||
import org.apache.poi.hwpf.usermodel.Picture; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.model.PAPX; | |||
/** | |||
* Test to see if Range.delete() works even if the Range contains a | |||
* CharacterRun that uses Unicode characters. | |||
* | |||
* TODO - re-enable me when unicode paragraph stuff is fixed! | |||
*/ | |||
public abstract class TestRangeDelete extends TestCase { | |||
public class TestRangeDelete extends TestCase { | |||
// u201c and u201d are "smart-quotes" | |||
private String introText = | |||
"Introduction\r"; | |||
private String fillerText = | |||
"${delete} This is an MS-Word 97 formatted document created using NeoOffice v. 2.2.4 Patch 0 (OpenOffice.org v. 2.2.1).\r"; | |||
private String originalText = | |||
"It is used to confirm that text delete works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present. Everybody should be thankful to the ${organization} ${delete} and all the POI contributors for their assistance in this matter.\r"; | |||
private String lastText = | |||
"Thank you, ${organization} ${delete}!\r"; | |||
private String searchText = "${delete}"; | |||
private String expectedText1 = " This is an MS-Word 97 formatted document created using NeoOffice v. 2.2.4 Patch 0 (OpenOffice.org v. 2.2.1).\r"; | |||
private String expectedText2 = | |||
@@ -68,32 +69,63 @@ public abstract class TestRangeDelete extends TestCase { | |||
public void testDocStructure() throws Exception { | |||
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile)); | |||
Range range; | |||
Section section; | |||
Paragraph para; | |||
PAPX paraDef; | |||
Range range = daDoc.getOverallRange(); | |||
// First, check overall | |||
range = daDoc.getOverallRange(); | |||
assertEquals(1, range.numSections()); | |||
Section section = range.getSection(0); | |||
assertEquals(5, section.numParagraphs()); | |||
Paragraph para = section.getParagraph(2); | |||
assertEquals(5, para.numCharacterRuns()); | |||
assertEquals(originalText, para.text()); | |||
assertEquals(5, range.numParagraphs()); | |||
// Now check on just the main text | |||
// Now, onto just the doc bit | |||
range = daDoc.getRange(); | |||
assertEquals(1, range.numSections()); | |||
assertEquals(1, daDoc.getSectionTable().getSections().size()); | |||
section = range.getSection(0); | |||
assertEquals(5, section.numParagraphs()); | |||
para = section.getParagraph(0); | |||
assertEquals(1, para.numCharacterRuns()); | |||
assertEquals(introText, para.text()); | |||
para = section.getParagraph(1); | |||
assertEquals(5, para.numCharacterRuns()); | |||
assertEquals(fillerText, para.text()); | |||
paraDef = (PAPX)daDoc.getParagraphTable().getParagraphs().get(2); | |||
assertEquals(132, paraDef.getStart()); | |||
assertEquals(400, paraDef.getEnd()); | |||
para = section.getParagraph(2); | |||
assertEquals(5, para.numCharacterRuns()); | |||
assertEquals(originalText, para.text()); | |||
paraDef = (PAPX)daDoc.getParagraphTable().getParagraphs().get(3); | |||
assertEquals(400, paraDef.getStart()); | |||
assertEquals(438, paraDef.getEnd()); | |||
para = section.getParagraph(3); | |||
assertEquals(1, para.numCharacterRuns()); | |||
assertEquals(lastText, para.text()); | |||
// Check things match on text length | |||
assertEquals(439, range.text().length()); | |||
assertEquals(439, section.text().length()); | |||
assertEquals(439, | |||
section.getParagraph(0).text().length() + | |||
section.getParagraph(1).text().length() + | |||
section.getParagraph(2).text().length() + | |||
section.getParagraph(3).text().length() + | |||
section.getParagraph(4).text().length() | |||
); | |||
} | |||
/** | |||
@@ -118,12 +150,7 @@ public abstract class TestRangeDelete extends TestCase { | |||
assertEquals(192, offset); | |||
int absOffset = para.getStartOffset() + offset; | |||
if (para.usesUnicode()) | |||
absOffset = para.getStartOffset() + (offset * 2); | |||
Range subRange = new Range(absOffset, (absOffset + searchText.length()), para.getDocument()); | |||
if (subRange.usesUnicode()) | |||
subRange = new Range(absOffset, (absOffset + (searchText.length() * 2)), para.getDocument()); | |||
assertEquals(searchText, subRange.text()); | |||
@@ -167,26 +194,23 @@ public abstract class TestRangeDelete extends TestCase { | |||
boolean keepLooking = true; | |||
while (keepLooking) { | |||
// Reload the range every time | |||
range = daDoc.getRange(); | |||
int offset = range.text().indexOf(searchText); | |||
if (offset >= 0) { | |||
int absOffset = range.getStartOffset() + offset; | |||
if (range.usesUnicode()) | |||
absOffset = range.getStartOffset() + (offset * 2); | |||
Range subRange = new Range( | |||
absOffset, (absOffset + searchText.length()), range.getDocument()); | |||
if (subRange.usesUnicode()) | |||
subRange = new Range( | |||
absOffset, (absOffset + (searchText.length() * 2)), range.getDocument()); | |||
assertEquals(searchText, subRange.text()); | |||
subRange.delete(); | |||
} else | |||
} else { | |||
keepLooking = false; | |||
} | |||
} | |||
// we need to let the model re-calculate the Range before we use it | |||
@@ -197,6 +221,10 @@ public abstract class TestRangeDelete extends TestCase { | |||
assertEquals(5, section.numParagraphs()); | |||
para = section.getParagraph(0); | |||
text = para.text(); | |||
assertEquals(introText, text); | |||
para = section.getParagraph(1); | |||
text = para.text(); | |||
assertEquals(expectedText1, text); |
@@ -18,29 +18,25 @@ | |||
package org.apache.poi.hwpf.usermodel; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.FileInputStream; | |||
import java.util.List; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.model.PicturesTable; | |||
import org.apache.poi.hwpf.usermodel.Picture; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
/** | |||
* Test to see if Range.insertBefore() works even if the Range contains a | |||
* CharacterRun that uses Unicode characters. | |||
* | |||
* TODO - re-enable me when unicode paragraph stuff is fixed! | |||
*/ | |||
public abstract class TestRangeInsertion extends TestCase { | |||
public class TestRangeInsertion extends TestCase { | |||
// u201c and u201d are "smart-quotes" | |||
private String originalText = | |||
"It is used to confirm that text insertion works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present.\r"; | |||
private String textToInsert = "Look at me! I'm cool! "; | |||
private int insertionPoint = 244; | |||
private int insertionPoint = 122; | |||
private String illustrativeDocFile; | |||
@@ -73,12 +69,18 @@ public abstract class TestRangeInsertion extends TestCase { | |||
assertEquals(3, section.numParagraphs()); | |||
Paragraph para = section.getParagraph(2); | |||
assertEquals(originalText, para.text()); | |||
assertEquals(3, para.numCharacterRuns()); | |||
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text(); | |||
String text = | |||
para.getCharacterRun(0).text() + | |||
para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text() | |||
; | |||
assertEquals(originalText, text); | |||
assertEquals(insertionPoint, para.getStartOffset()); | |||
} | |||
/** | |||
@@ -109,10 +111,14 @@ public abstract class TestRangeInsertion extends TestCase { | |||
assertEquals(3, section.numParagraphs()); | |||
Paragraph para = section.getParagraph(2); | |||
assertEquals((textToInsert + originalText), para.text()); | |||
assertEquals(3, para.numCharacterRuns()); | |||
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text(); | |||
String text = | |||
para.getCharacterRun(0).text() + | |||
para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text() | |||
; | |||
// System.out.println(text); | |||
@@ -18,8 +18,10 @@ package org.apache.poi.hwpf.usermodel; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.util.List; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.model.PropertyNode; | |||
import junit.framework.TestCase; | |||
@@ -30,7 +32,7 @@ import junit.framework.TestCase; | |||
* | |||
* TODO - re-enable me when unicode paragraph stuff is fixed! | |||
*/ | |||
public abstract class TestRangeProperties extends TestCase { | |||
public class TestRangeProperties extends TestCase { | |||
private static final char page_break = (char)12; | |||
private static final String u_page_1 = | |||
@@ -85,6 +87,16 @@ public abstract class TestRangeProperties extends TestCase { | |||
r.text() | |||
); | |||
assertEquals(1, r.numSections()); | |||
assertEquals(1, a.getSectionTable().getSections().size()); | |||
Section s = r.getSection(0); | |||
assertEquals( | |||
a_page_1 + | |||
page_break + "\r" + | |||
a_page_2, | |||
s.text() | |||
); | |||
assertEquals( | |||
7, | |||
r.numParagraphs() | |||
@@ -142,8 +154,102 @@ public abstract class TestRangeProperties extends TestCase { | |||
assertEquals(22, c1.getFontSize()); | |||
assertEquals(32, c7.getFontSize()); | |||
} | |||
/** | |||
* Tests the raw definitions of the paragraphs of | |||
* a unicode document | |||
*/ | |||
public void testUnicodeParagraphDefinitions() throws Exception { | |||
Range r = u.getRange(); | |||
String[] p1_parts = u_page_1.split("\r"); | |||
String[] p2_parts = u_page_2.split("\r"); | |||
assertEquals( | |||
u_page_1 + page_break + "\r" + u_page_2, | |||
r.text() | |||
); | |||
assertEquals( | |||
408, r.text().length() | |||
); | |||
assertEquals(1, r.numSections()); | |||
assertEquals(1, u.getSectionTable().getSections().size()); | |||
Section s = r.getSection(0); | |||
assertEquals( | |||
u_page_1 + | |||
page_break + "\r" + | |||
u_page_2, | |||
s.text() | |||
); | |||
assertEquals(0, s.getStartOffset()); | |||
assertEquals(408, s.getEndOffset()); | |||
List pDefs = r._paragraphs; | |||
assertEquals(35, pDefs.size()); | |||
// Check that the last paragraph ends where it should do | |||
assertEquals(531, u.getOverallRange().text().length()); | |||
assertEquals(530, u.getCPSplitCalculator().getHeaderTextboxEnd()); | |||
PropertyNode pLast = (PropertyNode)pDefs.get(34); | |||
// assertEquals(530, pLast.getEnd()); | |||
// Only care about the first few really though | |||
PropertyNode p0 = (PropertyNode)pDefs.get(0); | |||
PropertyNode p1 = (PropertyNode)pDefs.get(1); | |||
PropertyNode p2 = (PropertyNode)pDefs.get(2); | |||
PropertyNode p3 = (PropertyNode)pDefs.get(3); | |||
PropertyNode p4 = (PropertyNode)pDefs.get(4); | |||
// 5 paragraphs should get us to the end of our text | |||
assertTrue(p0.getStart() < 408); | |||
assertTrue(p0.getEnd() < 408); | |||
assertTrue(p1.getStart() < 408); | |||
assertTrue(p1.getEnd() < 408); | |||
assertTrue(p2.getStart() < 408); | |||
assertTrue(p2.getEnd() < 408); | |||
assertTrue(p3.getStart() < 408); | |||
assertTrue(p3.getEnd() < 408); | |||
assertTrue(p4.getStart() < 408); | |||
assertTrue(p4.getEnd() < 408); | |||
// Paragraphs should match with lines | |||
assertEquals( | |||
0, | |||
p0.getStart() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1, | |||
p0.getEnd() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1, | |||
p1.getStart() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1, | |||
p1.getEnd() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1, | |||
p2.getStart() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1 + | |||
p1_parts[2].length() + 1, | |||
p2.getEnd() | |||
); | |||
} | |||
/** | |||
* Tests the paragraph text of a unicode document | |||
*/ | |||
public void testUnicodeTextParagraphs() throws Exception { | |||
Range r = u.getRange(); | |||
assertEquals( | |||
@@ -154,16 +260,112 @@ public abstract class TestRangeProperties extends TestCase { | |||
); | |||
assertEquals( | |||
5, | |||
12, | |||
r.numParagraphs() | |||
); | |||
String[] p1_parts = u_page_1.split("\r"); | |||
String[] p2_parts = u_page_2.split("\r"); | |||
System.out.println(r.getParagraph(2).text()); | |||
// TODO | |||
// Check text all matches up properly | |||
assertEquals(p1_parts[0] + "\r", r.getParagraph(0).text()); | |||
assertEquals(p1_parts[1] + "\r", r.getParagraph(1).text()); | |||
assertEquals(p1_parts[2] + "\r", r.getParagraph(2).text()); | |||
assertEquals(p1_parts[3] + "\r", r.getParagraph(3).text()); | |||
assertEquals(p1_parts[4] + "\r", r.getParagraph(4).text()); | |||
assertEquals(p1_parts[5] + "\r", r.getParagraph(5).text()); | |||
assertEquals(p1_parts[6] + "\r", r.getParagraph(6).text()); | |||
assertEquals(p1_parts[7] + "\r", r.getParagraph(7).text()); | |||
assertEquals(p1_parts[8] + "\r", r.getParagraph(8).text()); | |||
assertEquals(p1_parts[9] + "\r", r.getParagraph(9).text()); | |||
assertEquals(page_break + "\r", r.getParagraph(10).text()); | |||
assertEquals(p2_parts[0] + "\r", r.getParagraph(11).text()); | |||
} | |||
public void testUnicodeStyling() throws Exception { | |||
// TODO | |||
Range r = u.getRange(); | |||
String[] p1_parts = u_page_1.split("\r"); | |||
Paragraph p1 = r.getParagraph(0); | |||
Paragraph p7 = r.getParagraph(6); | |||
// Line ending in its own run each time! | |||
assertEquals(2, p1.numCharacterRuns()); | |||
assertEquals(2, p7.numCharacterRuns()); | |||
CharacterRun c1a = p1.getCharacterRun(0); | |||
CharacterRun c1b = p1.getCharacterRun(1); | |||
CharacterRun c7a = p7.getCharacterRun(0); | |||
CharacterRun c7b = p7.getCharacterRun(1); | |||
assertEquals("Times New Roman", c1a.getFontName()); // No Calibri | |||
assertEquals(22, c1a.getFontSize()); | |||
assertEquals("Times New Roman", c1b.getFontName()); // No Calibri | |||
assertEquals(22, c1b.getFontSize()); | |||
assertEquals("Times New Roman", c7a.getFontName()); | |||
assertEquals(48, c7a.getFontSize()); | |||
assertEquals("Times New Roman", c7b.getFontName()); | |||
assertEquals(48, c7b.getFontSize()); | |||
// Now check where they crop up | |||
assertEquals( | |||
0, | |||
c1a.getStartOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length(), | |||
c1a.getEndOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length(), | |||
c1b.getStartOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length()+1, | |||
c1b.getEndOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1 + | |||
p1_parts[2].length() + 1 + | |||
p1_parts[3].length() + 1 + | |||
p1_parts[4].length() + 1 + | |||
p1_parts[5].length() + 1, | |||
c7a.getStartOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1 + | |||
p1_parts[2].length() + 1 + | |||
p1_parts[3].length() + 1 + | |||
p1_parts[4].length() + 1 + | |||
p1_parts[5].length() + 1 + | |||
1, | |||
c7a.getEndOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1 + | |||
p1_parts[2].length() + 1 + | |||
p1_parts[3].length() + 1 + | |||
p1_parts[4].length() + 1 + | |||
p1_parts[5].length() + 1 + | |||
1, | |||
c7b.getStartOffset() | |||
); | |||
assertEquals( | |||
p1_parts[0].length() + 1 + | |||
p1_parts[1].length() + 1 + | |||
p1_parts[2].length() + 1 + | |||
p1_parts[3].length() + 1 + | |||
p1_parts[4].length() + 1 + | |||
p1_parts[5].length() + 1 + | |||
p1_parts[6].length() + 1, | |||
c7b.getEndOffset() | |||
); | |||
} | |||
} |
@@ -18,23 +18,19 @@ | |||
package org.apache.poi.hwpf.usermodel; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.FileInputStream; | |||
import java.util.List; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.hwpf.model.PicturesTable; | |||
import org.apache.poi.hwpf.usermodel.Picture; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
/** | |||
* Test to see if Range.replaceText() works even if the Range contains a | |||
* CharacterRun that uses Unicode characters. | |||
* | |||
* TODO - re-enable me when unicode paragraph stuff is fixed! | |||
*/ | |||
public abstract class TestRangeReplacement extends TestCase { | |||
public class TestRangeReplacement extends TestCase { | |||
// u201c and u201d are "smart-quotes" | |||
private String originalText = | |||
@@ -70,16 +66,23 @@ public abstract class TestRangeReplacement extends TestCase { | |||
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile)); | |||
Range range = daDoc.getRange(); | |||
assertEquals(414, range.text().length()); | |||
assertEquals(1, range.numSections()); | |||
Section section = range.getSection(0); | |||
assertEquals(414, section.text().length()); | |||
assertEquals(5, section.numParagraphs()); | |||
Paragraph para = section.getParagraph(2); | |||
assertEquals(5, para.numCharacterRuns()); | |||
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text() + para.getCharacterRun(3).text() + para.getCharacterRun(4).text(); | |||
String text = | |||
para.getCharacterRun(0).text() + | |||
para.getCharacterRun(1).text() + | |||
para.getCharacterRun(2).text() + | |||
para.getCharacterRun(3).text() + | |||
para.getCharacterRun(4).text() | |||
; | |||
assertEquals(originalText, text); | |||
} |
@@ -20,6 +20,7 @@ package org.apache.poi.hssf.model; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.model.FormulaParser.FormulaParseException; | |||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; | |||
import org.apache.poi.hssf.record.formula.AddPtg; | |||
@@ -123,12 +124,15 @@ public final class TestFormulaParser extends TestCase { | |||
} | |||
public void testMacroFunction() { | |||
HSSFWorkbook w = new HSSFWorkbook(); | |||
Ptg[] ptg = FormulaParser.parse("FOO()", w); | |||
// testNames.xls contains a VB function called 'myFunc' | |||
HSSFWorkbook w = HSSFTestDataSamples.openSampleWorkbook("testNames.xls"); | |||
Ptg[] ptg = FormulaParser.parse("myFunc()", w); | |||
// myFunc() actually takes 1 parameter. Don't know if POI will ever be able to detect this problem | |||
// the name gets encoded as the first arg | |||
NamePtg tname = (NamePtg) ptg[0]; | |||
assertEquals("FOO", tname.toFormulaString(w)); | |||
assertEquals("myFunc", tname.toFormulaString(w)); | |||
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1]; | |||
assertTrue(tfunc.isExternalFunction()); |
@@ -17,11 +17,19 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.model.FormulaParser; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue; | |||
/** | |||
* Tests for functions from external workbooks (e.g. YEARFRAC). | |||
* | |||
@@ -30,17 +38,45 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
*/ | |||
public final class TestExternalFunctionFormulas extends TestCase { | |||
/** | |||
* tests <tt>NameXPtg.toFormulaString(Workbook)</tt> and logic in Workbook below that | |||
*/ | |||
public void testReadFormulaContainingExternalFunction() { | |||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); | |||
String expectedFormula = "YEARFRAC(B1,C1)"; | |||
HSSFSheet sht = wb.getSheetAt(0); | |||
String cellFormula = sht.getRow(0).getCell((short)0).getCellFormula(); | |||
assertEquals(expectedFormula, cellFormula); | |||
} | |||
/** | |||
* tests <tt>NameXPtg.toFormulaString(Workbook)</tt> and logic in Workbook below that | |||
*/ | |||
public void testReadFormulaContainingExternalFunction() { | |||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); | |||
String expectedFormula = "YEARFRAC(B1,C1)"; | |||
HSSFSheet sht = wb.getSheetAt(0); | |||
String cellFormula = sht.getRow(0).getCell(0).getCellFormula(); | |||
assertEquals(expectedFormula, cellFormula); | |||
} | |||
public void testParse() { | |||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); | |||
Ptg[] ptgs = FormulaParser.parse("YEARFRAC(B1,C1)", wb); | |||
assertEquals(4, ptgs.length); | |||
assertEquals(NameXPtg.class, ptgs[0].getClass()); | |||
wb.getSheetAt(0).getRow(0).createCell(6).setCellFormula("YEARFRAC(C1,B1)"); | |||
if (false) { | |||
// In case you fancy checking in excel | |||
try { | |||
File tempFile = File.createTempFile("testExtFunc", ".xls"); | |||
FileOutputStream fout = new FileOutputStream(tempFile); | |||
wb.write(fout); | |||
fout.close(); | |||
System.out.println("check out " + tempFile.getAbsolutePath()); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} | |||
public void DISABLEDtestEvaluate() { | |||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); | |||
HSSFSheet sheet = wb.getSheetAt(0); | |||
HSSFCell cell = sheet.getRow(0).getCell(0); | |||
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); | |||
CellValue evalResult = fe.evaluate(cell); | |||
evalResult.toString(); | |||
} | |||
} |
@@ -17,8 +17,11 @@ | |||
package org.apache.poi.hssf.record.formula.eval; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; | |||
import org.apache.poi.hssf.usermodel.HSSFName; | |||
@@ -34,20 +37,33 @@ public final class TestExternalFunction extends TestCase { | |||
/** | |||
* Checks that an external function can get invoked from the formula evaluator. | |||
* @throws IOException | |||
*/ | |||
public void testInvoke() { | |||
HSSFWorkbook wb = new HSSFWorkbook(); | |||
HSSFSheet sheet = wb.createSheet(); | |||
wb.setSheetName(0, "Sheet1"); | |||
HSSFRow row = sheet.createRow(0); | |||
HSSFCell cell = row.createCell(0); | |||
HSSFName hssfName = wb.createName(); | |||
hssfName.setNameName("myFunc"); | |||
cell.setCellFormula("myFunc()"); | |||
String actualFormula=cell.getCellFormula(); | |||
assertEquals("myFunc()", actualFormula); | |||
HSSFWorkbook wb; | |||
HSSFSheet sheet; | |||
HSSFCell cell; | |||
if (false) { | |||
// TODO - this code won't work until we can create user-defined functions directly with POI | |||
wb = new HSSFWorkbook(); | |||
sheet = wb.createSheet(); | |||
wb.setSheetName(0, "Sheet1"); | |||
HSSFName hssfName = wb.createName(); | |||
hssfName.setNameName("myFunc"); | |||
} else { | |||
// This sample spreadsheet already has a VB function called 'myFunc' | |||
wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls"); | |||
sheet = wb.getSheetAt(0); | |||
HSSFRow row = sheet.createRow(0); | |||
cell = row.createCell(1); | |||
} | |||
cell.setCellFormula("myFunc()"); | |||
String actualFormula=cell.getCellFormula(); | |||
assertEquals("myFunc()", actualFormula); | |||
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); | |||
CellValue evalResult = fe.evaluate(cell); |
@@ -417,6 +417,9 @@ public final class TestBugs extends TestCase { | |||
for(int i = 0 ; i < wb.getNumberOfNames(); i++){ | |||
HSSFName name = wb.getNameAt(i); | |||
name.getNameName(); | |||
if (name.isFunctionName()) { | |||
continue; | |||
} | |||
name.getReference(); | |||
} | |||
} | |||
@@ -1004,7 +1007,6 @@ public final class TestBugs extends TestCase { | |||
* Test that we can delete sheets without | |||
* breaking the build in named ranges | |||
* used for printing stuff. | |||
* Currently broken, as we change the Ptg | |||
*/ | |||
public void test30978() { | |||
HSSFWorkbook wb = openSample("30978-alt.xls"); | |||
@@ -1016,7 +1018,7 @@ public final class TestBugs extends TestCase { | |||
Workbook w = wb.getWorkbook(); | |||
for(int i=0; i<w.getNumNames(); i++) { | |||
NameRecord r = w.getNameRecord(i); | |||
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); | |||
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); | |||
List nd = r.getNameDefinition(); | |||
assertEquals(1, nd.size()); | |||
@@ -1034,7 +1036,7 @@ public final class TestBugs extends TestCase { | |||
for(int i=0; i<w.getNumNames(); i++) { | |||
NameRecord r = w.getNameRecord(i); | |||
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); | |||
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); | |||
List nd = r.getNameDefinition(); | |||
assertEquals(1, nd.size()); | |||
@@ -1051,7 +1053,7 @@ public final class TestBugs extends TestCase { | |||
for(int i=0; i<w.getNumNames(); i++) { | |||
NameRecord r = w.getNameRecord(i); | |||
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); | |||
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); | |||
List nd = r.getNameDefinition(); | |||
assertEquals(1, nd.size()); |
@@ -50,7 +50,7 @@ public final class TestHSSFWorkbook extends TestCase { | |||
b.createSheet(); | |||
b.setRepeatingRowsAndColumns( 2, 0,1,-1,-1 ); | |||
NameRecord nameRecord = b.getWorkbook().getNameRecord( 0 ); | |||
assertEquals( 3, nameRecord.getIndexToSheet() ); | |||
assertEquals(3, nameRecord.getSheetNumber()); | |||
} | |||
public void testCaseInsensitiveNames() { | |||
@@ -411,84 +411,84 @@ public final class TestHSSFWorkbook extends TestCase { | |||
* that point to deleted sheets | |||
*/ | |||
public void testNamesToDeleteSheets() throws Exception { | |||
HSSFWorkbook b = openSample("30978-deleted.xls"); | |||
assertEquals(3, b.getNumberOfNames()); | |||
// Sheet 2 is deleted | |||
assertEquals("Sheet1", b.getSheetName(0)); | |||
assertEquals("Sheet3", b.getSheetName(1)); | |||
Area3DPtg ptg; | |||
NameRecord nr; | |||
HSSFName n; | |||
/* ======= Name pointing to deleted sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(0); | |||
assertEquals("On2", nr.getNameText()); | |||
assertEquals(0, nr.getIndexToSheet()); | |||
assertEquals(1, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(1, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(0, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(2, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(0); | |||
assertEquals("On2", n.getNameName()); | |||
assertEquals("", n.getSheetName()); | |||
assertEquals("#REF!$A$1:$A$3", n.getReference()); | |||
/* ======= Name pointing to 1st sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(1); | |||
assertEquals("OnOne", nr.getNameText()); | |||
assertEquals(0, nr.getIndexToSheet()); | |||
assertEquals(0, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(0, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(2, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(3, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(1); | |||
assertEquals("OnOne", n.getNameName()); | |||
assertEquals("Sheet1", n.getSheetName()); | |||
assertEquals("Sheet1!$A$3:$A$4", n.getReference()); | |||
/* ======= Name pointing to 3rd sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(2); | |||
assertEquals("OnSheet3", nr.getNameText()); | |||
assertEquals(0, nr.getIndexToSheet()); | |||
assertEquals(2, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(2, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(0, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(1, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(2); | |||
assertEquals("OnSheet3", n.getNameName()); | |||
assertEquals("Sheet3", n.getSheetName()); | |||
assertEquals("Sheet3!$A$1:$A$2", n.getReference()); | |||
HSSFWorkbook b = openSample("30978-deleted.xls"); | |||
assertEquals(3, b.getNumberOfNames()); | |||
// Sheet 2 is deleted | |||
assertEquals("Sheet1", b.getSheetName(0)); | |||
assertEquals("Sheet3", b.getSheetName(1)); | |||
Area3DPtg ptg; | |||
NameRecord nr; | |||
HSSFName n; | |||
/* ======= Name pointing to deleted sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(0); | |||
assertEquals("On2", nr.getNameText()); | |||
assertEquals(0, nr.getSheetNumber()); | |||
assertEquals(1, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(1, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(0, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(2, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(0); | |||
assertEquals("On2", n.getNameName()); | |||
assertEquals("", n.getSheetName()); | |||
assertEquals("#REF!$A$1:$A$3", n.getReference()); | |||
/* ======= Name pointing to 1st sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(1); | |||
assertEquals("OnOne", nr.getNameText()); | |||
assertEquals(0, nr.getSheetNumber()); | |||
assertEquals(0, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(0, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(2, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(3, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(1); | |||
assertEquals("OnOne", n.getNameName()); | |||
assertEquals("Sheet1", n.getSheetName()); | |||
assertEquals("Sheet1!$A$3:$A$4", n.getReference()); | |||
/* ======= Name pointing to 3rd sheet ====== */ | |||
// First at low level | |||
nr = b.getWorkbook().getNameRecord(2); | |||
assertEquals("OnSheet3", nr.getNameText()); | |||
assertEquals(0, nr.getSheetNumber()); | |||
assertEquals(2, nr.getExternSheetNumber()); | |||
assertEquals(1, nr.getNameDefinition().size()); | |||
ptg = (Area3DPtg)nr.getNameDefinition().get(0); | |||
assertEquals(2, ptg.getExternSheetIndex()); | |||
assertEquals(0, ptg.getFirstColumn()); | |||
assertEquals(0, ptg.getFirstRow()); | |||
assertEquals(0, ptg.getLastColumn()); | |||
assertEquals(1, ptg.getLastRow()); | |||
// Now at high level | |||
n = b.getNameAt(2); | |||
assertEquals("OnSheet3", n.getNameName()); | |||
assertEquals("Sheet3", n.getSheetName()); | |||
assertEquals("Sheet3!$A$1:$A$2", n.getReference()); | |||
} | |||
/** | |||
@@ -519,12 +519,12 @@ public final class TestHSSFWorkbook extends TestCase { | |||
* The sample file provided with bug 45582 seems to have one extra byte after the EOFRecord | |||
*/ | |||
public void testExtraDataAfterEOFRecord() { | |||
try { | |||
HSSFTestDataSamples.openSampleWorkbook("ex45582-22397.xls"); | |||
} catch (RecordFormatException e) { | |||
if (e.getCause() instanceof LittleEndian.BufferUnderrunException) { | |||
throw new AssertionFailedError("Identified bug 45582"); | |||
} | |||
} | |||
} | |||
try { | |||
HSSFTestDataSamples.openSampleWorkbook("ex45582-22397.xls"); | |||
} catch (RecordFormatException e) { | |||
if (e.getCause() instanceof LittleEndian.BufferUnderrunException) { | |||
throw new AssertionFailedError("Identified bug 45582"); | |||
} | |||
} | |||
} | |||
} |
@@ -17,17 +17,15 @@ | |||
package org.apache.poi.hssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.util.AreaReference; | |||
import org.apache.poi.hssf.util.CellReference; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
/** | |||
* | |||
@@ -42,10 +40,6 @@ public final class TestNamedRange extends TestCase { | |||
return HSSFTestDataSamples.openSampleWorkbook(sampleFileName); | |||
} | |||
public static void main(String[] args) { | |||
junit.textui.TestRunner.run(TestNamedRange.class); | |||
} | |||
/** Test of TestCase method, of class test.RangeTest. */ | |||
public void testNamedRange() { | |||
HSSFWorkbook wb = openSample("Simple.xls"); | |||
@@ -411,7 +405,7 @@ public final class TestNamedRange extends TestCase { | |||
String cellValue = "TEST Value"; | |||
HSSFWorkbook wb = new HSSFWorkbook(); | |||
HSSFSheet sheet = wb.createSheet(sheetName); | |||
sheet.createRow(0).createCell((short) 0).setCellValue(cellValue); | |||
sheet.createRow(0).createCell(0).setCellValue(new HSSFRichTextString(cellValue)); | |||
// create named range for a single cell using areareference | |||
HSSFName namedCell = wb.createName(); | |||
@@ -447,7 +441,7 @@ public final class TestNamedRange extends TestCase { | |||
String sname = "TestSheet", cname = "TestName", cvalue = "TestVal"; | |||
HSSFWorkbook wb = new HSSFWorkbook(); | |||
HSSFSheet sheet = wb.createSheet(sname); | |||
sheet.createRow(0).createCell((short) 0).setCellValue(cvalue); | |||
sheet.createRow(0).createCell(0).setCellValue(new HSSFRichTextString(cvalue)); | |||
// create named range for a single cell using cellreference | |||
HSSFName namedCell = wb.createName(); | |||
@@ -492,56 +486,60 @@ public final class TestNamedRange extends TestCase { | |||
} | |||
} | |||
public void testRepeatingRowsAndColumsNames() throws Exception { | |||
public void testRepeatingRowsAndColumsNames() { | |||
// First test that setting RR&C for same sheet more than once only creates a | |||
// single Print_Titles built-in record | |||
HSSFWorkbook wb = new HSSFWorkbook(); | |||
HSSFSheet sheet = wb.createSheet(); | |||
HSSFSheet sheet = wb.createSheet("FirstSheet"); | |||
for (int rowItem = 0; rowItem < 10; rowItem++) { | |||
HSSFRow r = sheet.createRow(rowItem); | |||
for (int column = 0; column < 2; column++) { | |||
HSSFCell cellItem = r.createCell((short) column); | |||
cellItem.setCellType(HSSFCell.CELL_TYPE_STRING); | |||
cellItem.setCellValue(new HSSFRichTextString("Some value here")); | |||
if (rowItem == 2) { | |||
wb.setRepeatingRowsAndColumns(0, 0, 0, 0, 3 - 1); | |||
sheet.createFreezePane(0, 3); | |||
} | |||
} | |||
// set repeating rows and columns twice for the first sheet | |||
for (int i = 0; i < 2; i++) { | |||
wb.setRepeatingRowsAndColumns(0, 0, 0, 0, 3-1); | |||
sheet.createFreezePane(0, 3); | |||
} | |||
assertEquals(2, wb.getNumberOfNames()); | |||
assertEquals(1, wb.getNumberOfNames()); | |||
HSSFName nr1 = wb.getNameAt(0); | |||
HSSFName nr2 = wb.getNameAt(1); | |||
assertEquals("Print_Titles", nr1.getNameName()); | |||
assertEquals("Sheet0!$A$1:$A$0,Sheet0!$A$1:$IV$3", nr1.getReference()); | |||
assertEquals("Excel_Name_Record_Titles_1_1", nr2.getNameName()); | |||
assertEquals("Sheet0!$A$1:$A$0,Sheet0!$A$1:$IV$3", nr2.getReference()); | |||
if (false) { | |||
// TODO - full column references not rendering properly, absolute markers not present either | |||
assertEquals("FirstSheet!$A:$A,FirstSheet!$1:$3", nr1.getReference()); | |||
} else { | |||
assertEquals("FirstSheet!A:A,FirstSheet!$A$1:$IV$3", nr1.getReference()); | |||
} | |||
// Save and re-open | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
wb.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
HSSFWorkbook nwb = new HSSFWorkbook(new POIFSFileSystem(bais)); | |||
HSSFWorkbook nwb = HSSFTestDataSamples.writeOutAndReadBack(wb); | |||
assertEquals(2, nwb.getNumberOfNames()); | |||
assertEquals(1, nwb.getNumberOfNames()); | |||
nr1 = nwb.getNameAt(0); | |||
nr2 = nwb.getNameAt(1); | |||
// TODO - | |||
// should these references really have been corrected? | |||
// and if so, why not also above? | |||
assertEquals("Print_Titles", nr1.getNameName()); | |||
assertEquals("Sheet0!A:A,Sheet0!$A$1:$IV$3", nr1.getReference()); | |||
assertEquals("FirstSheet!A:A,FirstSheet!$A$1:$IV$3", nr1.getReference()); | |||
// check that setting RR&C on a second sheet causes a new Print_Titles built-in | |||
// name to be created | |||
sheet = nwb.createSheet("SecondSheet"); | |||
nwb.setRepeatingRowsAndColumns(1, 1, 2, 0, 0); | |||
assertEquals(2, nwb.getNumberOfNames()); | |||
HSSFName nr2 = nwb.getNameAt(1); | |||
assertEquals("Excel_Name_Record_Titles_1_1", nr2.getNameName()); | |||
assertEquals("Sheet0!A:A,Sheet0!$A$1:$IV$3", nr2.getReference()); | |||
assertEquals("Print_Titles", nr2.getNameName()); | |||
assertEquals("SecondSheet!B:C,SecondSheet!$A$1:$IV$1", nr2.getReference()); | |||
// In case you fancy checking in excel, to ensure it | |||
// won't complain about the file now | |||
FileOutputStream fout = new FileOutputStream(File.createTempFile("POI-45126-", ".xls")); | |||
wb.write(fout); | |||
fout.close(); | |||
if (false) { | |||
// In case you fancy checking in excel, to ensure it | |||
// won't complain about the file now | |||
try { | |||
File tempFile = File.createTempFile("POI-45126-", ".xls"); | |||
FileOutputStream fout = new FileOutputStream(tempFile); | |||
nwb.write(fout); | |||
fout.close(); | |||
System.out.println("check out " + tempFile.getAbsolutePath()); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} | |||
} |