613 lines
19 KiB
Java
613 lines
19 KiB
Java
package jxl.biff;
|
|
|
|
import java.text.DecimalFormat;
|
|
import java.text.MessageFormat;
|
|
import java.util.Collection;
|
|
import java.util.Iterator;
|
|
import jxl.WorkbookSettings;
|
|
import jxl.biff.formula.ExternalSheet;
|
|
import jxl.biff.formula.FormulaException;
|
|
import jxl.biff.formula.FormulaParser;
|
|
import jxl.biff.formula.ParseContext;
|
|
import jxl.common.Assert;
|
|
import jxl.common.Logger;
|
|
|
|
public class DVParser {
|
|
private static Logger logger = Logger.getLogger(DVParser.class);
|
|
|
|
public static class DVType {
|
|
private int value;
|
|
|
|
private String desc;
|
|
|
|
private static DVType[] types = new DVType[0];
|
|
|
|
DVType(int v, String d) {
|
|
this.value = v;
|
|
this.desc = d;
|
|
DVType[] oldtypes = types;
|
|
types = new DVType[oldtypes.length + 1];
|
|
System.arraycopy(oldtypes, 0, types, 0, oldtypes.length);
|
|
types[oldtypes.length] = this;
|
|
}
|
|
|
|
static DVType getType(int v) {
|
|
DVType found = null;
|
|
for (int i = 0; i < types.length && found == null; i++) {
|
|
if ((types[i]).value == v)
|
|
found = types[i];
|
|
}
|
|
return found;
|
|
}
|
|
|
|
public int getValue() {
|
|
return this.value;
|
|
}
|
|
|
|
public String getDescription() {
|
|
return this.desc;
|
|
}
|
|
}
|
|
|
|
public static class ErrorStyle {
|
|
private int value;
|
|
|
|
private static ErrorStyle[] types = new ErrorStyle[0];
|
|
|
|
ErrorStyle(int v) {
|
|
this.value = v;
|
|
ErrorStyle[] oldtypes = types;
|
|
types = new ErrorStyle[oldtypes.length + 1];
|
|
System.arraycopy(oldtypes, 0, types, 0, oldtypes.length);
|
|
types[oldtypes.length] = this;
|
|
}
|
|
|
|
static ErrorStyle getErrorStyle(int v) {
|
|
ErrorStyle found = null;
|
|
for (int i = 0; i < types.length && found == null; i++) {
|
|
if ((types[i]).value == v)
|
|
found = types[i];
|
|
}
|
|
return found;
|
|
}
|
|
|
|
public int getValue() {
|
|
return this.value;
|
|
}
|
|
}
|
|
|
|
public static class Condition {
|
|
private int value;
|
|
|
|
private MessageFormat format;
|
|
|
|
private static Condition[] types = new Condition[0];
|
|
|
|
Condition(int v, String pattern) {
|
|
this.value = v;
|
|
this.format = new MessageFormat(pattern);
|
|
Condition[] oldtypes = types;
|
|
types = new Condition[oldtypes.length + 1];
|
|
System.arraycopy(oldtypes, 0, types, 0, oldtypes.length);
|
|
types[oldtypes.length] = this;
|
|
}
|
|
|
|
static Condition getCondition(int v) {
|
|
Condition found = null;
|
|
for (int i = 0; i < types.length && found == null; i++) {
|
|
if ((types[i]).value == v)
|
|
found = types[i];
|
|
}
|
|
return found;
|
|
}
|
|
|
|
public int getValue() {
|
|
return this.value;
|
|
}
|
|
|
|
public String getConditionString(String s1, String s2) {
|
|
return this.format.format(new String[] { s1, s2 });
|
|
}
|
|
}
|
|
|
|
public static final DVType ANY = new DVType(0, "any");
|
|
|
|
public static final DVType INTEGER = new DVType(1, "int");
|
|
|
|
public static final DVType DECIMAL = new DVType(2, "dec");
|
|
|
|
public static final DVType LIST = new DVType(3, "list");
|
|
|
|
public static final DVType DATE = new DVType(4, "date");
|
|
|
|
public static final DVType TIME = new DVType(5, "time");
|
|
|
|
public static final DVType TEXT_LENGTH = new DVType(6, "strlen");
|
|
|
|
public static final DVType FORMULA = new DVType(7, "form");
|
|
|
|
public static final ErrorStyle STOP = new ErrorStyle(0);
|
|
|
|
public static final ErrorStyle WARNING = new ErrorStyle(1);
|
|
|
|
public static final ErrorStyle INFO = new ErrorStyle(2);
|
|
|
|
public static final Condition BETWEEN = new Condition(0, "{0} <= x <= {1}");
|
|
|
|
public static final Condition NOT_BETWEEN = new Condition(1, "!({0} <= x <= {1}");
|
|
|
|
public static final Condition EQUAL = new Condition(2, "x == {0}");
|
|
|
|
public static final Condition NOT_EQUAL = new Condition(3, "x != {0}");
|
|
|
|
public static final Condition GREATER_THAN = new Condition(4, "x > {0}");
|
|
|
|
public static final Condition LESS_THAN = new Condition(5, "x < {0}");
|
|
|
|
public static final Condition GREATER_EQUAL = new Condition(6, "x >= {0}");
|
|
|
|
public static final Condition LESS_EQUAL = new Condition(7, "x <= {0}");
|
|
|
|
private static final int STRING_LIST_GIVEN_MASK = 128;
|
|
|
|
private static final int EMPTY_CELLS_ALLOWED_MASK = 256;
|
|
|
|
private static final int SUPPRESS_ARROW_MASK = 512;
|
|
|
|
private static final int SHOW_PROMPT_MASK = 262144;
|
|
|
|
private static final int SHOW_ERROR_MASK = 524288;
|
|
|
|
private static DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#");
|
|
|
|
private static final int MAX_VALIDATION_LIST_LENGTH = 254;
|
|
|
|
private static final int MAX_ROWS = 65535;
|
|
|
|
private static final int MAX_COLUMNS = 255;
|
|
|
|
private DVType type;
|
|
|
|
private ErrorStyle errorStyle;
|
|
|
|
private Condition condition;
|
|
|
|
private boolean stringListGiven;
|
|
|
|
private boolean emptyCellsAllowed;
|
|
|
|
private boolean suppressArrow;
|
|
|
|
private boolean showPrompt;
|
|
|
|
private boolean showError;
|
|
|
|
private String promptTitle;
|
|
|
|
private String errorTitle;
|
|
|
|
private String promptText;
|
|
|
|
private String errorText;
|
|
|
|
private FormulaParser formula1;
|
|
|
|
private String formula1String;
|
|
|
|
private FormulaParser formula2;
|
|
|
|
private String formula2String;
|
|
|
|
private int column1;
|
|
|
|
private int row1;
|
|
|
|
private int column2;
|
|
|
|
private int row2;
|
|
|
|
private boolean extendedCellsValidation;
|
|
|
|
private boolean copied;
|
|
|
|
public DVParser(byte[] data, ExternalSheet es, WorkbookMethods nt, WorkbookSettings ws) {
|
|
Assert.verify((nt != null));
|
|
this.copied = false;
|
|
int options = IntegerHelper.getInt(data[0], data[1], data[2], data[3]);
|
|
int typeVal = options & 0xF;
|
|
this.type = DVType.getType(typeVal);
|
|
int errorStyleVal = (options & 0x70) >> 4;
|
|
this.errorStyle = ErrorStyle.getErrorStyle(errorStyleVal);
|
|
int conditionVal = (options & 0xF00000) >> 20;
|
|
this.condition = Condition.getCondition(conditionVal);
|
|
this.stringListGiven = ((options & 0x80) != 0);
|
|
this.emptyCellsAllowed = ((options & 0x100) != 0);
|
|
this.suppressArrow = ((options & 0x200) != 0);
|
|
this.showPrompt = ((options & 0x40000) != 0);
|
|
this.showError = ((options & 0x80000) != 0);
|
|
int pos = 4;
|
|
int length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
if (length > 0 && data[pos + 2] == 0) {
|
|
this.promptTitle = StringHelper.getString(data, length, pos + 3, ws);
|
|
pos += length + 3;
|
|
} else if (length > 0) {
|
|
this.promptTitle = StringHelper.getUnicodeString(data, length, pos + 3);
|
|
pos += length * 2 + 3;
|
|
} else {
|
|
pos += 3;
|
|
}
|
|
length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
if (length > 0 && data[pos + 2] == 0) {
|
|
this.errorTitle = StringHelper.getString(data, length, pos + 3, ws);
|
|
pos += length + 3;
|
|
} else if (length > 0) {
|
|
this.errorTitle = StringHelper.getUnicodeString(data, length, pos + 3);
|
|
pos += length * 2 + 3;
|
|
} else {
|
|
pos += 3;
|
|
}
|
|
length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
if (length > 0 && data[pos + 2] == 0) {
|
|
this.promptText = StringHelper.getString(data, length, pos + 3, ws);
|
|
pos += length + 3;
|
|
} else if (length > 0) {
|
|
this.promptText = StringHelper.getUnicodeString(data, length, pos + 3);
|
|
pos += length * 2 + 3;
|
|
} else {
|
|
pos += 3;
|
|
}
|
|
length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
if (length > 0 && data[pos + 2] == 0) {
|
|
this.errorText = StringHelper.getString(data, length, pos + 3, ws);
|
|
pos += length + 3;
|
|
} else if (length > 0) {
|
|
this.errorText = StringHelper.getUnicodeString(data, length, pos + 3);
|
|
pos += length * 2 + 3;
|
|
} else {
|
|
pos += 3;
|
|
}
|
|
int formula1Length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 4;
|
|
int formula1Pos = pos;
|
|
pos += formula1Length;
|
|
int formula2Length = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 4;
|
|
int formula2Pos = pos;
|
|
pos += formula2Length;
|
|
pos += 2;
|
|
this.row1 = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 2;
|
|
this.row2 = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 2;
|
|
this.column1 = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 2;
|
|
this.column2 = IntegerHelper.getInt(data[pos], data[pos + 1]);
|
|
pos += 2;
|
|
this.extendedCellsValidation = !(this.row1 == this.row2 && this.column1 == this.column2);
|
|
try {
|
|
EmptyCell tmprt = new EmptyCell(this.column1, this.row1);
|
|
if (formula1Length != 0) {
|
|
byte[] tokens = new byte[formula1Length];
|
|
System.arraycopy(data, formula1Pos, tokens, 0, formula1Length);
|
|
this.formula1 = new FormulaParser(tokens, tmprt, es, nt, ws, ParseContext.DATA_VALIDATION);
|
|
this.formula1.parse();
|
|
}
|
|
if (formula2Length != 0) {
|
|
byte[] tokens = new byte[formula2Length];
|
|
System.arraycopy(data, formula2Pos, tokens, 0, formula2Length);
|
|
this.formula2 = new FormulaParser(tokens, tmprt, es, nt, ws, ParseContext.DATA_VALIDATION);
|
|
this.formula2.parse();
|
|
}
|
|
} catch (FormulaException e) {
|
|
logger.warn(e.getMessage() + " for cells " + CellReferenceHelper.getCellReference(this.column1, this.row1) + "-" + CellReferenceHelper.getCellReference(this.column2, this.row2));
|
|
}
|
|
}
|
|
|
|
public DVParser(Collection strings) {
|
|
this.copied = false;
|
|
this.type = LIST;
|
|
this.errorStyle = STOP;
|
|
this.condition = BETWEEN;
|
|
this.extendedCellsValidation = false;
|
|
this.stringListGiven = true;
|
|
this.emptyCellsAllowed = true;
|
|
this.suppressArrow = false;
|
|
this.showPrompt = true;
|
|
this.showError = true;
|
|
this.promptTitle = "\000";
|
|
this.errorTitle = "\000";
|
|
this.promptText = "\000";
|
|
this.errorText = "\000";
|
|
if (strings.size() == 0)
|
|
logger.warn("no validation strings - ignoring");
|
|
Iterator i = strings.iterator();
|
|
StringBuffer formulaString = new StringBuffer();
|
|
formulaString.append(i.next().toString());
|
|
while (i.hasNext()) {
|
|
formulaString.append('\000');
|
|
formulaString.append(' ');
|
|
formulaString.append(i.next().toString());
|
|
}
|
|
if (formulaString.length() > 254) {
|
|
logger.warn("Validation list exceeds maximum number of characters - truncating");
|
|
formulaString.delete(254, formulaString.length());
|
|
}
|
|
formulaString.insert(0, '"');
|
|
formulaString.append('"');
|
|
this.formula1String = formulaString.toString();
|
|
}
|
|
|
|
public DVParser(String namedRange) {
|
|
if (namedRange.length() == 0) {
|
|
this.copied = false;
|
|
this.type = FORMULA;
|
|
this.errorStyle = STOP;
|
|
this.condition = EQUAL;
|
|
this.extendedCellsValidation = false;
|
|
this.stringListGiven = false;
|
|
this.emptyCellsAllowed = false;
|
|
this.suppressArrow = false;
|
|
this.showPrompt = true;
|
|
this.showError = true;
|
|
this.promptTitle = "\000";
|
|
this.errorTitle = "\000";
|
|
this.promptText = "\000";
|
|
this.errorText = "\000";
|
|
this.formula1String = "\"\"";
|
|
return;
|
|
}
|
|
this.copied = false;
|
|
this.type = LIST;
|
|
this.errorStyle = STOP;
|
|
this.condition = BETWEEN;
|
|
this.extendedCellsValidation = false;
|
|
this.stringListGiven = false;
|
|
this.emptyCellsAllowed = true;
|
|
this.suppressArrow = false;
|
|
this.showPrompt = true;
|
|
this.showError = true;
|
|
this.promptTitle = "\000";
|
|
this.errorTitle = "\000";
|
|
this.promptText = "\000";
|
|
this.errorText = "\000";
|
|
this.formula1String = namedRange;
|
|
}
|
|
|
|
public DVParser(int c1, int r1, int c2, int r2) {
|
|
this.copied = false;
|
|
this.type = LIST;
|
|
this.errorStyle = STOP;
|
|
this.condition = BETWEEN;
|
|
this.extendedCellsValidation = false;
|
|
this.stringListGiven = false;
|
|
this.emptyCellsAllowed = true;
|
|
this.suppressArrow = false;
|
|
this.showPrompt = true;
|
|
this.showError = true;
|
|
this.promptTitle = "\000";
|
|
this.errorTitle = "\000";
|
|
this.promptText = "\000";
|
|
this.errorText = "\000";
|
|
StringBuffer formulaString = new StringBuffer();
|
|
CellReferenceHelper.getCellReference(c1, r1, formulaString);
|
|
formulaString.append(':');
|
|
CellReferenceHelper.getCellReference(c2, r2, formulaString);
|
|
this.formula1String = formulaString.toString();
|
|
}
|
|
|
|
public DVParser(double val1, double val2, Condition c) {
|
|
this.copied = false;
|
|
this.type = DECIMAL;
|
|
this.errorStyle = STOP;
|
|
this.condition = c;
|
|
this.extendedCellsValidation = false;
|
|
this.stringListGiven = false;
|
|
this.emptyCellsAllowed = true;
|
|
this.suppressArrow = false;
|
|
this.showPrompt = true;
|
|
this.showError = true;
|
|
this.promptTitle = "\000";
|
|
this.errorTitle = "\000";
|
|
this.promptText = "\000";
|
|
this.errorText = "\000";
|
|
this.formula1String = DECIMAL_FORMAT.format(val1);
|
|
if (!Double.isNaN(val2))
|
|
this.formula2String = DECIMAL_FORMAT.format(val2);
|
|
}
|
|
|
|
public DVParser(DVParser copy) {
|
|
this.copied = true;
|
|
this.type = copy.type;
|
|
this.errorStyle = copy.errorStyle;
|
|
this.condition = copy.condition;
|
|
this.stringListGiven = copy.stringListGiven;
|
|
this.emptyCellsAllowed = copy.emptyCellsAllowed;
|
|
this.suppressArrow = copy.suppressArrow;
|
|
this.showPrompt = copy.showPrompt;
|
|
this.showError = copy.showError;
|
|
this.promptTitle = copy.promptTitle;
|
|
this.promptText = copy.promptText;
|
|
this.errorTitle = copy.errorTitle;
|
|
this.errorText = copy.errorText;
|
|
this.extendedCellsValidation = copy.extendedCellsValidation;
|
|
this.row1 = copy.row1;
|
|
this.row2 = copy.row2;
|
|
this.column1 = copy.column1;
|
|
this.column2 = copy.column2;
|
|
if (copy.formula1String != null) {
|
|
this.formula1String = copy.formula1String;
|
|
this.formula2String = copy.formula2String;
|
|
} else {
|
|
try {
|
|
this.formula1String = copy.formula1.getFormula();
|
|
this.formula2String = (copy.formula2 != null) ? copy.formula2.getFormula() : null;
|
|
} catch (FormulaException e) {
|
|
logger.warn("Cannot parse validation formula: " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
public byte[] getData() {
|
|
byte[] f1Bytes = (this.formula1 != null) ? this.formula1.getBytes() : new byte[0];
|
|
byte[] f2Bytes = (this.formula2 != null) ? this.formula2.getBytes() : new byte[0];
|
|
int dataLength = 4 + this.promptTitle.length() * 2 + 3 + this.errorTitle.length() * 2 + 3 + this.promptText.length() * 2 + 3 + this.errorText.length() * 2 + 3 + f1Bytes.length + 2 + f2Bytes.length + 2 + 4 + 10;
|
|
byte[] data = new byte[dataLength];
|
|
int pos = 0;
|
|
int options = 0;
|
|
options |= this.type.getValue();
|
|
options |= this.errorStyle.getValue() << 4;
|
|
options |= this.condition.getValue() << 20;
|
|
if (this.stringListGiven)
|
|
options |= 0x80;
|
|
if (this.emptyCellsAllowed)
|
|
options |= 0x100;
|
|
if (this.suppressArrow)
|
|
options |= 0x200;
|
|
if (this.showPrompt)
|
|
options |= 0x40000;
|
|
if (this.showError)
|
|
options |= 0x80000;
|
|
IntegerHelper.getFourBytes(options, data, pos);
|
|
pos += 4;
|
|
IntegerHelper.getTwoBytes(this.promptTitle.length(), data, pos);
|
|
pos += 2;
|
|
data[pos] = 1;
|
|
pos++;
|
|
StringHelper.getUnicodeBytes(this.promptTitle, data, pos);
|
|
pos += this.promptTitle.length() * 2;
|
|
IntegerHelper.getTwoBytes(this.errorTitle.length(), data, pos);
|
|
pos += 2;
|
|
data[pos] = 1;
|
|
pos++;
|
|
StringHelper.getUnicodeBytes(this.errorTitle, data, pos);
|
|
pos += this.errorTitle.length() * 2;
|
|
IntegerHelper.getTwoBytes(this.promptText.length(), data, pos);
|
|
pos += 2;
|
|
data[pos] = 1;
|
|
pos++;
|
|
StringHelper.getUnicodeBytes(this.promptText, data, pos);
|
|
pos += this.promptText.length() * 2;
|
|
IntegerHelper.getTwoBytes(this.errorText.length(), data, pos);
|
|
pos += 2;
|
|
data[pos] = 1;
|
|
pos++;
|
|
StringHelper.getUnicodeBytes(this.errorText, data, pos);
|
|
pos += this.errorText.length() * 2;
|
|
IntegerHelper.getTwoBytes(f1Bytes.length, data, pos);
|
|
pos += 4;
|
|
System.arraycopy(f1Bytes, 0, data, pos, f1Bytes.length);
|
|
pos += f1Bytes.length;
|
|
IntegerHelper.getTwoBytes(f2Bytes.length, data, pos);
|
|
pos += 4;
|
|
System.arraycopy(f2Bytes, 0, data, pos, f2Bytes.length);
|
|
pos += f2Bytes.length;
|
|
IntegerHelper.getTwoBytes(1, data, pos);
|
|
pos += 2;
|
|
IntegerHelper.getTwoBytes(this.row1, data, pos);
|
|
pos += 2;
|
|
IntegerHelper.getTwoBytes(this.row2, data, pos);
|
|
pos += 2;
|
|
IntegerHelper.getTwoBytes(this.column1, data, pos);
|
|
pos += 2;
|
|
IntegerHelper.getTwoBytes(this.column2, data, pos);
|
|
pos += 2;
|
|
return data;
|
|
}
|
|
|
|
public void insertRow(int row) {
|
|
if (this.formula1 != null)
|
|
this.formula1.rowInserted(0, row, true);
|
|
if (this.formula2 != null)
|
|
this.formula2.rowInserted(0, row, true);
|
|
if (this.row1 >= row)
|
|
this.row1++;
|
|
if (this.row2 >= row && this.row2 != 65535)
|
|
this.row2++;
|
|
}
|
|
|
|
public void insertColumn(int col) {
|
|
if (this.formula1 != null)
|
|
this.formula1.columnInserted(0, col, true);
|
|
if (this.formula2 != null)
|
|
this.formula2.columnInserted(0, col, true);
|
|
if (this.column1 >= col)
|
|
this.column1++;
|
|
if (this.column2 >= col && this.column2 != 255)
|
|
this.column2++;
|
|
}
|
|
|
|
public void removeRow(int row) {
|
|
if (this.formula1 != null)
|
|
this.formula1.rowRemoved(0, row, true);
|
|
if (this.formula2 != null)
|
|
this.formula2.rowRemoved(0, row, true);
|
|
if (this.row1 > row)
|
|
this.row1--;
|
|
if (this.row2 >= row)
|
|
this.row2--;
|
|
}
|
|
|
|
public void removeColumn(int col) {
|
|
if (this.formula1 != null)
|
|
this.formula1.columnRemoved(0, col, true);
|
|
if (this.formula2 != null)
|
|
this.formula2.columnRemoved(0, col, true);
|
|
if (this.column1 > col)
|
|
this.column1--;
|
|
if (this.column2 >= col && this.column2 != 255)
|
|
this.column2--;
|
|
}
|
|
|
|
public int getFirstColumn() {
|
|
return this.column1;
|
|
}
|
|
|
|
public int getLastColumn() {
|
|
return this.column2;
|
|
}
|
|
|
|
public int getFirstRow() {
|
|
return this.row1;
|
|
}
|
|
|
|
public int getLastRow() {
|
|
return this.row2;
|
|
}
|
|
|
|
String getValidationFormula() throws FormulaException {
|
|
if (this.type == LIST)
|
|
return this.formula1.getFormula();
|
|
String s1 = this.formula1.getFormula();
|
|
String s2 = (this.formula2 != null) ? this.formula2.getFormula() : null;
|
|
return this.condition.getConditionString(s1, s2) + "; x " + this.type.getDescription();
|
|
}
|
|
|
|
public void setCell(int col, int row, ExternalSheet es, WorkbookMethods nt, WorkbookSettings ws) throws FormulaException {
|
|
if (this.extendedCellsValidation)
|
|
return;
|
|
this.row1 = row;
|
|
this.row2 = row;
|
|
this.column1 = col;
|
|
this.column2 = col;
|
|
this.formula1 = new FormulaParser(this.formula1String, es, nt, ws, ParseContext.DATA_VALIDATION);
|
|
this.formula1.parse();
|
|
if (this.formula2String != null) {
|
|
this.formula2 = new FormulaParser(this.formula2String, es, nt, ws, ParseContext.DATA_VALIDATION);
|
|
this.formula2.parse();
|
|
}
|
|
}
|
|
|
|
public void extendCellValidation(int cols, int rows) {
|
|
this.row2 = this.row1 + rows;
|
|
this.column2 = this.column1 + cols;
|
|
this.extendedCellsValidation = true;
|
|
}
|
|
|
|
public boolean extendedCellsValidation() {
|
|
return this.extendedCellsValidation;
|
|
}
|
|
|
|
public boolean copied() {
|
|
return this.copied;
|
|
}
|
|
}
|