#56737 Sometimes Excel writes an internal reference to a local name in an odd way, without an ExternalNameRecord, try to detect and work around those

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1611681 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2014-07-18 15:59:55 +00:00
parent c9d90732e5
commit d4a1239efa
5 changed files with 116 additions and 19 deletions

View File

@ -26,8 +26,61 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.apache.poi.ddf.*; import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.hssf.record.*; import org.apache.poi.ddf.EscherBoolProperty;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherDgRecord;
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.EscherSimpleProperty;
import org.apache.poi.ddf.EscherSpRecord;
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.EscherAggregate;
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.NameCommentRecord;
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.UseSelFSRecord;
import org.apache.poi.hssf.record.WindowOneRecord;
import org.apache.poi.hssf.record.WindowProtectRecord;
import org.apache.poi.hssf.record.WriteAccessRecord;
import org.apache.poi.hssf.record.WriteProtectRecord;
import org.apache.poi.hssf.record.common.UnicodeString; import org.apache.poi.hssf.record.common.UnicodeString;
import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
@ -1776,7 +1829,7 @@ public final class InternalWorkbook {
return new ExternalSheet(extNames[0], extNames[1]); return new ExternalSheet(extNames[0], extNames[1]);
} }
public ExternalName getExternalName(int externSheetIndex, int externNameIndex) { public ExternalName getExternalName(int externSheetIndex, int externNameIndex) {
String nameName = linkTable.resolveNameXText(externSheetIndex, externNameIndex); String nameName = linkTable.resolveNameXText(externSheetIndex, externNameIndex, this);
if(nameName == null) { if(nameName == null) {
return null; return null;
} }
@ -2299,7 +2352,7 @@ public final class InternalWorkbook {
* @return the string representation of the defined or external name * @return the string representation of the defined or external name
*/ */
public String resolveNameXText(int refIndex, int definedNameIndex) { public String resolveNameXText(int refIndex, int definedNameIndex) {
return linkTable.resolveNameXText(refIndex, definedNameIndex); return linkTable.resolveNameXText(refIndex, definedNameIndex, this);
} }
/** /**

View File

@ -31,6 +31,7 @@ import org.apache.poi.hssf.record.NameCommentRecord;
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.ss.formula.SheetNameFormatter;
import org.apache.poi.ss.formula.ptg.Area3DPtg; import org.apache.poi.ss.formula.ptg.Area3DPtg;
import org.apache.poi.ss.formula.ptg.ErrPtg; import org.apache.poi.ss.formula.ptg.ErrPtg;
import org.apache.poi.ss.formula.ptg.NameXPtg; import org.apache.poi.ss.formula.ptg.NameXPtg;
@ -206,7 +207,7 @@ final class LinkTable {
// collect zero or more DEFINEDNAMEs id=0x18, // collect zero or more DEFINEDNAMEs id=0x18,
// with their comments if present // with their comments if present
while(true) { while(true) {
Class nextClass = rs.peekNextClass(); Class<? extends Record> nextClass = rs.peekNextClass();
if (nextClass == NameRecord.class) { if (nextClass == NameRecord.class) {
NameRecord nr = (NameRecord)rs.getNext(); NameRecord nr = (NameRecord)rs.getNext();
_definedNames.add(nr); _definedNames.add(nr);
@ -280,10 +281,9 @@ final class LinkTable {
* @param sheetNumber 1-based sheet number * @param sheetNumber 1-based sheet number
*/ */
public NameRecord getSpecificBuiltinRecord(byte builtInCode, int sheetNumber) { public NameRecord getSpecificBuiltinRecord(byte builtInCode, int sheetNumber) {
Iterator<NameRecord> iterator = _definedNames.iterator();
Iterator iterator = _definedNames.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
NameRecord record = ( NameRecord ) iterator.next(); NameRecord record = iterator.next();
//print areas are one based //print areas are one based
if (record.getBuiltInName() == builtInCode && record.getSheetNumber() == sheetNumber) { if (record.getBuiltInName() == builtInCode && record.getSheetNumber() == sheetNumber) {
@ -471,10 +471,37 @@ final class LinkTable {
} }
return -1; return -1;
} }
public String resolveNameXText(int refIndex, int definedNameIndex) { public String resolveNameXText(int refIndex, int definedNameIndex, InternalWorkbook workbook) {
int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex); int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex);
return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex); int firstTabIndex = _externSheetRecord.getFirstSheetIndexFromRefIndex(refIndex);
if (firstTabIndex == -1) {
// The referenced sheet could not be found
throw new RuntimeException("Referenced sheet could not be found");
}
// Does it exist via the external book block?
if (_externalBookBlocks.length > extBookIndex) {
return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex);
} else if (firstTabIndex == -2) {
// Workbook scoped name, not actually external after all
NameRecord nr = getNameRecord(definedNameIndex);
int sheetNumber = nr.getSheetNumber();
StringBuffer text = new StringBuffer();
if (sheetNumber > 0) {
String sheetName = workbook.getSheetName(sheetNumber-1);
SheetNameFormatter.appendFormat(text, sheetName);
text.append("!");
}
text.append(nr.getNameText());
return text.toString();
} else {
throw new ArrayIndexOutOfBoundsException(
"Ext Book Index relative but beyond the supported length, was " +
extBookIndex + " but maximum is " + _externalBookBlocks.length
);
}
} }
public int resolveNameXIx(int refIndex, int definedNameIndex) { public int resolveNameXIx(int refIndex, int definedNameIndex) {
int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex); int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex);

View File

@ -200,9 +200,13 @@ public class ExternSheetRecord extends StandardRecord {
return sid; return sid;
} }
public int getExtbookIndexFromRefIndex(int refIndex) { /**
return getRef(refIndex).getExtBookIndex(); * Returns the index of the SupBookRecord for this index
} */
public int getExtbookIndexFromRefIndex(int refIndex) {
RefSubRecord refRec = getRef(refIndex);
return refRec.getExtBookIndex();
}
/** /**
* @return -1 if not found * @return -1 if not found
@ -217,6 +221,11 @@ public class ExternSheetRecord extends StandardRecord {
return -1; return -1;
} }
/**
* Returns the first sheet that the reference applies to, or
* -1 if the referenced sheet can't be found, or -2 if the
* reference is workbook scoped.
*/
public int getFirstSheetIndexFromRefIndex(int extRefIndex) { public int getFirstSheetIndexFromRefIndex(int extRefIndex) {
return getRef(extRefIndex).getFirstSheetIndex(); return getRef(extRefIndex).getFirstSheetIndex();
} }

View File

@ -27,7 +27,16 @@ import junit.framework.AssertionFailedError;
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.record.*; import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.CountryRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.ExternSheetRecord;
import org.apache.poi.hssf.record.ExternalNameRecord;
import org.apache.poi.hssf.record.NameCommentRecord;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.SupBookRecord;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.ptg.NameXPtg; import org.apache.poi.ss.formula.ptg.NameXPtg;
@ -230,7 +239,7 @@ public final class TestLinkTable extends TestCase {
//check that //check that
assertEquals(0, tbl.resolveNameXIx(namex1.getSheetRefIndex(), namex1.getNameIndex())); assertEquals(0, tbl.resolveNameXIx(namex1.getSheetRefIndex(), namex1.getNameIndex()));
assertEquals("ISODD", tbl.resolveNameXText(namex1.getSheetRefIndex(), namex1.getNameIndex())); assertEquals("ISODD", tbl.resolveNameXText(namex1.getSheetRefIndex(), namex1.getNameIndex(), null));
assertNull(tbl.getNameXPtg("ISEVEN")); assertNull(tbl.getNameXPtg("ISEVEN"));
NameXPtg namex2 = tbl.addNameXPtg("ISEVEN"); // adds two new rercords NameXPtg namex2 = tbl.addNameXPtg("ISEVEN"); // adds two new rercords
@ -256,7 +265,7 @@ public final class TestLinkTable extends TestCase {
assertTrue(wrl.get(7) instanceof EOFRecord); assertTrue(wrl.get(7) instanceof EOFRecord);
assertEquals(0, tbl.resolveNameXIx(namex2.getSheetRefIndex(), namex2.getNameIndex())); assertEquals(0, tbl.resolveNameXIx(namex2.getSheetRefIndex(), namex2.getNameIndex()));
assertEquals("ISEVEN", tbl.resolveNameXText(namex2.getSheetRefIndex(), namex2.getNameIndex())); assertEquals("ISEVEN", tbl.resolveNameXText(namex2.getSheetRefIndex(), namex2.getNameIndex(), null));
} }
} }

View File

@ -2619,7 +2619,6 @@ public final class TestBugs extends BaseTestBugzillaIssues {
* Currently failing with * Currently failing with
* java.lang.RuntimeException: Unexpected eval class (org.apache.poi.ss.formula.eval.NameXEval) * java.lang.RuntimeException: Unexpected eval class (org.apache.poi.ss.formula.eval.NameXEval)
*/ */
@Ignore
@Test @Test
public void bug56737() throws IOException { public void bug56737() throws IOException {
Workbook wb = openSample("56737.xls"); Workbook wb = openSample("56737.xls");