bug 56781,60246: fix named range validation to match valid name rules per Excel docs

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1764854 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2016-10-14 10:11:23 +00:00
parent 999c6499ec
commit 1e5dc11721
3 changed files with 123 additions and 29 deletions

View File

@ -21,9 +21,11 @@ import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.model.InternalWorkbook; import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.record.NameCommentRecord; 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.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.util.CellReference;
/** /**
* High Level Representation of a 'defined name' which could be a 'built-in' name, * High Level Representation of a 'defined name' which could be a 'built-in' name,
@ -155,36 +157,70 @@ public final class HSSFName implements Name {
} }
} }
private static void validateName(String name) { /**
/* equivalent to: * https://support.office.com/en-us/article/Define-and-use-names-in-formulas-4D0F13AC-53B7-422E-AFD2-ABD7FF379C64#bmsyntax_rules_for_names
Pattern.compile( *
"[\\p{IsAlphabetic}_]" + * Valid characters:
"[\\p{IsAlphabetic}0-9_\\\\]*", * First character: { letter | underscore | backslash }
Pattern.CASE_INSENSITIVE).matcher(name).matches(); * Remaining characters: { letter | number | period | underscore }
\p{IsAlphabetic} doesn't work on Java 6, and other regex-based character classes don't work on unicode *
thus we are stuck with Character.isLetter (for now). * Cell shorthand: cannot be { "C" | "c" | "R" | "r" }
*
* Cell references disallowed: cannot be a cell reference $A$1 or R1C1
*
* Spaces are not valid (follows from valid characters above)
*
* Name length: (XSSF-specific?) 255 characters maximum
*
* Case sensitivity: all names are case-insensitive
*
* Uniqueness: must be unique (for names with the same scope)
*
* @param name
*/ */
private static void validateName(String name) {
if (name.length() == 0) { if (name.length() == 0) {
throw new IllegalArgumentException("Name cannot be blank"); throw new IllegalArgumentException("Name cannot be blank");
} }
if (name.length() > 255) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot exceed 255 characters in length");
}
if (name.equalsIgnoreCase("R") || name.equalsIgnoreCase("C")) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be special shorthand R or C");
}
// is first character valid? // is first character valid?
char c = name.charAt(0); char c = name.charAt(0);
String allowedSymbols = "_"; String allowedSymbols = "_\\";
boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1); boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1);
if (!characterIsValid) { if (!characterIsValid) {
throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter"); throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter");
} }
// are all other characters valid? // are all other characters valid?
allowedSymbols = "_\\"; //backslashes needed for unicode escape allowedSymbols = "_.\\"; //backslashes needed for unicode escape
for (final char ch : name.toCharArray()) { for (final char ch : name.toCharArray()) {
characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1); characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1);
if (!characterIsValid) { if (!characterIsValid) {
throw new IllegalArgumentException("Invalid name: '"+name+"'"); throw new IllegalArgumentException("Invalid name: '"+name+"': name must be letter, digit, period, or underscore");
} }
} }
// Is the name a valid $A$1 cell reference
// Because $, :, and ! are disallowed characters, A1-style references become just a letter-number combination
if (name.matches("[A-Za-z]+\\d+")) {
String col = name.replaceAll("\\d", "");
String row = name.replaceAll("[A-Za-z]", "");
if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
}
}
// Is the name a valid R1C1 cell reference?
if (name.matches("[Rr]\\d+[Cc]\\d+")) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be R1C1-style cell reference");
}
} }
public void setRefersToFormula(String formulaText) { public void setRefersToFormula(String formulaText) {

View File

@ -16,12 +16,14 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.xssf.usermodel; package org.apache.poi.xssf.usermodel;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
/** /**
@ -345,35 +347,69 @@ public final class XSSFName implements Name {
return _ctName.toString().equals(cf.getCTName().toString()); return _ctName.toString().equals(cf.getCTName().toString());
} }
private static void validateName(String name) { /**
/* equivalent to: * https://support.office.com/en-us/article/Define-and-use-names-in-formulas-4D0F13AC-53B7-422E-AFD2-ABD7FF379C64#bmsyntax_rules_for_names
Pattern.compile( *
"[\\p{IsAlphabetic}_]" + * Valid characters:
"[\\p{IsAlphabetic}0-9_\\\\]*", * First character: { letter | underscore | backslash }
Pattern.CASE_INSENSITIVE).matcher(name).matches(); * Remaining characters: { letter | number | period | underscore }
\p{IsAlphabetic} doesn't work on Java 6, and other regex-based character classes don't work on unicode *
thus we are stuck with Character.isLetter (for now). * Cell shorthand: cannot be { "C" | "c" | "R" | "r" }
*
* Cell references disallowed: cannot be a cell reference $A$1 or R1C1
*
* Spaces are not valid (follows from valid characters above)
*
* Name length: (XSSF-specific?) 255 characters maximum
*
* Case sensitivity: all names are case-insensitive
*
* Uniqueness: must be unique (for names with the same scope)
*
* @param name
*/ */
private static void validateName(String name) {
if (name.length() == 0) { if (name.length() == 0) {
throw new IllegalArgumentException("Name cannot be blank"); throw new IllegalArgumentException("Name cannot be blank");
} }
if (name.length() > 255) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot exceed 255 characters in length");
}
if (name.equalsIgnoreCase("R") || name.equalsIgnoreCase("C")) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be special shorthand R or C");
}
// is first character valid? // is first character valid?
char c = name.charAt(0); char c = name.charAt(0);
String allowedSymbols = "_"; String allowedSymbols = "_\\";
boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1); boolean characterIsValid = (Character.isLetter(c) || allowedSymbols.indexOf(c) != -1);
if (!characterIsValid) { if (!characterIsValid) {
throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter"); throw new IllegalArgumentException("Invalid name: '"+name+"': first character must be underscore or a letter");
} }
// are all other characters valid? // are all other characters valid?
allowedSymbols = "_\\"; //backslashes needed for unicode escape allowedSymbols = "_.\\"; //backslashes needed for unicode escape
for (final char ch : name.toCharArray()) { for (final char ch : name.toCharArray()) {
characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1); characterIsValid = (Character.isLetterOrDigit(ch) || allowedSymbols.indexOf(ch) != -1);
if (!characterIsValid) { if (!characterIsValid) {
throw new IllegalArgumentException("Invalid name: '"+name+"'"); throw new IllegalArgumentException("Invalid name: '"+name+"': name must be letter, digit, period, or underscore");
} }
}
// Is the name a valid $A$1 cell reference
// Because $, :, and ! are disallowed characters, A1-style references become just a letter-number combination
if (name.matches("[A-Za-z]+\\d+")) {
String col = name.replaceAll("\\d", "");
String row = name.replaceAll("[A-Za-z]", "");
if (CellReference.cellReferenceIsWithinRange(col, row, SpreadsheetVersion.EXCEL97)) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be $A$1-style cell reference");
}
}
// Is the name a valid R1C1 cell reference?
if (name.matches("[Rr]\\d+[Cc]\\d+")) {
throw new IllegalArgumentException("Invalid name: '"+name+"': cannot be R1C1-style cell reference");
} }
} }
} }

View File

@ -673,22 +673,33 @@ public abstract class BaseTestNamedRange {
wb.close(); wb.close();
} }
// bug 56781: name validation only checks for first character's validity and presence of spaces
// bug 60246: validate name does not allow DOT in named ranges
@Test @Test
public void test56781() throws IOException { public void testValid() throws IOException {
Workbook wb = _testDataProvider.createWorkbook(); Workbook wb = _testDataProvider.createWorkbook();
Name name = wb.createName(); Name name = wb.createName();
for (String valid : Arrays.asList( for (String valid : Arrays.asList(
"Hello", "Hello",
"number1", "number1",
"_underscore" "_underscore",
//"p.e.r.o.i.d.s", "underscore_",
//"\\Backslash", "p.e.r.o.i.d.s",
//"Backslash\\" "\\Backslash",
"Backslash\\"
)) { )) {
name.setNameName(valid); name.setNameName(valid);
} }
wb.close();
}
@Test
public void testInvalid() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Name name = wb.createName();
try { try {
name.setNameName(""); name.setNameName("");
fail("expected exception: (blank)"); fail("expected exception: (blank)");
@ -704,7 +715,18 @@ public abstract class BaseTestNamedRange {
"Colon:", "Colon:",
"A-Minus", "A-Minus",
"A+Plus", "A+Plus",
"Dollar$")) { "Dollar$",
".periodAtBeginning",
"R", //special shorthand
"C", //special shorthand
"A1", // A1-style cell reference
"R1C1", // R1C1-style cell reference
"NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters..."+
"NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters..."+
"NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters..."+
"NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters..."+
"NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters.NameThatIsLongerThan255Characters"
)) {
try { try {
name.setNameName(invalid); name.setNameName(invalid);
fail("expected exception: " + invalid); fail("expected exception: " + invalid);