Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684986 via svnmerge from

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
This commit is contained in:
Nick Burch 2008-08-11 23:58:54 +00:00
parent 67eb843e51
commit 22fc15b0b9
47 changed files with 2185 additions and 1593 deletions

View File

@ -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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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">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="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> <action dev="POI-DEVELOPERS" type="fix">Improve how HWPF deals with unicode internally. Should avoid some odd behaviour when manipulating unicode text</action>

View File

@ -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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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">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="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> <action dev="POI-DEVELOPERS" type="fix">Improve how HWPF deals with unicode internally. Should avoid some odd behaviour when manipulating unicode text</action>

View File

@ -25,6 +25,7 @@ import java.util.Stack;
import org.apache.poi.hssf.record.formula.*; import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata; import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; 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.ss.usermodel.Workbook;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; 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). * @param name case preserved function name (as it was entered/appeared in the formula).
*/ */
private ParseNode function(String name) { private ParseNode function(String name) {
NamePtg nameToken = null; Ptg nameToken = null;
// Note regarding parameter - if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
if(!AbstractFunctionPtg.isInternalFunctionName(name)) { // user defined function
// external functions get a Name token which points to a defined name record
nameToken = new NamePtg(name, this.book);
// in the token tree, the name is more or less the first argument // 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('('); Match('(');
@ -390,11 +410,11 @@ public final class FormulaParser {
* Generates the variable function ptg for the formula. * Generates the variable function ptg for the formula.
* <p> * <p>
* For IF Formulas, additional PTGs are added to the tokens * 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 * @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 * @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()); FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase());
int numArgs = args.length; int numArgs = args.length;

View File

@ -25,11 +25,11 @@ import org.apache.poi.hssf.record.CRNCountRecord;
import org.apache.poi.hssf.record.CRNRecord; import org.apache.poi.hssf.record.CRNRecord;
import org.apache.poi.hssf.record.CountryRecord; import org.apache.poi.hssf.record.CountryRecord;
import org.apache.poi.hssf.record.ExternSheetRecord; 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.ExternalNameRecord;
import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SupBookRecord; import org.apache.poi.hssf.record.SupBookRecord;
import org.apache.poi.hssf.record.formula.NameXPtg;
/** /**
* Link Table (OOO pdf reference: 4.10.3 ) <p/> * Link Table (OOO pdf reference: 4.10.3 ) <p/>
@ -122,6 +122,19 @@ final class LinkTable {
public String getNameText(int definedNameIndex) { public String getNameText(int definedNameIndex) {
return _externalNameRecords[definedNameIndex].getText(); 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; 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(); Iterator iterator = _definedNames.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
NameRecord record = ( NameRecord ) iterator.next(); NameRecord record = ( NameRecord ) iterator.next();
//print areas are one based //print areas are one based
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) { if (record.getBuiltInName() == builtInCode && record.getSheetNumber() == sheetNumber) {
return record; return record;
} }
} }
@ -241,69 +258,56 @@ final class LinkTable {
_definedNames.remove(namenum); _definedNames.remove(namenum);
} }
/** /**
* checks if the given name is already included in the linkTable * checks if the given name is already included in the linkTable
*/ */
public boolean nameAlreadyExists(NameRecord name) public boolean nameAlreadyExists(NameRecord name)
{ {
// Check to ensure no other names have the same case-insensitive name // Check to ensure no other names have the same case-insensitive name
for ( int i = getNumNames()-1; i >=0; i-- ) { for ( int i = getNumNames()-1; i >=0; i-- ) {
NameRecord rec = getNameRecord(i); NameRecord rec = getNameRecord(i);
if (rec != name) { if (rec != name) {
if (isDuplicatedNames(name, rec)) if (isDuplicatedNames(name, rec))
return true; 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();
}
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) {
return -1;
}
return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
}
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 short checkExternSheet(int sheetNumber) {
//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;
} }
} }
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 extRefIndex) {
if (extRefIndex >= _externSheetRecord.getNumOfRefs()) {
return -1;
}
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
}
public int addSheetIndexToExternSheet(int sheetNumber) {
// TODO - what about the first parameter (extBookIndex)?
return _externSheetRecord.addRef(0, sheetNumber, sheetNumber);
}
public short checkExternSheet(int sheetIndex) {
//Trying to find reference to this sheet
int i = _externSheetRecord.getRefIxForSheet(sheetIndex);
if (i>=0) {
return (short)i;
}
//We Haven't found reference to this sheet //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() { public int getNumberOfREFStructures() {
return _externSheetRecord.getNumOfREFStructures(); return _externSheetRecord.getNumOfRefs();
} }
public String resolveNameXText(int refIndex, int definedNameIndex) { public String resolveNameXText(int refIndex, int definedNameIndex) {
short extBookIndex = _externSheetRecord.getREFRecordAt(refIndex).getIndexToSupBook(); int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex);
return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex); 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);
}
} }

View File

@ -15,21 +15,70 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.model; 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.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; 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 * Low level model implementation of a Workbook. Provides creational methods
* for settings and objects contained in the workbook object. * for settings and objects contained in the workbook object.
@ -54,18 +103,13 @@ import java.util.Locale;
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
* @version 1.0-pre * @version 1.0-pre
*/ */
public final class Workbook implements Model {
public class Workbook implements Model
{
private static final int DEBUG = POILogger.DEBUG; private static final int DEBUG = POILogger.DEBUG;
// public static Workbook currentBook = null;
/** /**
* constant used to set the "codepage" wherever "codepage" is set in records * 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; private final static short CODEPAGE = ( short ) 0x4b0;
/** /**
@ -105,8 +149,6 @@ public class Workbook implements Model
private static POILogger log = POILogFactory.getLogger(Workbook.class); 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 * Creates new Workbook with no intitialization --useless right now
* @see #createWorkbook(List) * @see #createWorkbook(List)
@ -250,9 +292,9 @@ public class Workbook implements Model
for ( ; k < recs.size(); k++) { for ( ; k < recs.size(); k++) {
Record rec = ( Record ) recs.get(k); Record rec = ( Record ) recs.get(k);
switch (rec.getSid()) { switch (rec.getSid()) {
case HyperlinkRecord.sid: case HyperlinkRecord.sid:
retval.hyperlinks.add(rec); retval.hyperlinks.add(rec);
break; break;
} }
} }
@ -358,22 +400,22 @@ public class Workbook implements Model
} }
/**Retrieves the Builtin NameRecord that matches the name and index /**Retrieves the Builtin NameRecord that matches the name and index
* There shouldn't be too many names to make the sequential search too slow * 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 name byte representation of the builtin name to match
* @param sheetIndex Index to match * @param sheetNumber 1-based sheet number
* @return null if no builtin NameRecord matches * @return null if no builtin NameRecord matches
*/ */
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex) 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 * Removes the specified Builtin NameRecord that matches the name and index
* @param name byte representation of the builtin to match * @param name byte representation of the builtin to match
* @param sheetIndex zero-based sheet reference * @param sheetIndex zero-based sheet reference
*/ */
public void removeBuiltinRecord(byte name, int sheetIndex) { public void removeBuiltinRecord(byte name, int sheetIndex) {
linkTable.removeBuiltinRecord(name, sheetIndex); linkTable.removeBuiltinRecord(name, sheetIndex);
// TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}? // 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 * Retrieves the index of the given font
*/ */
public int getFontIndex(FontRecord font) { public int getFontIndex(FontRecord font) {
for(int i=0; i<=numfonts; i++) { for(int i=0; i<=numfonts; i++) {
FontRecord thisFont = FontRecord thisFont =
( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + i); ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + i);
if(thisFont == font) { if(thisFont == font) {
// There is no 4! // There is no 4!
if(i > 3) { if(i > 3) {
return (i+1); return (i+1);
} }
return i; 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! * so you'll need to update those yourself!
*/ */
public void removeFontRecord(FontRecord rec) { public void removeFontRecord(FontRecord rec) {
records.remove(rec); // this updates FontPos for us records.remove(rec); // this updates FontPos for us
numfonts--; numfonts--;
} }
@ -468,19 +510,23 @@ public class Workbook implements Model
/** /**
* Sets the BOF for a given sheet * 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 * @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 )) 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)); " at pos=", new Integer(pos));
checkSheets(sheetnum); checkSheets(sheetIndex);
(( BoundSheetRecord ) boundsheets.get(sheetnum)) getBoundSheetRec(sheetIndex)
.setPositionOfBof(pos); .setPositionOfBof(pos);
} }
private BoundSheetRecord getBoundSheetRec(int sheetIndex) {
return ((BoundSheetRecord) boundsheets.get(sheetIndex));
}
/** /**
* Returns the position of the backup record. * Returns the position of the backup record.
*/ */
@ -516,7 +562,7 @@ public class Workbook implements Model
{ {
for ( int i = 0; i < boundsheets.size(); i++ ) 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())) if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname()))
return true; return true;
} }
@ -533,35 +579,33 @@ public class Workbook implements Model
*/ */
public void setSheetName(int sheetnum, String sheetname, short encoding ) { public void setSheetName(int sheetnum, String sheetname, short encoding ) {
checkSheets(sheetnum); checkSheets(sheetnum);
BoundSheetRecord sheet = (BoundSheetRecord)boundsheets.get( sheetnum ); BoundSheetRecord sheet = getBoundSheetRec(sheetnum);
sheet.setSheetname(sheetname); sheet.setSheetname(sheetname);
sheet.setSheetnameLength( (byte)sheetname.length() ); sheet.setSheetnameLength( (byte)sheetname.length() );
sheet.setCompressedUnicodeFlag( (byte)encoding ); sheet.setCompressedUnicodeFlag( (byte)encoding );
} }
/** /**
* sets the order of appearance for a given sheet. * sets the order of appearance for a given sheet.
* *
* @param sheetname the name of the sheet to reorder * @param sheetname the name of the sheet to reorder
* @param pos the position that we want to insert the sheet into (0 based) * @param pos the position that we want to insert the sheet into (0 based)
*/ */
public void setSheetOrder(String sheetname, int pos ) { public void setSheetOrder(String sheetname, int pos ) {
int sheetNumber = getSheetIndex(sheetname); int sheetNumber = getSheetIndex(sheetname);
//remove the sheet that needs to be reordered and place it in the spot we want //remove the sheet that needs to be reordered and place it in the spot we want
boundsheets.add(pos, boundsheets.remove(sheetNumber)); boundsheets.add(pos, boundsheets.remove(sheetNumber));
} }
/** /**
* gets the name for a given sheet. * 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 * @return sheetname the name for the sheet
*/ */
public String getSheetName(int sheetIndex) {
public String getSheetName(int sheetnum) { return getBoundSheetRec(sheetIndex).getSheetname();
return (( BoundSheetRecord ) boundsheets.get(sheetnum))
.getSheetname();
} }
/** /**
@ -572,8 +616,7 @@ public class Workbook implements Model
*/ */
public boolean isSheetHidden(int sheetnum) { public boolean isSheetHidden(int sheetnum) {
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum); return getBoundSheetRec(sheetnum).isHidden();
return bsr.isHidden();
} }
/** /**
@ -584,8 +627,7 @@ public class Workbook implements Model
*/ */
public void setSheetHidden(int sheetnum, boolean hidden) { public void setSheetHidden(int sheetnum, boolean hidden) {
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum); getBoundSheetRec(sheetnum).setHidden(hidden);
bsr.setHidden(hidden);
} }
/** /**
* get the sheet's index * get the sheet's index
@ -627,11 +669,13 @@ public class Workbook implements Model
} }
} }
public void removeSheet(int sheetnum) { /**
if (boundsheets.size() > sheetnum) { * @param sheetIndex zero based sheet index
records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetnum); */
// records.bspos--; public void removeSheet(int sheetIndex) {
boundsheets.remove(sheetnum); if (boundsheets.size() > sheetIndex) {
records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetIndex);
boundsheets.remove(sheetIndex);
fixTabIdRecord(); fixTabIdRecord();
} }
@ -642,20 +686,18 @@ public class Workbook implements Model
// However, the sheet index must be adjusted, or // However, the sheet index must be adjusted, or
// excel will break. (Sheet index is either 0 for // excel will break. (Sheet index is either 0 for
// global, or 1 based index to sheet) // global, or 1 based index to sheet)
int sheetNum1Based = sheetnum + 1; int sheetNum1Based = sheetIndex + 1;
for(int i=0; i<getNumNames(); i++) { for(int i=0; i<getNumNames(); i++) {
NameRecord nr = getNameRecord(i); NameRecord nr = getNameRecord(i);
if(nr.getIndexToSheet() == sheetNum1Based) { if(nr.getSheetNumber() == sheetNum1Based) {
// Excel re-writes these to point to no sheet // Excel re-writes these to point to no sheet
nr.setEqualsToIndexToSheet((short)0); nr.setSheetNumber(0);
} else if(nr.getIndexToSheet() > sheetNum1Based) { } else if(nr.getSheetNumber() > sheetNum1Based) {
// Bump down by one, so still points // Bump down by one, so still points
// at the same sheet // at the same sheet
nr.setEqualsToIndexToSheet((short)( nr.setSheetNumber(nr.getSheetNumber()-1);
nr.getEqualsToIndexToSheet()-1 }
));
}
} }
} }
@ -721,7 +763,7 @@ public class Workbook implements Model
* so you'll need to update those yourself! * so you'll need to update those yourself!
*/ */
public void removeExFormatRecord(ExtendedFormatRecord rec) { public void removeExFormatRecord(ExtendedFormatRecord rec) {
records.remove(rec); // this updates XfPos for us records.remove(rec); // this updates XfPos for us
numxfs--; numxfs--;
} }
@ -813,9 +855,9 @@ public class Workbook implements Model
// //
// Record record = records.get(k); // Record record = records.get(k);
//// Let's skip RECALCID records, as they are only use for optimization //// 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; // pos += record.serialize(pos, retval); // rec.length;
// } // }
// } // }
// log.log(DEBUG, "Exiting serialize workbook"); // log.log(DEBUG, "Exiting serialize workbook");
// return retval; // return retval;
@ -858,7 +900,7 @@ public class Workbook implements Model
if (record instanceof BoundSheetRecord) { if (record instanceof BoundSheetRecord) {
if(!wroteBoundSheets) { if(!wroteBoundSheets) {
for (int i = 0; i < boundsheets.size(); i++) { for (int i = 0; i < boundsheets.size(); i++) {
len+= ((BoundSheetRecord)boundsheets.get(i)) len+= getBoundSheetRec(i)
.serialize(pos+offset+len, data); .serialize(pos+offset+len, data);
} }
wroteBoundSheets = true; wroteBoundSheets = true;
@ -1141,8 +1183,8 @@ public class Workbook implements Model
retval.setWidth(( short ) 0x3a5c); retval.setWidth(( short ) 0x3a5c);
retval.setHeight(( short ) 0x23be); retval.setHeight(( short ) 0x23be);
retval.setOptions(( short ) 0x38); retval.setOptions(( short ) 0x38);
retval.setSelectedTab(( short ) 0x0); retval.setActiveSheetIndex( 0x0);
retval.setDisplayedTab(( short ) 0x0); retval.setFirstVisibleTab(0x0);
retval.setNumSelectedTabs(( short ) 1); retval.setNumSelectedTabs(( short ) 1);
retval.setTabWidthRatio(( short ) 0x258); retval.setTabWidthRatio(( short ) 0x258);
return retval; return retval;
@ -1821,10 +1863,10 @@ public class Workbook implements Model
// from Russia with love ;) // from Russia with love ;)
if ( Locale.getDefault().toString().equals( "ru_RU" ) ) { if ( Locale.getDefault().toString().equals( "ru_RU" ) ) {
retval.setCurrentCountry(( short ) 7); retval.setCurrentCountry(( short ) 7);
} }
else { else {
retval.setCurrentCountry(( short ) 1); retval.setCurrentCountry(( short ) 1);
} }
return retval; return retval;
@ -1889,15 +1931,15 @@ public class Workbook implements Model
* @return sheet name * @return sheet name
*/ */
public String findSheetNameFromExternSheet(short num){ 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! if (indexToSheet < 0) {
result = getSheetName(indexToSheet); // TODO - what does '-1' mean here?
//error check, bail out gracefully!
return "";
} }
return getSheetName(indexToSheet);
return result;
} }
/** /**
@ -1950,10 +1992,10 @@ public class Workbook implements Model
*/ */
public NameRecord addName(NameRecord name) public NameRecord addName(NameRecord name)
{ {
LinkTable linkTable = getOrCreateLinkTable(); LinkTable linkTable = getOrCreateLinkTable();
if(linkTable.nameAlreadyExists(name)) { if(linkTable.nameAlreadyExists(name)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"You are trying to assign a duplicated name record: " "You are trying to assign a duplicated name record: "
+ name.getNameText()); + name.getNameText());
} }
@ -1964,27 +2006,18 @@ public class Workbook implements Model
/** /**
* Generates a NameRecord to represent a built-in region * 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) public NameRecord createBuiltInName(byte builtInName, int sheetNumber) {
{ if (sheetNumber < 0 || sheetNumber+1 > Short.MAX_VALUE) {
if (index == -1 || index+1 > Short.MAX_VALUE) throw new IllegalArgumentException("Sheet number ["+sheetNumber+"]is not valid ");
throw new IllegalArgumentException("Index is not valid ["+index+"]"); }
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)) { while(linkTable.nameAlreadyExists(name)) {
cont++; throw new RuntimeException("Builtin (" + builtInName
String altNameName = prefix + cont; + ") already exists for sheet (" + sheetNumber + ")");
// 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());
} }
addName(name); addName(name);
return name; return name;
@ -1992,16 +2025,15 @@ public class Workbook implements Model
/** removes the name /** 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); int idx = findFirstRecordLocBySid(NameRecord.sid);
records.remove(idx + namenum); records.remove(idx + nameIndex);
linkTable.removeName(namenum); 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 * @return the format id of a format that matches or -1 if none found and createIfNotFound
*/ */
public short getFormat(String format, boolean createIfNotFound) { public short getFormat(String format, boolean createIfNotFound) {
Iterator iterator; Iterator iterator;
for (iterator = formats.iterator(); iterator.hasNext();) { for (iterator = formats.iterator(); iterator.hasNext();) {
FormatRecord r = (FormatRecord)iterator.next(); FormatRecord r = (FormatRecord)iterator.next();
if (r.getFormatString().equals(format)) { if (r.getFormatString().equals(format)) {
return r.getIndexCode(); return r.getIndexCode();
} }
} }
if (createIfNotFound) { if (createIfNotFound) {
return createFormat(format); return createFormat(format);
} }
return -1; return -1;
} }
/** /**
@ -2031,7 +2063,7 @@ public class Workbook implements Model
* @return ArrayList of FormatRecords in the notebook * @return ArrayList of FormatRecords in the notebook
*/ */
public ArrayList getFormats() { public ArrayList getFormats() {
return formats; return formats;
} }
/** /**
@ -2043,7 +2075,7 @@ public class Workbook implements Model
*/ */
public short createFormat( String format ) 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; // ++palettepos;
// ++bspos; // ++bspos;
FormatRecord rec = new FormatRecord(); FormatRecord rec = new FormatRecord();
@ -2113,7 +2145,7 @@ public class Workbook implements Model
public List getHyperlinks() public List getHyperlinks()
{ {
return hyperlinks; return hyperlinks;
} }
public List getRecords() public List getRecords()
@ -2170,54 +2202,54 @@ public class Workbook implements Model
* Finds the primary drawing group, if one already exists * Finds the primary drawing group, if one already exists
*/ */
public void findDrawingGroup() { public void findDrawingGroup() {
// Need to find a DrawingGroupRecord that // Need to find a DrawingGroupRecord that
// contains a EscherDggRecord // contains a EscherDggRecord
for(Iterator rit = records.iterator(); rit.hasNext();) { for(Iterator rit = records.iterator(); rit.hasNext();) {
Record r = (Record)rit.next(); Record r = (Record)rit.next();
if(r instanceof DrawingGroupRecord) { if(r instanceof DrawingGroupRecord) {
DrawingGroupRecord dg = (DrawingGroupRecord)r; DrawingGroupRecord dg = (DrawingGroupRecord)r;
dg.processChildRecords(); dg.processChildRecords();
EscherContainerRecord cr = EscherContainerRecord cr =
dg.getEscherContainer(); dg.getEscherContainer();
if(cr == null) { if(cr == null) {
continue; continue;
} }
EscherDggRecord dgg = null; EscherDggRecord dgg = null;
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) { for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
Object er = it.next(); Object er = it.next();
if(er instanceof EscherDggRecord) { if(er instanceof EscherDggRecord) {
dgg = (EscherDggRecord)er; dgg = (EscherDggRecord)er;
} }
} }
if(dgg != null) { if(dgg != null) {
drawingManager = new DrawingManager2(dgg); drawingManager = new DrawingManager2(dgg);
return; return;
} }
} }
} }
// Look for the DrawingGroup record // Look for the DrawingGroup record
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); 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) { if(dgLoc != -1) {
DrawingGroupRecord dg = DrawingGroupRecord dg =
(DrawingGroupRecord)records.get(dgLoc); (DrawingGroupRecord)records.get(dgLoc);
EscherDggRecord dgg = null; EscherDggRecord dgg = null;
for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) { for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) {
Object er = it.next(); Object er = it.next();
if(er instanceof EscherDggRecord) { if(er instanceof EscherDggRecord) {
dgg = (EscherDggRecord)er; dgg = (EscherDggRecord)er;
} }
} }
if(dgg != null) { if(dgg != null) {
drawingManager = new DrawingManager2(dgg); drawingManager = new DrawingManager2(dgg);
} }
} }
} }
@ -2379,7 +2411,7 @@ public class Workbook implements Model
*/ */
public boolean isWriteProtected() { public boolean isWriteProtected() {
if (this.fileShare == null) { if (this.fileShare == null) {
return false; return false;
} }
FileSharingRecord frec = getFileSharing(); FileSharingRecord frec = getFileSharing();
return (frec.getReadOnly() == 1); return (frec.getReadOnly() == 1);
@ -2419,6 +2451,8 @@ public class Workbook implements Model
public String resolveNameXText(int refIndex, int definedNameIndex) { public String resolveNameXText(int refIndex, int definedNameIndex) {
return linkTable.resolveNameXText(refIndex, definedNameIndex); return linkTable.resolveNameXText(refIndex, definedNameIndex);
} }
public NameXPtg getNameXPtg(String name) {
return getOrCreateLinkTable().getNameXPtg(name);
}
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,29 +14,85 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import java.util.ArrayList;
/** /**
* Title: Extern Sheet <P> * EXTERNSHEET (0x0017)<br/>
* Description: A List of Inndexes to SupBook <P> * A List of Indexes to EXTERNALBOOK (supplemental book) Records <p/>
* REFERENCE: <P> *
* @author Libin Roman (Vista Portal LDT. Developer) * @author Libin Roman (Vista Portal LDT. Developer)
* @version 1.0-pre
*/ */
public class ExternSheetRecord extends Record { public class ExternSheetRecord extends Record {
public final static short sid = 0x17; public final static short sid = 0x0017;
private short field_1_number_of_REF_sturcutres; private List _list;
private ArrayList field_2_REF_structures;
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() { 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 * @param in the RecordInputstream to read the record from
*/ */
protected void fillFields(RecordInputStream in) { 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) { for (int i = 0 ; i < nItems ; ++i) {
ExternSheetSubRecord rec = new ExternSheetSubRecord(in); 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 structures
* @return number of REF structs
*/ */
public short getNumOfREFStructures() { public int getNumOfRefs() {
return field_1_number_of_REF_sturcutres; return _list.size();
} }
/** /**
* adds REF struct (ExternSheetSubRecord) * adds REF struct (ExternSheetSubRecord)
* @param rec REF struct * @param rec REF struct
*/ */
public void addREFRecord(ExternSheetSubRecord rec) { public void addREFRecord(RefSubRecord rec) {
field_2_REF_structures.add(rec); _list.add(rec);
} }
/** returns the number of REF Records, which is in model /** returns the number of REF Records, which is in model
* @return number of REF records * @return number of REF records
*/ */
public int getNumOfREFRecords() { 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() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer sb = new StringBuffer();
int nItems = _list.size();
buffer.append("[EXTERNSHEET]\n"); sb.append("[EXTERNSHEET]\n");
buffer.append(" numOfRefs = ").append(getNumOfREFStructures()).append("\n"); sb.append(" numOfRefs = ").append(nItems).append("\n");
for (int k=0; k < this.getNumOfREFRecords(); k++) { for (int i=0; i < nItems; i++) {
buffer.append("refrec #").append(k).append('\n'); sb.append("refrec #").append(i).append(": ");
buffer.append(getREFRecordAt(k).toString()); sb.append(getRef(i).toString());
buffer.append("----refrec #").append(k).append('\n'); 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 * @return number of bytes written
*/ */
public int serialize(int offset, byte [] data) { public int serialize(int offset, byte [] data) {
LittleEndian.putShort(data, 0 + offset, sid); int dataSize = getDataSize();
LittleEndian.putShort(data, 2 + offset,(short)(2 + (getNumOfREFRecords() *6)));
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 ; int pos = 6 ;
for (int k = 0; k < getNumOfREFRecords(); k++) { for (int i = 0; i < nItems; i++) {
ExternSheetSubRecord record = getREFRecordAt(k); getRef(i).serialize(offset + pos, data);
System.arraycopy(record.serialize(), 0, data, pos + offset, 6);
pos +=6; pos +=6;
} }
return getRecordSize(); return dataSize + 4;
} }
private RefSubRecord getRef(int i) {
return (RefSubRecord) _list.get(i);
}
public int getRecordSize() { public int getRecordSize() {
return 4 + 2 + getNumOfREFRecords() * 6; return 4 + getDataSize();
} }
/** /**
@ -172,4 +220,44 @@ public class ExternSheetRecord extends Record {
public short getSid() { public short getSid() {
return sid; 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;
}
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ import org.apache.poi.util.LittleEndian;
/** /**
* Title: Sup Book (EXTERNALBOOK) <P> * 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> * Its only a dummy record for making new ExternSheet Record <P>
* REFERENCE: 5.38<P> * REFERENCE: 5.38<P>
* @author Libin Roman (Vista Portal LDT. Developer) * @author Libin Roman (Vista Portal LDT. Developer)

View File

@ -110,7 +110,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
* @return <code>true</code> if the name specifies a standard worksheet function, * @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. * <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()); short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase());
return ix >= 0; return ix >= 0;
} }

View File

@ -22,41 +22,55 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.RecordInputStream;
/** /**
* *
* @author aviks * @author aviks
*/ */
public final class NameXPtg extends OperandPtg { public final class NameXPtg extends OperandPtg {
public final static short sid = 0x39; public final static short sid = 0x39;
private final static int SIZE = 7; 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
/** 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;
public NameXPtg(RecordInputStream in) { private NameXPtg(int sheetRefIndex, int nameNumber, int reserved) {
field_1_ixals = in.readShort(); _sheetRefIndex = sheetRefIndex;
field_2_ilbl = in.readShort(); _nameNumber = nameNumber;
field_3_reserved = in.readShort(); _reserved = reserved;
} }
public void writeBytes(byte [] array, int offset) { /**
array[ offset + 0 ] = (byte)(sid + getPtgClass()); * @param sheetRefIndex index to REF entry in externsheet record
LittleEndian.putShort(array, offset + 1, field_1_ixals); * @param nameIndex index to defined name or externname table
LittleEndian.putShort(array,offset+3, field_2_ilbl); */
LittleEndian.putShort(array, offset + 5, field_3_reserved); public NameXPtg(int sheetRefIndex, int nameIndex) {
} this(sheetRefIndex, nameIndex + 1, 0);
}
public int getSize() { public NameXPtg(RecordInputStream in) {
return SIZE; this(in.readUShort(), in.readUShort(), in.readUShort());
} }
public String toFormulaString(Workbook book) public void writeBytes(byte[] array, int offset) {
{ LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
// -1 to convert definedNameIndex from 1-based to zero-based LittleEndian.putUShort(array, offset + 1, _sheetRefIndex);
return book.resolveNameXText(field_1_ixals, field_2_ilbl-1); LittleEndian.putUShort(array, offset + 3, _nameNumber);
} LittleEndian.putUShort(array, offset + 5, _reserved);
}
public byte getDefaultOperandClass() {
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; return Ptg.CLASS_VALUE;
} }
} }

View File

@ -23,67 +23,57 @@ import org.apache.poi.hssf.util.RangeAddress;
import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Name;
/** /**
* Title: High Level Represantion of Named Range <P> * High Level Representation of a 'defined name' which could be a 'built-in' name,
* REFERENCE: <P> * 'named range' or name of a user defined function.
*
* @author Libin Roman (Vista Portal LDT. Developer) * @author Libin Roman (Vista Portal LDT. Developer)
*/ */
public class HSSFName implements Name { public class HSSFName implements Name {
private HSSFWorkbook book; private HSSFWorkbook _book;
private NameRecord name; private NameRecord _definedNameRec;
/** Creates new HSSFName - called by HSSFWorkbook to create a sheet from /** Creates new HSSFName - called by HSSFWorkbook to create a sheet from
* scratch. * scratch.
* *
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName() * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName()
* @param name the Name Record * @param name the Name Record
* @param book lowlevel Workbook object associated with the sheet. * @param book workbook object associated with the sheet.
*/ */
/* package */ HSSFName(HSSFWorkbook book, NameRecord name) {
protected HSSFName(HSSFWorkbook book, NameRecord name) { _book = book;
this.book = book; _definedNameRec = name;
this.name = name;
} }
/** Get the sheets name which this named range is referenced to /** 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() { public String getSheetName() {
String result ; short indexToExternSheet = _definedNameRec.getExternSheetNumber();
short indexToExternSheet = name.getExternSheetNumber();
result = book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet);
return result;
} }
/** /**
* gets the name of the named range * @return text name of this defined name
* @return named range name
*/ */
public String getNameName(){ public String getNameName(){
String result = name.getNameText(); return _definedNameRec.getNameText();
return result;
} }
/** /**
* sets the name of the named range * sets the name of the named range
* @param nameName named range name to set * @param nameName named range name to set
*/ */
public void setNameName(String nameName){ public void setNameName(String nameName){
name.setNameText(nameName); _definedNameRec.setNameText(nameName);
name.setNameTextLength((byte)nameName.length()); _definedNameRec.setNameTextLength((byte)nameName.length());
Workbook wb = book.getWorkbook(); Workbook wb = _book.getWorkbook();
//Check to ensure no other names have the same case-insensitive name //Check to ensure no other names have the same case-insensitive name
for ( int i = wb.getNumNames()-1; i >=0; i-- ) for ( int i = wb.getNumNames()-1; i >=0; i-- )
{ {
NameRecord rec = wb.getNameRecord(i); NameRecord rec = wb.getNameRecord(i);
if (rec != name) { if (rec != _definedNameRec) {
if (rec.getNameText().equalsIgnoreCase(getNameName())) if (rec.getNameText().equalsIgnoreCase(getNameName()))
throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); 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 * Note - this method only applies to named ranges
* @return reference of the named range * @return the formula text defining the named range
*/ */
public String getReference() { public String getReference() {
String result; if (_definedNameRec.isFunctionName()) {
result = name.getAreaReference(book); throw new IllegalStateException("Only applicable to named ranges");
}
return result; return _definedNameRec.getAreaReference(_book);
} }
/** /**
* sets the sheet name which this named range referenced to * sets the sheet name which this named range referenced to
* @param sheetName the sheet name of the reference * @param sheetName the sheet name of the reference
*/ */
private void setSheetName(String sheetName){ private void setSheetName(String sheetName){
int sheetNumber = book.getSheetIndex(sheetName); int sheetNumber = _book.getSheetIndex(sheetName);
short externSheetNumber = (short) short externSheetNumber = (short)
book.getExternalSheetIndex(sheetNumber); _book.getExternalSheetIndex(sheetNumber);
name.setExternSheetNumber(externSheetNumber); _definedNameRec.setExternSheetNumber(externSheetNumber);
// name.setIndexToSheet(externSheetNumber);
} }
@ -124,7 +107,6 @@ public class HSSFName implements Name {
* sets the reference of this named range * sets the reference of this named range
* @param ref the reference to set * @param ref the reference to set
*/ */
public void setReference(String ref){ public void setReference(String ref){
RangeAddress ra = new RangeAddress(ref); RangeAddress ra = new RangeAddress(ref);
@ -136,8 +118,7 @@ public class HSSFName implements Name {
} }
//allow the poi utilities to parse it out //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(); String ref = getReference();
return "#REF!".endsWith(ref); 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();
}
} }

View File

@ -53,6 +53,7 @@ import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.UnknownRecord; import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg; 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.record.formula.UnionPtg;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.util.SheetReferences; 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 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; 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 * 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 * because it creates/modifies name records which are stored at the
* workbook level. * workbook level.
* <p> * <p>
@ -886,10 +891,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
// Check arguments // Check arguments
if (startColumn == -1 && endColumn != -1) throw new IllegalArgumentException("Invalid column range specification"); 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 (startRow == -1 && endRow != -1) throw new IllegalArgumentException("Invalid row range specification");
if (startColumn < -1 || startColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification"); if (startColumn < -1 || startColumn >= MAX_COLUMN) throw new IllegalArgumentException("Invalid column range specification");
if (endColumn < -1 || endColumn >= 0xFF) throw new IllegalArgumentException("Invalid column range specification"); if (endColumn < -1 || endColumn >= MAX_COLUMN) throw new IllegalArgumentException("Invalid column range specification");
if (startRow < -1 || startRow > 65535) throw new IllegalArgumentException("Invalid row range specification"); if (startRow < -1 || startRow > MAX_ROW) throw new IllegalArgumentException("Invalid row range specification");
if (endRow < -1 || endRow > 65535) 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 (startColumn > endColumn) throw new IllegalArgumentException("Invalid column range specification");
if (startRow > endRow) throw new IllegalArgumentException("Invalid row 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 = boolean removingRange =
startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1; startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
boolean isNewRecord = false; int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex);
NameRecord nameRecord; if (removingRange) {
nameRecord = findExistingRowColHeaderNameRecord(sheetIndex); if (rowColHeaderNameIndex >= 0) {
if (removingRange ) workbook.removeName(rowColHeaderNameIndex);
{ }
if (nameRecord != null)
workbook.removeName(findExistingRowColHeaderNameRecordIdx(sheetIndex+1));
return; return;
} }
if ( nameRecord == null ) boolean isNewRecord;
{ NameRecord nameRecord;
nameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_TITLE, sheetIndex+1); if (rowColHeaderNameIndex < 0) {
//does a lot of the house keeping for builtin records, like setting lengths to zero etc //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; isNewRecord = true;
} else {
nameRecord = workbook.getNameRecord(rowColHeaderNameIndex);
isNewRecord = false;
} }
short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b;
nameRecord.setDefinitionTextLength(definitionTextLength); nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove
Stack ptgs = new Stack(); Stack ptgs = new Stack();
if (settingRowAndColumn) if (settingRowAndColumn) {
{ final int exprsSize = 2 * 11 + 1; // Area3DPtg.SIZE + UnionPtg.SIZE
ptgs.add(new MemFuncPtg(23)); // TODO - where did constant '23' come from? ptgs.add(new MemFuncPtg(exprsSize));
} }
if (startColumn >= 0) if (startColumn >= 0)
{ {
Area3DPtg area3DPtg1 = new Area3DPtg(); Area3DPtg colArea = new Area3DPtg();
area3DPtg1.setExternSheetIndex(externSheetIndex); colArea.setExternSheetIndex(externSheetIndex);
area3DPtg1.setFirstColumn((short)startColumn); colArea.setFirstColumn((short)startColumn);
area3DPtg1.setLastColumn((short)endColumn); colArea.setLastColumn((short)endColumn);
area3DPtg1.setFirstRow((short)0); colArea.setFirstRow(0);
area3DPtg1.setLastRow((short)0xFFFF); colArea.setLastRow(MAX_ROW);
ptgs.add(area3DPtg1); colArea.setFirstColRelative(false);
colArea.setLastColRelative(false);
colArea.setFirstRowRelative(false);
colArea.setLastRowRelative(false);
ptgs.add(colArea);
} }
if (startRow >= 0) if (startRow >= 0)
{ {
Area3DPtg area3DPtg2 = new Area3DPtg(); Area3DPtg rowArea = new Area3DPtg();
area3DPtg2.setExternSheetIndex(externSheetIndex); rowArea.setExternSheetIndex(externSheetIndex);
area3DPtg2.setFirstColumn((short)0); rowArea.setFirstColumn((short)0);
area3DPtg2.setLastColumn((short)0x00FF); rowArea.setLastColumn(MAX_COLUMN);
area3DPtg2.setFirstRow((short)startRow); rowArea.setFirstRow(startRow);
area3DPtg2.setLastRow((short)endRow); rowArea.setLastRow(endRow);
ptgs.add(area3DPtg2); rowArea.setFirstColRelative(false);
rowArea.setLastColRelative(false);
rowArea.setFirstRowRelative(false);
rowArea.setLastRowRelative(false);
ptgs.add(rowArea);
} }
if (settingRowAndColumn) if (settingRowAndColumn)
{ {
@ -964,38 +979,31 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
sheet.setActive(true); 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 ) private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) {
{ for(int defNameIndex =0; defNameIndex<names.size(); defNameIndex++) {
int index = 0; NameRecord r = workbook.getNameRecord(defNameIndex);
NameRecord r = null; if (r == null) {
while ((r = (NameRecord) workbook.findNextRecordBySid(NameRecord.sid, index)) != null) throw new RuntimeException("Unable to find all defined names to iterate over");
{ }
int indexToSheet = r.getEqualsToIndexToSheet() -1; if (!isRowColHeaderRecord( r )) {
if(indexToSheet > -1) { //ignore "GLOBAL" name records continue;
int nameRecordSheetIndex = workbook.getSheetIndexFromExternSheetIndex(indexToSheet); }
if (isRowColHeaderRecord( r ) && nameRecordSheetIndex == sheetIndex) if(r.getSheetNumber() == 0) {
{ //ignore "GLOBAL" name records
return index; continue;
} }
int externIndex = r.getSheetNumber() -1;
int nameRecordSheetIndex = workbook.getSheetIndexFromExternSheetIndex(externIndex);
if (nameRecordSheetIndex == sheetIndex) {
return defNameIndex;
} }
index++;
} }
return -1; return -1;
} }
private boolean isRowColHeaderRecord( NameRecord r ) private static boolean isRowColHeaderRecord(NameRecord r) {
{ return r.isBuiltInName() && r.getBuiltInName() == NameRecord.BUILTIN_PRINT_TITLE;
return r.getOptionFlag() == 0x20 && ("" + ((char)7)).equals(r.getNameText());
} }
/** /**
@ -1089,7 +1097,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
* and that's not something you should normally do * and that's not something you should normally do
*/ */
protected void resetFontCache() { 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); return new HSSFCreationHelper(this);
} }
private byte[] newUID() private static byte[] newUID() {
{ return new byte[16];
byte[] bytes = new byte[16];
return bytes;
} }
/**
* 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);
}
} }

View File

@ -19,4 +19,5 @@ package org.apache.poi.ss.usermodel;
public interface Name { public interface Name {
void setNameName(String nameName); void setNameName(String nameName);
boolean isFunctionName();
} }

View File

@ -33,6 +33,7 @@ public interface Workbook {
HSSFName createName(); HSSFName createName();
HSSFName getNameAt(int index); HSSFName getNameAt(int index);
int getNameIndex(String name);
String getNameName(int index); String getNameName(int index);
String resolveNameXText(int refIndex, int definedNameIndex); String resolveNameXText(int refIndex, int definedNameIndex);

View File

@ -53,4 +53,5 @@ public interface Name {
void setReference(String ref); void setReference(String ref);
} boolean isFunctionName();
}

View File

@ -219,24 +219,25 @@ public class HWPFDocument extends POIDocument
_dataStream = new byte[0]; _dataStream = new byte[0];
} }
// get the start of text in the main stream // Get the cp of the start of text in the main stream
int fcMin = _fib.getFcMin(); // 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()); _dop = new DocumentProperties(_tableStream, _fib.getFcDop());
_cft = new ComplexFileTable(_mainStream, _tableStream, _fib.getFcClx(), fcMin); _cft = new ComplexFileTable(_mainStream, _tableStream, _fib.getFcClx(), fcMin);
_tpt = _cft.getTextPieceTable(); _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 and later all put in a zero filled buffer in
// front of the text. This screws up the system for offsets,
// Word XP puts in a zero filled buffer in front of the text and it screws // which assume we always start at zero. This is an adjustment.
// up my system for offsets. This is an adjustment.
int cpMin = _tpt.getCpMin(); int cpMin = _tpt.getCpMin();
if (cpMin > 0)
{ // Now load the rest of the properties, which need to be adjusted
_cbt.adjustForDelete(0, 0, cpMin); // for where text really begin
_pbt.adjustForDelete(0, 0, cpMin); _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 // Read FSPA and Escher information
_fspa = new FSPATable(_tableStream, _fib.getFcPlcspaMom(), _fib.getLcbPlcspaMom(), getTextTable().getTextPieces()); _fspa = new FSPATable(_tableStream, _fib.getFcPlcspaMom(), _fib.getLcbPlcspaMom(), getTextTable().getTextPieces());
@ -252,7 +253,7 @@ public class HWPFDocument extends POIDocument
// read in the pictures stream // read in the pictures stream
_pictures = new PicturesTable(this, _dataStream, _mainStream, _fspa, _dgg); _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()); _ss = new StyleSheet(_tableStream, _fib.getFcStshf());
_ft = new FontTable(_tableStream, _fib.getFcSttbfffn(), _fib.getLcbSttbfffn()); _ft = new FontTable(_tableStream, _fib.getFcSttbfffn(), _fib.getLcbSttbfffn());
@ -316,6 +317,11 @@ public class HWPFDocument extends POIDocument
* document, but excludes any headers and footers. * document, but excludes any headers and footers.
*/ */
public Range getRange() { public Range getRange() {
// First up, trigger a full-recalculate
// Needed in case of deletes etc
getOverallRange();
// Now, return the real one
return new Range( return new Range(
_cpSplit.getMainDocumentStart(), _cpSplit.getMainDocumentStart(),
_cpSplit.getMainDocumentEnd(), _cpSplit.getMainDocumentEnd(),

View File

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

View File

@ -37,6 +37,8 @@ public class CHPBinTable
/** List of character properties.*/ /** List of character properties.*/
protected ArrayList _textRuns = new ArrayList(); protected ArrayList _textRuns = new ArrayList();
/** So we can know if things are unicode or not */
private TextPieceTable tpt;
public CHPBinTable() public CHPBinTable()
{ {
@ -52,9 +54,10 @@ public class CHPBinTable
* @param fcMin * @param fcMin
*/ */
public CHPBinTable(byte[] documentStream, byte[] tableStream, int offset, 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); PlexOfCps binTable = new PlexOfCps(tableStream, offset, size, 4);
this.tpt = tpt;
int length = binTable.length(); int length = binTable.length();
for (int x = 0; x < length; x++) for (int x = 0; x < length; x++)
@ -65,7 +68,7 @@ public class CHPBinTable
int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum; int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum;
CHPFormattedDiskPage cfkp = new CHPFormattedDiskPage(documentStream, CHPFormattedDiskPage cfkp = new CHPFormattedDiskPage(documentStream,
pageOffset, fcMin); pageOffset, fcMin, tpt);
int fkpSize = cfkp.size(); int fkpSize = cfkp.size();
@ -116,7 +119,14 @@ public class CHPBinTable
public void insert(int listIndex, int cpStart, SprmBuffer buf) 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()) if (listIndex == _textRuns.size())
{ {
_textRuns.add(insertChpx); _textRuns.add(insertChpx);
@ -126,7 +136,16 @@ public class CHPBinTable
CHPX chpx = (CHPX)_textRuns.get(listIndex); CHPX chpx = (CHPX)_textRuns.get(listIndex);
if (chpx.getStart() < cpStart) 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); chpx.setEnd(cpStart);
_textRuns.add(listIndex + 1, insertChpx); _textRuns.add(listIndex + 1, insertChpx);

View File

@ -55,13 +55,14 @@ public class CHPFormattedDiskPage extends FormattedDiskPage
* This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array * This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array
* read from a Word file). * 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); super(documentStream, offset);
for (int x = 0; x < _crun; x++) 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); chpx = (CHPX)_chpxList.get(x);
byte[] grpprl = chpx.getGrpprl(); byte[] grpprl = chpx.getGrpprl();
LittleEndian.putInt(buf, fcOffset, chpx.getStart() + fcMin); LittleEndian.putInt(buf, fcOffset, chpx.getStartBytes() + fcMin);
grpprlOffset -= (1 + grpprl.length); grpprlOffset -= (1 + grpprl.length);
grpprlOffset -= (grpprlOffset % 2); grpprlOffset -= (grpprlOffset % 2);
buf[offsetOffset] = (byte)(grpprlOffset/2); buf[offsetOffset] = (byte)(grpprlOffset/2);
@ -168,7 +169,7 @@ public class CHPFormattedDiskPage extends FormattedDiskPage
fcOffset += FC_SIZE; fcOffset += FC_SIZE;
} }
// put the last chpx's end in // put the last chpx's end in
LittleEndian.putInt(buf, fcOffset, chpx.getEnd() + fcMin); LittleEndian.putInt(buf, fcOffset, chpx.getEndBytes() + fcMin);
return buf; return buf;
} }

View File

@ -25,22 +25,26 @@ import org.apache.poi.hwpf.sprm.SprmBuffer;
import org.apache.poi.hwpf.sprm.CharacterSprmUncompressor; 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 * @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);
} }

View File

@ -39,14 +39,18 @@ public class PAPBinTable
protected ArrayList _paragraphs = new ArrayList(); protected ArrayList _paragraphs = new ArrayList();
byte[] _dataStream; byte[] _dataStream;
/** So we can know if things are unicode or not */
private TextPieceTable tpt;
public PAPBinTable() public PAPBinTable()
{ {
} }
public PAPBinTable(byte[] documentStream, byte[] tableStream, byte[] dataStream, int offset, 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); PlexOfCps binTable = new PlexOfCps(tableStream, offset, size, 4);
this.tpt = tpt;
int length = binTable.length(); int length = binTable.length();
for (int x = 0; x < length; x++) for (int x = 0; x < length; x++)
@ -57,13 +61,14 @@ public class PAPBinTable
int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum; int pageOffset = POIFSConstants.BIG_BLOCK_SIZE * pageNum;
PAPFormattedDiskPage pfkp = new PAPFormattedDiskPage(documentStream, PAPFormattedDiskPage pfkp = new PAPFormattedDiskPage(documentStream,
dataStream, pageOffset, fcMin); dataStream, pageOffset, fcMin, tpt);
int fkpSize = pfkp.size(); int fkpSize = pfkp.size();
for (int y = 0; y < fkpSize; y++) for (int y = 0; y < fkpSize; y++)
{ {
_paragraphs.add(pfkp.getPAPX(y)); PAPX papx = pfkp.getPAPX(y);
_paragraphs.add(papx);
} }
} }
_dataStream = dataStream; _dataStream = dataStream;
@ -71,7 +76,14 @@ public class PAPBinTable
public void insert(int listIndex, int cpStart, SprmBuffer buf) 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()) if (listIndex == _paragraphs.size())
{ {
_paragraphs.add(forInsert); _paragraphs.add(forInsert);
@ -90,10 +102,21 @@ public class PAPBinTable
{ {
exc.printStackTrace(); 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); currentPap.setEnd(cpStart);
PAPX splitPap = new PAPX(cpStart, currentPap.getEnd(), clonedBuf, _dataStream);
_paragraphs.add(++listIndex, forInsert); _paragraphs.add(listIndex + 1, forInsert);
_paragraphs.add(++listIndex, splitPap); _paragraphs.add(listIndex + 2, clone);
} }
else else
{ {

View File

@ -60,13 +60,17 @@ public class PAPFormattedDiskPage extends FormattedDiskPage
/** /**
* Creates a PAPFormattedDiskPage from a 512 byte array * 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); super(documentStream, offset);
for (int x = 0; x < _crun; x++) for (int x = 0; x < _crun; x++) {
{ int startAt = getStart(x) - fcMin;
_papxList.add(new PAPX(getStart(x) - fcMin, getEnd(x) - fcMin, getGrpprl(x), getParagraphHeight(x), dataStream)); 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; _fkp = null;
_dataStream = dataStream; _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. * @param index The index of the papx to get.
* @return a papx grpprl. * @return a papx grpprl.
@ -259,7 +263,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage
grpprlOffset -= (grpprl.length + (2 - grpprl.length % 2)); grpprlOffset -= (grpprl.length + (2 - grpprl.length % 2));
grpprlOffset -= (grpprlOffset % 2); grpprlOffset -= (grpprlOffset % 2);
} }
LittleEndian.putInt(buf, fcOffset, papx.getStart() + fcMin); LittleEndian.putInt(buf, fcOffset, papx.getStartBytes() + fcMin);
buf[bxOffset] = (byte)(grpprlOffset/2); buf[bxOffset] = (byte)(grpprlOffset/2);
System.arraycopy(phe, 0, buf, bxOffset + 1, phe.length); 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; return buf;
} }

View File

@ -29,29 +29,32 @@ import org.apache.poi.hwpf.sprm.SprmBuffer;
import org.apache.poi.hwpf.sprm.SprmOperation; 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 * @author Ryan Ackley
*/ */
public class PAPX extends PropertyNode public class PAPX extends BytePropertyNode {
{
private ParagraphHeight _phe; private ParagraphHeight _phe;
private int _hugeGrpprlOffset = -1; 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; _phe = phe;
SprmBuffer buf = findHuge(new SprmBuffer(papx), dataStream); SprmBuffer buf = findHuge(new SprmBuffer(papx), dataStream);
if(buf != null) if(buf != null)
_buf = buf; _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(); _phe = new ParagraphHeight();
buf = findHuge(buf, dataStream); buf = findHuge(buf, dataStream);
if(buf != null) if(buf != null)

View File

@ -22,7 +22,10 @@ import java.util.Arrays;
/** /**
* Represents a lightweight node in the Trees used to store content * 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 * @author Ryan Ackley
*/ */
@ -45,6 +48,11 @@ public abstract class PropertyNode implements Comparable, Cloneable
_cpStart = fcStart; _cpStart = fcStart;
_cpEnd = fcEnd; _cpEnd = fcEnd;
_buf = buf; _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; int end = start + length;
if (_cpEnd > start) if (_cpEnd > start) {
{ // The start of the change is before we end
if (_cpStart < end)
{ if (_cpStart < end) {
_cpEnd = end >= _cpEnd ? start : _cpEnd - length; // The delete was somewhere in the middle of us
_cpStart = Math.min(start, _cpStart); _cpEnd = end >= _cpEnd ? start : _cpEnd - length;
} _cpStart = Math.min(start, _cpStart);
else } else {
{ // The delete was before us
_cpEnd -= length; _cpEnd -= length;
_cpStart -= length; _cpStart -= length;
} }
} }
} }

View File

@ -20,19 +20,20 @@
package org.apache.poi.hwpf.model; 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.SectionSprmCompressor;
import org.apache.poi.hwpf.sprm.SectionSprmUncompressor;
import org.apache.poi.hwpf.usermodel.SectionProperties; import org.apache.poi.hwpf.usermodel.SectionProperties;
public class SEPX extends PropertyNode /**
*/
public class SEPX extends BytePropertyNode
{ {
SectionDescriptor _sed; 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; _sed = sed;
} }

View File

@ -34,6 +34,9 @@ public class SectionTable
protected ArrayList _sections = new ArrayList(); protected ArrayList _sections = new ArrayList();
protected List _text; protected List _text;
/** So we can know if things are unicode or not */
private TextPieceTable tpt;
public SectionTable() public SectionTable()
{ {
} }
@ -41,10 +44,11 @@ public class SectionTable
public SectionTable(byte[] documentStream, byte[] tableStream, int offset, public SectionTable(byte[] documentStream, byte[] tableStream, int offset,
int size, int fcMin, int size, int fcMin,
List tpt) TextPieceTable tpt, CPSplitCalculator cps)
{ {
PlexOfCps sedPlex = new PlexOfCps(tableStream, offset, size, SED_SIZE); PlexOfCps sedPlex = new PlexOfCps(tableStream, offset, size, SED_SIZE);
_text = tpt; this.tpt = tpt;
this._text = tpt.getTextPieces();
int length = sedPlex.length(); int length = sedPlex.length();
@ -54,11 +58,16 @@ public class SectionTable
SectionDescriptor sed = new SectionDescriptor(node.getBytes(), 0); SectionDescriptor sed = new SectionDescriptor(node.getBytes(), 0);
int fileOffset = sed.getFc(); 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 // check for the optimization
if (fileOffset == 0xffffffff) 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 else
{ {
@ -67,9 +76,34 @@ public class SectionTable
byte[] buf = new byte[sepxSize]; byte[] buf = new byte[sepxSize];
fileOffset += LittleEndian.SHORT_SIZE; fileOffset += LittleEndian.SHORT_SIZE;
System.arraycopy(documentStream, fileOffset, buf, 0, buf.length); 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) public void adjustForInsert(int listIndex, int length)
@ -171,7 +205,7 @@ public class SectionTable
// Line using Ryan's FCtoCP() conversion method - // Line using Ryan's FCtoCP() conversion method -
// unable to observe any effect on our testcases when using this code - piers // 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); plex.addProperty(property);

View File

@ -118,6 +118,9 @@ public class TextPiece extends PropertyNode implements Comparable
if(end > buf.length()) { if(end > buf.length()) {
throw new StringIndexOutOfBoundsException("Index " + end + " out of range 0 -> " + 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); return buf.substring(start, end);
} }

View File

@ -25,6 +25,9 @@ import org.apache.poi.poifs.common.POIFSConstants;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List; import java.util.List;
/** /**
@ -62,8 +65,17 @@ public class TextPieceTable
pieces[x] = new PieceDescriptor(node.getBytes(), 0); 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. // using the PieceDescriptors, build our list of TextPieces.
for (int x = 0; x < pieces.length; x++) for (int x = 0; x < pieces.length; x++)
@ -93,6 +105,15 @@ public class TextPieceTable
// And now build the piece // And now build the piece
_textPieces.add(new TextPiece(nodeStartChars, nodeEndChars, buf, pieces[x], node.getStart())); _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() public int getCpMin()
@ -104,6 +125,62 @@ public class TextPieceTable
{ {
return _textPieces; 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) public byte[] writeTo(HWPFOutputStream docStream)
throws IOException throws IOException

View File

@ -70,10 +70,10 @@ public abstract class FIBAbstractType
private static BitField fFutureSavedUndo = BitFieldFactory.getInstance(0x0008); private static BitField fFutureSavedUndo = BitFieldFactory.getInstance(0x0008);
private static BitField fWord97Saved = BitFieldFactory.getInstance(0x0010); private static BitField fWord97Saved = BitFieldFactory.getInstance(0x0010);
private static BitField fSpare0 = BitFieldFactory.getInstance(0x00FE); private static BitField fSpare0 = BitFieldFactory.getInstance(0x00FE);
protected int field_11_chs; protected int field_11_chs; /** Latest docs say this is Reserved3! */
protected int field_12_chsTables; protected int field_12_chsTables; /** Latest docs say this is Reserved4! */
protected int field_13_fcMin; protected int field_13_fcMin; /** Latest docs say this is Reserved5! */
protected int field_14_fcMac; protected int field_14_fcMac; /** Latest docs say this is Reserved6! */
public FIBAbstractType() public FIBAbstractType()

View File

@ -155,6 +155,8 @@ public class Range
_characters = _doc.getCharacterTable().getTextRuns(); _characters = _doc.getCharacterTable().getTextRuns();
_text = _doc.getTextTable().getTextPieces(); _text = _doc.getTextTable().getTextPieces();
_parent = new WeakReference(null); _parent = new WeakReference(null);
sanityCheckStartEnd();
} }
@ -175,6 +177,8 @@ public class Range
_characters = parent._characters; _characters = parent._characters;
_text = parent._text; _text = parent._text;
_parent = new WeakReference(parent); _parent = new WeakReference(parent);
sanityCheckStartEnd();
} }
/** /**
@ -226,6 +230,22 @@ public class Range
_textRangeFound = true; _textRangeFound = true;
break; 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++) for (int x = _parStart; x < numParagraphs; x++)
{ {
PAPX papx = (PAPX)_paragraphs.get(x); PAPX papx = (PAPX)_paragraphs.get(x);
//System.err.println("Paragraph " + x + " was " + papx.getStart() + " -> " + papx.getEnd());
papx.adjustForDelete(_start, _end - _start); papx.adjustForDelete(_start, _end - _start);
//System.err.println("Paragraph " + x + " is now " + papx.getStart() + " -> " + papx.getEnd());
} }
for (int x = _sectionStart; x < numSections; x++) for (int x = _sectionStart; x < numSections; x++)
{ {
SEPX sepx = (SEPX)_sections.get(x); SEPX sepx = (SEPX)_sections.get(x);
//System.err.println("Section " + x + " was " + sepx.getStart() + " -> " + sepx.getEnd());
sepx.adjustForDelete(_start, _end - _start); sepx.adjustForDelete(_start, _end - _start);
//System.err.println("Section " + x + " is now " + sepx.getStart() + " -> " + sepx.getEnd());
} }
for (int x = _textStart; x < numTextPieces; x++) for (int x = _textStart; x < numTextPieces; x++)
@ -650,14 +674,6 @@ public class Range
absPlaceHolderIndex, absPlaceHolderIndex,
(absPlaceHolderIndex + pPlaceHolder.length()), getDocument() (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 // this Range isn't a proper parent of the subRange() so we'll have to keep
// track of an updated endOffset on our own // track of an updated endOffset on our own
@ -674,12 +690,6 @@ public class Range
(absPlaceHolderIndex + pPlaceHolder.length() + pValue.length()), (absPlaceHolderIndex + pPlaceHolder.length() + pValue.length()),
getDocument() getDocument()
); );
if (subRange.usesUnicode())
subRange = new Range(
(absPlaceHolderIndex + (pValue.length() * 2)),
(absPlaceHolderIndex + (pPlaceHolder.length() * 2) +
(pValue.length() * 2)), getDocument()
);
// deletes are automagically propagated // deletes are automagically propagated
subRange.delete(); subRange.delete();
@ -820,6 +830,10 @@ public class Range
{ {
throw new ArrayIndexOutOfBoundsException("The table's bounds fall outside of this 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()); return new Table(r._parStart, tableEnd, r._doc.getRange(), paragraph.getTableLevel());
} }

View File

@ -32,6 +32,8 @@ public class TestCHPBinTable
{ {
private CHPBinTable _cHPBinTable = null; private CHPBinTable _cHPBinTable = null;
private HWPFDocFixture _hWPFDocFixture; private HWPFDocFixture _hWPFDocFixture;
private TextPieceTable fakeTPT = new TextPieceTable();
public TestCHPBinTable(String name) public TestCHPBinTable(String name)
{ {
@ -46,7 +48,7 @@ public class TestCHPBinTable
byte[] tableStream = _hWPFDocFixture._tableStream; byte[] tableStream = _hWPFDocFixture._tableStream;
int fcMin = fib.getFcMin(); 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(); HWPFFileSystem fileSys = new HWPFFileSystem();
@ -57,7 +59,7 @@ public class TestCHPBinTable
byte[] newTableStream = tableOut.toByteArray(); byte[] newTableStream = tableOut.toByteArray();
byte[] newMainStream = mainOut.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 oldTextRuns = _cHPBinTable._textRuns;
ArrayList newTextRuns = newBinTable._textRuns; ArrayList newTextRuns = newBinTable._textRuns;

View File

@ -32,6 +32,8 @@ public class TestPAPBinTable
private PAPBinTable _pAPBinTable = null; private PAPBinTable _pAPBinTable = null;
private HWPFDocFixture _hWPFDocFixture; private HWPFDocFixture _hWPFDocFixture;
private TextPieceTable fakeTPT = new TextPieceTable();
public TestPAPBinTable(String name) public TestPAPBinTable(String name)
{ {
super(name); super(name);
@ -45,7 +47,7 @@ public class TestPAPBinTable
byte[] tableStream = _hWPFDocFixture._tableStream; byte[] tableStream = _hWPFDocFixture._tableStream;
int fcMin = fib.getFcMin(); 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(); HWPFFileSystem fileSys = new HWPFFileSystem();
@ -56,7 +58,7 @@ public class TestPAPBinTable
byte[] newTableStream = tableOut.toByteArray(); byte[] newTableStream = tableOut.toByteArray();
byte[] newMainStream = mainOut.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 oldTextRuns = _pAPBinTable.getParagraphs();
ArrayList newTextRuns = newBinTable.getParagraphs(); ArrayList newTextRuns = newBinTable.getParagraphs();

View File

@ -45,13 +45,15 @@ public class TestSectionTable
byte[] tableStream = _hWPFDocFixture._tableStream; byte[] tableStream = _hWPFDocFixture._tableStream;
int fcMin = fib.getFcMin(); int fcMin = fib.getFcMin();
CPSplitCalculator cps = new CPSplitCalculator(fib);
ComplexFileTable cft = new ComplexFileTable(mainStream, tableStream, fib.getFcClx(), fcMin); ComplexFileTable cft = new ComplexFileTable(mainStream, tableStream, fib.getFcClx(), fcMin);
TextPieceTable tpt = cft.getTextPieceTable(); TextPieceTable tpt = cft.getTextPieceTable();
SectionTable sectionTable = new SectionTable(mainStream, tableStream, SectionTable sectionTable = new SectionTable(mainStream, tableStream,
fib.getFcPlcfsed(), fib.getFcPlcfsed(),
fib.getLcbPlcfsed(), fib.getLcbPlcfsed(),
fcMin, tpt.getTextPieces()); fcMin, tpt, cps);
HWPFFileSystem fileSys = new HWPFFileSystem(); HWPFFileSystem fileSys = new HWPFFileSystem();
sectionTable.writeTo(fileSys, 0); sectionTable.writeTo(fileSys, 0);
@ -61,7 +63,9 @@ public class TestSectionTable
byte[] newTableStream = tableOut.toByteArray(); byte[] newTableStream = tableOut.toByteArray();
byte[] newMainStream = mainOut.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 oldSections = sectionTable.getSections();
ArrayList newSections = newSectionTable.getSections(); ArrayList newSections = newSectionTable.getSections();

View File

@ -81,9 +81,16 @@ public class TestProblems extends TestCase {
HWPFDocument doc = new HWPFDocument(new FileInputStream( HWPFDocument doc = new HWPFDocument(new FileInputStream(
new File(dirname, "Bug44292.doc"))); new File(dirname, "Bug44292.doc")));
Range r = doc.getRange(); 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); Paragraph p = r.getParagraph(0);
assertEquals(0, p.getStartOffset());
assertEquals(20, p.getEndOffset());
// Get the table
Table t = r.getTable(p); Table t = r.getTable(p);
//get the only row //get the only row

View File

@ -18,27 +18,28 @@
package org.apache.poi.hwpf.usermodel; package org.apache.poi.hwpf.usermodel;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; 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 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 * Test to see if Range.delete() works even if the Range contains a
* CharacterRun that uses Unicode characters. * 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" // 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 = 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"; "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 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 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 = private String expectedText2 =
@ -68,32 +69,63 @@ public abstract class TestRangeDelete extends TestCase {
public void testDocStructure() throws Exception { public void testDocStructure() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile)); 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()); assertEquals(1, range.numSections());
Section section = range.getSection(0); assertEquals(5, range.numParagraphs());
assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2);
assertEquals(5, para.numCharacterRuns());
assertEquals(originalText, para.text());
// Now check on just the main text // Now, onto just the doc bit
range = daDoc.getRange(); range = daDoc.getRange();
assertEquals(1, range.numSections()); assertEquals(1, range.numSections());
assertEquals(1, daDoc.getSectionTable().getSections().size());
section = range.getSection(0); section = range.getSection(0);
assertEquals(5, section.numParagraphs()); assertEquals(5, section.numParagraphs());
para = section.getParagraph(2);
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(5, para.numCharacterRuns());
assertEquals(originalText, para.text()); 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); assertEquals(192, offset);
int absOffset = para.getStartOffset() + offset; int absOffset = para.getStartOffset() + offset;
if (para.usesUnicode())
absOffset = para.getStartOffset() + (offset * 2);
Range subRange = new Range(absOffset, (absOffset + searchText.length()), para.getDocument()); 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()); assertEquals(searchText, subRange.text());
@ -167,26 +194,23 @@ public abstract class TestRangeDelete extends TestCase {
boolean keepLooking = true; boolean keepLooking = true;
while (keepLooking) { while (keepLooking) {
// Reload the range every time
range = daDoc.getRange();
int offset = range.text().indexOf(searchText); int offset = range.text().indexOf(searchText);
if (offset >= 0) { if (offset >= 0) {
int absOffset = range.getStartOffset() + offset; int absOffset = range.getStartOffset() + offset;
if (range.usesUnicode())
absOffset = range.getStartOffset() + (offset * 2);
Range subRange = new Range( Range subRange = new Range(
absOffset, (absOffset + searchText.length()), range.getDocument()); absOffset, (absOffset + searchText.length()), range.getDocument());
if (subRange.usesUnicode())
subRange = new Range(
absOffset, (absOffset + (searchText.length() * 2)), range.getDocument());
assertEquals(searchText, subRange.text()); assertEquals(searchText, subRange.text());
subRange.delete(); subRange.delete();
} else } else {
keepLooking = false; keepLooking = false;
}
} }
// we need to let the model re-calculate the Range before we use it // 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()); assertEquals(5, section.numParagraphs());
para = section.getParagraph(0);
text = para.text();
assertEquals(introText, text);
para = section.getParagraph(1); para = section.getParagraph(1);
text = para.text(); text = para.text();
assertEquals(expectedText1, text); assertEquals(expectedText1, text);

View File

@ -18,29 +18,25 @@
package org.apache.poi.hwpf.usermodel; package org.apache.poi.hwpf.usermodel;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; 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 junit.framework.TestCase;
import org.apache.poi.hwpf.HWPFDocument;
/** /**
* Test to see if Range.insertBefore() works even if the Range contains a * Test to see if Range.insertBefore() works even if the Range contains a
* CharacterRun that uses Unicode characters. * CharacterRun that uses Unicode characters.
* *
* TODO - re-enable me when unicode paragraph stuff is fixed! * 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" // u201c and u201d are "smart-quotes"
private String originalText = 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"; "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 String textToInsert = "Look at me! I'm cool! ";
private int insertionPoint = 244; private int insertionPoint = 122;
private String illustrativeDocFile; private String illustrativeDocFile;
@ -73,12 +69,18 @@ public abstract class TestRangeInsertion extends TestCase {
assertEquals(3, section.numParagraphs()); assertEquals(3, section.numParagraphs());
Paragraph para = section.getParagraph(2); Paragraph para = section.getParagraph(2);
assertEquals(originalText, para.text());
assertEquals(3, para.numCharacterRuns()); assertEquals(3, para.numCharacterRuns());
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + String text =
para.getCharacterRun(2).text(); para.getCharacterRun(0).text() +
para.getCharacterRun(1).text() +
para.getCharacterRun(2).text()
;
assertEquals(originalText, text); assertEquals(originalText, text);
assertEquals(insertionPoint, para.getStartOffset());
} }
/** /**
@ -109,10 +111,14 @@ public abstract class TestRangeInsertion extends TestCase {
assertEquals(3, section.numParagraphs()); assertEquals(3, section.numParagraphs());
Paragraph para = section.getParagraph(2); Paragraph para = section.getParagraph(2);
assertEquals((textToInsert + originalText), para.text());
assertEquals(3, para.numCharacterRuns()); assertEquals(3, para.numCharacterRuns());
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + String text =
para.getCharacterRun(2).text(); para.getCharacterRun(0).text() +
para.getCharacterRun(1).text() +
para.getCharacterRun(2).text()
;
// System.out.println(text); // System.out.println(text);

View File

@ -18,8 +18,10 @@ package org.apache.poi.hwpf.usermodel;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.List;
import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.PropertyNode;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -30,7 +32,7 @@ import junit.framework.TestCase;
* *
* TODO - re-enable me when unicode paragraph stuff is fixed! * 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 char page_break = (char)12;
private static final String u_page_1 = private static final String u_page_1 =
@ -85,6 +87,16 @@ public abstract class TestRangeProperties extends TestCase {
r.text() 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( assertEquals(
7, 7,
r.numParagraphs() r.numParagraphs()
@ -142,8 +154,102 @@ public abstract class TestRangeProperties extends TestCase {
assertEquals(22, c1.getFontSize()); assertEquals(22, c1.getFontSize());
assertEquals(32, c7.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 { public void testUnicodeTextParagraphs() throws Exception {
Range r = u.getRange(); Range r = u.getRange();
assertEquals( assertEquals(
@ -154,16 +260,112 @@ public abstract class TestRangeProperties extends TestCase {
); );
assertEquals( assertEquals(
5, 12,
r.numParagraphs() r.numParagraphs()
); );
String[] p1_parts = u_page_1.split("\r"); String[] p1_parts = u_page_1.split("\r");
String[] p2_parts = u_page_2.split("\r"); String[] p2_parts = u_page_2.split("\r");
System.out.println(r.getParagraph(2).text()); // Check text all matches up properly
// TODO 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 { 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()
);
} }
} }

View File

@ -18,23 +18,19 @@
package org.apache.poi.hwpf.usermodel; package org.apache.poi.hwpf.usermodel;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; 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 junit.framework.TestCase;
import org.apache.poi.hwpf.HWPFDocument;
/** /**
* Test to see if Range.replaceText() works even if the Range contains a * Test to see if Range.replaceText() works even if the Range contains a
* CharacterRun that uses Unicode characters. * CharacterRun that uses Unicode characters.
* *
* TODO - re-enable me when unicode paragraph stuff is fixed! * 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" // u201c and u201d are "smart-quotes"
private String originalText = private String originalText =
@ -70,16 +66,23 @@ public abstract class TestRangeReplacement extends TestCase {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile)); HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
Range range = daDoc.getRange(); Range range = daDoc.getRange();
assertEquals(414, range.text().length());
assertEquals(1, range.numSections()); assertEquals(1, range.numSections());
Section section = range.getSection(0); Section section = range.getSection(0);
assertEquals(414, section.text().length());
assertEquals(5, section.numParagraphs()); assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2); Paragraph para = section.getParagraph(2);
assertEquals(5, para.numCharacterRuns()); assertEquals(5, para.numCharacterRuns());
String text = para.getCharacterRun(0).text() + para.getCharacterRun(1).text() + String text =
para.getCharacterRun(2).text() + para.getCharacterRun(3).text() + para.getCharacterRun(4).text(); para.getCharacterRun(0).text() +
para.getCharacterRun(1).text() +
para.getCharacterRun(2).text() +
para.getCharacterRun(3).text() +
para.getCharacterRun(4).text()
;
assertEquals(originalText, text); assertEquals(originalText, text);
} }

Binary file not shown.

View File

@ -20,6 +20,7 @@ package org.apache.poi.hssf.model;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.FormulaParser.FormulaParseException; import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg; import org.apache.poi.hssf.record.formula.AddPtg;
@ -123,12 +124,15 @@ public final class TestFormulaParser extends TestCase {
} }
public void testMacroFunction() { public void testMacroFunction() {
HSSFWorkbook w = new HSSFWorkbook(); // testNames.xls contains a VB function called 'myFunc'
Ptg[] ptg = FormulaParser.parse("FOO()", w); 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 // the name gets encoded as the first arg
NamePtg tname = (NamePtg) ptg[0]; NamePtg tname = (NamePtg) ptg[0];
assertEquals("FOO", tname.toFormulaString(w)); assertEquals("myFunc", tname.toFormulaString(w));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1]; AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertTrue(tfunc.isExternalFunction()); assertTrue(tfunc.isExternalFunction());

View File

@ -17,11 +17,19 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; 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.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
/** /**
* Tests for functions from external workbooks (e.g. YEARFRAC). * 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 { public final class TestExternalFunctionFormulas extends TestCase {
/**
/** * tests <tt>NameXPtg.toFormulaString(Workbook)</tt> and logic in Workbook below that
* tests <tt>NameXPtg.toFormulaString(Workbook)</tt> and logic in Workbook below that */
*/ public void testReadFormulaContainingExternalFunction() {
public void testReadFormulaContainingExternalFunction() { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
String expectedFormula = "YEARFRAC(B1,C1)";
String expectedFormula = "YEARFRAC(B1,C1)"; HSSFSheet sht = wb.getSheetAt(0);
HSSFSheet sht = wb.getSheetAt(0); String cellFormula = sht.getRow(0).getCell(0).getCellFormula();
String cellFormula = sht.getRow(0).getCell((short)0).getCellFormula(); assertEquals(expectedFormula, cellFormula);
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();
}
} }

View File

@ -17,8 +17,11 @@
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFName; 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. * Checks that an external function can get invoked from the formula evaluator.
* @throws IOException
*/ */
public void testInvoke() { public void testInvoke() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(); HSSFWorkbook wb;
wb.setSheetName(0, "Sheet1"); HSSFSheet sheet;
HSSFRow row = sheet.createRow(0); HSSFCell cell;
HSSFCell cell = row.createCell(0); if (false) {
// TODO - this code won't work until we can create user-defined functions directly with POI
HSSFName hssfName = wb.createName(); wb = new HSSFWorkbook();
hssfName.setNameName("myFunc"); sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");
cell.setCellFormula("myFunc()"); HSSFName hssfName = wb.createName();
String actualFormula=cell.getCellFormula(); hssfName.setNameName("myFunc");
assertEquals("myFunc()", actualFormula);
} 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); HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
CellValue evalResult = fe.evaluate(cell); CellValue evalResult = fe.evaluate(cell);

View File

@ -417,6 +417,9 @@ public final class TestBugs extends TestCase {
for(int i = 0 ; i < wb.getNumberOfNames(); i++){ for(int i = 0 ; i < wb.getNumberOfNames(); i++){
HSSFName name = wb.getNameAt(i); HSSFName name = wb.getNameAt(i);
name.getNameName(); name.getNameName();
if (name.isFunctionName()) {
continue;
}
name.getReference(); name.getReference();
} }
} }
@ -1004,7 +1007,6 @@ public final class TestBugs extends TestCase {
* Test that we can delete sheets without * Test that we can delete sheets without
* breaking the build in named ranges * breaking the build in named ranges
* used for printing stuff. * used for printing stuff.
* Currently broken, as we change the Ptg
*/ */
public void test30978() { public void test30978() {
HSSFWorkbook wb = openSample("30978-alt.xls"); HSSFWorkbook wb = openSample("30978-alt.xls");
@ -1016,7 +1018,7 @@ public final class TestBugs extends TestCase {
Workbook w = wb.getWorkbook(); Workbook w = wb.getWorkbook();
for(int i=0; i<w.getNumNames(); i++) { for(int i=0; i<w.getNumNames(); i++) {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); List nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.size());
@ -1034,7 +1036,7 @@ public final class TestBugs extends TestCase {
for(int i=0; i<w.getNumNames(); i++) { for(int i=0; i<w.getNumNames(); i++) {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); List nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.size());
@ -1051,7 +1053,7 @@ public final class TestBugs extends TestCase {
for(int i=0; i<w.getNumNames(); i++) { for(int i=0; i<w.getNumNames(); i++) {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getIndexToSheet() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); List nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.size());

View File

@ -50,7 +50,7 @@ public final class TestHSSFWorkbook extends TestCase {
b.createSheet(); b.createSheet();
b.setRepeatingRowsAndColumns( 2, 0,1,-1,-1 ); b.setRepeatingRowsAndColumns( 2, 0,1,-1,-1 );
NameRecord nameRecord = b.getWorkbook().getNameRecord( 0 ); NameRecord nameRecord = b.getWorkbook().getNameRecord( 0 );
assertEquals( 3, nameRecord.getIndexToSheet() ); assertEquals(3, nameRecord.getSheetNumber());
} }
public void testCaseInsensitiveNames() { public void testCaseInsensitiveNames() {
@ -411,84 +411,84 @@ public final class TestHSSFWorkbook extends TestCase {
* that point to deleted sheets * that point to deleted sheets
*/ */
public void testNamesToDeleteSheets() throws Exception { public void testNamesToDeleteSheets() throws Exception {
HSSFWorkbook b = openSample("30978-deleted.xls"); HSSFWorkbook b = openSample("30978-deleted.xls");
assertEquals(3, b.getNumberOfNames()); assertEquals(3, b.getNumberOfNames());
// Sheet 2 is deleted // Sheet 2 is deleted
assertEquals("Sheet1", b.getSheetName(0)); assertEquals("Sheet1", b.getSheetName(0));
assertEquals("Sheet3", b.getSheetName(1)); assertEquals("Sheet3", b.getSheetName(1));
Area3DPtg ptg; Area3DPtg ptg;
NameRecord nr; NameRecord nr;
HSSFName n; HSSFName n;
/* ======= Name pointing to deleted sheet ====== */ /* ======= Name pointing to deleted sheet ====== */
// First at low level // First at low level
nr = b.getWorkbook().getNameRecord(0); nr = b.getWorkbook().getNameRecord(0);
assertEquals("On2", nr.getNameText()); assertEquals("On2", nr.getNameText());
assertEquals(0, nr.getIndexToSheet()); assertEquals(0, nr.getSheetNumber());
assertEquals(1, nr.getExternSheetNumber()); assertEquals(1, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size()); assertEquals(1, nr.getNameDefinition().size());
ptg = (Area3DPtg)nr.getNameDefinition().get(0); ptg = (Area3DPtg)nr.getNameDefinition().get(0);
assertEquals(1, ptg.getExternSheetIndex()); assertEquals(1, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow()); assertEquals(0, ptg.getFirstRow());
assertEquals(0, ptg.getLastColumn()); assertEquals(0, ptg.getLastColumn());
assertEquals(2, ptg.getLastRow()); assertEquals(2, ptg.getLastRow());
// Now at high level // Now at high level
n = b.getNameAt(0); n = b.getNameAt(0);
assertEquals("On2", n.getNameName()); assertEquals("On2", n.getNameName());
assertEquals("", n.getSheetName()); assertEquals("", n.getSheetName());
assertEquals("#REF!$A$1:$A$3", n.getReference()); assertEquals("#REF!$A$1:$A$3", n.getReference());
/* ======= Name pointing to 1st sheet ====== */ /* ======= Name pointing to 1st sheet ====== */
// First at low level // First at low level
nr = b.getWorkbook().getNameRecord(1); nr = b.getWorkbook().getNameRecord(1);
assertEquals("OnOne", nr.getNameText()); assertEquals("OnOne", nr.getNameText());
assertEquals(0, nr.getIndexToSheet()); assertEquals(0, nr.getSheetNumber());
assertEquals(0, nr.getExternSheetNumber()); assertEquals(0, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size()); assertEquals(1, nr.getNameDefinition().size());
ptg = (Area3DPtg)nr.getNameDefinition().get(0); ptg = (Area3DPtg)nr.getNameDefinition().get(0);
assertEquals(0, ptg.getExternSheetIndex()); assertEquals(0, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(2, ptg.getFirstRow()); assertEquals(2, ptg.getFirstRow());
assertEquals(0, ptg.getLastColumn()); assertEquals(0, ptg.getLastColumn());
assertEquals(3, ptg.getLastRow()); assertEquals(3, ptg.getLastRow());
// Now at high level // Now at high level
n = b.getNameAt(1); n = b.getNameAt(1);
assertEquals("OnOne", n.getNameName()); assertEquals("OnOne", n.getNameName());
assertEquals("Sheet1", n.getSheetName()); assertEquals("Sheet1", n.getSheetName());
assertEquals("Sheet1!$A$3:$A$4", n.getReference()); assertEquals("Sheet1!$A$3:$A$4", n.getReference());
/* ======= Name pointing to 3rd sheet ====== */ /* ======= Name pointing to 3rd sheet ====== */
// First at low level // First at low level
nr = b.getWorkbook().getNameRecord(2); nr = b.getWorkbook().getNameRecord(2);
assertEquals("OnSheet3", nr.getNameText()); assertEquals("OnSheet3", nr.getNameText());
assertEquals(0, nr.getIndexToSheet()); assertEquals(0, nr.getSheetNumber());
assertEquals(2, nr.getExternSheetNumber()); assertEquals(2, nr.getExternSheetNumber());
assertEquals(1, nr.getNameDefinition().size()); assertEquals(1, nr.getNameDefinition().size());
ptg = (Area3DPtg)nr.getNameDefinition().get(0); ptg = (Area3DPtg)nr.getNameDefinition().get(0);
assertEquals(2, ptg.getExternSheetIndex()); assertEquals(2, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow()); assertEquals(0, ptg.getFirstRow());
assertEquals(0, ptg.getLastColumn()); assertEquals(0, ptg.getLastColumn());
assertEquals(1, ptg.getLastRow()); assertEquals(1, ptg.getLastRow());
// Now at high level // Now at high level
n = b.getNameAt(2); n = b.getNameAt(2);
assertEquals("OnSheet3", n.getNameName()); assertEquals("OnSheet3", n.getNameName());
assertEquals("Sheet3", n.getSheetName()); assertEquals("Sheet3", n.getSheetName());
assertEquals("Sheet3!$A$1:$A$2", n.getReference()); 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 * The sample file provided with bug 45582 seems to have one extra byte after the EOFRecord
*/ */
public void testExtraDataAfterEOFRecord() { public void testExtraDataAfterEOFRecord() {
try { try {
HSSFTestDataSamples.openSampleWorkbook("ex45582-22397.xls"); HSSFTestDataSamples.openSampleWorkbook("ex45582-22397.xls");
} catch (RecordFormatException e) { } catch (RecordFormatException e) {
if (e.getCause() instanceof LittleEndian.BufferUnderrunException) { if (e.getCause() instanceof LittleEndian.BufferUnderrunException) {
throw new AssertionFailedError("Identified bug 45582"); throw new AssertionFailedError("Identified bug 45582");
} }
} }
} }
} }

View File

@ -17,17 +17,15 @@
package org.apache.poi.hssf.usermodel; package org.apache.poi.hssf.usermodel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference; 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); return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
} }
public static void main(String[] args) {
junit.textui.TestRunner.run(TestNamedRange.class);
}
/** Test of TestCase method, of class test.RangeTest. */ /** Test of TestCase method, of class test.RangeTest. */
public void testNamedRange() { public void testNamedRange() {
HSSFWorkbook wb = openSample("Simple.xls"); HSSFWorkbook wb = openSample("Simple.xls");
@ -411,7 +405,7 @@ public final class TestNamedRange extends TestCase {
String cellValue = "TEST Value"; String cellValue = "TEST Value";
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(sheetName); 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 // create named range for a single cell using areareference
HSSFName namedCell = wb.createName(); HSSFName namedCell = wb.createName();
@ -447,7 +441,7 @@ public final class TestNamedRange extends TestCase {
String sname = "TestSheet", cname = "TestName", cvalue = "TestVal"; String sname = "TestSheet", cname = "TestName", cvalue = "TestVal";
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(sname); 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 // create named range for a single cell using cellreference
HSSFName namedCell = wb.createName(); 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(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(); HSSFSheet sheet = wb.createSheet("FirstSheet");
for (int rowItem = 0; rowItem < 10; rowItem++) { // set repeating rows and columns twice for the first sheet
HSSFRow r = sheet.createRow(rowItem); for (int i = 0; i < 2; i++) {
for (int column = 0; column < 2; column++) { wb.setRepeatingRowsAndColumns(0, 0, 0, 0, 3-1);
HSSFCell cellItem = r.createCell((short) column); sheet.createFreezePane(0, 3);
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);
}
}
} }
assertEquals(1, wb.getNumberOfNames());
assertEquals(2, wb.getNumberOfNames());
HSSFName nr1 = wb.getNameAt(0); HSSFName nr1 = wb.getNameAt(0);
HSSFName nr2 = wb.getNameAt(1);
assertEquals("Print_Titles", nr1.getNameName()); assertEquals("Print_Titles", nr1.getNameName());
assertEquals("Sheet0!$A$1:$A$0,Sheet0!$A$1:$IV$3", nr1.getReference()); if (false) {
// TODO - full column references not rendering properly, absolute markers not present either
assertEquals("Excel_Name_Record_Titles_1_1", nr2.getNameName()); assertEquals("FirstSheet!$A:$A,FirstSheet!$1:$3", nr1.getReference());
assertEquals("Sheet0!$A$1:$A$0,Sheet0!$A$1:$IV$3", nr2.getReference()); } else {
assertEquals("FirstSheet!A:A,FirstSheet!$A$1:$IV$3", nr1.getReference());
}
// Save and re-open // Save and re-open
ByteArrayOutputStream baos = new ByteArrayOutputStream(); HSSFWorkbook nwb = HSSFTestDataSamples.writeOutAndReadBack(wb);
wb.write(baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); assertEquals(1, nwb.getNumberOfNames());
HSSFWorkbook nwb = new HSSFWorkbook(new POIFSFileSystem(bais)); nr1 = nwb.getNameAt(0);
assertEquals("Print_Titles", nr1.getNameName());
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()); assertEquals(2, nwb.getNumberOfNames());
nr1 = nwb.getNameAt(0); HSSFName nr2 = nwb.getNameAt(1);
nr2 = nwb.getNameAt(1);
// TODO - assertEquals("Print_Titles", nr2.getNameName());
// should these references really have been corrected? assertEquals("SecondSheet!B:C,SecondSheet!$A$1:$IV$1", nr2.getReference());
// and if so, why not also above?
assertEquals("Print_Titles", nr1.getNameName());
assertEquals("Sheet0!A:A,Sheet0!$A$1:$IV$3", nr1.getReference());
assertEquals("Excel_Name_Record_Titles_1_1", nr2.getNameName()); if (false) {
assertEquals("Sheet0!A:A,Sheet0!$A$1:$IV$3", nr2.getReference()); // In case you fancy checking in excel, to ensure it
// won't complain about the file now
// In case you fancy checking in excel, to ensure it try {
// won't complain about the file now File tempFile = File.createTempFile("POI-45126-", ".xls");
FileOutputStream fout = new FileOutputStream(File.createTempFile("POI-45126-", ".xls")); FileOutputStream fout = new FileOutputStream(tempFile);
wb.write(fout); nwb.write(fout);
fout.close(); fout.close();
System.out.println("check out " + tempFile.getAbsolutePath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} }
} }