diff --git a/datastructures-xslx/NOTICE.txt b/datastructures-xslx/NOTICE.txt
new file mode 100644
index 0000000..61b7dfb
--- /dev/null
+++ b/datastructures-xslx/NOTICE.txt
@@ -0,0 +1,15 @@
+datastructures-xslx is composed of the following works
+
+jxl
+https://sourceforge.net/projects/jexcelapi/
+GNU Library or Lesser General Public License version 2.0 (LGPLv2)
+
+with the log4j dependency removed
+
+and
+
+com.incesoft.tools.excel
+https://code.google.com/archive/p/sjxslx/ -> https://github.com/davidpelfree/sjxlsx
+License: Apache License 2.0
+
+The code will be refactored to the org.xbib package group.
\ No newline at end of file
diff --git a/datastructures-xslx/build.gradle b/datastructures-xslx/build.gradle
deleted file mode 100644
index cc87491..0000000
--- a/datastructures-xslx/build.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-dependencies {
- implementation libs.jxl
-}
diff --git a/datastructures-xslx/src/main/java/jxl/BooleanCell.java b/datastructures-xslx/src/main/java/jxl/BooleanCell.java
new file mode 100755
index 0000000..8504e00
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/BooleanCell.java
@@ -0,0 +1,45 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * This type represents the Microsoft concept of a Boolean. Accordingly, this
+ * cell represents either TRUE, FALSE or an error condition. This third
+ * state naturally makes handling BooleanCells quite tricky, and use of
+ * the specific access methods should be handled with care
+ */
+public interface BooleanCell extends Cell {
+ /**
+ * Gets the boolean value stored in this cell. If this cell contains an
+ * error, then returns FALSE. Always query this cell type using the
+ * accessor method isError() prior to calling this method
+ *
+ * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or
+ * an error code
+ */
+ boolean getValue();
+}
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java b/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java
new file mode 100755
index 0000000..2a69c81
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/BooleanFormulaCell.java
@@ -0,0 +1,27 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * A mixin interface for numerical formulas, which combines the interfaces
+ * for formulas and for numbers
+ */
+public interface BooleanFormulaCell extends BooleanCell, FormulaCell {
+}
diff --git a/datastructures-xslx/src/main/java/jxl/Cell.java b/datastructures-xslx/src/main/java/jxl/Cell.java
new file mode 100755
index 0000000..7644dce
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/Cell.java
@@ -0,0 +1,86 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import jxl.format.CellFormat;
+
+/**
+ * Represents an individual Cell within a Sheet. May be queried for its
+ * type and its content
+ */
+public interface Cell {
+ /**
+ * Returns the row number of this cell
+ *
+ * @return the row number of this cell
+ */
+ int getRow();
+
+ /**
+ * Returns the column number of this cell
+ *
+ * @return the column number of this cell
+ */
+ int getColumn();
+
+ /**
+ * Returns the content type of this cell
+ *
+ * @return the content type for this cell
+ */
+ CellType getType();
+
+ /**
+ * Indicates whether or not this cell is hidden, by virtue of either
+ * the entire row or column being collapsed
+ *
+ * @return TRUE if this cell is hidden, FALSE otherwise
+ */
+ boolean isHidden();
+
+ /**
+ * Quick and dirty function to return the contents of this cell as a string.
+ * For more complex manipulation of the contents, it is necessary to cast
+ * this interface to correct subinterface
+ *
+ * @return the contents of this cell as a string
+ */
+ String getContents();
+
+ /**
+ * Gets the cell format which applies to this cell
+ * Note that for cell with a cell type of EMPTY, which has no formatting
+ * information, this method will return null. Some empty cells (eg. on
+ * template spreadsheets) may have a cell type of EMPTY, but will
+ * actually contain formatting information
+ *
+ * @return the cell format applied to this cell, or NULL if this is an
+ * empty cell
+ */
+ CellFormat getCellFormat();
+
+ /**
+ * Gets any special cell features, such as comments (notes) or cell
+ * validation present for this cell
+ *
+ * @return the cell features, or NULL if this cell has no special features
+ */
+ CellFeatures getCellFeatures();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/CellFeatures.java b/datastructures-xslx/src/main/java/jxl/CellFeatures.java
new file mode 100755
index 0000000..5ac5ea7
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/CellFeatures.java
@@ -0,0 +1,74 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import jxl.biff.BaseCellFeatures;
+
+/**
+ * Container for any additional cell features
+ */
+public class CellFeatures extends BaseCellFeatures {
+ /**
+ * Constructor
+ */
+ public CellFeatures() {
+ super();
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param cf cell to copy
+ */
+ protected CellFeatures(CellFeatures cf) {
+ super(cf);
+ }
+
+ /**
+ * Accessor for the cell comment
+ *
+ * @return the cell comment, or NULL if this cell doesn't have
+ * a comment associated with it
+ */
+ public String getComment() {
+ return super.getComment();
+ }
+
+ /**
+ * Gets the data validation list
+ *
+ * @return the data validation list
+ */
+ public String getDataValidationList() {
+ return super.getDataValidationList();
+ }
+
+ /**
+ * Gets the range of cells to which the data validation applies. If the
+ * validation applies to just this cell, this will be reflected in the
+ * returned range
+ *
+ * @return the range to which the same validation extends, or NULL if this
+ * cell doesn't have a validation
+ */
+ public Range getSharedDataValidationRange() {
+ return super.getSharedDataValidationRange();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/CellFormat.java b/datastructures-xslx/src/main/java/jxl/CellFormat.java
new file mode 100755
index 0000000..0932039
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/CellFormat.java
@@ -0,0 +1,28 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * Interface for cell formats - used for typing information
+ *
+ * @deprecated Repackaged as jxl.format.CellFormat
+ */
+public interface CellFormat extends jxl.format.CellFormat {
+}
diff --git a/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java b/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java
new file mode 100755
index 0000000..32a2d47
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/CellReferenceHelper.java
@@ -0,0 +1,254 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import jxl.write.WritableWorkbook;
+
+/**
+ * Exposes some cell reference helper methods to the public interface.
+ * This class merely delegates to the internally used reference helper
+ */
+public final class CellReferenceHelper {
+ /**
+ * Hide the default constructor
+ */
+ private CellReferenceHelper() {
+ }
+
+ /**
+ * Appends the cell reference for the column and row passed in to the string
+ * buffer
+ *
+ * @param column the column
+ * @param row the row
+ * @param buf the string buffer to append
+ */
+ public static void getCellReference(int column, int row, StringBuffer buf) {
+ jxl.biff.CellReferenceHelper.getCellReference(column, row, buf);
+ }
+
+ /**
+ * Overloaded method which prepends $ for absolute reference
+ *
+ * @param column the column number
+ * @param colabs TRUE if the column reference is absolute
+ * @param row the row number
+ * @param rowabs TRUE if the row reference is absolute
+ * @param buf the string buffer
+ */
+ public static void getCellReference(int column,
+ boolean colabs,
+ int row,
+ boolean rowabs,
+ StringBuffer buf) {
+ jxl.biff.CellReferenceHelper.getCellReference(column, colabs,
+ row, rowabs,
+ buf);
+ }
+
+
+ /**
+ * Gets the cell reference for the specified column and row
+ *
+ * @param column the column
+ * @param row the row
+ * @return the cell reference
+ */
+ public static String getCellReference(int column, int row) {
+ return jxl.biff.CellReferenceHelper.getCellReference(column, row);
+ }
+
+ /**
+ * Gets the columnn number of the string cell reference
+ *
+ * @param s the string to parse
+ * @return the column portion of the cell reference
+ */
+ public static int getColumn(String s) {
+ return jxl.biff.CellReferenceHelper.getColumn(s);
+ }
+
+ /**
+ * Gets the column letter corresponding to the 0-based column number
+ *
+ * @param c the column number
+ * @return the letter for that column number
+ */
+ public static String getColumnReference(int c) {
+ return jxl.biff.CellReferenceHelper.getColumnReference(c);
+ }
+
+ /**
+ * Gets the row number of the cell reference
+ *
+ * @param s the cell reference
+ * @return the row number
+ */
+ public static int getRow(String s) {
+ return jxl.biff.CellReferenceHelper.getRow(s);
+ }
+
+ /**
+ * Sees if the column component is relative or not
+ *
+ * @param s the cell
+ * @return TRUE if the column is relative, FALSE otherwise
+ */
+ public static boolean isColumnRelative(String s) {
+ return jxl.biff.CellReferenceHelper.isColumnRelative(s);
+ }
+
+ /**
+ * Sees if the row component is relative or not
+ *
+ * @param s the cell
+ * @return TRUE if the row is relative, FALSE otherwise
+ */
+ public static boolean isRowRelative(String s) {
+ return jxl.biff.CellReferenceHelper.isRowRelative(s);
+ }
+
+ /**
+ * Gets the fully qualified cell reference given the column, row
+ * external sheet reference etc
+ *
+ * @param sheet the sheet index
+ * @param column the column index
+ * @param row the row index
+ * @param workbook the workbook
+ * @param buf a string buffer
+ */
+ public static void getCellReference
+ (int sheet, int column, int row,
+ Workbook workbook, StringBuffer buf) {
+ jxl.biff.CellReferenceHelper.getCellReference
+ (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf);
+ }
+
+ /**
+ * Gets the fully qualified cell reference given the column, row
+ * external sheet reference etc
+ *
+ * @param sheet the sheet
+ * @param column the column
+ * @param row the row
+ * @param workbook the workbook
+ * @param buf the buffer
+ */
+ public static void getCellReference(int sheet,
+ int column,
+ int row,
+ WritableWorkbook workbook,
+ StringBuffer buf) {
+ jxl.biff.CellReferenceHelper.getCellReference
+ (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook, buf);
+ }
+
+ /**
+ * Gets the fully qualified cell reference given the column, row
+ * external sheet reference etc
+ *
+ * @param sheet the sheet
+ * @param column the column
+ * @param colabs TRUE if the column is an absolute reference
+ * @param row the row
+ * @param rowabs TRUE if the row is an absolute reference
+ * @param workbook the workbook
+ * @param buf the string buffer
+ */
+ public static void getCellReference(int sheet,
+ int column,
+ boolean colabs,
+ int row,
+ boolean rowabs,
+ Workbook workbook,
+ StringBuffer buf) {
+ jxl.biff.CellReferenceHelper.getCellReference
+ (sheet, column, colabs, row, rowabs,
+ (jxl.biff.formula.ExternalSheet) workbook, buf);
+ }
+
+ /**
+ * Gets the fully qualified cell reference given the column, row
+ * external sheet reference etc
+ *
+ * @param sheet the sheet
+ * @param column the column
+ * @param row the row
+ * @param workbook the workbook
+ * @return the cell reference in the form 'Sheet 1'!A1
+ */
+ public static String getCellReference(int sheet,
+ int column,
+ int row,
+ Workbook workbook) {
+ return jxl.biff.CellReferenceHelper.getCellReference
+ (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook);
+ }
+
+ /**
+ * Gets the fully qualified cell reference given the column, row
+ * external sheet reference etc
+ *
+ * @param sheet the sheet
+ * @param column the column
+ * @param row the row
+ * @param workbook the workbook
+ * @return the cell reference in the form 'Sheet 1'!A1
+ */
+ public static String getCellReference(int sheet,
+ int column,
+ int row,
+ WritableWorkbook workbook) {
+ return jxl.biff.CellReferenceHelper.getCellReference
+ (sheet, column, row, (jxl.biff.formula.ExternalSheet) workbook);
+ }
+
+
+ /**
+ * Gets the sheet name from the cell reference string
+ *
+ * @param ref the cell reference
+ * @return the sheet name
+ */
+ public static String getSheet(String ref) {
+ return jxl.biff.CellReferenceHelper.getSheet(ref);
+ }
+
+ /**
+ * Gets the cell reference for the cell
+ *
+ * @param the cell
+ */
+ public static String getCellReference(Cell c) {
+ return getCellReference(c.getColumn(), c.getRow());
+ }
+
+ /**
+ * Gets the cell reference for the cell
+ *
+ * @param c the cell
+ * @param sb string buffer
+ */
+ public static void getCellReference(Cell c, StringBuffer sb) {
+ getCellReference(c.getColumn(), c.getRow(), sb);
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/CellType.java b/datastructures-xslx/src/main/java/jxl/CellType.java
new file mode 100755
index 0000000..a8c1a97
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/CellType.java
@@ -0,0 +1,97 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * An enumeration type listing the available content types for a cell
+ */
+public final class CellType {
+
+ /**
+ * An empty cell can still contain formatting information and comments
+ */
+ public static final CellType EMPTY = new CellType("Empty");
+ /**
+ *
+ */
+ public static final CellType LABEL = new CellType("Label");
+ /**
+ *
+ */
+ public static final CellType NUMBER = new CellType("Number");
+ /**
+ *
+ */
+ public static final CellType BOOLEAN = new CellType("Boolean");
+ /**
+ *
+ */
+ public static final CellType ERROR = new CellType("Error");
+ /**
+ *
+ */
+ public static final CellType NUMBER_FORMULA =
+ new CellType("Numerical Formula");
+ /**
+ *
+ */
+ public static final CellType DATE_FORMULA = new CellType("Date Formula");
+ /**
+ *
+ */
+ public static final CellType STRING_FORMULA = new CellType("String Formula");
+ /**
+ *
+ */
+ public static final CellType BOOLEAN_FORMULA =
+ new CellType("Boolean Formula");
+ /**
+ *
+ */
+ public static final CellType FORMULA_ERROR = new CellType("Formula Error");
+ /**
+ *
+ */
+ public static final CellType DATE = new CellType("Date");
+ /**
+ * The text description of this cell type
+ */
+ private final String description;
+ /**
+ * Private constructor
+ *
+ * @param desc the description of this type
+ */
+ private CellType(String desc) {
+ description = desc;
+ }
+
+ /**
+ * Returns a string description of this cell
+ *
+ * @return the string description for this type
+ */
+ public String toString() {
+ return description;
+ }
+
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/CellView.java b/datastructures-xslx/src/main/java/jxl/CellView.java
new file mode 100755
index 0000000..117a8ba
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/CellView.java
@@ -0,0 +1,196 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import jxl.format.CellFormat;
+
+/**
+ * This is a bean which client applications may use to get/set various
+ * properties for a row or column on a spreadsheet
+ */
+public final class CellView {
+ /**
+ * The dimension for the associated group of cells. For columns this
+ * will be width in characters, for rows this will be the
+ * height in points
+ * This attribute is deprecated in favour of the size attribute
+ */
+ private int dimension;
+
+ /**
+ * The size for the associated group of cells. For columns this
+ * will be width in characters multiplied by 256, for rows this will be the
+ * height in points
+ */
+ private int size;
+
+ /**
+ * Indicates whether the deprecated function was used to set the dimension
+ */
+ private boolean depUsed;
+
+ /**
+ * Indicates whether or not this sheet is hidden
+ */
+ private boolean hidden;
+
+ /**
+ * The cell format for the row/column
+ */
+ private CellFormat format;
+
+ /**
+ * Indicates that this column/row should be autosized
+ */
+ private boolean autosize;
+
+ /**
+ * Default constructor
+ */
+ public CellView() {
+ hidden = false;
+ depUsed = false;
+ dimension = 1;
+ size = 1;
+ autosize = false;
+ }
+
+ /**
+ * Copy constructor
+ */
+ public CellView(CellView cv) {
+ hidden = cv.hidden;
+ depUsed = cv.depUsed;
+ dimension = cv.dimension;
+ size = cv.size;
+ autosize = cv.autosize;
+ }
+
+ /**
+ * Accessor for the hidden nature of this row/column
+ *
+ * @return TRUE if this row/column is hidden, FALSE otherwise
+ */
+ public boolean isHidden() {
+ return hidden;
+ }
+
+ /**
+ * Sets the hidden status of this row/column
+ *
+ * @param h the hidden flag
+ */
+ public void setHidden(boolean h) {
+ hidden = h;
+ }
+
+ /**
+ * Gets the width of the column in characters or the height of the
+ * row in 1/20ths
+ *
+ * @return the dimension
+ * @deprecated use getSize() instead
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Sets the dimension for this view
+ *
+ * @param d the width of the column in characters, or the height of the
+ * row in 1/20ths of a point
+ * @deprecated use the setSize method instead
+ */
+ public void setDimension(int d) {
+ dimension = d;
+ depUsed = true;
+ }
+
+ /**
+ * Gets the width of the column in characters multiplied by 256, or the
+ * height of the row in 1/20ths of a point
+ *
+ * @return the dimension
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Sets the dimension for this view
+ *
+ * @param d the width of the column in characters multiplied by 256,
+ * or the height of the row in 1/20ths of a point
+ */
+ public void setSize(int d) {
+ size = d;
+ depUsed = false;
+ }
+
+ /**
+ * Accessor for the cell format for this group.
+ *
+ * @return the format for the column/row, or NULL if no format was
+ * specified
+ */
+ public CellFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Sets the cell format for this group of cells
+ *
+ * @param cf the format for every cell in the column/row
+ */
+ public void setFormat(CellFormat cf) {
+ format = cf;
+ }
+
+ /**
+ * Accessor for the depUsed attribute
+ *
+ * @return TRUE if the deprecated methods were used to set the size,
+ * FALSE otherwise
+ */
+ public boolean depUsed() {
+ return depUsed;
+ }
+
+ /**
+ * Accessor for the autosize flag
+ * NOTE: use of the autosize function is very processor intensive, so
+ * use with care
+ *
+ * @return TRUE if this row/column is to be autosized
+ */
+ public boolean isAutosize() {
+ return autosize;
+ }
+
+ /**
+ * Sets the autosize flag. Currently, this only works for column views
+ *
+ * @param a autosize
+ */
+ public void setAutosize(boolean a) {
+ autosize = a;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/DateCell.java b/datastructures-xslx/src/main/java/jxl/DateCell.java
new file mode 100755
index 0000000..9b73ba1
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/DateCell.java
@@ -0,0 +1,53 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * A date cell
+ */
+public interface DateCell extends Cell {
+ /**
+ * Gets the date contained in this cell
+ *
+ * @return the cell contents
+ */
+ Date getDate();
+
+ /**
+ * Indicates whether the date value contained in this cell refers to a date,
+ * or merely a time
+ *
+ * @return TRUE if the value refers to a time
+ */
+ boolean isTime();
+
+ /**
+ * Gets the DateFormat used to format the cell. This will normally be
+ * the format specified in the excel spreadsheet, but in the event of any
+ * difficulty parsing this, it will revert to the default date/time format.
+ *
+ * @return the DateFormat object used to format the date in the original
+ * excel cell
+ */
+ DateFormat getDateFormat();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java b/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java
new file mode 100755
index 0000000..b798d7f
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/DateFormulaCell.java
@@ -0,0 +1,27 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * A mixin interface for date formulas, which combines the interfaces
+ * for formulas and for dates
+ */
+public interface DateFormulaCell extends DateCell, FormulaCell {
+}
diff --git a/datastructures-xslx/src/main/java/jxl/ErrorCell.java b/datastructures-xslx/src/main/java/jxl/ErrorCell.java
new file mode 100755
index 0000000..ab7f1ee
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/ErrorCell.java
@@ -0,0 +1,36 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * This type represents a cell which contains an error. This error will
+ * usually, but not always be the result of some error resulting from
+ * a formula
+ */
+public interface ErrorCell extends Cell {
+ /**
+ * Gets the error code for this cell. If this cell does not represent
+ * an error, then it returns 0. Always use the method isError() to
+ * determine this prior to calling this method
+ *
+ * @return the error code if this cell contains an error, 0 otherwise
+ */
+ int getErrorCode();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java b/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java
new file mode 100755
index 0000000..0819934
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/ErrorFormulaCell.java
@@ -0,0 +1,27 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * A mixin interface for numerical formulas, which combines the interfaces
+ * for formulas and for numbers
+ */
+public interface ErrorFormulaCell extends ErrorCell, FormulaCell {
+}
diff --git a/datastructures-xslx/src/main/java/jxl/FormulaCell.java b/datastructures-xslx/src/main/java/jxl/FormulaCell.java
new file mode 100755
index 0000000..84cc24f
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/FormulaCell.java
@@ -0,0 +1,35 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+import jxl.biff.formula.FormulaException;
+
+/**
+ * Interface for formulas which allow clients to read the Excel formula
+ */
+public interface FormulaCell extends Cell {
+ /**
+ * Gets the formula as a string
+ *
+ * @return the formula as a string
+ * @throws FormulaException if an error occurred whilst parsing
+ */
+ String getFormula() throws FormulaException;
+}
diff --git a/datastructures-xslx/src/main/java/jxl/HeaderFooter.java b/datastructures-xslx/src/main/java/jxl/HeaderFooter.java
new file mode 100755
index 0000000..a0520d3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/HeaderFooter.java
@@ -0,0 +1,344 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan, Eric Jung
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl;
+
+/**
+ * Class which represents an Excel header or footer.
+ */
+public final class HeaderFooter extends jxl.biff.HeaderFooter {
+ /**
+ * Default constructor.
+ */
+ public HeaderFooter() {
+ super();
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param hf the item to copy
+ */
+ public HeaderFooter(HeaderFooter hf) {
+ super(hf);
+ }
+
+ /**
+ * Constructor used when reading workbooks to separate the left, right
+ * a central part of the strings into their constituent parts
+ *
+ * @param s the header string
+ */
+ public HeaderFooter(String s) {
+ super(s);
+ }
+
+ /**
+ * Retrieves a String
ified
+ * version of this object
+ *
+ * @return the header string
+ */
+ public String toString() {
+ return super.toString();
+ }
+
+ /**
+ * Accessor for the contents which appear on the right hand side of the page
+ *
+ * @return the right aligned contents
+ */
+ public Contents getRight() {
+ return (Contents) super.getRightText();
+ }
+
+ /**
+ * Accessor for the contents which in the centre of the page
+ *
+ * @return the centrally aligned contents
+ */
+ public Contents getCentre() {
+ return (Contents) super.getCentreText();
+ }
+
+ /**
+ * Accessor for the contents which appear on the left hand side of the page
+ *
+ * @return the left aligned contents
+ */
+ public Contents getLeft() {
+ return (Contents) super.getLeftText();
+ }
+
+ /**
+ * Clears the contents of the header/footer
+ */
+ public void clear() {
+ super.clear();
+ }
+
+ /**
+ * Creates internal class of the appropriate type
+ *
+ * @return the created contents
+ */
+ protected jxl.biff.HeaderFooter.Contents createContents() {
+ return new Contents();
+ }
+
+ /**
+ * Creates internal class of the appropriate type
+ *
+ * @param s the string to create the contents
+ * @return the created contents
+ */
+ protected jxl.biff.HeaderFooter.Contents createContents(String s) {
+ return new Contents(s);
+ }
+
+ /**
+ * Creates internal class of the appropriate type
+ *
+ * @param c the contents to copy
+ * @return the new contents
+ */
+ protected jxl.biff.HeaderFooter.Contents
+ createContents(jxl.biff.HeaderFooter.Contents c) {
+ return new Contents((Contents) c);
+ }
+
+ /**
+ * The contents - a simple wrapper around a string buffer
+ */
+ public static class Contents extends jxl.biff.HeaderFooter.Contents {
+ /**
+ * The constructor
+ */
+ Contents() {
+ super();
+ }
+
+ /**
+ * Constructor used when reading worksheets. The string contains all
+ * the formatting (but not alignment characters
+ *
+ * @param s the format string
+ */
+ Contents(String s) {
+ super(s);
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param copy the contents to copy
+ */
+ Contents(Contents copy) {
+ super(copy);
+ }
+
+ /**
+ * Appends the text to the string buffer
+ *
+ * @param txt the text to append
+ */
+ public void append(String txt) {
+ super.append(txt);
+ }
+
+ /**
+ * Turns bold printing on or off. Bold printing
+ * is initially off. Text subsequently appended to
+ * this object will be bolded until this method is
+ * called again.
+ */
+ public void toggleBold() {
+ super.toggleBold();
+ }
+
+ /**
+ * Turns underline printing on or off. Underline printing
+ * is initially off. Text subsequently appended to
+ * this object will be underlined until this method is
+ * called again.
+ */
+ public void toggleUnderline() {
+ super.toggleUnderline();
+ }
+
+ /**
+ * Turns italics printing on or off. Italics printing
+ * is initially off. Text subsequently appended to
+ * this object will be italicized until this method is
+ * called again.
+ */
+ public void toggleItalics() {
+ super.toggleItalics();
+ }
+
+ /**
+ * Turns strikethrough printing on or off. Strikethrough printing
+ * is initially off. Text subsequently appended to
+ * this object will be striked out until this method is
+ * called again.
+ */
+ public void toggleStrikethrough() {
+ super.toggleStrikethrough();
+ }
+
+ /**
+ * Turns double-underline printing on or off. Double-underline printing
+ * is initially off. Text subsequently appended to
+ * this object will be double-underlined until this method is
+ * called again.
+ */
+ public void toggleDoubleUnderline() {
+ super.toggleDoubleUnderline();
+ }
+
+ /**
+ * Turns superscript printing on or off. Superscript printing
+ * is initially off. Text subsequently appended to
+ * this object will be superscripted until this method is
+ * called again.
+ */
+ public void toggleSuperScript() {
+ super.toggleSuperScript();
+ }
+
+ /**
+ * Turns subscript printing on or off. Subscript printing
+ * is initially off. Text subsequently appended to
+ * this object will be subscripted until this method is
+ * called again.
+ */
+ public void toggleSubScript() {
+ super.toggleSubScript();
+ }
+
+ /**
+ * Turns outline printing on or off (Macintosh only).
+ * Outline printing is initially off. Text subsequently appended
+ * to this object will be outlined until this method is
+ * called again.
+ */
+ public void toggleOutline() {
+ super.toggleOutline();
+ }
+
+ /**
+ * Turns shadow printing on or off (Macintosh only).
+ * Shadow printing is initially off. Text subsequently appended
+ * to this object will be shadowed until this method is
+ * called again.
+ */
+ public void toggleShadow() {
+ super.toggleShadow();
+ }
+
+ /**
+ * Sets the font of text subsequently appended to this
+ * object.. Previously appended text is not affected.
+ *
+ * Hyperlinks may apply to a range of cells; in such cases the methods + * getRow and getColumn return the cell at the top left of the range + * the hyperlink refers to. Hyperlinks have no specific cell format + * information applied to them, so the getCellFormat method will return null + */ +public interface Hyperlink { + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + int getRow(); + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + int getColumn(); + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + Range getRange(); + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + boolean isFile(); + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + boolean isURL(); + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + boolean isLocation(); + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + int getLastRow(); + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + int getLastColumn(); + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + URL getURL(); + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + File getFile(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/Image.java b/datastructures-xslx/src/main/java/jxl/Image.java new file mode 100755 index 0000000..e3600a8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Image.java @@ -0,0 +1,120 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.File; +import jxl.common.LengthUnit; + +/** + * Accessor functions for an image + */ +public interface Image { + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + double getColumn(); + + /** + * Accessor for the image position + * + * @return the row number at which the image is positioned + */ + double getRow(); + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + double getWidth(); + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + double getHeight(); + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + File getImageFile(); + + /** + * Accessor for the image data + * + * @return the image data + */ + byte[] getImageData(); + + /** + * Get the width of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the width of the image within Excel + */ + double getWidth(LengthUnit unit); + + /** + * Get the height of this image as rendered within Excel + * + * @param unit the unit of measurement + * @return the height of the image within Excel + */ + double getHeight(LengthUnit unit); + + /** + * Gets the width of the image. Note that this is the width of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image width in pixels + */ + int getImageWidth(); + + /** + * Gets the height of the image. Note that this is the height of the + * underlying image, and does not take into account any size manipulations + * that may have occurred when the image was added into Excel + * + * @return the image height in pixels + */ + int getImageHeight(); + + /** + * Gets the horizontal resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + double getHorizontalResolution(LengthUnit unit); + + /** + * Gets the vertical resolution of the image, if that information + * is available. + * + * @return the number of dots per unit specified, if available, 0 otherwise + */ + double getVerticalResolution(LengthUnit unit); +} diff --git a/datastructures-xslx/src/main/java/jxl/JXLException.java b/datastructures-xslx/src/main/java/jxl/JXLException.java new file mode 100755 index 0000000..fa32309 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/JXLException.java @@ -0,0 +1,34 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Base exception class for JExcelAPI exceptions + */ +public class JXLException extends Exception { + /** + * Constructor + * + * @param message the exception message + */ + protected JXLException(String message) { + super(message); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/LabelCell.java b/datastructures-xslx/src/main/java/jxl/LabelCell.java new file mode 100755 index 0000000..938efa4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/LabelCell.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A label cell + */ +public interface LabelCell extends Cell { + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + String getString(); +} diff --git a/datastructures-xslx/src/main/java/jxl/NumberCell.java b/datastructures-xslx/src/main/java/jxl/NumberCell.java new file mode 100755 index 0000000..bc88e11 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/NumberCell.java @@ -0,0 +1,43 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.text.NumberFormat; + +/** + * A cell which contains a numerical value + */ +public interface NumberCell extends Cell { + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + double getValue(); + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + NumberFormat getNumberFormat(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java b/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java new file mode 100755 index 0000000..148fa5a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/NumberFormulaCell.java @@ -0,0 +1,27 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for numbers + */ +public interface NumberFormulaCell extends NumberCell, FormulaCell { +} diff --git a/datastructures-xslx/src/main/java/jxl/Range.java b/datastructures-xslx/src/main/java/jxl/Range.java new file mode 100755 index 0000000..dd4be76 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Range.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * Represents a 3-D range of cells in a workbook. This object is + * returned by the method findByName in a workbook + */ +public interface Range { + /** + * Gets the cell at the top left of this range + * + * @return the cell at the top left + */ + Cell getTopLeft(); + + /** + * Gets the cell at the bottom right of this range + * + * @return the cell at the bottom right + */ + Cell getBottomRight(); + + /** + * Gets the index of the first sheet in the range + * + * @return the index of the first sheet in the range + */ + int getFirstSheetIndex(); + + /** + * Gets the index of the last sheet in the range + * + * @return the index of the last sheet in the range + */ + int getLastSheetIndex(); +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/Sheet.java b/datastructures-xslx/src/main/java/jxl/Sheet.java new file mode 100755 index 0000000..1aa5ceb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Sheet.java @@ -0,0 +1,278 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.util.regex.Pattern; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + */ +public interface Sheet { + /** + * Returns the cell specified at this row and at this column. + * If a column/row combination forms part of a merged group of cells + * then (unless it is the first cell of the group) a blank cell + * will be returned + * + * @param column the column number + * @param row the row number + * @return the cell at the specified co-ordinates + */ + Cell getCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + Cell getCell(String loc); + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + int getRows(); + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + int getColumns(); + + /** + * Gets all the cells on the specified row + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + Cell[] getRow(int row); + + /** + * Gets all the cells on the specified column + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + Cell[] getColumn(int col); + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + String getName(); + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings() method + */ + boolean isHidden(); + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() method + */ + boolean isProtected(); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + Cell findCell(String contents); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the rang + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse); + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell method in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + LabelCell findLabelCell(String contents); + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + Hyperlink[] getHyperlinks(); + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + Range[] getMergedCells(); + + /** + * Gets the settings used on a particular sheet + * + * @return the sheet settings + */ + SheetSettings getSettings(); + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated Use getColumnView and the CellView bean instead + */ + CellFormat getColumnFormat(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + * @deprecated Use getColumnView instead + */ + int getColumnWidth(int col); + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + * specified + */ + CellView getColumnView(int col); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the column has no + * specified format + * @deprecated use getRowView instead + */ + int getRowHeight(int row); + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row format, which may be the default format if no format + * is specified + */ + CellView getRowView(int row); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + Image getDrawing(int i); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + int[] getRowPageBreaks(); + + /** + * Accessor for the page breaks on this sheet + * + * @return the page breaks on this sheet + */ + int[] getColumnPageBreaks(); + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/SheetSettings.java b/datastructures-xslx/src/main/java/jxl/SheetSettings.java new file mode 100755 index 0000000..3eb20b2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/SheetSettings.java @@ -0,0 +1,1204 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import jxl.biff.SheetRangeImpl; +import jxl.common.Assert; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * This is a bean which client applications may use to get/set various + * properties which are associated with a particular worksheet, such + * as headers and footers, page orientation etc. + */ +public final class SheetSettings { + /** + * The default value for the default row height + */ + public static final int DEFAULT_DEFAULT_ROW_HEIGHT = 0xff; + // *** + // The defaults + // ** + private static final PageOrientation DEFAULT_ORIENTATION = + PageOrientation.PORTRAIT; + private static final PageOrder DEFAULT_ORDER = + PageOrder.RIGHT_THEN_DOWN; + private static final PaperSize DEFAULT_PAPER_SIZE = PaperSize.A4; + private static final double DEFAULT_HEADER_MARGIN = 0.5; + private static final double DEFAULT_FOOTER_MARGIN = 0.5; + private static final int DEFAULT_PRINT_RESOLUTION = 0x12c; + private static final double DEFAULT_WIDTH_MARGIN = 0.75; + private static final double DEFAULT_HEIGHT_MARGIN = 1; + private static final int DEFAULT_DEFAULT_COLUMN_WIDTH = 8; + private static final int DEFAULT_ZOOM_FACTOR = 100; + private static final int DEFAULT_NORMAL_MAGNIFICATION = 100; + private static final int DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION = 60; + /** + * The page orientation + */ + private PageOrientation orientation; + /** + * The page order + */ + private PageOrder pageOrder; + /** + * The paper size for printing + */ + private PaperSize paperSize; + /** + * Indicates whether or not this sheet is protected + */ + private boolean sheetProtected; + /** + * Indicates whether or not this sheet is hidden + */ + private boolean hidden; + /** + * Indicates whether or not this sheet is selected + */ + private boolean selected; + /** + * The header + */ + private HeaderFooter header; + /** + * The margin allocated for any page headers, in inches + */ + private double headerMargin; + /** + * The footer + */ + private HeaderFooter footer; + /** + * The margin allocated for any page footers, in inches + */ + private double footerMargin; + /** + * The scale factor used when printing + */ + private int scaleFactor; + /** + * The zoom factor used when viewing. Note the difference between + * this and the scaleFactor which is used when printing + */ + private int zoomFactor; + /** + * The page number at which to commence printing + */ + private int pageStart; + /** + * The number of pages into which this excel sheet is squeezed widthwise + */ + private int fitWidth; + /** + * The number of pages into which this excel sheet is squeezed heightwise + */ + private int fitHeight; + /** + * The horizontal print resolution + */ + private int horizontalPrintResolution; + /** + * The vertical print resolution + */ + private int verticalPrintResolution; + /** + * The margin from the left hand side of the paper in inches + */ + private double leftMargin; + /** + * The margin from the right hand side of the paper in inches + */ + private double rightMargin; + /** + * The margin from the top of the paper in inches + */ + private double topMargin; + /** + * The margin from the bottom of the paper in inches + */ + private double bottomMargin; + /** + * Indicates whether to fit the print to the pages or scale the output + * This field is manipulated indirectly by virtue of the setFitWidth/Height + * methods + */ + private boolean fitToPages; + /** + * Indicates whether grid lines should be displayed + */ + private boolean showGridLines; + /** + * Indicates whether grid lines should be printed + */ + private boolean printGridLines; + /** + * Indicates whether sheet headings should be printed + */ + private boolean printHeaders; + /** + * Indicates the view mode + */ + private boolean pageBreakPreviewMode; + /** + * Indicates whether the sheet should display zero values + */ + private boolean displayZeroValues; + /** + * The password for protected sheets + */ + private String password; + /** + * The password hashcode - used when copying sheets + */ + private int passwordHash; + /** + * The default column width, in characters + */ + private int defaultColumnWidth; + /** + * The default row height, in 1/20th of a point + */ + private int defaultRowHeight; + /** + * The horizontal freeze pane + */ + private int horizontalFreeze; + /** + * The vertical freeze position + */ + private int verticalFreeze; + /** + * Vertical centre flag + */ + private boolean verticalCentre; + /** + * Horizontal centre flag + */ + private boolean horizontalCentre; + /** + * The number of copies to print + */ + private int copies; + /** + * Automatic formula calculation + */ + private boolean automaticFormulaCalculation; + /** + * Recalculate the formulas before save + */ + private boolean recalculateFormulasBeforeSave; + /** + * The magnification factor for use during page break preview mode (in + * percent) + */ + private int pageBreakPreviewMagnification; + /** + * The magnification factor for use during normal mode (in percent) + */ + private int normalMagnification; + /** + * The print area + */ + private Range printArea; + /** + * The print row titles + */ + private Range printTitlesRow; + /** + * The print column titles + */ + private Range printTitlesCol; + + // The publicly accessible values + /** + * A handle to the sheet - used internally for ranges + */ + private final Sheet sheet; + + /** + * Default constructor + */ + public SheetSettings(Sheet s) { + sheet = s; // for internal use, when accessing ranges + orientation = DEFAULT_ORIENTATION; + pageOrder = DEFAULT_ORDER; + paperSize = DEFAULT_PAPER_SIZE; + sheetProtected = false; + hidden = false; + selected = false; + headerMargin = DEFAULT_HEADER_MARGIN; + footerMargin = DEFAULT_FOOTER_MARGIN; + horizontalPrintResolution = DEFAULT_PRINT_RESOLUTION; + verticalPrintResolution = DEFAULT_PRINT_RESOLUTION; + leftMargin = DEFAULT_WIDTH_MARGIN; + rightMargin = DEFAULT_WIDTH_MARGIN; + topMargin = DEFAULT_HEIGHT_MARGIN; + bottomMargin = DEFAULT_HEIGHT_MARGIN; + fitToPages = false; + showGridLines = true; + printGridLines = false; + printHeaders = false; + pageBreakPreviewMode = false; + displayZeroValues = true; + defaultColumnWidth = DEFAULT_DEFAULT_COLUMN_WIDTH; + defaultRowHeight = DEFAULT_DEFAULT_ROW_HEIGHT; + zoomFactor = DEFAULT_ZOOM_FACTOR; + pageBreakPreviewMagnification = DEFAULT_PAGE_BREAK_PREVIEW_MAGNIFICATION; + normalMagnification = DEFAULT_NORMAL_MAGNIFICATION; + horizontalFreeze = 0; + verticalFreeze = 0; + copies = 1; + header = new HeaderFooter(); + footer = new HeaderFooter(); + automaticFormulaCalculation = true; + recalculateFormulasBeforeSave = true; + } + + /** + * Copy constructor. Called when copying sheets + * + * @param copy the settings to copy + */ + public SheetSettings(SheetSettings copy, Sheet s) { + Assert.verify(copy != null); + + sheet = s; // for internal use when accessing ranges + orientation = copy.orientation; + pageOrder = copy.pageOrder; + paperSize = copy.paperSize; + sheetProtected = copy.sheetProtected; + hidden = copy.hidden; + selected = false; // don't copy the selected flag + headerMargin = copy.headerMargin; + footerMargin = copy.footerMargin; + scaleFactor = copy.scaleFactor; + pageStart = copy.pageStart; + fitWidth = copy.fitWidth; + fitHeight = copy.fitHeight; + horizontalPrintResolution = copy.horizontalPrintResolution; + verticalPrintResolution = copy.verticalPrintResolution; + leftMargin = copy.leftMargin; + rightMargin = copy.rightMargin; + topMargin = copy.topMargin; + bottomMargin = copy.bottomMargin; + fitToPages = copy.fitToPages; + password = copy.password; + passwordHash = copy.passwordHash; + defaultColumnWidth = copy.defaultColumnWidth; + defaultRowHeight = copy.defaultRowHeight; + zoomFactor = copy.zoomFactor; + pageBreakPreviewMagnification = copy.pageBreakPreviewMagnification; + normalMagnification = copy.normalMagnification; + showGridLines = copy.showGridLines; + displayZeroValues = copy.displayZeroValues; + pageBreakPreviewMode = copy.pageBreakPreviewMode; + horizontalFreeze = copy.horizontalFreeze; + verticalFreeze = copy.verticalFreeze; + horizontalCentre = copy.horizontalCentre; + verticalCentre = copy.verticalCentre; + copies = copy.copies; + header = new HeaderFooter(copy.header); + footer = new HeaderFooter(copy.footer); + automaticFormulaCalculation = copy.automaticFormulaCalculation; + recalculateFormulasBeforeSave = copy.recalculateFormulasBeforeSave; + + if (copy.printArea != null) { + printArea = new SheetRangeImpl + (sheet, + copy.getPrintArea().getTopLeft().getColumn(), + copy.getPrintArea().getTopLeft().getRow(), + copy.getPrintArea().getBottomRight().getColumn(), + copy.getPrintArea().getBottomRight().getRow()); + } + + if (copy.printTitlesRow != null) { + printTitlesRow = new SheetRangeImpl + (sheet, + copy.getPrintTitlesRow().getTopLeft().getColumn(), + copy.getPrintTitlesRow().getTopLeft().getRow(), + copy.getPrintTitlesRow().getBottomRight().getColumn(), + copy.getPrintTitlesRow().getBottomRight().getRow()); + } + + if (copy.printTitlesCol != null) { + printTitlesCol = new SheetRangeImpl + (sheet, + copy.getPrintTitlesCol().getTopLeft().getColumn(), + copy.getPrintTitlesCol().getTopLeft().getRow(), + copy.getPrintTitlesCol().getBottomRight().getColumn(), + copy.getPrintTitlesCol().getBottomRight().getRow()); + } + } + + /** + * Accessor for the orientation + * + * @return the orientation + */ + public PageOrientation getOrientation() { + return orientation; + } + + /** + * Sets the paper orientation for printing this sheet + * + * @param po the orientation + */ + public void setOrientation(PageOrientation po) { + orientation = po; + } + + /** + * Accessor for the order + * + * @return + */ + public PageOrder getPageOrder() { + return pageOrder; + } + + /** + * Sets the page order for printing this sheet + * + * @param order + */ + public void setPageOrder(PageOrder order) { + this.pageOrder = order; + } + + /** + * Accessor for the paper size + * + * @return the paper size + */ + public PaperSize getPaperSize() { + return paperSize; + } + + /** + * Sets the paper size to be used when printing this sheet + * + * @param ps the paper size + */ + public void setPaperSize(PaperSize ps) { + paperSize = ps; + } + + /** + * Queries whether this sheet is protected (ie. read only) + * + * @return TRUE if this sheet is read only, FALSE otherwise + */ + public boolean isProtected() { + return sheetProtected; + } + + /** + * Sets the protected (ie. read only) status of this sheet + * + * @param p the protected status + */ + public void setProtected(boolean p) { + sheetProtected = p; + } + + /** + * Accessor for the header margin + * + * @return the header margin + */ + public double getHeaderMargin() { + return headerMargin; + } + + /** + * Sets the margin for any page headers + * + * @param d the margin in inches + */ + public void setHeaderMargin(double d) { + headerMargin = d; + } + + /** + * Accessor for the footer margin + * + * @return the footer margin + */ + public double getFooterMargin() { + return footerMargin; + } + + /** + * Sets the margin for any page footer + * + * @param d the footer margin in inches + */ + public void setFooterMargin(double d) { + footerMargin = d; + } + + /** + * Accessor for the hidden nature of this sheet + * + * @return TRUE if this sheet is hidden, FALSE otherwise + */ + public boolean isHidden() { + return hidden; + } + + /** + * Sets the hidden status of this worksheet + * + * @param h the hidden flag + */ + public void setHidden(boolean h) { + hidden = h; + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @deprecated use overloaded version which takes a boolean + */ + public void setSelected() { + setSelected(true); + } + + /** + * Accessor for the selected nature of the sheet + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() { + return selected; + } + + /** + * Sets this sheet to be when it is opened in excel + * + * @param s sets whether this sheet is selected or not + */ + public void setSelected(boolean s) { + selected = s; + } + + /** + * Accessor for the scale factor + * + * @return the scale factor + */ + public int getScaleFactor() { + return scaleFactor; + } + + /** + * Sets the scale factor for this sheet to be used when printing. The + * parameter is a percentage, therefore setting a scale factor of 100 will + * print at normal size, 50 half size, 200 double size etc + * + * @param sf the scale factor as a percentage + */ + public void setScaleFactor(int sf) { + scaleFactor = sf; + fitToPages = false; + } + + /** + * Accessor for the page start + * + * @return the page start + */ + public int getPageStart() { + return pageStart; + } + + /** + * Sets the page number at which to commence printing + * + * @param ps the page start number + */ + public void setPageStart(int ps) { + pageStart = ps; + } + + /** + * Accessor for the fit width + * + * @return the number of pages this sheet will be printed into widthwise + */ + public int getFitWidth() { + return fitWidth; + } + + /** + * Sets the number of pages widthwise which this sheet should be + * printed into + * + * @param fw the number of pages + */ + public void setFitWidth(int fw) { + fitWidth = fw; + fitToPages = true; + } + + /** + * Accessor for the fit height + * + * @return the number of pages this sheet will be printed into heightwise + */ + public int getFitHeight() { + return fitHeight; + } + + /** + * Sets the number of pages vertically that this sheet will be printed into + * + * @param fh the number of pages this sheet will be printed into heightwise + */ + public void setFitHeight(int fh) { + fitHeight = fh; + fitToPages = true; + } + + /** + * Accessor for the horizontal print resolution + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() { + return horizontalPrintResolution; + } + + /** + * Sets the horizontal print resolution + * + * @param hpw the print resolution + */ + public void setHorizontalPrintResolution(int hpw) { + horizontalPrintResolution = hpw; + } + + /** + * Accessor for the vertical print resolution + * + * @return the vertical print resolution + */ + public int getVerticalPrintResolution() { + return verticalPrintResolution; + } + + /** + * Sets the vertical print reslution + * + * @param vpw the vertical print resolution + */ + public void setVerticalPrintResolution(int vpw) { + verticalPrintResolution = vpw; + } + + /** + * Accessor for the right margin + * + * @return the right margin in inches + */ + public double getRightMargin() { + return rightMargin; + } + + /** + * Sets the right margin + * + * @param m the right margin in inches + */ + public void setRightMargin(double m) { + rightMargin = m; + } + + /** + * Accessor for the left margin + * + * @return the left margin in inches + */ + public double getLeftMargin() { + return leftMargin; + } + + /** + * Sets the left margin + * + * @param m the left margin in inches + */ + public void setLeftMargin(double m) { + leftMargin = m; + } + + /** + * Accessor for the top margin + * + * @return the top margin in inches + */ + public double getTopMargin() { + return topMargin; + } + + /** + * Sets the top margin + * + * @param m the top margin in inches + */ + public void setTopMargin(double m) { + topMargin = m; + } + + /** + * Accessor for the bottom margin + * + * @return the bottom margin in inches + */ + public double getBottomMargin() { + return bottomMargin; + } + + /** + * Sets the bottom margin + * + * @param m the bottom margin in inches + */ + public void setBottomMargin(double m) { + bottomMargin = m; + } + + /** + * Gets the default margin width + * + * @return the default margin width + */ + public double getDefaultWidthMargin() { + return DEFAULT_WIDTH_MARGIN; + } + + /** + * Gets the default margin height + * + * @return the default margin height + */ + public double getDefaultHeightMargin() { + return DEFAULT_HEIGHT_MARGIN; + } + + /** + * Accessor for the fit width print flag + * + * @return TRUE if the print is to fit to pages, false otherwise + */ + public boolean getFitToPages() { + return fitToPages; + } + + /** + * Accessor for the fit to pages flag + * + * @param b TRUE to fit to pages, FALSE to use a scale factor + */ + public void setFitToPages(boolean b) { + fitToPages = b; + } + + /** + * Accessor for the password + * + * @return the password to unlock this sheet, or NULL if not protected + */ + public String getPassword() { + return password; + } + + /** + * Sets the password for this sheet + * + * @param s the password + */ + public void setPassword(String s) { + password = s; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @return passwordHash + */ + public int getPasswordHash() { + return passwordHash; + } + + /** + * Accessor for the password hash - used only when copying sheets + * + * @param ph the password hash + */ + public void setPasswordHash(int ph) { + passwordHash = ph; + } + + /** + * Accessor for the default column width + * + * @return the default column width, in characters + */ + public int getDefaultColumnWidth() { + return defaultColumnWidth; + } + + /** + * Sets the default column width + * + * @param w the new default column width + */ + public void setDefaultColumnWidth(int w) { + defaultColumnWidth = w; + } + + /** + * Accessor for the default row height + * + * @return the default row height, in 1/20ths of a point + */ + public int getDefaultRowHeight() { + return defaultRowHeight; + } + + /** + * Sets the default row height + * + * @param h the default row height, in 1/20ths of a point + */ + public void setDefaultRowHeight(int h) { + defaultRowHeight = h; + } + + /** + * Accessor for the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @return the zoom factor as a percentage + */ + public int getZoomFactor() { + return zoomFactor; + } + + /** + * Sets the zoom factor. Do not confuse zoom factor (which relates + * to the on screen view) with scale factor (which refers to the scale factor + * when printing) + * + * @param zf the zoom factor as a percentage + */ + public void setZoomFactor(int zf) { + zoomFactor = zf; + } + + /** + * Accessor for the page break preview mangificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getPageBreakPreviewMagnification() { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the page break preview magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setPageBreakPreviewMagnification(int f) { + pageBreakPreviewMagnification = f; + } + + /** + * Accessor for the nomral view magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @return the page break preview magnification a percentage + */ + public int getNormalMagnification() { + return normalMagnification; + } + + /** + * Accessor for the normal magnificaton factor. + * Do not confuse zoom factor or scale factor + * + * @param f the page break preview magnification as a percentage + */ + public void setNormalMagnification(int f) { + normalMagnification = f; + } + + + /** + * Accessor for the displayZeroValues property + * + * @return TRUE to display zero values, FALSE not to bother + */ + public boolean getDisplayZeroValues() { + return displayZeroValues; + } + + /** + * Sets the displayZeroValues property + * + * @param b TRUE to show zero values, FALSE not to bother + */ + public void setDisplayZeroValues(boolean b) { + displayZeroValues = b; + } + + /** + * Accessor for the showGridLines property + * + * @return TRUE if grid lines will be shown, FALSE otherwise + */ + public boolean getShowGridLines() { + return showGridLines; + } + + /** + * Sets the showGridLines property + * + * @param b TRUE to show grid lines on this sheet, FALSE otherwise + */ + public void setShowGridLines(boolean b) { + showGridLines = b; + } + + /** + * Accessor for the pageBreakPreview mode + * + * @return TRUE if page break preview is enabled, FALSE otherwise + */ + public boolean getPageBreakPreviewMode() { + return pageBreakPreviewMode; + } + + /** + * Sets the pageBreakPreviewMode property + * + * @param b TRUE to launch in page break preview mode, FALSE otherwise + */ + public void setPageBreakPreviewMode(boolean b) { + pageBreakPreviewMode = b; + } + + /** + * Accessor for the printGridLines property + * + * @return TRUE if grid lines will be printed, FALSE otherwise + */ + public boolean getPrintGridLines() { + return printGridLines; + } + + /** + * Sets the printGridLines property + * + * @param b TRUE to print grid lines on this sheet, FALSE otherwise + */ + public void setPrintGridLines(boolean b) { + printGridLines = b; + } + + /** + * Accessor for the printHeaders property + * + * @return TRUE if headers will be printed, FALSE otherwise + */ + public boolean getPrintHeaders() { + return printHeaders; + } + + /** + * Sets the printHeaders property + * + * @param b TRUE to print headers on this sheet, FALSE otherwise + */ + public void setPrintHeaders(boolean b) { + printHeaders = b; + } + + /** + * Gets the row at which the pane is frozen horizontally + * + * @return the row at which the pane is horizontally frozen, or 0 if there + * is no freeze + */ + public int getHorizontalFreeze() { + return horizontalFreeze; + } + + /** + * Sets the row at which the pane is frozen horizontally + * + * @param row the row number to freeze at + */ + public void setHorizontalFreeze(int row) { + horizontalFreeze = Math.max(row, 0); + } + + /** + * Gets the column at which the pane is frozen vertically + * + * @return the column at which the pane is vertically frozen, or 0 if there + * is no freeze + */ + public int getVerticalFreeze() { + return verticalFreeze; + } + + /** + * Sets the row at which the pane is frozen vertically + * + * @param col the column number to freeze at + */ + public void setVerticalFreeze(int col) { + verticalFreeze = Math.max(col, 0); + } + + /** + * Accessor for the number of copies to print + * + * @return the number of copies + */ + public int getCopies() { + return copies; + } + + /** + * Sets the number of copies + * + * @param c the number of copies + */ + public void setCopies(int c) { + copies = c; + } + + /** + * Accessor for the header + * + * @return the header + */ + public HeaderFooter getHeader() { + return header; + } + + /** + * Sets the header + * + * @param h the header + */ + public void setHeader(HeaderFooter h) { + header = h; + } + + /** + * Accessor for the footer + * + * @return the footer + */ + public HeaderFooter getFooter() { + return footer; + } + + /** + * Sets the footer + * + * @param f the footer + */ + public void setFooter(HeaderFooter f) { + footer = f; + } + + /** + * Accessor for the horizontal centre + * + * @return Returns the horizontalCentre. + */ + public boolean isHorizontalCentre() { + return horizontalCentre; + } + + /** + * Sets the horizontal centre + * + * @param horizCentre The horizontalCentre to set. + */ + public void setHorizontalCentre(boolean horizCentre) { + this.horizontalCentre = horizCentre; + } + + /** + * Accessor for the vertical centre + * + * @return Returns the verticalCentre. + */ + public boolean isVerticalCentre() { + return verticalCentre; + } + + /** + * Sets the vertical centre + * + * @param vertCentre The verticalCentre to set. + */ + public void setVerticalCentre(boolean vertCentre) { + this.verticalCentre = vertCentre; + } + + /** + * Retrieves the automatic formula calculation flag + * + * @return TRUE if formulas are calculated automatically, FALSE if they + * are calculated manually + */ + public boolean getAutomaticFormulaCalculation() { + return automaticFormulaCalculation; + } + + /** + * Sets the automatic formula calculation flag + * + * @param auto - TRUE to automatically calculate the formulas, + * FALSE otherwise + */ + public void setAutomaticFormulaCalculation(boolean auto) { + automaticFormulaCalculation = auto; + } + + /** + * Retrieves the recalculate formulas before save flag + * + * @return TRUE if formulas are calculated before the sheet is saved, + * FALSE otherwise + */ + public boolean getRecalculateFormulasBeforeSave() { + return recalculateFormulasBeforeSave; + } + + /** + * Sets the recalculate formulas when the sheet is saved flag + * + * @param recalc - TRUE to automatically calculate the formulas when the, + * spreadsheet is saved, FALSE otherwise + */ + public void setRecalculateFormulasBeforeSave(boolean recalc) { + recalculateFormulasBeforeSave = recalc; + } + + /** + * Sets the print area for this sheet + * + * @param firstCol the first column of the print area + * @param firstRow the first row of the print area + * @param lastCol the last column of the print area + * @param lastRow the last row of the print area + */ + public void setPrintArea(int firstCol, + int firstRow, + int lastCol, + int lastRow) { + printArea = new SheetRangeImpl(sheet, firstCol, firstRow, + lastCol, lastRow); + } + + /** + * Accessor for the print area + * + * @return the print area, or NULL if one is not defined for this sheet + */ + public Range getPrintArea() { + return printArea; + } + + /** + * Sets both of the print titles for this sheet + * + * @param firstRow the first row of the print row titles + * @param lastRow the last row of the print row titles + * @param firstCol the first column of the print column titles + * @param lastCol the last column of the print column titles + */ + public void setPrintTitles(int firstRow, + int lastRow, + int firstCol, + int lastCol) { + setPrintTitlesRow(firstRow, lastRow); + setPrintTitlesCol(firstCol, lastCol); + } + + /** + * Sets the print row titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesRow(int firstRow, + int lastRow) { + printTitlesRow = new SheetRangeImpl(sheet, 0, firstRow, + 255, lastRow); + } + + /** + * Sets the print column titles for this sheet + * + * @param firstRow the first row of the print titles + * @param lastRow the last row of the print titles + */ + public void setPrintTitlesCol(int firstCol, + int lastCol) { + printTitlesCol = new SheetRangeImpl(sheet, firstCol, 0, + lastCol, 65535); + } + + /** + * Accessor for the print row titles + * + * @return the print row titles, or NULL if one is not defined for this sheet + */ + public Range getPrintTitlesRow() { + return printTitlesRow; + } + + /** + * Accessor for the print column titles + * + * @return the print column titles, or NULL if one is not defined for this + * sheet + */ + public Range getPrintTitlesCol() { + return printTitlesCol; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java b/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java new file mode 100755 index 0000000..4a1793a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/StringFormulaCell.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +/** + * A mixin interface for numerical formulas, which combines the interfaces + * for formulas and for strings + */ +public interface StringFormulaCell extends LabelCell, FormulaCell { +} + + diff --git a/datastructures-xslx/src/main/java/jxl/Workbook.java b/datastructures-xslx/src/main/java/jxl/Workbook.java new file mode 100755 index 0000000..97315a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/Workbook.java @@ -0,0 +1,397 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import jxl.read.biff.BiffException; +import jxl.read.biff.File; +import jxl.read.biff.PasswordException; +import jxl.read.biff.WorkbookParser; +import jxl.write.WritableWorkbook; +import jxl.write.biff.WritableWorkbookImpl; + +/** + * Represents a Workbook. Contains the various factory methods and provides + * a variety of accessors which provide access to the work sheets. + */ +public abstract class Workbook { + /** + * The current version of the software + */ + private static final String VERSION = "2.6.12"; + + /** + * The constructor + */ + protected Workbook() { + } + + /** + * Accessor for the software version + * + * @return the version + */ + public static String getVersion() { + return VERSION; + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param file the excel 97 spreadsheet to parse + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(java.io.File file) + throws IOException, BiffException { + return getWorkbook(file, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param file the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(java.io.File file, WorkbookSettings ws) + throws IOException, BiffException { + FileInputStream fis = new FileInputStream(file); + + // Always close down the input stream, regardless of whether or not the + // file can be parsed. Thanks to Steve Hahn for this + File dataFile = null; + + try { + dataFile = new File(fis, ws); + } catch (IOException e) { + fis.close(); + throw e; + } catch (BiffException e) { + fis.close(); + throw e; + } + + fis.close(); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(InputStream is) + throws IOException, BiffException { + return getWorkbook(is, new WorkbookSettings()); + } + + /** + * A factory method which takes in an excel file and reads in the contents. + * + * @param is an open stream which is the the excel 97 spreadsheet to parse + * @param ws the settings for the workbook + * @return a workbook instance + * @throws IOException + * @throws BiffException + */ + public static Workbook getWorkbook(InputStream is, WorkbookSettings ws) + throws IOException, BiffException { + File dataFile = new File(is, ws); + + Workbook workbook = new WorkbookParser(dataFile, ws); + workbook.parse(); + + return workbook; + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file) + throws IOException { + return createWorkbook(file, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given file name + * + * @param file the file to copy from + * @param ws the global workbook settings + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + WorkbookSettings ws) + throws IOException { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, true, ws); + return w; + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in) + throws IOException { + return createWorkbook(file, in, new WorkbookSettings()); + } + + /** + * Creates a writable workbook with the given filename as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param file the output file for the copy + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + */ + public static WritableWorkbook createWorkbook(java.io.File file, + Workbook in, + WorkbookSettings ws) + throws IOException { + FileOutputStream fos = new FileOutputStream(file); + WritableWorkbook w = new WritableWorkbookImpl(fos, in, true, ws); + return w; + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the stream to write to + * @param in the workbook to copy + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in) + throws IOException { + return createWorkbook(os, in, ((WorkbookParser) in).getSettings()); + } + + /** + * Creates a writable workbook as a copy of + * the workbook passed in. Once created, the contents of the writable + * workbook may be modified + * + * @param os the output stream to write to + * @param in the workbook to copy + * @param ws the configuration for this workbook + * @return a writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + Workbook in, + WorkbookSettings ws) + throws IOException { + WritableWorkbook w = new WritableWorkbookImpl(os, in, false, ws); + return w; + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @return the writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os) + throws IOException { + return createWorkbook(os, new WorkbookSettings()); + } + + /** + * Creates a writable workbook. When the workbook is closed, + * it will be streamed directly to the output stream. In this + * manner, a generated excel spreadsheet can be passed from + * a servlet to the browser over HTTP + * + * @param os the output stream + * @param ws the configuration for this workbook + * @return the writable workbook + * @throws IOException + */ + public static WritableWorkbook createWorkbook(OutputStream os, + WorkbookSettings ws) + throws IOException { + WritableWorkbook w = new WritableWorkbookImpl(os, false, ws); + return w; + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract Sheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @throws IndexOutOfBoundException when index refers to a non-existent + * sheet + */ + public abstract Sheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook. + * As described in the accompanying technical notes, each call + * to getSheet forces a reread of the sheet (for memory reasons). + * Therefore, do not make unnecessary calls to this method. Furthermore, + * do not hold unnecessary references to Sheets in client code, as + * this will prevent the garbage collector from freeing the memory + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract Sheet getSheet(String name); + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned. + * This is a convenience function to quickly access the contents + * of a single cell. If you need further information (such as the + * sheet or adjacent cells in the range) use the functionally + * richer method, findByName which returns a list of ranges + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract Cell findCellByName(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract Cell getCell(String loc); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells, or NULL if the range does not exist + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + /** + * Determines whether the sheet is protected + * + * @return TRUE if the workbook is protected, FALSE otherwise + */ + public abstract boolean isProtected(); + + /** + * Parses the excel file. + * If the workbook is password protected a PasswordException is thrown + * in case consumers of the API wish to handle this in a particular way + * + * @throws BiffException + * @throws PasswordException + */ + protected abstract void parse() throws BiffException, PasswordException; + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public abstract void close(); +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java b/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java new file mode 100755 index 0000000..510aa2c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/WorkbookSettings.java @@ -0,0 +1,798 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl; + +import java.io.File; +import java.util.HashMap; +import java.util.Locale; +import jxl.biff.CountryCode; +import jxl.biff.formula.FunctionNames; +import jxl.common.Logger; + +/** + * This is a bean which client applications may use to set various advanced + * workbook properties. Use of this bean is not mandatory, and its absence + * will merely result in workbooks being read/written using the default + * settings + */ +public final class WorkbookSettings { + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_HIDE_ALL = 2; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_SHOW_PLACEHOLDERS = 1; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + public final static int HIDEOBJ_SHOW_ALL = 0; + // ** + // The default values + // ** + private static final int DEFAULT_INITIAL_FILE_SIZE = 5 * 1024 * 1024; + // 5 megabytes + private static final int DEFAULT_ARRAY_GROW_SIZE = 1024 * 1024; // 1 megabyte + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WorkbookSettings.class); + /** + * The amount of memory allocated to store the workbook data when + * reading a worksheet. For processeses reading many small workbooks inside + * a WAS it might be necessary to reduce the default size + */ + private int initialFileSize; + /** + * The amount of memory allocated to the array containing the workbook + * data when its current amount is exhausted. + */ + private int arrayGrowSize; + /** + * Flag to indicate whether the drawing feature is enabled or not + * Drawings deactivated using -Djxl.nodrawings=true on the JVM command line + * Activated by default or by using -Djxl.nodrawings=false on the JVM command + * line + */ + private boolean drawingsDisabled; + /** + * Flag to indicate whether the name feature is enabled or not + * Names deactivated using -Djxl.nonames=true on the JVM command line + * Activated by default or by using -Djxl.nonames=false on the JVM command + * line + */ + private boolean namesDisabled; + /** + * Flag to indicate whether formula cell references should be adjusted + * following row/column insertion/deletion + */ + private boolean formulaReferenceAdjustDisabled; + /** + * Flag to indicate whether the system hint garbage collection + * is enabled or not. + * As a rule of thumb, it is desirable to enable garbage collection + * when reading large spreadsheets from a batch process or from the + * command line, but better to deactivate the feature when reading + * large spreadsheets within a WAS, as the calls to System.gc() not + * only garbage collect the junk in JExcelApi, but also in the + * webservers JVM and can cause significant slowdown + * GC deactivated using -Djxl.nogc=true on the JVM command line + * Activated by default or by using -Djxl.nogc=false on the JVM command line + */ + private boolean gcDisabled; + /** + * Flag to indicate whether the rationalization of cell formats is + * disabled or not. + * Rationalization is enabled by default, but may be disabled for + * performance reasons. It can be deactivated using -Djxl.norat=true on + * the JVM command line + */ + private boolean rationalizationDisabled; + /** + * Flag to indicate whether or not the merged cell checking has been + * disabled + */ + private boolean mergedCellCheckingDisabled; + /** + * Flag to indicate whether the copying of additional property sets + * are disabled + */ + private boolean propertySetsDisabled; + /** + * Flag to indicate that cell validation criteria are ignored + */ + private boolean cellValidationDisabled; + /** + * Flag to indicate whether or not to ignore blank cells when processing + * sheets. Cells which are identified as blank can still have associated + * cell formats which the processing program may still need to read + */ + private boolean ignoreBlankCells; + /** + * Flag to indicate whether auto filtering should be read/copied + */ + private boolean autoFilterDisabled; + /** + * Flag to indicate whether a temporary file should be used when + * writing out the workbook + */ + private boolean useTemporaryFileDuringWrite; + /** + * The directory for used for the temporary file during write. If this + * is NULL, the default system directory is used + */ + private File temporaryFileDuringWriteDirectory; + /** + * The locale. Normally this is the same as the system locale, but there + * may be cases (eg. where you are uploading many spreadsheets from foreign + * sources) where you may want to specify the locale on an individual + * worksheet basis + * The locale may also be specified on the command line using the lang and + * country System properties eg. -Djxl.lang=en -Djxl.country=UK for UK + * English + */ + private Locale locale; + /** + * The locale specific function names for this workbook + */ + private FunctionNames functionNames; + /** + * The character encoding used for reading non-unicode strings. This can + * be different from the default platform encoding if processing spreadsheets + * from abroad. This may also be set using the system property jxl.encoding + */ + private String encoding; + /** + * The character set used by the readable spreadsheeet + */ + private int characterSet; + /** + * The display language used by Excel (ISO 3166 mnemonic) + */ + private String excelDisplayLanguage; + /** + * The regional settings used by Excel (ISO 3166 mnemonic) + */ + private String excelRegionalSettings; + /** + * A hash map of function names keyed on locale + */ + private final HashMap localeFunctionNames; + /** + * Flag to indicate whether all external data and pivot stuff should + * refreshed + */ + private boolean refreshAll; + /** + * Flag to indicate whether the file is a template or not (Usually with .xlt + * file name extension) + */ + private boolean template; + /** + * Flag to indicate whether the file has been written by excel 2000. + *
+ * The EXCEL9FILE record indicates the file was written by Excel 2000. It has + * no record data field and is C0010000h. Any application other than Excel + * 2000 that edits the file should not write out this record. + *
+ * However, it seemas that excel 2003 + 2007 still set this flag.... + */ + private boolean excel9file = false; + /** + * The WINDOWPROTECT record stores an option from the Protect Workbook + * dialog box. + *
+ * =1 if the workbook windows are protected + */ + private boolean windowProtected; + /** + * Write access user name. + * When not set (null) then we set it to Java Excel API + Version number + */ + private String writeAccess; + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab. + */ + private int hideobj; + + /** + * Default constructor + */ + public WorkbookSettings() { + initialFileSize = DEFAULT_INITIAL_FILE_SIZE; + arrayGrowSize = DEFAULT_ARRAY_GROW_SIZE; + localeFunctionNames = new HashMap(); + excelDisplayLanguage = CountryCode.USA.getCode(); + excelRegionalSettings = CountryCode.UK.getCode(); + refreshAll = false; + template = false; + excel9file = false; + windowProtected = false; + hideobj = HIDEOBJ_SHOW_ALL; + + // Initialize other properties from the system properties + try { + boolean suppressWarnings = Boolean.getBoolean("jxl.nowarnings"); + setSuppressWarnings(suppressWarnings); + drawingsDisabled = Boolean.getBoolean("jxl.nodrawings"); + namesDisabled = Boolean.getBoolean("jxl.nonames"); + gcDisabled = Boolean.getBoolean("jxl.nogc"); + rationalizationDisabled = Boolean.getBoolean("jxl.norat"); + mergedCellCheckingDisabled = + Boolean.getBoolean("jxl.nomergedcellchecks"); + formulaReferenceAdjustDisabled = + Boolean.getBoolean("jxl.noformulaadjust"); + propertySetsDisabled = Boolean.getBoolean("jxl.nopropertysets"); + ignoreBlankCells = Boolean.getBoolean("jxl.ignoreblanks"); + cellValidationDisabled = Boolean.getBoolean("jxl.nocellvalidation"); + autoFilterDisabled = !Boolean.getBoolean("jxl.autofilter"); + // autofilter currently disabled by default + useTemporaryFileDuringWrite = + Boolean.getBoolean("jxl.usetemporaryfileduringwrite"); + String tempdir = + System.getProperty("jxl.temporaryfileduringwritedirectory"); + + if (tempdir != null) { + temporaryFileDuringWriteDirectory = new File(tempdir); + } + + encoding = System.getProperty("file.encoding"); + } catch (SecurityException e) { + logger.warn("Error accessing system properties.", e); + } + + // Initialize the locale to the system locale + try { + if (System.getProperty("jxl.lang") == null || + System.getProperty("jxl.country") == null) { + locale = Locale.getDefault(); + } else { + locale = new Locale(System.getProperty("jxl.lang"), + System.getProperty("jxl.country")); + } + + if (System.getProperty("jxl.encoding") != null) { + encoding = System.getProperty("jxl.encoding"); + } + } catch (SecurityException e) { + logger.warn("Error accessing system properties.", e); + locale = Locale.getDefault(); + } + } + + /** + * Accessor for the array grow size property + * + * @return the array grow size + */ + public int getArrayGrowSize() { + return arrayGrowSize; + } + + /** + * Sets the amount of memory by which to increase the amount of + * memory allocated to storing the workbook data. + * For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 1 megabyte + * + * @param sz the file size in bytes + */ + public void setArrayGrowSize(int sz) { + arrayGrowSize = sz; + } + + /** + * Accessor for the initial file size property + * + * @return the initial file size + */ + public int getInitialFileSize() { + return initialFileSize; + } + + /** + * Sets the initial amount of memory allocated to store the workbook data + * when reading a worksheet. For processeses reading many small workbooks + * inside a WAS it might be necessary to reduce the default size + * Default value is 5 megabytes + * + * @param sz the file size in bytes + */ + public void setInitialFileSize(int sz) { + initialFileSize = sz; + } + + /** + * Gets the drawings disabled flag + * + * @return TRUE if drawings are disabled, FALSE otherwise + */ + public boolean getDrawingsDisabled() { + return drawingsDisabled; + } + + /** + * Disables the handling of drawings + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setDrawingsDisabled(boolean b) { + drawingsDisabled = b; + } + + /** + * Accessor for the disabling of garbage collection + * + * @return FALSE if JExcelApi hints for garbage collection, TRUE otherwise + */ + public boolean getGCDisabled() { + return gcDisabled; + } + + /** + * Sets the garbage collection disabled + * + * @param disabled TRUE to disable garbage collection, FALSE to enable it + */ + public void setGCDisabled(boolean disabled) { + gcDisabled = disabled; + } + + /** + * Accessor for the disabling of interpretation of named ranges + * + * @return FALSE if named cells are interpreted, TRUE otherwise + */ + public boolean getNamesDisabled() { + return namesDisabled; + } + + /** + * Disables the handling of names + * + * @param b TRUE to disable the names feature, FALSE otherwise + */ + public void setNamesDisabled(boolean b) { + namesDisabled = b; + } + + /** + * Sets whether or not to rationalize the cell formats before + * writing out the sheet. The default value is true + * + * @param r the rationalization flag + */ + public void setRationalization(boolean r) { + rationalizationDisabled = !r; + } + + /** + * Accessor to retrieve the rationalization flag + * + * @return TRUE if rationalization is off, FALSE if rationalization is on + */ + public boolean getRationalizationDisabled() { + return rationalizationDisabled; + } + + /** + * Accessor to retrieve the merged cell checking flag + * + * @return TRUE if merged cell checking is off, FALSE if it is on + */ + public boolean getMergedCellCheckingDisabled() { + return mergedCellCheckingDisabled; + } + + /** + * Accessor to set the merged cell checking + * + * @param b - TRUE to enable merged cell checking, FALSE otherwise + */ + public void setMergedCellChecking(boolean b) { + mergedCellCheckingDisabled = !b; + } + + /** + * Sets whether or not to enable any property sets (such as macros) + * to be copied along with the workbook + * Leaving this feature enabled will result in the JXL process using + * more memory + * + * @param r the property sets flag + */ + public void setPropertySets(boolean r) { + propertySetsDisabled = !r; + } + + /** + * Accessor to retrieve the property sets disabled flag + * + * @return TRUE if property sets are disabled, FALSE otherwise + */ + public boolean getPropertySetsDisabled() { + return propertySetsDisabled; + } + + /** + * Accessor to set the suppress warnings flag. Due to the change + * in logging in version 2.4, this will now set the warning + * behaviour across the JVM (depending on the type of logger used) + * + * @param w the flag + */ + public void setSuppressWarnings(boolean w) { + logger.setSuppressWarnings(w); + } + + /** + * Accessor for the formula adjust disabled + * + * @return TRUE if formulas are adjusted following row/column inserts/deletes + * FALSE otherwise + */ + public boolean getFormulaAdjust() { + return !formulaReferenceAdjustDisabled; + } + + /** + * Setter for the formula adjust disabled property + * + * @param b TRUE to adjust formulas, FALSE otherwise + */ + public void setFormulaAdjust(boolean b) { + formulaReferenceAdjustDisabled = !b; + } + + /** + * Returns the locale used by JExcelAPI to read the spreadsheet + * + * @return the locale + */ + public Locale getLocale() { + return locale; + } + + /** + * Sets the locale used by JExcelApi to generate the spreadsheet. + * Setting this value has no effect on the language or region of + * the generated excel file + * + * @param l the locale + */ + public void setLocale(Locale l) { + locale = l; + } + + /** + * Accessor for the character encoding + * + * @return the character encoding for this workbook + */ + public String getEncoding() { + return encoding; + } + + /** + * Sets the encoding for this workbook + * + * @param enc the encoding + */ + public void setEncoding(String enc) { + encoding = enc; + } + + /** + * Gets the function names. This is used by the formula parsing package + * in order to get the locale specific function names for this particular + * workbook + * + * @return the list of function names + */ + public FunctionNames getFunctionNames() { + if (functionNames == null) { + functionNames = (FunctionNames) localeFunctionNames.get(locale); + + // have not previously accessed function names for this locale, + // so create a brand new one and add it to the list + if (functionNames == null) { + functionNames = new FunctionNames(locale); + localeFunctionNames.put(locale, functionNames); + } + } + + return functionNames; + } + + /** + * Accessor for the character set. This value is only used for reading + * and has no effect when writing out the spreadsheet + * + * @return the character set used by this spreadsheet + */ + public int getCharacterSet() { + return characterSet; + } + + /** + * Sets the character set. This is only used when the spreadsheet is + * read, and has no effect when the spreadsheet is written + * + * @param cs the character set encoding value + */ + public void setCharacterSet(int cs) { + characterSet = cs; + } + + /** + * Accessor for the ignore blanks flag + * + * @return TRUE if blank cells are being ignored, FALSE otherwise + */ + public boolean getIgnoreBlanks() { + return ignoreBlankCells; + } + + /** + * Sets the ignore blanks flag + * + * @param ignoreBlanks TRUE to ignore blanks, FALSE to take them into account + */ + public void setIgnoreBlanks(boolean ignoreBlanks) { + ignoreBlankCells = ignoreBlanks; + } + + /** + * Accessor for the ignore cell validation + * + * @return TRUE if cell validation is disabled + */ + public boolean getCellValidationDisabled() { + return cellValidationDisabled; + } + + /** + * Sets the ignore cell validation flag + * + * @param cv TRUE to disable cell validation, FALSE to enable it + */ + public void setCellValidationDisabled(boolean cv) { + cellValidationDisabled = cv; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for user + * language displayto display + * + * @return the display language + */ + public String getExcelDisplayLanguage() { + return excelDisplayLanguage; + } + + /** + * Sets the language in which the generated file will display + * + * @param code the two character ISO 3166 country code + */ + public void setExcelDisplayLanguage(String code) { + excelDisplayLanguage = code; + } + + /** + * Returns the two character ISO 3166 mnemonic used by excel for + * its regional settings + * + * @return the regional settings + */ + public String getExcelRegionalSettings() { + return excelRegionalSettings; + } + + /** + * Sets the regional settings for the generated excel file + * + * @param code the two character ISO 3166 country code + */ + public void setExcelRegionalSettings(String code) { + excelRegionalSettings = code; + } + + /** + * Accessor for the autofilter disabled feature + * + * @return TRUE if autofilter is disabled, FALSE otherwise + */ + public boolean getAutoFilterDisabled() { + return autoFilterDisabled; + } + + /** + * Sets the autofilter disabled + * + * @param disabled + */ + public void setAutoFilterDisabled(boolean disabled) { + autoFilterDisabled = disabled; + } + + /** + * Accessor for the temporary file during write. If this is set, then + * when the workbook is written a temporary file will be used to store + * the interim binary data, otherwise it will take place in memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public boolean getUseTemporaryFileDuringWrite() { + return useTemporaryFileDuringWrite; + } + + /** + * Sets whether a temporary file is used during the generation of + * the workbook. If not set, the workbook will take place entirely in + * memory. Setting + * this flag involves an assessment of the trade-offs between memory usage + * and performance + * + * @return TRUE if a temporary is file is used during writing, + * FALSE otherwise + */ + public void setUseTemporaryFileDuringWrite(boolean temp) { + useTemporaryFileDuringWrite = temp; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. This value can + * be NULL, in which case the normal system default temporary directory + * is used instead + * + * @return the temporary directory used during write, or NULL if it is + * not set + */ + public File getTemporaryFileDuringWriteDirectory() { + return temporaryFileDuringWriteDirectory; + } + + /** + * Used in conjunction with the UseTemporaryFileDuringWrite setting to + * set the target directory for the temporary files. If this is not set, + * the system default temporary directory is used. + * This has no effect unless the useTemporaryFileDuringWrite setting + * is TRUE + * + * @param dir the directory to which temporary files should be written + */ + public void setTemporaryFileDuringWriteDirectory(File dir) { + temporaryFileDuringWriteDirectory = dir; + } + + /** + * When true then Refresh All should be done on all external data ranges and + * PivotTables when loading the workbook (the default is =0) + * + * @return the refreshAll value + */ + public boolean getRefreshAll() { + return refreshAll; + } + + /** + * When true then Refresh All should be done on all external data ranges and + * PivotTables when loading the workbook (the default is =0) + * + * @param refreshAll the refreshAll to set + */ + public void setRefreshAll(boolean refreshAll) { + this.refreshAll = refreshAll; + } + + /** + * Workbook Is a Template + * + * @return the template + */ + public boolean getTemplate() { + return template; + } + + /** + * Workbook Is a Template + * + * @param template the template to set + */ + public void setTemplate(boolean template) { + this.template = template; + } + + /** + * Has this file been written by excel 2000? + * + * @return the excel9file + */ + public boolean getExcel9File() { + return excel9file; + } + + /** + * @param excel9file the excel9file to set + */ + public void setExcel9File(boolean excel9file) { + this.excel9file = excel9file; + } + + /** + * @return the windowprotected + */ + public boolean getWindowProtected() { + return windowProtected; + } + + /** + * @param windowprotected the windowprotected to set + */ + public void setWindowProtected(boolean windowprotected) { + this.windowProtected = windowProtected; + } + + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab + *
+ * Possible values are: + * HIDEOBJ_HIDE_ALL, HIDEOBJ_SHOW_ALL and HIDEOBJ_SHOW_PLACEHOLDERS + * + * @return the hideobj + */ + public int getHideobj() { + return hideobj; + } + + /** + * The HIDEOBJ record stores options selected in the Options dialog,View tab + *
+ * Possible values are: + * HIDEOBJ_HIDE_ALL, HIDEOBJ_SHOW_ALL and HIDEOBJ_SHOW_PLACEHOLDERS + * + * @param hideobj the hideobj to set + */ + public void setHideobj(int hideobj) { + this.hideobj = hideobj; + } + + /** + * @return the writeAccess + */ + public String getWriteAccess() { + return writeAccess; + } + + /** + * @param writeAccess the writeAccess to set + */ + public void setWriteAccess(String writeAccess) { + this.writeAccess = writeAccess; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java new file mode 100755 index 0000000..da7bf70 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilter.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import jxl.write.biff.File; + +/** + * Information for autofiltering + */ +public class AutoFilter { + private final FilterModeRecord filterMode; + private final AutoFilterInfoRecord autoFilterInfo; + private AutoFilterRecord autoFilter; + + /** + * Constructor + */ + public AutoFilter(FilterModeRecord fmr, + AutoFilterInfoRecord afir) { + filterMode = fmr; + autoFilterInfo = afir; + } + + public void add(AutoFilterRecord af) { + autoFilter = af; // make this into a list sometime + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + if (filterMode != null) { + outputFile.write(filterMode); + } + + if (autoFilterInfo != null) { + outputFile.write(autoFilterInfo); + } + + if (autoFilter != null) { + outputFile.write(autoFilter); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java new file mode 100755 index 0000000..8ce86ed --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterInfoRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterInfoRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(AutoFilterInfoRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public AutoFilterInfoRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java new file mode 100755 index 0000000..5f4994e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/AutoFilterRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class AutoFilterRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(AutoFilterRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public AutoFilterRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java b/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java new file mode 100755 index 0000000..8023531 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BaseCellFeatures.java @@ -0,0 +1,489 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.util.Collection; +import jxl.CellReferenceHelper; +import jxl.Range; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.CellValue; + +/** + * Container for any additional cell features + */ +public class BaseCellFeatures { + public static final ValidationCondition BETWEEN = + new ValidationCondition(DVParser.BETWEEN); + public static final ValidationCondition NOT_BETWEEN = + new ValidationCondition(DVParser.NOT_BETWEEN); + public static final ValidationCondition EQUAL = + new ValidationCondition(DVParser.EQUAL); + public static final ValidationCondition NOT_EQUAL = + new ValidationCondition(DVParser.NOT_EQUAL); + public static final ValidationCondition GREATER_THAN = + new ValidationCondition(DVParser.GREATER_THAN); + public static final ValidationCondition LESS_THAN = + new ValidationCondition(DVParser.LESS_THAN); + public static final ValidationCondition GREATER_EQUAL = + new ValidationCondition(DVParser.GREATER_EQUAL); + public static final ValidationCondition LESS_EQUAL = + new ValidationCondition(DVParser.LESS_EQUAL); + // Constants + private final static double defaultCommentWidth = 3; + private final static double defaultCommentHeight = 4; + /** + * The logger + */ + public static Logger logger = Logger.getLogger(BaseCellFeatures.class); + /** + * The comment + */ + private String comment; + /** + * The comment width in cells + */ + private double commentWidth; + /** + * The comment height in cells + */ + private double commentHeight; + /** + * A handle to the drawing object + */ + private Comment commentDrawing; + /** + * A handle to the combo box object + */ + private ComboBox comboBox; + /** + * The data validation settings + */ + private DataValiditySettingsRecord validationSettings; + /** + * The DV Parser used to contain the validation details + */ + private DVParser dvParser; + /** + * Indicates whether a drop down is required + */ + private boolean dropDown; + /** + * Indicates whether this cell features has data validation + */ + private boolean dataValidation; + /** + * The cell to which this is attached, and which may need to be notified + */ + private CellValue writableCell; + /** + * Constructor + */ + protected BaseCellFeatures() { + } + + /** + * Copy constructor + * + * @param the cell to copy + */ + public BaseCellFeatures(BaseCellFeatures cf) { + // The comment stuff + comment = cf.comment; + commentWidth = cf.commentWidth; + commentHeight = cf.commentHeight; + + // The data validation stuff. + dropDown = cf.dropDown; + dataValidation = cf.dataValidation; + + validationSettings = cf.validationSettings; // ? + + if (cf.dvParser != null) { + dvParser = new DVParser(cf.dvParser); + } + } + + /** + * Accessor for the cell comment + */ + protected String getComment() { + return comment; + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) { + setComment(s, defaultCommentWidth, defaultCommentHeight); + } + + /** + * Accessor for the comment width + */ + public double getCommentWidth() { + return commentWidth; + } + + /** + * Accessor for the comment height + */ + public double getCommentHeight() { + return commentHeight; + } + + /** + * Called by the cell when the features are added + * + * @param wc the writable cell + */ + public final void setWritableCell(CellValue wc) { + writableCell = wc; + } + + /** + * Internal method to set the cell comment. Used when reading + */ + public void setReadComment(String s, double w, double h) { + comment = s; + commentWidth = w; + commentHeight = h; + } + + /** + * Internal method to set the data validation. Used when reading + */ + public void setValidationSettings(DataValiditySettingsRecord dvsr) { + Assert.verify(dvsr != null); + + validationSettings = dvsr; + dataValidation = true; + } + + /** + * Sets the cell comment + * + * @param s the comment + * @param height the height of the comment box in cells + * @param width the width of the comment box in cells + */ + public void setComment(String s, double width, double height) { + comment = s; + commentWidth = width; + commentHeight = height; + + if (commentDrawing != null) { + commentDrawing.setCommentText(s); + commentDrawing.setWidth(width); + commentDrawing.setWidth(height); + // commentDrawing is set up when trying to modify a copied cell + } + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() { + // Set the comment string to be empty + comment = null; + + // Remove the drawing from the drawing group + if (commentDrawing != null) { + // do not call DrawingGroup.remove() because comments are not present + // on the Workbook DrawingGroup record + writableCell.removeComment(commentDrawing); + commentDrawing = null; + } + } + + /** + * Public function which removes any data validation, if present + */ + public void removeDataValidation() { + if (!dataValidation) { + return; + } + + // If the data validation is shared, then generate a warning + DVParser dvp = getDVParser(); + if (dvp.extendedCellsValidation()) { + logger.warn("Cannot remove data validation from " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of the shared reference " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow())); + return; + } + + // Remove the validation from the WritableSheet object if present + writableCell.removeDataValidation(); + clearValidationSettings(); + } + + /** + * Internal function which removes any data validation, including + * shared ones, if present. This is called from WritableSheetImpl + * in response to a call to removeDataValidation + */ + public void removeSharedDataValidation() { + if (!dataValidation) { + return; + } + + // Remove the validation from the WritableSheet object if present + writableCell.removeDataValidation(); + clearValidationSettings(); + } + + /** + * Accessor for the comment drawing + */ + public final Comment getCommentDrawing() { + return commentDrawing; + } + + /** + * Sets the comment drawing object + */ + public final void setCommentDrawing(Comment c) { + commentDrawing = c; + } + + /** + * Gets the data validation list as a formula. Used only when reading + * + * @return the validation formula as a list + */ + public String getDataValidationList() { + if (validationSettings == null) { + return null; + } + + return validationSettings.getValidationFormula(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(c); + dropDown = true; + dataValidation = true; + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param c the list of valid values + */ + public void setDataValidationRange(int col1, int r1, int col2, int r2) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(col1, r1, col2, r2); + dropDown = true; + dataValidation = true; + } + + /** + * Sets the data validation based upon a named range + */ + public void setDataValidationRange(String namedRange) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(namedRange); + dropDown = true; + dataValidation = true; + } + + /** + * Sets the data validation based upon a numerical condition + */ + public void setNumberValidation(double val, ValidationCondition c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(val, Double.NaN, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + public void setNumberValidation(double val1, double val2, + ValidationCondition c) { + if (dataValidation && getDVParser().extendedCellsValidation()) { + logger.warn("Cannot set data validation on " + + CellReferenceHelper.getCellReference(writableCell) + + " as it is part of a shared data validation"); + return; + } + clearValidationSettings(); + dvParser = new DVParser(val1, val2, c.getCondition()); + dropDown = false; + dataValidation = true; + } + + /** + * Accessor for the data validation + * + * @return TRUE if this has a data validation associated with it, + * FALSE otherwise + */ + public boolean hasDataValidation() { + return dataValidation; + } + + /** + * Clears out any existing validation settings + */ + private void clearValidationSettings() { + validationSettings = null; + dvParser = null; + dropDown = false; + comboBox = null; + dataValidation = false; + } + + /** + * Accessor for whether a drop down is required + * + * @return TRUE if this requires a drop down, FALSE otherwise + */ + public boolean hasDropDown() { + return dropDown; + } + + /** + * Sets the combo box drawing object for list validations + * + * @param cb the combo box + */ + public void setComboBox(ComboBox cb) { + comboBox = cb; + } + + /** + * Gets the dv parser + */ + public DVParser getDVParser() { + // straightforward - this was created as a writable cell + if (dvParser != null) { + return dvParser; + } + + // this was copied from a readable cell, and then copied again + if (validationSettings != null) { + dvParser = new DVParser(validationSettings.getDVParser()); + return dvParser; + } + + return null; // keep the compiler happy + } + + /** + * Use the same data validation logic as the specified cell features + * + * @param cf the data validation to reuse + */ + public void shareDataValidation(BaseCellFeatures source) { + if (dataValidation) { + logger.warn("Attempting to share a data validation on cell " + + CellReferenceHelper.getCellReference(writableCell) + + " which already has a data validation"); + return; + } + clearValidationSettings(); + dvParser = source.getDVParser(); + validationSettings = null; + dataValidation = true; + dropDown = source.dropDown; + comboBox = source.comboBox; + } + + /** + * Gets the range of cells to which the data validation applies. If the + * validation applies to just this cell, this will be reflected in the + * returned range + * + * @return the range to which the same validation extends, or NULL if this + * cell doesn't have a validation + */ + public Range getSharedDataValidationRange() { + if (!dataValidation) { + return null; + } + + DVParser dvp = getDVParser(); + + return new SheetRangeImpl(writableCell.getSheet(), + dvp.getFirstColumn(), + dvp.getFirstRow(), + dvp.getLastColumn(), + dvp.getLastRow()); + } + + // Validation conditions + protected static class ValidationCondition { + private static ValidationCondition[] types = new ValidationCondition[0]; + private final DVParser.Condition condition; + + ValidationCondition(DVParser.Condition c) { + condition = c; + ValidationCondition[] oldtypes = types; + types = new ValidationCondition[oldtypes.length + 1]; + System.arraycopy(oldtypes, 0, types, 0, oldtypes.length); + types[oldtypes.length] = this; + } + + public DVParser.Condition getCondition() { + return condition; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java b/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java new file mode 100755 index 0000000..3c93ced --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BaseCompoundFile.java @@ -0,0 +1,349 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Contains the common data for a compound file + */ +public abstract class BaseCompoundFile { + /** + * The standard property sets + */ + public final static String ROOT_ENTRY_NAME = "Root Entry"; + public final static String WORKBOOK_NAME = "Workbook"; + public final static String SUMMARY_INFORMATION_NAME = + "\u0005SummaryInformation"; + public final static String DOCUMENT_SUMMARY_INFORMATION_NAME = + "\u0005DocumentSummaryInformation"; + public final static String COMP_OBJ_NAME = + "\u0001CompObj"; + public final static String[] STANDARD_PROPERTY_SETS = + new String[]{ROOT_ENTRY_NAME, WORKBOOK_NAME, + SUMMARY_INFORMATION_NAME, + DOCUMENT_SUMMARY_INFORMATION_NAME}; + /** + * Property storage types + */ + public final static int NONE_PS_TYPE = 0; + public final static int DIRECTORY_PS_TYPE = 1; + public final static int FILE_PS_TYPE = 2; + public final static int ROOT_ENTRY_PS_TYPE = 5; + /** + * The identifier at the beginning of every OLE file + */ + protected static final byte[] IDENTIFIER = new byte[] + {(byte) 0xd0, + (byte) 0xcf, + (byte) 0x11, + (byte) 0xe0, + (byte) 0xa1, + (byte) 0xb1, + (byte) 0x1a, + (byte) 0xe1}; + /** + * + */ + protected static final int NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c; + /** + * + */ + protected static final int SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c; + + // property storage offsets + /** + * + */ + protected static final int NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS = 0x40; + /** + * + */ + protected static final int ROOT_START_BLOCK_POS = 0x30; + /** + * + */ + protected static final int BIG_BLOCK_SIZE = 0x200; + /** + * + */ + protected static final int SMALL_BLOCK_SIZE = 0x40; + /** + * + */ + protected static final int EXTENSION_BLOCK_POS = 0x44; + /** + * + */ + protected static final int NUM_EXTENSION_BLOCK_POS = 0x48; + /** + * + */ + protected static final int PROPERTY_STORAGE_BLOCK_SIZE = 0x80; + /** + * + */ + protected static final int BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c; + /** + * + */ + protected static final int SMALL_BLOCK_THRESHOLD = 0x1000; + /** + * + */ + private static final int SIZE_OF_NAME_POS = 0x40; + /** + * + */ + private static final int TYPE_POS = 0x42; + /** + * + */ + private static final int COLOUR_POS = 0x43; + /** + * + */ + private static final int PREVIOUS_POS = 0x44; + /** + * + */ + private static final int NEXT_POS = 0x48; + /** + * + */ + private static final int CHILD_POS = 0x4c; + /** + * + */ + private static final int START_BLOCK_POS = 0x74; + /** + * + */ + private static final int SIZE_POS = 0x78; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BaseCompoundFile.class); + + + /** + * Constructor + */ + protected BaseCompoundFile() { + } + + /** + * Inner class to represent the property storage sets. Access is public + * to allow access from the PropertySetsReader demo utility + */ + public class PropertyStorage { + /** + * The name of this property set + */ + public String name; + /** + * The type of the property set + */ + public int type; + /** + * The colour of the property set + */ + public int colour; + /** + * The block number in the stream which this property sets starts at + */ + public int startBlock; + /** + * The size, in bytes, of this property set + */ + public int size; + /** + * The previous property set + */ + public int previous; + /** + * The next property set + */ + public int next; + /** + * The child for this property set + */ + public int child; + + /** + * The data that created this set + */ + public byte[] data; + + /** + * Constructs a property set + * + * @param d the bytes + */ + public PropertyStorage(byte[] d) { + data = d; + int nameSize = IntegerHelper.getInt(data[SIZE_OF_NAME_POS], + data[SIZE_OF_NAME_POS + 1]); + + if (nameSize > SIZE_OF_NAME_POS) { + logger.warn("property set name exceeds max length - truncating"); + nameSize = SIZE_OF_NAME_POS; + + } + type = data[TYPE_POS]; + colour = data[COLOUR_POS]; + + startBlock = IntegerHelper.getInt + (data[START_BLOCK_POS], + data[START_BLOCK_POS + 1], + data[START_BLOCK_POS + 2], + data[START_BLOCK_POS + 3]); + size = IntegerHelper.getInt + (data[SIZE_POS], + data[SIZE_POS + 1], + data[SIZE_POS + 2], + data[SIZE_POS + 3]); + previous = IntegerHelper.getInt + (data[PREVIOUS_POS], + data[PREVIOUS_POS + 1], + data[PREVIOUS_POS + 2], + data[PREVIOUS_POS + 3]); + next = IntegerHelper.getInt + (data[NEXT_POS], + data[NEXT_POS + 1], + data[NEXT_POS + 2], + data[NEXT_POS + 3]); + child = IntegerHelper.getInt + (data[CHILD_POS], + data[CHILD_POS + 1], + data[CHILD_POS + 2], + data[CHILD_POS + 3]); + + int chars = 0; + if (nameSize > 2) { + chars = (nameSize - 1) / 2; + } + + StringBuffer n = new StringBuffer(); + for (int i = 0; i < chars; i++) { + n.append((char) data[i * 2]); + } + + name = n.toString(); + } + + /** + * Constructs an empty property set. Used when writing the file + * + * @param name the property storage name + */ + public PropertyStorage(String name) { + data = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + + Assert.verify(name.length() < 32); + + IntegerHelper.getTwoBytes((name.length() + 1) * 2, + data, + SIZE_OF_NAME_POS); + // add one to the name length to allow for the null character at + // the end + for (int i = 0; i < name.length(); i++) { + data[i * 2] = (byte) name.charAt(i); + } + } + + /** + * Sets the type + * + * @param t the type + */ + public void setType(int t) { + type = t; + data[TYPE_POS] = (byte) t; + } + + /** + * Sets the number of the start block + * + * @param sb the number of the start block + */ + public void setStartBlock(int sb) { + startBlock = sb; + IntegerHelper.getFourBytes(sb, data, START_BLOCK_POS); + } + + /** + * Sets the size of the file + * + * @param s the size + */ + public void setSize(int s) { + size = s; + IntegerHelper.getFourBytes(s, data, SIZE_POS); + } + + /** + * Sets the previous block + * + * @param prev the previous block + */ + public void setPrevious(int prev) { + previous = prev; + IntegerHelper.getFourBytes(prev, data, PREVIOUS_POS); + } + + /** + * Sets the next block + * + * @param nxt the next block + */ + public void setNext(int nxt) { + next = nxt; + IntegerHelper.getFourBytes(next, data, NEXT_POS); + } + + /** + * Sets the child + * + * @param dir the child + */ + public void setChild(int dir) { + child = dir; + IntegerHelper.getFourBytes(child, data, CHILD_POS); + } + + /** + * Sets the colour + * + * @param col colour + */ + public void setColour(int col) { + colour = col == 0 ? 0 : 1; + data[COLOUR_POS] = (byte) colour; + } + + } + +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java new file mode 100755 index 0000000..f424e79 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInFormat.java @@ -0,0 +1,166 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.format.Format; + +/** + * The excel string for the various built in formats. Used to present + * the cell format information back to the user + *
+ * The difference between this class and the various format object contained + * in the jxl.write package is that this object contains the Excel strings, + * not their java equivalents + */ +final class BuiltInFormat implements Format, DisplayFormat { + /** + * The list of built in formats + */ + public static BuiltInFormat[] builtIns = new BuiltInFormat[0x32]; + + // Populate the built ins + static { + builtIns[0x0] = new BuiltInFormat("", 0); + builtIns[0x1] = new BuiltInFormat("0", 1); + builtIns[0x2] = new BuiltInFormat("0.00", 2); + builtIns[0x3] = new BuiltInFormat("#,##0", 3); + builtIns[0x4] = new BuiltInFormat("#,##0.00", 4); + builtIns[0x5] = new BuiltInFormat("($#,##0_);($#,##0)", 5); + builtIns[0x6] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 6); + builtIns[0x7] = new BuiltInFormat("($#,##0_);[Red]($#,##0)", 7); + builtIns[0x8] = new BuiltInFormat("($#,##0.00_);[Red]($#,##0.00)", 8); + builtIns[0x9] = new BuiltInFormat("0%", 9); + builtIns[0xa] = new BuiltInFormat("0.00%", 10); + builtIns[0xb] = new BuiltInFormat("0.00E+00", 11); + builtIns[0xc] = new BuiltInFormat("# ?/?", 12); + builtIns[0xd] = new BuiltInFormat("# ??/??", 13); + builtIns[0xe] = new BuiltInFormat("dd/mm/yyyy", 14); + builtIns[0xf] = new BuiltInFormat("d-mmm-yy", 15); + builtIns[0x10] = new BuiltInFormat("d-mmm", 16); + builtIns[0x11] = new BuiltInFormat("mmm-yy", 17); + builtIns[0x12] = new BuiltInFormat("h:mm AM/PM", 18); + builtIns[0x13] = new BuiltInFormat("h:mm:ss AM/PM", 19); + builtIns[0x14] = new BuiltInFormat("h:mm", 20); + builtIns[0x15] = new BuiltInFormat("h:mm:ss", 21); + builtIns[0x16] = new BuiltInFormat("m/d/yy h:mm", 22); + builtIns[0x25] = new BuiltInFormat("(#,##0_);(#,##0)", 0x25); + builtIns[0x26] = new BuiltInFormat("(#,##0_);[Red](#,##0)", 0x26); + builtIns[0x27] = new BuiltInFormat("(#,##0.00_);(#,##0.00)", 0x27); + builtIns[0x28] = new BuiltInFormat("(#,##0.00_);[Red](#,##0.00)", 0x28); + builtIns[0x29] = new BuiltInFormat + ("_(*#,##0_);_(*(#,##0);_(*\"-\"_);(@_)", 0x29); + builtIns[0x2a] = new BuiltInFormat + ("_($*#,##0_);_($*(#,##0);_($*\"-\"_);(@_)", 0x2a); + builtIns[0x2b] = new BuiltInFormat + ("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);(@_)", 0x2b); + builtIns[0x2c] = new BuiltInFormat + ("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);(@_)", 0x2c); + builtIns[0x2d] = new BuiltInFormat("mm:ss", 0x2d); + builtIns[0x2e] = new BuiltInFormat("[h]mm:ss", 0x2e); + builtIns[0x2f] = new BuiltInFormat("mm:ss.0", 0x2f); + builtIns[0x30] = new BuiltInFormat("##0.0E+0", 0x30); + builtIns[0x31] = new BuiltInFormat("@", 0x31); + } + + /** + * The excel format string + */ + private final String formatString; + /** + * The index + */ + private final int formatIndex; + + /** + * Constructor + * + * @param s the format string + * @param i the format index + */ + private BuiltInFormat(String s, int i) { + formatIndex = i; + formatString = s; + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + public int getFormatIndex() { + return formatIndex; + } + + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + public boolean isInitialized() { + return true; + } + + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + public void initialize(int pos) { + } + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Equals method + * + * @return TRUE if the two built in formats are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + return (formatIndex == bif.formatIndex); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java new file mode 100755 index 0000000..5aeafb8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInName.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Enumeration of built in names + */ +public class BuiltInName { + // The list of built in names + public static final BuiltInName CONSOLIDATE_AREA = + new BuiltInName("Consolidate_Area", 0x0); + public static final BuiltInName AUTO_OPEN = + new BuiltInName("Auto_Open", 0x1); + public static final BuiltInName AUTO_CLOSE = + new BuiltInName("Auto_Open", 0x2); + public static final BuiltInName EXTRACT = + new BuiltInName("Extract", 0x3); + public static final BuiltInName DATABASE = + new BuiltInName("Database", 0x4); + public static final BuiltInName CRITERIA = + new BuiltInName("Criteria", 0x5); + public static final BuiltInName PRINT_AREA = + new BuiltInName("Print_Area", 0x6); + public static final BuiltInName PRINT_TITLES = + new BuiltInName("Print_Titles", 0x7); + public static final BuiltInName RECORDER = + new BuiltInName("Recorder", 0x8); + public static final BuiltInName DATA_FORM = + new BuiltInName("Data_Form", 0x9); + public static final BuiltInName AUTO_ACTIVATE = + new BuiltInName("Auto_Activate", 0xa); + public static final BuiltInName AUTO_DEACTIVATE = + new BuiltInName("Auto_Deactivate", 0xb); + public static final BuiltInName SHEET_TITLE = + new BuiltInName("Sheet_Title", 0xb); + public static final BuiltInName FILTER_DATABASE = + new BuiltInName("_FilterDatabase", 0xd); + /** + * The list of name + */ + private static BuiltInName[] builtInNames = new BuiltInName[0]; + /** + * The name + */ + private final String name; + /** + * The value + */ + private final int value; + /** + * Constructor + */ + private BuiltInName(String n, int v) { + name = n; + value = v; + + BuiltInName[] oldnames = builtInNames; + builtInNames = new BuiltInName[oldnames.length + 1]; + System.arraycopy(oldnames, 0, builtInNames, 0, oldnames.length); + builtInNames[oldnames.length] = this; + } + + /** + * Gets the built in name for the value + */ + public static BuiltInName getBuiltInName(int val) { + BuiltInName ret = FILTER_DATABASE; + for (int i = 0; i < builtInNames.length; i++) { + if (builtInNames[i].getValue() == val) { + ret = builtInNames[i]; + } + } + return ret; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Accessor for the value + * + * @return the value + */ + public int getValue() { + return value; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java b/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java new file mode 100755 index 0000000..780f8d8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/BuiltInStyle.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Represents a built in, rather than a user defined, style. + * This class is used by the FormattingRecords class when writing out the hard* + * coded styles + */ +class BuiltInStyle extends WritableRecordData { + /** + * The XF index of this style + */ + private final int xfIndex; + /** + * The reference number of this style + */ + private final int styleNumber; + + /** + * Constructor + * + * @param xfind the xf index of this style + * @param sn the style number of this style + */ + public BuiltInStyle(int xfind, int sn) { + super(Type.STYLE); + + xfIndex = xfind; + styleNumber = sn; + } + + /** + * Abstract method implementation to get the raw byte data ready to write out + * + * @return The byte data + */ + public byte[] getData() { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(xfIndex, data, 0); + + // Set the built in bit + data[1] |= 0x80; + + data[2] = (byte) styleNumber; + + // Set the outline level + data[3] = (byte) 0xff; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java b/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java new file mode 100755 index 0000000..322e841 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ByteArray.java @@ -0,0 +1,106 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * A growable array of bytes + */ +public class ByteArray { + // The default grow size + private final static int defaultGrowSize = 1024; + /** + * The array grow size + */ + private final int growSize; + /** + * The current array + */ + private byte[] bytes; + /** + * The current position + */ + private int pos; + + /** + * Constructor + */ + public ByteArray() { + this(defaultGrowSize); + } + + /** + * Constructor + * + * @param gs + */ + public ByteArray(int gs) { + growSize = gs; + bytes = new byte[defaultGrowSize]; + pos = 0; + } + + /** + * Adds a byte onto the array + * + * @param b the byte + */ + public void add(byte b) { + checkSize(1); + bytes[pos] = b; + pos++; + } + + /** + * Adds an array of bytes onto the array + * + * @param b the array of bytes + */ + public void add(byte[] b) { + checkSize(b.length); + System.arraycopy(b, 0, bytes, pos, b.length); + pos += b.length; + } + + /** + * Gets the complete array + * + * @return the array + */ + public byte[] getBytes() { + byte[] returnArray = new byte[pos]; + System.arraycopy(bytes, 0, returnArray, 0, pos); + return returnArray; + } + + /** + * Checks to see if there is sufficient space left on the array. If not, + * then it grows the array + * + * @param sz the amount of bytes to add + */ + private void checkSize(int sz) { + while (pos + sz >= bytes.length) { + // Grow the array + byte[] newArray = new byte[bytes.length + growSize]; + System.arraycopy(bytes, 0, newArray, 0, pos); + bytes = newArray; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ByteData.java b/datastructures-xslx/src/main/java/jxl/biff/ByteData.java new file mode 100755 index 0000000..ba6cf5a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ByteData.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Interface which provides a method for transferring chunks of binary + * data from one Excel file (read in) to another (written out) + */ +public interface ByteData { + /** + * Used when writing out records + * + * @return the full data to be included in the final compound file + */ + byte[] getBytes(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java b/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java new file mode 100755 index 0000000..0ee9b6d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CellFinder.java @@ -0,0 +1,196 @@ +/********************************************************************** + * + * Copyright (C) 2008 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jxl.Cell; +import jxl.CellType; +import jxl.LabelCell; +import jxl.Sheet; + +/** + * Refactorisation to provide more sophisticated find cell by contents + * functionality + */ +public class CellFinder { + private final Sheet sheet; + + public CellFinder(Sheet s) { + sheet = s; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) { + for (int j = 0; j <= numRows && found == false; j++) { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) { + if (c.getContents().equals(contents)) { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Finds a cell within a given range of cells + * + * @param contents the string to match + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents) { + Cell cell = null; + boolean found = false; + + for (int i = 0; i < sheet.getRows() && found == false; i++) { + Cell[] row = sheet.getRow(i); + for (int j = 0; j < row.length && found == false; j++) { + if (row[j].getContents().equals(contents)) { + cell = row[j]; + found = true; + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + Cell cell = null; + boolean found = false; + + int numCols = lastCol - firstCol; + int numRows = lastRow - firstRow; + + int row1 = reverse ? lastRow : firstRow; + int row2 = reverse ? firstRow : lastRow; + int col1 = reverse ? lastCol : firstCol; + int col2 = reverse ? firstCol : lastCol; + int inc = reverse ? -1 : 1; + + for (int i = 0; i <= numCols && found == false; i++) { + for (int j = 0; j <= numRows && found == false; j++) { + int curCol = col1 + i * inc; + int curRow = row1 + j * inc; + if (curCol < sheet.getColumns() && curRow < sheet.getRows()) { + Cell c = sheet.getCell(curCol, curRow); + if (c.getType() != CellType.EMPTY) { + Matcher m = pattern.matcher(c.getContents()); + if (m.matches()) { + cell = c; + found = true; + } + } + } + } + } + + return cell; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) { + LabelCell cell = null; + boolean found = false; + + for (int i = 0; i < sheet.getRows() && !found; i++) { + Cell[] row = sheet.getRow(i); + for (int j = 0; j < row.length && !found; j++) { + if ((row[j].getType() == CellType.LABEL || + row[j].getType() == CellType.STRING_FORMULA) && + row[j].getContents().equals(contents)) { + cell = (LabelCell) row[j]; + found = true; + } + } + } + + return cell; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java b/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java new file mode 100755 index 0000000..001dad2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CellReferenceHelper.java @@ -0,0 +1,313 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.biff.formula.ExternalSheet; +import jxl.common.Logger; + +/** + * A helper to transform between excel cell references and + * sheet:column:row notation + * Because this function will be called when generating a string + * representation of a formula, the cell reference will merely + * be appened to the string buffer instead of returning a full + * blooded string, for performance reasons + */ +public final class CellReferenceHelper { + /** + * The character which indicates whether a reference is fixed + */ + private static final char fixedInd = '$'; + /** + * The character which indicates the sheet name terminator + */ + private static final char sheetInd = '!'; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellReferenceHelper.class); + + /** + * Constructor to prevent instantiation + */ + private CellReferenceHelper() { + } + + /** + * Gets the cell reference + * + * @param column + * @param row + * @param buf + */ + public static void getCellReference(int column, int row, StringBuffer buf) { + // Put the column letter into the buffer + getColumnReference(column, buf); + + // Add the row into the buffer + buf.append(row + 1); + } + + /** + * Overloaded method which prepends $ for absolute reference + * + * @param column + * @param colabs TRUE if the column reference is absolute + * @param row + * @param rowabs TRUE if the row reference is absolute + * @param buf + */ + public static void getCellReference(int column, boolean colabs, + int row, boolean rowabs, + StringBuffer buf) { + if (colabs) { + buf.append(fixedInd); + } + + // Put the column letter into the buffer + getColumnReference(column, buf); + + if (rowabs) { + buf.append(fixedInd); + } + + // Add the row into the buffer + buf.append(row + 1); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @return the letter for that column number + */ + public static String getColumnReference(int column) { + StringBuffer buf = new StringBuffer(); + getColumnReference(column, buf); + return buf.toString(); + } + + /** + * Gets the column letter corresponding to the 0-based column number + * + * @param column the column number + * @param buf the string buffer in which to write the column letter + */ + public static void getColumnReference(int column, StringBuffer buf) { + int v = column / 26; + int r = column % 26; + + StringBuffer tmp = new StringBuffer(); + while (v != 0) { + char col = (char) ('A' + r); + + tmp.append(col); + + r = v % 26 - 1; // subtract one because only rows >26 preceded by A + v = v / 26; + } + + char col = (char) ('A' + r); + tmp.append(col); + + // Insert into the proper string buffer in reverse order + for (int i = tmp.length() - 1; i >= 0; i--) { + buf.append(tmp.charAt(i)); + } + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, int row, + ExternalSheet workbook, StringBuffer buf) { + // Quotes are added by the WorkbookParser + String name = workbook.getExternalSheetName(sheet); + buf.append(StringHelper.replace(name, "'", "''")); + buf.append(sheetInd); + getCellReference(column, row, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param colabs TRUE if the column is an absolute reference + * @param row + * @param rowabs TRUE if the row is an absolute reference + * @param workbook + * @param buf + */ + public static void getCellReference + (int sheet, int column, boolean colabs, + int row, boolean rowabs, + ExternalSheet workbook, StringBuffer buf) { + // WorkbookParser now appends quotes and escapes apostrophes + String name = workbook.getExternalSheetName(sheet); + buf.append(name); + buf.append(sheetInd); + getCellReference(column, colabs, row, rowabs, buf); + } + + /** + * Gets the fully qualified cell reference given the column, row + * external sheet reference etc + * + * @param sheet + * @param column + * @param row + * @param workbook + * @return the cell reference in the form 'Sheet 1'!A1 + */ + public static String getCellReference + (int sheet, int column, int row, + ExternalSheet workbook) { + StringBuffer sb = new StringBuffer(); + getCellReference(sheet, column, row, workbook, sb); + return sb.toString(); + } + + + /** + * Gets the cell reference for the specified column and row + * + * @param column + * @param row + * @return + */ + public static String getCellReference(int column, int row) { + StringBuffer buf = new StringBuffer(); + getCellReference(column, row, buf); + return buf.toString(); + } + + /** + * Gets the columnn number of the string cell reference + * + * @param s the string to parse + * @return the column portion of the cell reference + */ + public static int getColumn(String s) { + int colnum = 0; + int numindex = getNumberIndex(s); + + String s2 = s.toUpperCase(); + + int startPos = s.lastIndexOf(sheetInd) + 1; + if (s.charAt(startPos) == fixedInd) { + startPos++; + } + + int endPos = numindex; + if (s.charAt(numindex - 1) == fixedInd) { + endPos--; + } + + for (int i = startPos; i < endPos; i++) { + + if (i != startPos) { + colnum = (colnum + 1) * 26; + } + colnum += (int) s2.charAt(i) - (int) 'A'; + } + + return colnum; + } + + /** + * Gets the row number of the cell reference + */ + public static int getRow(String s) { + try { + return (Integer.parseInt(s.substring(getNumberIndex(s))) - 1); + } catch (NumberFormatException e) { + logger.warn(e, e); + return 0xffff; + } + } + + /** + * Finds the position where the first number occurs in the string + */ + private static int getNumberIndex(String s) { + // Find the position of the first number + boolean numberFound = false; + int pos = s.lastIndexOf(sheetInd) + 1; + char c = '\0'; + + while (!numberFound && pos < s.length()) { + c = s.charAt(pos); + + if (c >= '0' && c <= '9') { + numberFound = true; + } else { + pos++; + } + } + + return pos; + } + + /** + * Sees if the column component is relative or not + * + * @param s + * @return TRUE if the column is relative, FALSE otherwise + */ + public static boolean isColumnRelative(String s) { + return s.charAt(0) != fixedInd; + } + + /** + * Sees if the row component is relative or not + * + * @param s + * @return TRUE if the row is relative, FALSE otherwise + */ + public static boolean isRowRelative(String s) { + return s.charAt(getNumberIndex(s) - 1) != fixedInd; + } + + /** + * Gets the sheet name from the cell reference string + * + * @param ref + * @return the sheet reference + */ + public static String getSheet(String ref) { + int sheetPos = ref.lastIndexOf(sheetInd); + if (sheetPos == -1) { + return ""; + } + + return ref.substring(0, sheetPos); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java new file mode 100755 index 0000000..8489d51 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormat.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.write.biff.File; + +/** + * Class containing the CONDFMT and CF records for conditionally formatting + * a cell + */ +public class ConditionalFormat { + /** + * The range of the format + */ + private final ConditionalFormatRangeRecord range; + + /** + * The format conditions + */ + private final ArrayList conditions; + + /** + * Constructor + */ + public ConditionalFormat(ConditionalFormatRangeRecord cfrr) { + range = cfrr; + conditions = new ArrayList(); + } + + /** + * Adds a condition + * + * @param cond the condition + */ + public void addCondition(ConditionalFormatRecord cond) { + conditions.add(cond); + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + range.insertColumn(col); + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) { + range.removeColumn(col); + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + range.removeRow(row); + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + range.insertRow(row); + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + outputFile.write(range); + + for (Iterator i = conditions.iterator(); i.hasNext(); ) { + ConditionalFormatRecord cfr = (ConditionalFormatRecord) i.next(); + outputFile.write(cfr); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java new file mode 100755 index 0000000..0ebcd89 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRangeRecord.java @@ -0,0 +1,344 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class ConditionalFormatRangeRecord extends WritableRecordData { + // The logger + private static final Logger logger = + Logger.getLogger(ConditionalFormatRangeRecord.class); + + /** + * The enclosing range + */ + private Range enclosingRange; + + /** + * The discrete ranges + */ + private Range[] ranges; + + /** + * The number of ranges + */ + private int numRanges; + + /** + * Initialized flag + */ + private boolean initialized; + + /** + * Modified flag + */ + private boolean modified; + + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + */ + public ConditionalFormatRangeRecord(Record t) { + super(t); + + initialized = false; + modified = false; + data = getRecord().getData(); + } + + /** + * Initialization function + */ + private void initialize() { + enclosingRange = new Range(); + enclosingRange.firstRow = IntegerHelper.getInt(data[4], data[5]); + enclosingRange.lastRow = IntegerHelper.getInt(data[6], data[7]); + enclosingRange.firstColumn = IntegerHelper.getInt(data[8], data[9]); + enclosingRange.lastColumn = IntegerHelper.getInt(data[10], data[11]); + numRanges = IntegerHelper.getInt(data[12], data[13]); + ranges = new Range[numRanges]; + + int pos = 14; + + for (int i = 0; i < numRanges; i++) { + ranges[i] = new Range(); + ranges[i].firstRow = IntegerHelper.getInt(data[pos], data[pos + 1]); + ranges[i].lastRow = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + ranges[i].firstColumn = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + ranges[i].lastColumn = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + pos += 8; + } + + initialized = true; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (!initialized) { + initialize(); + } + + enclosingRange.insertColumn(col); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].insertColumn(col); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void removeColumn(int col) { + if (!initialized) { + initialize(); + } + + enclosingRange.removeColumn(col); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].removeColumn(col); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + if (!initialized) { + initialize(); + } + + enclosingRange.removeRow(row); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].removeRow(row); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (!initialized) { + initialize(); + } + + enclosingRange.insertRow(row); + if (enclosingRange.modified) { + modified = true; + } + + for (int i = 0; i < ranges.length; i++) { + ranges[i].insertRow(row); + + if (ranges[i].modified) { + modified = true; + } + } + + return; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (!modified) { + return data; + } + + byte[] d = new byte[14 + ranges.length * 8]; + + // Copy in the original information + System.arraycopy(data, 0, d, 0, 4); + + // Create the new range + IntegerHelper.getTwoBytes(enclosingRange.firstRow, d, 4); + IntegerHelper.getTwoBytes(enclosingRange.lastRow, d, 6); + IntegerHelper.getTwoBytes(enclosingRange.firstColumn, d, 8); + IntegerHelper.getTwoBytes(enclosingRange.lastColumn, d, 10); + + IntegerHelper.getTwoBytes(numRanges, d, 12); + + int pos = 14; + for (int i = 0; i < ranges.length; i++) { + IntegerHelper.getTwoBytes(ranges[i].firstRow, d, pos); + IntegerHelper.getTwoBytes(ranges[i].lastRow, d, pos + 2); + IntegerHelper.getTwoBytes(ranges[i].firstColumn, d, pos + 4); + IntegerHelper.getTwoBytes(ranges[i].lastColumn, d, pos + 6); + pos += 8; + } + + return d; + } + + private static class Range { + public int firstRow; + public int firstColumn; + public int lastRow; + public int lastColumn; + public boolean modified; + + public Range() { + modified = false; + } + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (col > lastColumn) { + return; + } + + if (col <= firstColumn) { + firstColumn++; + modified = true; + } + + if (col <= lastColumn) { + lastColumn++; + modified = true; + } + } + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + public void removeColumn(int col) { + if (col > lastColumn) { + return; + } + + if (col < firstColumn) { + firstColumn--; + modified = true; + } + + if (col <= lastColumn) { + lastColumn--; + modified = true; + } + } + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + public void removeRow(int row) { + if (row > lastRow) { + return; + } + + if (row < firstRow) { + firstRow--; + modified = true; + } + + if (row <= lastRow) { + lastRow--; + modified = true; + } + } + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (row > lastRow) { + return; + } + + if (row <= firstRow) { + firstRow++; + modified = true; + } + + if (row <= lastRow) { + lastRow++; + modified = true; + } + } + + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java new file mode 100755 index 0000000..d9b6dff --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ConditionalFormatRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * The conditional format conditions + */ +public class ConditionalFormatRecord extends WritableRecordData { + // the logger + private static final Logger logger = + Logger.getLogger(ConditionalFormatRecord.class); + + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + */ + public ConditionalFormatRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java b/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java new file mode 100755 index 0000000..d1a074f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/ContinueRecord.java @@ -0,0 +1,74 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.read.biff.Record; + +/** + * A continue record - only used explicitly in special circumstances, as + * the general continuation record is handled directly by the records + */ +public class ContinueRecord extends WritableRecordData { + /** + * The data + */ + private final byte[] data; + + /** + * Constructor + * + * @param t the raw bytes + */ + public ContinueRecord(Record t) { + super(t); + data = t.getData(); + } + + /** + * Constructor invoked when creating continue records + * + * @param d the data + */ + public ContinueRecord(byte[] d) { + super(Type.CONTINUE); + data = d; + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + return data; + } + + /** + * Accessor for the record. Used when forcibly changing this record + * into another type, notably a drawing record, as sometimes Excel appears + * to switch to writing Continue records instead of MsoDrawing records + * + * @return the record + */ + public Record getRecord() { + return super.getRecord(); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java b/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java new file mode 100755 index 0000000..71d3cba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/CountryCode.java @@ -0,0 +1,150 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Enumeration type for the excel country codes + */ +public class CountryCode { + // The country codes + public final static CountryCode USA = new CountryCode(0x1, "US", "USA"); + public final static CountryCode CANADA = + new CountryCode(0x2, "CA", "Canada"); + public final static CountryCode GREECE = + new CountryCode(0x1e, "GR", "Greece"); + public final static CountryCode NETHERLANDS = + new CountryCode(0x1f, "NE", "Netherlands"); + public final static CountryCode BELGIUM = + new CountryCode(0x20, "BE", "Belgium"); + public final static CountryCode FRANCE = + new CountryCode(0x21, "FR", "France"); + public final static CountryCode SPAIN = new CountryCode(0x22, "ES", "Spain"); + public final static CountryCode ITALY = new CountryCode(0x27, "IT", "Italy"); + public final static CountryCode SWITZERLAND = + new CountryCode(0x29, "CH", "Switzerland"); + public final static CountryCode UK = + new CountryCode(0x2c, "UK", "United Kingdowm"); + public final static CountryCode DENMARK = + new CountryCode(0x2d, "DK", "Denmark"); + public final static CountryCode SWEDEN = + new CountryCode(0x2e, "SE", "Sweden"); + public final static CountryCode NORWAY = + new CountryCode(0x2f, "NO", "Norway"); + public final static CountryCode GERMANY = + new CountryCode(0x31, "DE", "Germany"); + public final static CountryCode PHILIPPINES = + new CountryCode(0x3f, "PH", "Philippines"); + public final static CountryCode CHINA = + new CountryCode(0x56, "CN", "China"); + public final static CountryCode INDIA = + new CountryCode(0x5b, "IN", "India"); + public final static CountryCode UNKNOWN = + new CountryCode(0xffff, "??", "Unknown"); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CountryCode.class); + /** + * The array of country codes + */ + private static CountryCode[] codes = new CountryCode[0]; + /** + * The country code + */ + private final int value; + /** + * The ISO 3166 two letter country mnemonic (as used by the Locale class) + */ + private final String code; + /** + * The long description + */ + private final String description; + /** + * Constructor + */ + private CountryCode(int v, String c, String d) { + value = v; + code = c; + description = d; + + CountryCode[] newcodes = new CountryCode[codes.length + 1]; + System.arraycopy(codes, 0, newcodes, 0, codes.length); + newcodes[codes.length] = this; + codes = newcodes; + } + /** + * Constructor used to create an arbitrary code with a specified value. + * Doesn't add the latest value to the static array + */ + private CountryCode(int v) { + value = v; + description = "Arbitrary"; + code = "??"; + } + + /** + * Gets the country code for the given two character mnemonic string + */ + public static CountryCode getCountryCode(String s) { + if (s == null || s.length() != 2) { + logger.warn("Please specify two character ISO 3166 country code"); + return USA; + } + + CountryCode code = UNKNOWN; + for (int i = 0; i < codes.length && code == UNKNOWN; i++) { + if (codes[i].code.equals(s)) { + code = codes[i]; + } + } + + return code; + } + + /** + * Creates an arbitrary country code with the specified value. Used + * when copying sheets, and the country code isn't initialized as part + * of the static data below + */ + public static CountryCode createArbitraryCode(int i) { + return new CountryCode(i); + } + + /** + * Accessor for the excel value + * + * @return the excel value + */ + public int getValue() { + return value; + } + + /** + * Accessor for the string + * + * @return the two character iso 3166 string + */ + public String getCode() { + return code; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DVParser.java b/datastructures-xslx/src/main/java/jxl/biff/DVParser.java new file mode 100755 index 0000000..1a777fa --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DVParser.java @@ -0,0 +1,906 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +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; + +/** + * Class which parses the binary data associated with Data Validity (DV) + * setting + */ +public class DVParser { + // The values + 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"); + // The error styles + public static final ErrorStyle STOP = new ErrorStyle(0); + public static final ErrorStyle WARNING = new ErrorStyle(1); + public static final ErrorStyle INFO = new ErrorStyle(2); + // The conditions + 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}"); + // The masks + private static final int STRING_LIST_GIVEN_MASK = 0x80; + private static final int EMPTY_CELLS_ALLOWED_MASK = 0x100; + private static final int SUPPRESS_ARROW_MASK = 0x200; + private static final int SHOW_PROMPT_MASK = 0x40000; + private static final int SHOW_ERROR_MASK = 0x80000; + // The maximum string length for a data validation list + private static final int MAX_VALIDATION_LIST_LENGTH = 254; + // The maximum number of rows and columns + private static final int MAX_ROWS = 0xffff; + private static final int MAX_COLUMNS = 0xff; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DVParser.class); + // The decimal format + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#"); + /** + * The type + */ + private final DVType type; + /** + * The error style + */ + private final ErrorStyle errorStyle; + /** + * The condition + */ + private final Condition condition; + /** + * String list option + */ + private final boolean stringListGiven; + /** + * Empty cells allowed + */ + private final boolean emptyCellsAllowed; + /** + * Suppress arrow + */ + private final boolean suppressArrow; + /** + * Show prompt + */ + private final boolean showPrompt; + /** + * Show error + */ + private final boolean showError; + /** + * The title of the prompt box + */ + private String promptTitle; + /** + * The title of the error box + */ + private String errorTitle; + /** + * The text of the prompt box + */ + private String promptText; + /** + * The text of the error box + */ + private String errorText; + /** + * The first formula + */ + private FormulaParser formula1; + /** + * The first formula string + */ + private String formula1String; + /** + * The second formula + */ + private FormulaParser formula2; + /** + * The second formula string + */ + private String formula2String; + /** + * The column number of the cell at the top left of the range + */ + private int column1; + /** + * The row number of the cell at the top left of the range + */ + private int row1; + /** + * The column index of the cell at the bottom right + */ + private int column2; + /** + * The row index of the cell at the bottom right + */ + private int row2; + /** + * Flag to indicate that this DV Parser is shared amongst a group + * of cells + */ + private boolean extendedCellsValidation; + /** + * Flag indicated whether this has been copied + */ + private final boolean copied; + + /** + * Constructor + */ + public DVParser(byte[] data, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) { + Assert.verify(nt != null); + + copied = false; + int options = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + + int typeVal = options & 0xf; + type = DVType.getType(typeVal); + + int errorStyleVal = (options & 0x70) >> 4; + errorStyle = ErrorStyle.getErrorStyle(errorStyleVal); + + int conditionVal = (options & 0xf00000) >> 20; + condition = Condition.getCondition(conditionVal); + + stringListGiven = (options & STRING_LIST_GIVEN_MASK) != 0; + emptyCellsAllowed = (options & EMPTY_CELLS_ALLOWED_MASK) != 0; + suppressArrow = (options & SUPPRESS_ARROW_MASK) != 0; + showPrompt = (options & SHOW_PROMPT_MASK) != 0; + showError = (options & SHOW_ERROR_MASK) != 0; + + int pos = 4; + int length = IntegerHelper.getInt(data[pos], data[pos + 1]); + if (length > 0 && data[pos + 2] == 0) { + promptTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + 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) { + errorTitle = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + 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) { + promptText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + 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) { + errorText = StringHelper.getString(data, length, pos + 3, ws); + pos += length + 3; + } else if (length > 0) { + 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; + + row1 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + row2 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + column1 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + column2 = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + + extendedCellsValidation = row1 != row2 || column1 != column2; + + // Do the formulas + try { + // First, create a temporary blank cell for any formula relative + // references + EmptyCell tmprt = new EmptyCell(column1, row1); + + if (formula1Length != 0) { + byte[] tokens = new byte[formula1Length]; + System.arraycopy(data, formula1Pos, tokens, 0, formula1Length); + formula1 = new FormulaParser(tokens, tmprt, es, nt, ws, + ParseContext.DATA_VALIDATION); + formula1.parse(); + } + + if (formula2Length != 0) { + byte[] tokens = new byte[formula2Length]; + System.arraycopy(data, formula2Pos, tokens, 0, formula2Length); + formula2 = new FormulaParser(tokens, tmprt, es, nt, ws, + ParseContext.DATA_VALIDATION); + formula2.parse(); + } + } catch (FormulaException e) { + logger.warn(e.getMessage() + " for cells " + + CellReferenceHelper.getCellReference(column1, row1) + "-" + + CellReferenceHelper.getCellReference(column2, row2)); + } + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(Collection strings) { + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = true; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + 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('\0'); + formulaString.append(' '); + formulaString.append(i.next().toString()); + } + + // If the formula string exceeds + // the maximum validation list length, then truncate and stop there + if (formulaString.length() > MAX_VALIDATION_LIST_LENGTH) { + logger.warn("Validation list exceeds maximum number of characters - " + + "truncating"); + formulaString.delete(MAX_VALIDATION_LIST_LENGTH, + formulaString.length()); + } + + // Put the string in quotes + formulaString.insert(0, '\"'); + formulaString.append('\"'); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(String namedRange) { + // Handle the case for an empty string + if (namedRange.length() == 0) { + copied = false; + type = FORMULA; + errorStyle = STOP; + condition = EQUAL; + extendedCellsValidation = false; + // the options + stringListGiven = false; + emptyCellsAllowed = false; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = "\"\""; + return; + } + + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = namedRange; + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(int c1, int r1, int c2, int r2) { + copied = false; + type = LIST; + errorStyle = STOP; + condition = BETWEEN; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + StringBuffer formulaString = new StringBuffer(); + CellReferenceHelper.getCellReference(c1, r1, formulaString); + formulaString.append(':'); + CellReferenceHelper.getCellReference(c2, r2, formulaString); + formula1String = formulaString.toString(); + } + + /** + * Constructor called when creating a data validation from the API + */ + public DVParser(double val1, double val2, Condition c) { + copied = false; + type = DECIMAL; + errorStyle = STOP; + condition = c; + extendedCellsValidation = false; + + // the options + stringListGiven = false; + emptyCellsAllowed = true; + suppressArrow = false; + showPrompt = true; + showError = true; + + promptTitle = "\0"; + errorTitle = "\0"; + promptText = "\0"; + errorText = "\0"; + formula1String = DECIMAL_FORMAT.format(val1); + + if (!Double.isNaN(val2)) { + formula2String = DECIMAL_FORMAT.format(val2); + } + } + + /** + * Constructor called when doing a cell deep copy + */ + public DVParser(DVParser copy) { + copied = true; + type = copy.type; + errorStyle = copy.errorStyle; + condition = copy.condition; + stringListGiven = copy.stringListGiven; + emptyCellsAllowed = copy.emptyCellsAllowed; + suppressArrow = copy.suppressArrow; + showPrompt = copy.showPrompt; + showError = copy.showError; + promptTitle = copy.promptTitle; + promptText = copy.promptText; + errorTitle = copy.errorTitle; + errorText = copy.errorText; + extendedCellsValidation = copy.extendedCellsValidation; + + row1 = copy.row1; + row2 = copy.row2; + column1 = copy.column1; + column2 = copy.column2; + + // Don't copy the formula parsers - just take their string equivalents + if (copy.formula1String != null) { + formula1String = copy.formula1String; + formula2String = copy.formula2String; + } else { + try { + formula1String = copy.formula1.getFormula(); + formula2String = (copy.formula2 != null) ? + copy.formula2.getFormula() : null; + } catch (FormulaException e) { + logger.warn("Cannot parse validation formula: " + e.getMessage()); + } + } + // Don't copy the cell references - these will be added later + } + + /** + * Gets the data + */ + public byte[] getData() { + // Compute the length of the data + byte[] f1Bytes = formula1 != null ? formula1.getBytes() : new byte[0]; + byte[] f2Bytes = formula2 != null ? formula2.getBytes() : new byte[0]; + int dataLength = + 4 + // the options + promptTitle.length() * 2 + 3 + // the prompt title + errorTitle.length() * 2 + 3 + // the error title + promptText.length() * 2 + 3 + // the prompt text + errorText.length() * 2 + 3 + // the error text + f1Bytes.length + 2 + // first formula + f2Bytes.length + 2 + // second formula + +4 + // unused bytes + 10; // cell range + + byte[] data = new byte[dataLength]; + + // The position + int pos = 0; + + // The options + int options = 0; + options |= type.getValue(); + options |= errorStyle.getValue() << 4; + options |= condition.getValue() << 20; + + if (stringListGiven) { + options |= STRING_LIST_GIVEN_MASK; + } + + if (emptyCellsAllowed) { + options |= EMPTY_CELLS_ALLOWED_MASK; + } + + if (suppressArrow) { + options |= SUPPRESS_ARROW_MASK; + } + + if (showPrompt) { + options |= SHOW_PROMPT_MASK; + } + + if (showError) { + options |= SHOW_ERROR_MASK; + } + + // The text + IntegerHelper.getFourBytes(options, data, pos); + pos += 4; + + IntegerHelper.getTwoBytes(promptTitle.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(promptTitle, data, pos); + pos += promptTitle.length() * 2; + + IntegerHelper.getTwoBytes(errorTitle.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(errorTitle, data, pos); + pos += errorTitle.length() * 2; + + IntegerHelper.getTwoBytes(promptText.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(promptText, data, pos); + pos += promptText.length() * 2; + + IntegerHelper.getTwoBytes(errorText.length(), data, pos); + pos += 2; + + data[pos] = (byte) 0x1; // unicode indicator + pos++; + + StringHelper.getUnicodeBytes(errorText, data, pos); + pos += errorText.length() * 2; + + // Formula 1 + IntegerHelper.getTwoBytes(f1Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f1Bytes, 0, data, pos, f1Bytes.length); + pos += f1Bytes.length; + + // Formula 2 + IntegerHelper.getTwoBytes(f2Bytes.length, data, pos); + pos += 4; + + System.arraycopy(f2Bytes, 0, data, pos, f2Bytes.length); + pos += f2Bytes.length; + + // The cell ranges + IntegerHelper.getTwoBytes(1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(row2, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column1, data, pos); + pos += 2; + + IntegerHelper.getTwoBytes(column2, data, pos); + pos += 2; + + return data; + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (formula1 != null) { + formula1.rowInserted(0, row, true); + } + + if (formula2 != null) { + formula2.rowInserted(0, row, true); + } + + if (row1 >= row) { + row1++; + } + + if (row2 >= row && row2 != MAX_ROWS) { + row2++; + } + } + + /** + * Inserts a column + * + * @param col the column to insert + */ + public void insertColumn(int col) { + if (formula1 != null) { + formula1.columnInserted(0, col, true); + } + + if (formula2 != null) { + formula2.columnInserted(0, col, true); + } + + if (column1 >= col) { + column1++; + } + + if (column2 >= col && column2 != MAX_COLUMNS) { + column2++; + } + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) { + if (formula1 != null) { + formula1.rowRemoved(0, row, true); + } + + if (formula2 != null) { + formula2.rowRemoved(0, row, true); + } + + if (row1 > row) { + row1--; + } + + if (row2 >= row) { + row2--; + } + } + + /** + * Removes a column + * + * @param col the row to remove + */ + public void removeColumn(int col) { + if (formula1 != null) { + formula1.columnRemoved(0, col, true); + } + + if (formula2 != null) { + formula2.columnRemoved(0, col, true); + } + + if (column1 > col) { + column1--; + } + + if (column2 >= col && column2 != MAX_COLUMNS) { + column2--; + } + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() { + return column1; + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() { + return column2; + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() { + return row1; + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() { + return row2; + } + + /** + * Gets the formula present in the validation + * + * @return the validation formula as a string + * @throws FormulaException + */ + String getValidationFormula() throws FormulaException { + if (type == LIST) { + return formula1.getFormula(); + } + + String s1 = formula1.getFormula(); + String s2 = formula2 != null ? formula2.getFormula() : null; + return condition.getConditionString(s1, s2) + + "; x " + type.getDescription(); + } + + /** + * Called by the cell value when the cell features are added to the sheet + */ + public void setCell(int col, + int row, + ExternalSheet es, + WorkbookMethods nt, + WorkbookSettings ws) throws FormulaException { + // If this is part of an extended cells validation, then do nothing + // as this will already have been called and parsed when the top left + // cell was added + if (extendedCellsValidation) { + return; + } + + row1 = row; + row2 = row; + column1 = col; + column2 = col; + + formula1 = new FormulaParser(formula1String, + es, nt, ws, + ParseContext.DATA_VALIDATION); + formula1.parse(); + + if (formula2String != null) { + formula2 = new FormulaParser(formula2String, + es, nt, ws, + ParseContext.DATA_VALIDATION); + formula2.parse(); + } + } + + /** + * Indicates that the data validation extends across several more cells + * + * @param cols - the number of extra columns + * @param rows - the number of extra rows + */ + public void extendCellValidation(int cols, int rows) { + row2 = row1 + rows; + column2 = column1 + cols; + extendedCellsValidation = true; + } + + /** + * Accessor which indicates whether this validation applies across + * multiple cels + */ + public boolean extendedCellsValidation() { + return extendedCellsValidation; + } + + public boolean copied() { + return copied; + } + + // DV Type + public static class DVType { + private static DVType[] types = new DVType[0]; + private final int value; + private final String desc; + + DVType(int v, String d) { + value = v; + 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 value; + } + + public String getDescription() { + return desc; + } + } + + // Error Style + public static class ErrorStyle { + private static ErrorStyle[] types = new ErrorStyle[0]; + private final int value; + + ErrorStyle(int v) { + 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 value; + } + } + + // Conditions + public static class Condition { + private static Condition[] types = new Condition[0]; + private final int value; + private final MessageFormat format; + + Condition(int v, String pattern) { + value = v; + 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 value; + } + + public String getConditionString(String s1, String s2) { + return format.format(new String[]{s1, s2}); + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DValParser.java b/datastructures-xslx/src/main/java/jxl/biff/DValParser.java new file mode 100755 index 0000000..8d8dd24 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DValParser.java @@ -0,0 +1,149 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Class which parses the binary data associated with Data Validity (DVal) + * setting + */ +public class DValParser { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DValParser.class); + + // The option masks + private static final int PROMPT_BOX_VISIBLE_MASK = 0x1; + private static final int PROMPT_BOX_AT_CELL_MASK = 0x2; + private static final int VALIDITY_DATA_CACHED_MASK = 0x4; + + /** + * Prompt box visible + */ + private boolean promptBoxVisible; + + /** + * Empty cells allowed + */ + private boolean promptBoxAtCell; + + /** + * Cell validity data cached in following DV records + */ + private final boolean validityDataCached; + + /** + * The number of following DV records + */ + private int numDVRecords; + + /** + * The object id of the associated down arrow + */ + private final int objectId; + + /** + * Constructor + */ + public DValParser(byte[] data) { + int options = IntegerHelper.getInt(data[0], data[1]); + + promptBoxVisible = (options & PROMPT_BOX_VISIBLE_MASK) != 0; + promptBoxAtCell = (options & PROMPT_BOX_AT_CELL_MASK) != 0; + validityDataCached = (options & VALIDITY_DATA_CACHED_MASK) != 0; + + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numDVRecords = IntegerHelper.getInt(data[14], data[15], + data[16], data[17]); + } + + /** + * Constructor + */ + public DValParser(int objid, int num) { + objectId = objid; + numDVRecords = num; + validityDataCached = true; + } + + /** + * Gets the data + */ + public byte[] getData() { + byte[] data = new byte[18]; + + int options = 0; + + if (promptBoxVisible) { + options |= PROMPT_BOX_VISIBLE_MASK; + } + + if (promptBoxAtCell) { + options |= PROMPT_BOX_AT_CELL_MASK; + } + + if (validityDataCached) { + options |= VALIDITY_DATA_CACHED_MASK; + } + + IntegerHelper.getTwoBytes(options, data, 0); + + IntegerHelper.getFourBytes(objectId, data, 10); + + IntegerHelper.getFourBytes(numDVRecords, data, 14); + + return data; + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + public void dvRemoved() { + numDVRecords--; + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public int getNumberOfDVRecords() { + return numDVRecords; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() { + return objectId; + } + + /** + * Called when adding a DV record on a copied DVal + */ + public void dvAdded() { + numDVRecords++; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java b/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java new file mode 100755 index 0000000..f03a329 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValidation.java @@ -0,0 +1,306 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.biff.File; + + +/** + * Class which encapsulates a data validation. This encapsulates the + * base DVAL record (DataValidityListRecord) and all the individual DV + * (DataValiditySettingsRecord) records + */ +public class DataValidation { + public static final int DEFAULT_OBJECT_ID = 0xffffffff; + private static final int MAX_NO_OF_VALIDITY_SETTINGS = 0xfffd; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DataValidation.class); + /** + * The data validity list + */ + private DataValidityListRecord validityList; + /** + * The list of data validity (DV) records + */ + private ArrayList validitySettings; + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + /** + * Handle to the external sheet + */ + private ExternalSheet externalSheet; + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + /** + * The object id of the combo box used for drop downs + */ + private int comboBoxObjectId; + /** + * Indicates whether this was copied + */ + private final boolean copied; + + /** + * Constructor + */ + public DataValidation(DataValidityListRecord dvlr) { + validityList = dvlr; + validitySettings = new ArrayList(validityList.getNumberOfSettings()); + copied = false; + } + + /** + * Constructor used to create writable data validations + */ + public DataValidation(int objId, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + validitySettings = new ArrayList(); + comboBoxObjectId = objId; + copied = false; + } + + /** + * Copy constructor used to copy from read to write + */ + public DataValidation(DataValidation dv, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + workbook = wm; + externalSheet = es; + workbookSettings = ws; + copied = true; + validityList = new DataValidityListRecord(dv.getDataValidityList()); + + validitySettings = new ArrayList(); + DataValiditySettingsRecord[] settings = dv.getDataValiditySettings(); + + for (int i = 0; i < settings.length; i++) { + validitySettings.add(new DataValiditySettingsRecord(settings[i], + externalSheet, + workbook, + workbookSettings)); + } + } + + /** + * Adds a new settings object to this data validation + */ + public void add(DataValiditySettingsRecord dvsr) { + validitySettings.add(dvsr); + dvsr.setDataValidation(this); + + if (copied) { + // adding a writable dv record to a copied validity list + Assert.verify(validityList != null); + validityList.dvAdded(); + } + } + + /** + * Accessor for the validity list. Used when copying sheets + */ + public DataValidityListRecord getDataValidityList() { + return validityList; + } + + /** + * Accessor for the validity settings. Used when copying sheets + */ + public DataValiditySettingsRecord[] getDataValiditySettings() { + DataValiditySettingsRecord[] dvlr = new DataValiditySettingsRecord[0]; + return (DataValiditySettingsRecord[]) validitySettings.toArray(dvlr); + } + + /** + * Writes out the data validation + * + * @param outputFile the output file + * @throws IOException + */ + public void write(File outputFile) throws IOException { + if (validitySettings.size() > MAX_NO_OF_VALIDITY_SETTINGS) { + logger.warn("Maximum number of data validations exceeded - " + + "truncating..."); + validitySettings = new ArrayList + (validitySettings.subList(0, MAX_NO_OF_VALIDITY_SETTINGS - 1)); + Assert.verify(validitySettings.size() <= MAX_NO_OF_VALIDITY_SETTINGS); + } + + if (validityList == null) { + DValParser dvp = new DValParser(comboBoxObjectId, + validitySettings.size()); + validityList = new DataValidityListRecord(dvp); + } + + if (!validityList.hasDVRecords()) { + return; + } + + outputFile.write(validityList); + + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + outputFile.write(dvsr); + } + } + + /** + * Inserts a row + * + * @param row the inserted row + */ + public void insertRow(int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertRow(row); + } + } + + /** + * Removes row + * + * @param row the row to be removed + */ + public void removeRow(int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstRow() == row && dv.getLastRow() == row) { + i.remove(); + validityList.dvRemoved(); + } else { + dv.removeRow(row); + } + } + } + + /** + * Inserts a column + * + * @param col the inserted column + */ + public void insertColumn(int col) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + dv.insertColumn(col); + } + } + + /** + * Removes a column + * + * @param col the inserted column + */ + public void removeColumn(int col) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col) { + i.remove(); + validityList.dvRemoved(); + } else { + dv.removeColumn(col); + } + } + } + + /** + * Removes the data validation for a specific cell + * + * @param col the column + * @param row the row + */ + public void removeDataValidation(int col, int row) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col && dv.getLastColumn() == col && + dv.getFirstRow() == row && dv.getLastRow() == row) { + i.remove(); + validityList.dvRemoved(); + break; + } + } + } + + /** + * Removes the data validation for a specific cell + * + * @param col1 the first column + * @param row1 the first row + */ + public void removeSharedDataValidation(int col1, int row1, + int col2, int row2) { + for (Iterator i = validitySettings.iterator(); i.hasNext(); ) { + DataValiditySettingsRecord dv = (DataValiditySettingsRecord) i.next(); + + if (dv.getFirstColumn() == col1 && dv.getLastColumn() == col2 && + dv.getFirstRow() == row1 && dv.getLastRow() == row2) { + i.remove(); + validityList.dvRemoved(); + break; + } + } + } + + /** + * Used during the copy process to retrieve the validity settings for + * a particular cell + */ + public DataValiditySettingsRecord getDataValiditySettings(int col, int row) { + boolean found = false; + DataValiditySettingsRecord foundRecord = null; + for (Iterator i = validitySettings.iterator(); i.hasNext() && !found; ) { + DataValiditySettingsRecord dvsr = (DataValiditySettingsRecord) i.next(); + if (dvsr.getFirstColumn() == col && dvsr.getFirstRow() == row) { + found = true; + foundRecord = dvsr; + } + } + + return foundRecord; + } + + /** + * Accessor for the combo box, used when copying sheets + */ + public int getComboBoxObjectId() { + return comboBoxObjectId; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java b/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java new file mode 100755 index 0000000..2c41f7b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValidityListRecord.java @@ -0,0 +1,151 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Record containing the list of data validation settings for a given sheet + */ +public class DataValidityListRecord extends WritableRecordData { + private static final Logger logger = Logger.getLogger + (DataValidityListRecord.class); + + /** + * The number of settings records associated with this list + */ + private int numSettings; + + /** + * The object id of the associated down arrow + */ + private int objectId; + + /** + * The dval parser + */ + private DValParser dvalParser; + + /** + * The data + */ + private byte[] data; + + /** + * Constructor + */ + public DataValidityListRecord(Record t) { + super(t); + + data = getRecord().getData(); + objectId = IntegerHelper.getInt(data[10], data[11], data[12], data[13]); + numSettings = IntegerHelper.getInt(data[14], data[15], data[16], data[17]); + } + + /** + * Constructor called when generating a data validity list from the API + */ + public DataValidityListRecord(DValParser dval) { + super(Type.DVAL); + + dvalParser = dval; + } + + /** + * Copy constructor + * + * @param dvlr the record copied from a read only sheet + */ + DataValidityListRecord(DataValidityListRecord dvlr) { + super(Type.DVAL); + + data = dvlr.getData(); + } + + /** + * Accessor for the number of settings records associated with this list + */ + int getNumberOfSettings() { + return numSettings; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (dvalParser == null) { + return data; + } + + return dvalParser.getData(); + } + + /** + * Called when a remove row or column results in one of DV records being + * removed + */ + void dvRemoved() { + if (dvalParser == null) { + dvalParser = new DValParser(data); + } + + dvalParser.dvRemoved(); + } + + /** + * Called when a writable DV record is added to a copied validity list + */ + void dvAdded() { + if (dvalParser == null) { + dvalParser = new DValParser(data); + } + + dvalParser.dvAdded(); + } + + /** + * Accessor for the number of DV records + * + * @return the number of DV records for this list + */ + public boolean hasDVRecords() { + if (dvalParser == null) { + return true; + } + + return dvalParser.getNumberOfDVRecords() > 0; + } + + /** + * Accessor for the object id + * + * @return the object id + */ + public int getObjectId() { + if (dvalParser == null) { + return objectId; + } + + return dvalParser.getObjectId(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java b/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java new file mode 100755 index 0000000..9eeae31 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DataValiditySettingsRecord.java @@ -0,0 +1,282 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Data validity settings. Contains an individual Data validation (DV). + * All the computationa work is delegated to the DVParser object + */ +public class DataValiditySettingsRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(DataValiditySettingsRecord.class); + + /** + * The binary data + */ + private byte[] data; + + /** + * The reader + */ + private DVParser dvParser; + + /** + * Handle to the workbook + */ + private WorkbookMethods workbook; + + /** + * Handle to the externalSheet + */ + private ExternalSheet externalSheet; + + /** + * Handle to the workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Handle to the data validation record + */ + private DataValidation dataValidation; + + /** + * Constructor + */ + public DataValiditySettingsRecord(Record t, + ExternalSheet es, + WorkbookMethods wm, + WorkbookSettings ws) { + super(t); + + data = t.getData(); + externalSheet = es; + workbook = wm; + workbookSettings = ws; + } + + /** + * Copy constructor + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr) { + super(Type.DV); + + data = dvsr.getData(); + } + + /** + * Constructor used when copying sheets + * + * @param dvsr the record copied from a writable sheet + */ + DataValiditySettingsRecord(DataValiditySettingsRecord dvsr, + ExternalSheet es, + WorkbookMethods w, + WorkbookSettings ws) { + super(Type.DV); + + workbook = w; + externalSheet = es; + workbookSettings = ws; + + Assert.verify(w != null); + Assert.verify(es != null); + + data = new byte[dvsr.data.length]; + System.arraycopy(dvsr.data, 0, data, 0, data.length); + } + + /** + * Constructor called when the API creates a writable data validation + * + * @param dvsr the record copied from a writable sheet + */ + public DataValiditySettingsRecord(DVParser dvp) { + super(Type.DV); + dvParser = dvp; + } + + /** + * Initializes the dvParser + */ + private void initialize() { + if (dvParser == null) { + dvParser = new DVParser(data, externalSheet, + workbook, workbookSettings); + } + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + if (dvParser == null) { + return data; + } + + return dvParser.getData(); + } + + /** + * Inserts a row + * + * @param row the row to insert + */ + public void insertRow(int row) { + if (dvParser == null) { + initialize(); + } + + dvParser.insertRow(row); + } + + /** + * Removes a row + * + * @param row the row to insert + */ + public void removeRow(int row) { + if (dvParser == null) { + initialize(); + } + + dvParser.removeRow(row); + } + + /** + * Inserts a row + * + * @param col the row to insert + */ + public void insertColumn(int col) { + if (dvParser == null) { + initialize(); + } + + dvParser.insertColumn(col); + } + + /** + * Removes a column + * + * @param col the row to insert + */ + public void removeColumn(int col) { + if (dvParser == null) { + initialize(); + } + + dvParser.removeColumn(col); + } + + /** + * Accessor for first column + * + * @return the first column + */ + public int getFirstColumn() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getFirstColumn(); + } + + /** + * Accessor for the last column + * + * @return the last column + */ + public int getLastColumn() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getLastColumn(); + } + + /** + * Accessor for first row + * + * @return the first row + */ + public int getFirstRow() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getFirstRow(); + } + + /** + * Accessor for the last row + * + * @return the last row + */ + public int getLastRow() { + if (dvParser == null) { + initialize(); + } + + return dvParser.getLastRow(); + } + + /** + * Sets the handle to the data validation record + * + * @param dv the data validation + */ + void setDataValidation(DataValidation dv) { + dataValidation = dv; + } + + /** + * Gets the DVParser. This is used when doing a deep copy of cells + * on the writable side of things + */ + DVParser getDVParser() { + return dvParser; + } + + public String getValidationFormula() { + try { + if (dvParser == null) { + initialize(); + } + + return dvParser.getValidationFormula(); + } catch (FormulaException e) { + logger.warn("Cannot read drop down range " + e.getMessage()); + return ""; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java b/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java new file mode 100755 index 0000000..e2782ad --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DisplayFormat.java @@ -0,0 +1,55 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * The interface implemented by the various number and date format styles. + * The methods on this interface are called internally when generating a + * spreadsheet + */ +public interface DisplayFormat { + /** + * Accessor for the index style of this format + * + * @return the index for this format + */ + int getFormatIndex(); + + /** + * Accessor to see whether this format has been initialized + * + * @return TRUE if initialized, FALSE otherwise + */ + boolean isInitialized(); + + /** + * Initializes this format with the specified index number + * + * @param pos the position of this format record in the workbook + */ + void initialize(int pos); + + /** + * Accessor to determine whether or not this format is built in + * + * @return TRUE if this format is a built in format, FALSE otherwise + */ + boolean isBuiltIn(); +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java b/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java new file mode 100755 index 0000000..249d94d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/DoubleHelper.java @@ -0,0 +1,84 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +/** + * Class to help handle doubles + */ +public class DoubleHelper { + /** + * Private constructor to prevent instantiation + */ + private DoubleHelper() { + } + + /** + * Gets the IEEE value from the byte array passed in + * + * @param pos the position in the data block which contains the double value + * @param data the data block containing the raw bytes + * @return the double value converted from the raw data + */ + public static double getIEEEDouble(byte[] data, int pos) { + int num1 = IntegerHelper.getInt(data[pos], data[pos + 1], + data[pos + 2], data[pos + 3]); + int num2 = IntegerHelper.getInt(data[pos + 4], data[pos + 5], + data[pos + 6], data[pos + 7]); + + // Long.parseLong doesn't like the sign bit, so have to extract this + // information and put it in at the end. (Acknowledgment: thanks + // to Ruben for pointing this out) + boolean negative = ((num2 & 0x80000000) != 0); + + // Thanks to Lyle for the following improved IEEE double processing + long val = ((num2 & 0x7fffffff) * 0x100000000L) + + (num1 < 0 ? 0x100000000L + num1 : num1); + double value = Double.longBitsToDouble(val); + + if (negative) { + value = -value; + } + return value; + } + + /** + * Puts the IEEE representation of the double provided into the array + * at the designated position + * + * @param target the data block into which the binary representation is to + * be placed + * @param pos the position in target in which to place the bytes + * @param d the double value to convert to raw bytes + */ + public static void getIEEEBytes(double d, byte[] target, int pos) { + long val = Double.doubleToLongBits(d); + target[pos] = (byte) (val & 0xff); + target[pos + 1] = (byte) ((val & 0xff00) >> 8); + target[pos + 2] = (byte) ((val & 0xff0000) >> 16); + target[pos + 3] = (byte) ((val & 0xff000000) >> 24); + target[pos + 4] = (byte) ((val & 0xff00000000L) >> 32); + target[pos + 5] = (byte) ((val & 0xff0000000000L) >> 40); + target[pos + 6] = (byte) ((val & 0xff000000000000L) >> 48); + target[pos + 7] = (byte) ((val & 0xff00000000000000L) >> 56); + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java b/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java new file mode 100755 index 0000000..dac8e3d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/EmptyCell.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.CellFeatures; +import jxl.CellType; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.VerticalAlignment; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; + +/** + * An empty cell. Represents an empty, as opposed to a blank cell + * in the workbook + */ +public class EmptyCell implements WritableCell { + /** + * The row of this empty cell + */ + private final int row; + /** + * The column number of this empty cell + */ + private final int col; + + /** + * Constructs an empty cell at the specified position + * + * @param c the zero based column + * @param r the zero based row + */ + public EmptyCell(int c, int r) { + row = r; + col = c; + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() { + return col; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return an empty string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the format which is applied to this cell + * + * @return the format applied to this cell + */ + public CellFormat getCellFormat() { + return null; + } + + /** + * Dummy override + * + * @param cf dummy + */ + public void setCellFormat(CellFormat cf) { + } + + /** + * Dummy override + * + * @param cf dummy + * @deprecated + */ + public void setCellFormat(jxl.CellFormat cf) { + } + + /** + * Dummy override + * + * @param flag dummy + */ + public void setLocked(boolean flag) { + } + + /** + * Dummy override + * + * @param align dummy + */ + public void setAlignment(Alignment align) { + } + + /** + * Dummy override + * + * @param valign dummy + */ + public void setVerticalAlignment(VerticalAlignment valign) { + } + + /** + * Dummy override + * + * @param line dummy + * @param border dummy + */ + public void setBorder(Border border, BorderLineStyle line) { + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() { + return false; + } + + /** + * Dummy override + * + * @param flag the hidden flag + */ + public void setHidden(boolean flag) { + } + + /** + * Implementation of the deep copy function + * + * @param c the column which the new cell will occupy + * @param r the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int c, int r) { + return new EmptyCell(c, r); + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return null; + } + + /** + * Accessor for the cell features + */ + public void setCellFeatures(WritableCellFeatures wcf) { + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() { + return null; + } + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java b/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java new file mode 100755 index 0000000..7ea12f4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/EncodedURLHelper.java @@ -0,0 +1,117 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.common.Logger; + +/** + * Helper to get the Microsoft encoded URL from the given string + */ +public class EncodedURLHelper { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(EncodedURLHelper.class); + + // The control codes + private static final byte msDosDriveLetter = 0x01; + private static final byte sameDrive = 0x02; + private static final byte endOfSubdirectory = 0x03; + private static final byte parentDirectory = 0x04; + private static final byte unencodedUrl = 0x05; + + public static byte[] getEncodedURL(String s, WorkbookSettings ws) { + if (s.startsWith("http:")) { + return getURL(s, ws); + } else { + return getFile(s, ws); + } + } + + private static byte[] getFile(String s, WorkbookSettings ws) { + ByteArray byteArray = new ByteArray(); + + int pos = 0; + if (s.charAt(1) == ':') { + // we have a drive letter + byteArray.add(msDosDriveLetter); + byteArray.add((byte) s.charAt(0)); + pos = 2; + } else if (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') { + byteArray.add(sameDrive); + } + + while (s.charAt(pos) == '\\' || + s.charAt(pos) == '/') { + pos++; + } + + while (pos < s.length()) { + int nextSepIndex1 = s.indexOf('/', pos); + int nextSepIndex2 = s.indexOf('\\', pos); + int nextSepIndex = 0; + String nextFileNameComponent = null; + + if (nextSepIndex1 != -1 && nextSepIndex2 != -1) { + // choose the smallest (ie. nearest) separator + nextSepIndex = Math.min(nextSepIndex1, nextSepIndex2); + } else if (nextSepIndex1 == -1 || nextSepIndex2 == -1) { + // chose the maximum separator + nextSepIndex = Math.max(nextSepIndex1, nextSepIndex2); + } + + if (nextSepIndex == -1) { + // no more separators + nextFileNameComponent = s.substring(pos); + pos = s.length(); + } else { + nextFileNameComponent = s.substring(pos, nextSepIndex); + pos = nextSepIndex + 1; + } + + if (nextFileNameComponent.equals(".")) { + // current directory - do nothing + } else if (nextFileNameComponent.equals("..")) { + // parent directory + byteArray.add(parentDirectory); + } else { + // add the filename component + byteArray.add(StringHelper.getBytes(nextFileNameComponent, + ws)); + } + + if (pos < s.length()) { + byteArray.add(endOfSubdirectory); + } + } + + return byteArray.getBytes(); + } + + private static byte[] getURL(String s, WorkbookSettings ws) { + ByteArray byteArray = new ByteArray(); + byteArray.add(unencodedUrl); + byteArray.add((byte) s.length()); + byteArray.add(StringHelper.getBytes(s, ws)); + return byteArray.getBytes(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java new file mode 100755 index 0000000..89ddcb3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FilterModeRecord.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; +import jxl.read.biff.Record; + +/** + * Range information for conditional formatting + */ +public class FilterModeRecord extends WritableRecordData { + // The logger + private static final Logger logger = Logger.getLogger(FilterModeRecord.class); + + /** + * The data + */ + private final byte[] data; + + + /** + * Constructor + */ + public FilterModeRecord(Record t) { + super(t); + + data = getRecord().getData(); + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java new file mode 100755 index 0000000..4d22bd2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FontRecord.java @@ -0,0 +1,501 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.WorkbookSettings; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.read.biff.Record; + +/** + * A record containing the necessary data for the font information + */ +public class FontRecord extends WritableRecordData implements Font { + public static final Biff7 biff7 = new Biff7(); + /** + * The conversion factor between microsoft internal units and point size + */ + private static final int EXCEL_UNITS_PER_POINT = 20; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FontRecord.class); + /** + * The point height of this font + */ + private int pointHeight; + /** + * The index into the colour palette + */ + private int colourIndex; + /** + * The bold weight for this font (normal or bold) + */ + private int boldWeight; + /** + * The style of the script (italic or normal) + */ + private int scriptStyle; + /** + * The underline style for this font (none, single, double etc) + */ + private int underlineStyle; + /** + * The font family + */ + private byte fontFamily; + /** + * The character set + */ + private byte characterSet; + /** + * Indicates whether or not this font is italic + */ + private boolean italic; + /** + * Indicates whether or not this font is struck out + */ + private boolean struckout; + /** + * The name of this font + */ + private final String name; + /** + * Flag to indicate whether the derived data (such as the font index) has + * been initialized or not + */ + private boolean initialized; + + /** + * The index of this font in the font list + */ + private int fontIndex; + + /** + * Constructor, used when creating a new font for writing out. + * + * @param bold the bold indicator + * @param ps the point size + * @param us the underline style + * @param fn the name + * @param it italicised indicator + * @param ss the script style + * @param ci the colour index + */ + protected FontRecord(String fn, int ps, int bold, boolean it, + int us, int ci, int ss) { + super(Type.FONT); + boldWeight = bold; + underlineStyle = us; + name = fn; + pointHeight = ps; + italic = it; + scriptStyle = ss; + colourIndex = ci; + initialized = false; + struckout = false; + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + */ + public FontRecord(Record t, WorkbookSettings ws) { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + characterSet = data[12]; + initialized = false; + + if ((data[2] & 0x02) != 0) { + italic = true; + } + + if ((data[2] & 0x08) != 0) { + struckout = true; + } + + int numChars = data[14]; + if (data[15] == 0) { + name = StringHelper.getString(data, numChars, 16, ws); + } else if (data[15] == 1) { + name = StringHelper.getUnicodeString(data, numChars, 16); + } else { + // Some font names don't have the unicode indicator + name = StringHelper.getString(data, numChars, 15, ws); + } + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param dummy dummy overload + */ + public FontRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + + byte[] data = getRecord().getData(); + + pointHeight = IntegerHelper.getInt(data[0], data[1]) / + EXCEL_UNITS_PER_POINT; + colourIndex = IntegerHelper.getInt(data[4], data[5]); + boldWeight = IntegerHelper.getInt(data[6], data[7]); + scriptStyle = IntegerHelper.getInt(data[8], data[9]); + underlineStyle = data[10]; + fontFamily = data[11]; + initialized = false; + + if ((data[2] & 0x02) != 0) { + italic = true; + } + + if ((data[2] & 0x08) != 0) { + struckout = true; + } + + int numChars = data[14]; + name = StringHelper.getString(data, numChars, 15, ws); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + protected FontRecord(Font f) { + super(Type.FONT); + + Assert.verify(f != null); + + pointHeight = f.getPointSize(); + colourIndex = f.getColour().getValue(); + boldWeight = f.getBoldWeight(); + scriptStyle = f.getScriptStyle().getValue(); + underlineStyle = f.getUnderlineStyle().getValue(); + italic = f.isItalic(); + name = f.getName(); + struckout = f.isStruckout(); + initialized = false; + } + + /** + * Gets the byte data for writing out + * + * @return the raw data + */ + public byte[] getData() { + byte[] data = new byte[16 + name.length() * 2]; + + // Excel expects font heights in 1/20ths of a point + IntegerHelper.getTwoBytes(pointHeight * EXCEL_UNITS_PER_POINT, data, 0); + + // Set the font attributes to be zero for now + if (italic) { + data[2] |= 0x2; + } + + if (struckout) { + data[2] |= 0x08; + } + + // Set the index to the colour palette + IntegerHelper.getTwoBytes(colourIndex, data, 4); + + // Bold style + IntegerHelper.getTwoBytes(boldWeight, data, 6); + + // Script style + IntegerHelper.getTwoBytes(scriptStyle, data, 8); + + // Underline style + data[10] = (byte) underlineStyle; + + // Set the font family to be 0 + data[11] = fontFamily; + + // Set the character set to be zero + data[12] = characterSet; + + // Set the reserved bit to be zero + data[13] = 0; + + // Set the length of the font name + data[14] = (byte) name.length(); + + data[15] = (byte) 1; + + // Copy in the string + StringHelper.getUnicodeBytes(name, data, 16); + + return data; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public final boolean isInitialized() { + return initialized; + } + + /** + * Sets the font index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + public final void initialize(int pos) { + fontIndex = pos; + initialized = true; + } + + /** + * Resets the initialize flag. This is called by the constructor of + * WritableWorkbookImpl to reset the statically declared fonts + */ + public final void uninitialize() { + initialized = false; + } + + /** + * Accessor for the font index + * + * @return the font index + */ + public final int getFontIndex() { + return fontIndex; + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param ps the point size + */ + protected void setFontPointSize(int ps) { + Assert.verify(!initialized); + + pointHeight = ps; + } + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + public int getPointSize() { + return pointHeight; + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param bs the bold style + */ + protected void setFontBoldStyle(int bs) { + Assert.verify(!initialized); + + boldWeight = bs; + } + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + public int getBoldWeight() { + return boldWeight; + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param i the italic flag + */ + protected void setFontItalic(boolean i) { + Assert.verify(!initialized); + + italic = i; + } + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + public boolean isItalic() { + return italic; + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + */ + protected void setFontUnderlineStyle(int us) { + Assert.verify(!initialized); + + underlineStyle = us; + } + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + public UnderlineStyle getUnderlineStyle() { + return UnderlineStyle.getStyle(underlineStyle); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param c the colour + */ + protected void setFontColour(int c) { + Assert.verify(!initialized); + + colourIndex = c; + } + + /** + * Gets the colour for this font + * + * @return the colour + */ + public Colour getColour() { + return Colour.getInternalColour(colourIndex); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param ss the colour + */ + protected void setFontScriptStyle(int ss) { + Assert.verify(!initialized); + + scriptStyle = ss; + } + + /** + * Gets the script style + * + * @return the script style + */ + public ScriptStyle getScriptStyle() { + return ScriptStyle.getStyle(scriptStyle); + } + + /** + * Gets the name of this font + * + * @return the name of this font + */ + public String getName() { + return name; + } + + /** + * Standard hash code method + * + * @return the hash code for this object + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof FontRecord)) { + return false; + } + + FontRecord font = (FontRecord) o; + + return pointHeight == font.pointHeight && + colourIndex == font.colourIndex && + boldWeight == font.boldWeight && + scriptStyle == font.scriptStyle && + underlineStyle == font.underlineStyle && + italic == font.italic && + struckout == font.struckout && + fontFamily == font.fontFamily && + characterSet == font.characterSet && + name.equals(font.name); + } + + /** + * Accessor for the strike out flag + * + * @return TRUE if this font is struck out, FALSE otherwise + */ + public boolean isStruckout() { + return struckout; + } + + /** + * Sets the struck out flag + * + * @param os TRUE if the font is struck out, false otherwise + */ + protected void setFontStruckout(boolean os) { + struckout = os; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/Fonts.java b/datastructures-xslx/src/main/java/jxl/biff/Fonts.java new file mode 100755 index 0000000..a7548c8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/Fonts.java @@ -0,0 +1,162 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.common.Assert; +import jxl.write.biff.File; + +/** + * A container for the list of fonts used in this workbook + */ +public class Fonts { + /** + * The default number of fonts + */ + private static final int numDefaultFonts = 4; + /** + * The list of fonts + */ + private ArrayList fonts; + + /** + * Constructor + */ + public Fonts() { + fonts = new ArrayList(); + } + + /** + * Adds a font record to this workbook. If the FontRecord passed in has not + * been initialized, then its font index is determined based upon the size + * of the fonts list. The FontRecord's initialized method is called, and + * it is added to the list of fonts. + * + * @param f the font to add + */ + public void addFont(FontRecord f) { + if (!f.isInitialized()) { + int pos = fonts.size(); + + // Remember that the pos with index 4 is skipped + if (pos >= 4) { + pos++; + } + + f.initialize(pos); + fonts.add(f); + } + } + + /** + * Used by FormattingRecord for retrieving the fonts for the + * hardcoded styles + * + * @param index the index of the font to return + * @return the font with the specified font index + */ + public FontRecord getFont(int index) { + // remember to allow for the fact that font index 4 is not used + if (index > 4) { + index--; + } + + return (FontRecord) fonts.get(index); + } + + /** + * Writes out the list of fonts + * + * @param outputFile the compound file to write the data to + * @throws IOException + */ + public void write(File outputFile) throws IOException { + Iterator i = fonts.iterator(); + + FontRecord font = null; + while (i.hasNext()) { + font = (FontRecord) i.next(); + outputFile.write(font); + } + } + + /** + * Rationalizes all the fonts, removing any duplicates + * + * @return the mappings between new indexes and old ones + */ + IndexMapping rationalize() { + IndexMapping mapping = new IndexMapping(fonts.size() + 1); + // allow for skipping record 4 + + ArrayList newfonts = new ArrayList(); + FontRecord fr = null; + int numremoved = 0; + + // Preserve the default fonts + for (int i = 0; i < numDefaultFonts; i++) { + fr = (FontRecord) fonts.get(i); + newfonts.add(fr); + mapping.setMapping(fr.getFontIndex(), fr.getFontIndex()); + } + + // Now do the rest + Iterator it = null; + FontRecord fr2 = null; + boolean duplicate = false; + for (int i = numDefaultFonts; i < fonts.size(); i++) { + fr = (FontRecord) fonts.get(i); + + // Compare to all the fonts currently on the list + duplicate = false; + it = newfonts.iterator(); + while (it.hasNext() && !duplicate) { + fr2 = (FontRecord) it.next(); + if (fr.equals(fr2)) { + duplicate = true; + mapping.setMapping(fr.getFontIndex(), + mapping.getNewIndex(fr2.getFontIndex())); + numremoved++; + } + } + + if (!duplicate) { + // Add to the new list + newfonts.add(fr); + int newindex = fr.getFontIndex() - numremoved; + Assert.verify(newindex > 4); + mapping.setMapping(fr.getFontIndex(), newindex); + } + } + + // Iterate through the remaining fonts, updating all the font indices + it = newfonts.iterator(); + while (it.hasNext()) { + fr = (FontRecord) it.next(); + fr.initialize(mapping.getNewIndex(fr.getFontIndex())); + } + + fonts = newfonts; + + return mapping; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java b/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java new file mode 100755 index 0000000..22af783 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormatRecord.java @@ -0,0 +1,551 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import jxl.WorkbookSettings; +import jxl.common.Logger; +import jxl.format.Format; +import jxl.read.biff.Record; + +/** + * A non-built in format record + */ +public class FormatRecord extends WritableRecordData + implements DisplayFormat, Format { + public static final BiffType biff8 = new BiffType(); + public static final BiffType biff7 = new BiffType(); + /** + * The logger + */ + public static Logger logger = Logger.getLogger(FormatRecord.class); + /** + * The date strings to look for + */ + private static final String[] dateStrings = new String[] + { + "dd", + "mm", + "yy", + "hh", + "ss", + "m/", + "/d" + }; + /** + * Initialized flag + */ + private boolean initialized; + /** + * The raw data + */ + private byte[] data; + /** + * The index code + */ + private int indexCode; + /** + * The formatting string + */ + private String formatString; + /** + * Indicates whether this is a date formatting record + */ + private boolean date; + /** + * Indicates whether this a number formatting record + */ + private boolean number; + /** + * The format object + */ + private java.text.Format format; + /** + * Constructor invoked when copying sheets + * + * @param fmt the format string + * @param refno the index code + */ + FormatRecord(String fmt, int refno) { + super(Type.FORMAT); + formatString = fmt; + indexCode = refno; + initialized = true; + } + + /** + * Constructor used by writable formats + */ + protected FormatRecord() { + super(Type.FORMAT); + initialized = false; + } + + /** + * Copy constructor - can be invoked by public access + * + * @param fr the format to copy + */ + protected FormatRecord(FormatRecord fr) { + super(Type.FORMAT); + initialized = false; + + formatString = fr.formatString; + date = fr.date; + number = fr.number; + // format = (java.text.Format) fr.format.clone(); + } + + /** + * Constructs this object from the raw data. Used when reading in a + * format record + * + * @param t the raw data + * @param ws the workbook settings + * @param biffType biff type dummy overload + */ + public FormatRecord(Record t, WorkbookSettings ws, BiffType biffType) { + super(t); + + byte[] data = getRecord().getData(); + indexCode = IntegerHelper.getInt(data[0], data[1]); + initialized = true; + + if (biffType == biff8) { + int numchars = IntegerHelper.getInt(data[2], data[3]); + if (data[4] == 0) { + formatString = StringHelper.getString(data, numchars, 5, ws); + } else { + formatString = StringHelper.getUnicodeString(data, numchars, 5); + } + } else { + int numchars = data[2]; + byte[] chars = new byte[numchars]; + System.arraycopy(data, 3, chars, 0, chars.length); + formatString = new String(chars); + } + + date = false; + number = false; + + // First see if this is a date format + for (int i = 0; i < dateStrings.length; i++) { + String dateString = dateStrings[i]; + if (formatString.indexOf(dateString) != -1 || + formatString.indexOf(dateString.toUpperCase()) != -1) { + date = true; + break; + } + } + + // See if this is number format - look for the # or 0 characters + if (!date) { + if (formatString.indexOf('#') != -1 || + formatString.indexOf('0') != -1) { + number = true; + } + } + } + + /** + * Used to get the data when writing out the format record + * + * @return the raw data + */ + public byte[] getData() { + data = new byte[formatString.length() * 2 + 3 + 2]; + + IntegerHelper.getTwoBytes(indexCode, data, 0); + IntegerHelper.getTwoBytes(formatString.length(), data, 2); + data[4] = (byte) 1; // unicode indicator + StringHelper.getUnicodeBytes(formatString, data, 5); + + return data; + } + + /** + * Gets the format index of this record + * + * @return the format index of this record + */ + public int getFormatIndex() { + return indexCode; + } + + /** + * Accessor to see whether this object is initialized or not. + * + * @return TRUE if this font record has been initialized, FALSE otherwise + */ + public boolean isInitialized() { + return initialized; + } + + /** + * Sets the index of this record. Called from the FormattingRecords + * object + * + * @param pos the position of this font in the workbooks font list + */ + + public void initialize(int pos) { + indexCode = pos; + initialized = true; + } + + /** + * Replaces all instances of search with replace in the input. Used for + * replacing microsoft number formatting characters with java equivalents + * + * @param input the format string + * @param search the Excel character to be replaced + * @param replace the java equivalent + * @return the input string with the specified substring replaced + */ + protected final String replace(String input, String search, String replace) { + String fmtstr = input; + int pos = fmtstr.indexOf(search); + while (pos != -1) { + StringBuffer tmp = new StringBuffer(fmtstr.substring(0, pos)); + tmp.append(replace); + tmp.append(fmtstr.substring(pos + search.length())); + fmtstr = tmp.toString(); + pos = fmtstr.indexOf(search); + } + return fmtstr; + } + + /** + * Sees if this format is a date format + * + * @return TRUE if this format is a date + */ + public final boolean isDate() { + return date; + } + + /** + * Sees if this format is a number format + * + * @return TRUE if this format is a number + */ + public final boolean isNumber() { + return number; + } + + /** + * Gets the java equivalent number format for the formatString + * + * @return The java equivalent of the number format for this object + */ + public final NumberFormat getNumberFormat() { + if (format != null && format instanceof NumberFormat) { + return (NumberFormat) format; + } + + try { + String fs = formatString; + + // Replace the Excel formatting characters with java equivalents + fs = replace(fs, "E+", "E"); + fs = replace(fs, "_)", ""); + fs = replace(fs, "_", ""); + fs = replace(fs, "[Red]", ""); + fs = replace(fs, "\\", ""); + + format = new DecimalFormat(fs); + } catch (IllegalArgumentException e) { + // Something went wrong with the date format - fail silently + // and return a default value + format = new DecimalFormat("#.###"); + } + + return (NumberFormat) format; + } + + /** + * Gets the java equivalent date format for the formatString + * + * @return The java equivalent of the date format for this object + */ + public final DateFormat getDateFormat() { + if (format != null && format instanceof DateFormat) { + return (DateFormat) format; + } + + String fmt = formatString; + + // Replace the AM/PM indicator with an a + int pos = fmt.indexOf("AM/PM"); + while (pos != -1) { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append('a'); + sb.append(fmt.substring(pos + 5)); + fmt = sb.toString(); + pos = fmt.indexOf("AM/PM"); + } + + // Replace ss.0 with ss.SSS (necessary to always specify milliseconds + // because of NT) + pos = fmt.indexOf("ss.0"); + while (pos != -1) { + StringBuffer sb = new StringBuffer(fmt.substring(0, pos)); + sb.append("ss.SSS"); + + // Keep going until we run out of zeros + pos += 4; + while (pos < fmt.length() && fmt.charAt(pos) == '0') { + pos++; + } + + sb.append(fmt.substring(pos)); + fmt = sb.toString(); + pos = fmt.indexOf("ss.0"); + } + + + // Filter out the backslashes + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < fmt.length(); i++) { + if (fmt.charAt(i) != '\\') { + sb.append(fmt.charAt(i)); + } + } + + fmt = sb.toString(); + + // If the date format starts with anything inside square brackets then + // filter tham out + if (fmt.charAt(0) == '[') { + int end = fmt.indexOf(']'); + if (end != -1) { + fmt = fmt.substring(end + 1); + } + } + + // Get rid of some spurious characters that can creep in + fmt = replace(fmt, ";@", ""); + + // We need to convert the month indicator m, to upper case when we + // are dealing with dates + char[] formatBytes = fmt.toCharArray(); + + for (int i = 0; i < formatBytes.length; i++) { + if (formatBytes[i] == 'm') { + // Firstly, see if the preceding character is also an m. If so, + // copy that + if (i > 0 && (formatBytes[i - 1] == 'm' || formatBytes[i - 1] == 'M')) { + formatBytes[i] = formatBytes[i - 1]; + } else { + // There is no easy way out. We have to deduce whether this an + // minute or a month? See which is closest out of the + // letters H d s or y + // First, h + int minuteDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'h') { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'h') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'H') { + minuteDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'H') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + + // Now repeat for s + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 's') { + minuteDist = Math.min(minuteDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 's') { + minuteDist = Math.min(minuteDist, j - i); + break; + } + } + // We now have the distance of the closest character which could + // indicate the the m refers to a minute + // Repeat for d and y + int monthDist = Integer.MAX_VALUE; + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'd') { + monthDist = i - j; + break; + } + } + + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'd') { + monthDist = Math.min(monthDist, j - i); + break; + } + } + // Now repeat for y + for (int j = i - 1; j > 0; j--) { + if (formatBytes[j] == 'y') { + monthDist = Math.min(monthDist, i - j); + break; + } + } + for (int j = i + 1; j < formatBytes.length; j++) { + if (formatBytes[j] == 'y') { + monthDist = Math.min(monthDist, j - i); + break; + } + } + + if (monthDist < minuteDist) { + // The month indicator is closer, so convert to a capital M + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } else if ((monthDist == minuteDist) && + (monthDist != Integer.MAX_VALUE)) { + // They are equidistant. As a tie-breaker, take the formatting + // character which precedes the m + char ind = formatBytes[i - monthDist]; + if (ind == 'y' || ind == 'd') { + // The preceding item indicates a month measure, so convert + formatBytes[i] = Character.toUpperCase(formatBytes[i]); + } + } + } + } + } + + try { + this.format = new SimpleDateFormat(new String(formatBytes)); + } catch (IllegalArgumentException e) { + // There was a spurious character - fail silently + this.format = new SimpleDateFormat("dd MM yyyy hh:mm:ss"); + } + return (DateFormat) this.format; + } + + /** + * Gets the index code, for use as a hash value + * + * @return the ifmt code for this cell + */ + public int getIndexCode() { + return indexCode; + } + + /** + * Gets the formatting string. + * + * @return the excel format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Called by the immediate subclass to set the string + * once the Java-Excel replacements have been done + * + * @param s the format string + */ + protected final void setFormatString(String s) { + formatString = s; + } + + /** + * Indicates whether this formula is a built in + * + * @return FALSE + */ + public boolean isBuiltIn() { + return false; + } + + /** + * Standard hash code method + * + * @return the hash code value for this object + */ + public int hashCode() { + return formatString.hashCode(); + } + + /** + * Standard equals method. This compares the contents of two + * format records, and not their indexCodes, which are ignored + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof FormatRecord)) { + return false; + } + + FormatRecord fr = (FormatRecord) o; + + // Initialized format comparison + if (initialized && fr.initialized) { + // Must be either a number or a date format + if (date != fr.date || + number != fr.number) { + return false; + } + + return formatString.equals(fr.formatString); + } + + // Uninitialized format comparison + return formatString.equals(fr.formatString); + } + + // Type to distinguish between biff7 and biff8 + private static class BiffType { + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java b/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java new file mode 100755 index 0000000..473e182 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormattingRecords.java @@ -0,0 +1,512 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.Colour; +import jxl.format.RGB; +import jxl.write.biff.File; + +/** + * The list of XF records and formatting records for the workbook + */ +public class FormattingRecords { + /** + * The start index number for custom format records + */ + private static final int customFormatStartIndex = 0xa4; + /** + * The maximum number of format records. This is some weird internal + * Excel constraint + */ + private static final int maxFormatRecordsIndex = 0x1b9; + /** + * The minimum number of XF records for a sheet. The rationalization + * processes commences immediately after this number + */ + private static final int minXFRecords = 21; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormattingRecords.class); + /** + * A hash map of FormatRecords, for random access retrieval when reading + * in a spreadsheet + */ + private final HashMap formats; + /** + * A list of formats, used when writing out a spreadsheet + */ + private ArrayList formatsList; + /** + * The list of extended format records + */ + private ArrayList xfRecords; + /** + * The next available index number for custom format records + */ + private int nextCustomIndexNumber; + /** + * A handle to the available fonts + */ + private final Fonts fonts; + /** + * The colour palette + */ + private PaletteRecord palette; + + /** + * Constructor + * + * @param f the container for the fonts + */ + public FormattingRecords(Fonts f) { + xfRecords = new ArrayList(10); + formats = new HashMap(10); + formatsList = new ArrayList(10); + fonts = f; + nextCustomIndexNumber = customFormatStartIndex; + } + + /** + * Adds an extended formatting record to the list. If the XF record passed + * in has not been initialized, its index is determined based on the + * xfRecords list, and + * this position is passed to the XF records initialize method + * + * @param xf the xf record to add + * @throws NumFormatRecordsException + */ + public final void addStyle(XFRecord xf) + throws NumFormatRecordsException { + if (!xf.isInitialized()) { + int pos = xfRecords.size(); + xf.initialize(pos, this, fonts); + xfRecords.add(xf); + } else { + // The XF record has probably been read in. If the index is greater + // Than the size of the list, then it is not a preset format, + // so add it + if (xf.getXFIndex() >= xfRecords.size()) { + xfRecords.add(xf); + } + } + } + + /** + * Adds a cell format to the hash map, keyed on its index. If the format + * record is not initialized, then its index number is determined and its + * initialize method called. If the font is not a built in format, then it + * is added to the list of formats for writing out + * + * @param fr the format record + */ + public final void addFormat(DisplayFormat fr) + throws NumFormatRecordsException { + // Handle the case the where the index number in the read Excel + // file exhibits some major weirdness + if (fr.isInitialized() && + fr.getFormatIndex() >= maxFormatRecordsIndex) { + logger.warn("Format index exceeds Excel maximum - assigning custom " + + "number"); + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + // Initialize the format record with a custom index number + if (!fr.isInitialized()) { + fr.initialize(nextCustomIndexNumber); + nextCustomIndexNumber++; + } + + if (nextCustomIndexNumber > maxFormatRecordsIndex) { + nextCustomIndexNumber = maxFormatRecordsIndex; + throw new NumFormatRecordsException(); + } + + if (fr.getFormatIndex() >= nextCustomIndexNumber) { + nextCustomIndexNumber = fr.getFormatIndex() + 1; + } + + if (!fr.isBuiltIn()) { + formatsList.add(fr); + formats.put(new Integer(fr.getFormatIndex()), fr); + } + } + + /** + * Sees if the extended formatting record at the specified position + * represents a date. First checks against the built in formats, and + * then checks against the hash map of FormatRecords + * + * @param pos the xf format index + * @return TRUE if this format index is formatted as a Date + */ + public final boolean isDate(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) { + return true; + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + return fr != null && fr.isDate(); + } + + /** + * Gets the DateFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final DateFormat getDateFormat(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isDate()) { + return xfr.getDateFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) { + return null; + } + + return fr.isDate() ? fr.getDateFormat() : null; + } + + /** + * Gets the NumberFormat used to format the cell. + * + * @param pos the xf format index + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public final NumberFormat getNumberFormat(int pos) { + XFRecord xfr = (XFRecord) xfRecords.get(pos); + + if (xfr.isNumber()) { + return xfr.getNumberFormat(); + } + + FormatRecord fr = (FormatRecord) + formats.get(new Integer(xfr.getFormatRecord())); + + if (fr == null) { + return null; + } + + return fr.isNumber() ? fr.getNumberFormat() : null; + } + + /** + * Gets the format record + * + * @param index the formatting record index to retrieve + * @return the format record at the specified index + */ + FormatRecord getFormatRecord(int index) { + return (FormatRecord) + formats.get(new Integer(index)); + } + + /** + * Writes out all the format records and the XF records + * + * @param outputFile the file to write to + * @throws IOException + */ + public void write(File outputFile) throws IOException { + // Write out all the formats + Iterator i = formatsList.iterator(); + FormatRecord fr = null; + while (i.hasNext()) { + fr = (FormatRecord) i.next(); + outputFile.write(fr); + } + + // Write out the styles + i = xfRecords.iterator(); + XFRecord xfr = null; + while (i.hasNext()) { + xfr = (XFRecord) i.next(); + outputFile.write(xfr); + } + + // Write out the style records + BuiltInStyle style = new BuiltInStyle(0x10, 3); + outputFile.write(style); + + style = new BuiltInStyle(0x11, 6); + outputFile.write(style); + + style = new BuiltInStyle(0x12, 4); + outputFile.write(style); + + style = new BuiltInStyle(0x13, 7); + outputFile.write(style); + + style = new BuiltInStyle(0x0, 0); + outputFile.write(style); + + style = new BuiltInStyle(0x14, 5); + outputFile.write(style); + } + + /** + * Accessor for the fonts used by this workbook + * + * @return the fonts container + */ + protected final Fonts getFonts() { + return fonts; + } + + /** + * Gets the XFRecord for the specified index. Used when copying individual + * cells + * + * @param index the XF record to retrieve + * @return the XF record at the specified index + */ + public final XFRecord getXFRecord(int index) { + return (XFRecord) xfRecords.get(index); + } + + /** + * Gets the number of formatting records on the list. This is used by the + * writable subclass because there is an upper limit on the amount of + * format records that are allowed to be present in an excel sheet + * + * @return the number of format records present + */ + protected final int getNumberOfFormatRecords() { + return formatsList.size(); + } + + /** + * Rationalizes all the fonts, removing duplicate entries + * + * @return the list of new font index number + */ + public IndexMapping rationalizeFonts() { + return fonts.rationalize(); + } + + /** + * Rationalizes the cell formats. Duplicate + * formats are removed and the format indexed of the cells + * adjusted accordingly + * + * @param fontMapping the font mapping index numbers + * @param formatMapping the format mapping index numbers + * @return the list of new font index number + */ + public IndexMapping rationalize(IndexMapping fontMapping, + IndexMapping formatMapping) { + // Update the index codes for the XF records using the format + // mapping and the font mapping + // at the same time + XFRecord xfr = null; + for (Iterator it = xfRecords.iterator(); it.hasNext(); ) { + xfr = (XFRecord) it.next(); + + if (xfr.getFormatRecord() >= customFormatStartIndex) { + xfr.setFormatIndex(formatMapping.getNewIndex(xfr.getFormatRecord())); + } + + xfr.setFontIndex(fontMapping.getNewIndex(xfr.getFontIndex())); + } + + ArrayList newrecords = new ArrayList(minXFRecords); + IndexMapping mapping = new IndexMapping(xfRecords.size()); + int numremoved = 0; + + int numXFRecords = Math.min(minXFRecords, xfRecords.size()); + // Copy across the fundamental styles + for (int i = 0; i < numXFRecords; i++) { + newrecords.add(xfRecords.get(i)); + mapping.setMapping(i, i); + } + + if (numXFRecords < minXFRecords) { + logger.warn("There are less than the expected minimum number of " + + "XF records"); + return mapping; + } + + // Iterate through the old list + for (int i = minXFRecords; i < xfRecords.size(); i++) { + XFRecord xf = (XFRecord) xfRecords.get(i); + + // Compare against formats already on the list + boolean duplicate = false; + for (Iterator it = newrecords.iterator(); + it.hasNext() && !duplicate; ) { + XFRecord xf2 = (XFRecord) it.next(); + if (xf2.equals(xf)) { + duplicate = true; + mapping.setMapping(i, mapping.getNewIndex(xf2.getXFIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) { + newrecords.add(xf); + mapping.setMapping(i, i - numremoved); + } + } + + // It is sufficient to merely change the xf index field on all XFRecords + // In this case, CellValues which refer to defunct format records + // will nevertheless be written out with the correct index number + for (Iterator i = xfRecords.iterator(); i.hasNext(); ) { + XFRecord xf = (XFRecord) i.next(); + xf.rationalize(mapping); + } + + // Set the new list + xfRecords = newrecords; + + return mapping; + } + + /** + * Rationalizes the display formats. Duplicate + * formats are removed and the format indices of the cells + * adjusted accordingly. It is invoked immediately prior to writing + * writing out the sheet + * + * @return the index mapping between the old display formats and the + * rationalized ones + */ + public IndexMapping rationalizeDisplayFormats() { + ArrayList newformats = new ArrayList(); + int numremoved = 0; + IndexMapping mapping = new IndexMapping(nextCustomIndexNumber); + + // Iterate through the old list + Iterator i = formatsList.iterator(); + DisplayFormat df = null; + DisplayFormat df2 = null; + boolean duplicate = false; + while (i.hasNext()) { + df = (DisplayFormat) i.next(); + + Assert.verify(!df.isBuiltIn()); + + // Compare against formats already on the list + Iterator i2 = newformats.iterator(); + duplicate = false; + while (i2.hasNext() && !duplicate) { + df2 = (DisplayFormat) i2.next(); + if (df2.equals(df)) { + duplicate = true; + mapping.setMapping(df.getFormatIndex(), + mapping.getNewIndex(df2.getFormatIndex())); + numremoved++; + } + } + + // If this format is not a duplicate then add it to the new list + if (!duplicate) { + newformats.add(df); + int indexnum = df.getFormatIndex() - numremoved; + if (indexnum > maxFormatRecordsIndex) { + logger.warn("Too many number formats - using default format."); + indexnum = 0; // the default number format index + } + mapping.setMapping(df.getFormatIndex(), + df.getFormatIndex() - numremoved); + } + } + + // Set the new list + formatsList = newformats; + + // Update the index codes for the remaining formats + i = formatsList.iterator(); + + while (i.hasNext()) { + df = (DisplayFormat) i.next(); + df.initialize(mapping.getNewIndex(df.getFormatIndex())); + } + + return mapping; + } + + /** + * Accessor for the colour palette + * + * @return the palette + */ + public PaletteRecord getPalette() { + return palette; + } + + /** + * Called from the WorkbookParser to set the colour palette + * + * @param pr the palette + */ + public void setPalette(PaletteRecord pr) { + palette = pr; + } + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public void setColourRGB(Colour c, int r, int g, int b) { + if (palette == null) { + palette = new PaletteRecord(); + } + palette.setColourRGB(c, r, g, b); + } + + /** + * Accessor for the RGB value for the specified colour + * + * @return the RGB for the specified colour + */ + public RGB getColourRGB(Colour c) { + if (palette == null) { + return c.getDefaultRGB(); + } + + return palette.getColourRGB(c); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java b/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java new file mode 100755 index 0000000..c373c71 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/FormulaData.java @@ -0,0 +1,39 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.Cell; +import jxl.biff.formula.FormulaException; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface FormulaData extends Cell { + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array EXCLUDING the standard cell information + * (row, column, xfindex) + * + * @return the raw record data + * @throws FormulaException + */ + byte[] getFormulaData() throws FormulaException; +} diff --git a/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java b/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java new file mode 100755 index 0000000..766dd2a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/biff/HeaderFooter.java @@ -0,0 +1,615 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan, Eric Jung + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.biff; + +import jxl.common.Logger; + +/** + * Class which represents an Excel header or footer. Information for this + * class came from Microsoft Knowledge Base Article 142136 + * (previously Q142136). + *
+ * This class encapsulates three internal structures representing the header
+ * or footer contents which appear on the left, right or central part of the
+ * page
+ */
+public abstract class HeaderFooter {
+ /**
+ * Turns bold printing on or off
+ */
+ private static final String BOLD_TOGGLE = "&B";
+
+ // Codes to format text
+ /**
+ * Turns underline printing on or off
+ */
+ private static final String UNDERLINE_TOGGLE = "&U";
+ /**
+ * Turns italic printing on or off
+ */
+ private static final String ITALICS_TOGGLE = "&I";
+ /**
+ * Turns strikethrough printing on or off
+ */
+ private static final String STRIKETHROUGH_TOGGLE = "&S";
+ /**
+ * Turns double-underline printing on or off
+ */
+ private static final String DOUBLE_UNDERLINE_TOGGLE = "&E";
+ /**
+ * Turns superscript printing on or off
+ */
+ private static final String SUPERSCRIPT_TOGGLE = "&X";
+ /**
+ * Turns subscript printing on or off
+ */
+ private static final String SUBSCRIPT_TOGGLE = "&Y";
+ /**
+ * Turns outline printing on or off (Macintosh only)
+ */
+ private static final String OUTLINE_TOGGLE = "&O";
+ /**
+ * Turns shadow printing on or off (Macintosh only)
+ */
+ private static final String SHADOW_TOGGLE = "&H";
+ /**
+ * Left-aligns the characters that follow
+ */
+ private static final String LEFT_ALIGN = "&L";
+ /**
+ * Centres the characters that follow
+ */
+ private static final String CENTRE = "&C";
+ /**
+ * Right-aligns the characters that follow
+ */
+ private static final String RIGHT_ALIGN = "&R";
+ /**
+ * Prints the page number
+ */
+ private static final String PAGENUM = "&P";
+
+ // Codes to insert specific data
+ /**
+ * Prints the total number of pages in the document
+ */
+ private static final String TOTAL_PAGENUM = "&N";
+ /**
+ * Prints the current date
+ */
+ private static final String DATE = "&D";
+ /**
+ * Prints the current time
+ */
+ private static final String TIME = "&T";
+ /**
+ * Prints the name of the workbook
+ */
+ private static final String WORKBOOK_NAME = "&F";
+ /**
+ * Prints the name of the worksheet
+ */
+ private static final String WORKSHEET_NAME = "&A";
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(HeaderFooter.class);
+ /**
+ * The left aligned header/footer contents
+ */
+ private Contents left;
+ /**
+ * The right aligned header/footer contents
+ */
+ private Contents right;
+ /**
+ * The centrally aligned header/footer contents
+ */
+ private Contents centre;
+
+ /**
+ * Default constructor.
+ */
+ protected HeaderFooter() {
+ left = createContents();
+ right = createContents();
+ centre = createContents();
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param c the item to copy
+ */
+ protected HeaderFooter(HeaderFooter hf) {
+ left = createContents(hf.left);
+ right = createContents(hf.right);
+ centre = createContents(hf.centre);
+ }
+
+ /**
+ * Constructor used when reading workbooks to separate the left, right
+ * a central part of the strings into their constituent parts
+ */
+ protected HeaderFooter(String s) {
+ if (s == null || s.length() == 0) {
+ left = createContents();
+ right = createContents();
+ centre = createContents();
+ return;
+ }
+
+ int leftPos = s.indexOf(LEFT_ALIGN);
+ int rightPos = s.indexOf(RIGHT_ALIGN);
+ int centrePos = s.indexOf(CENTRE);
+
+ if (leftPos == -1 && rightPos == -1 && centrePos == -1) {
+ // When no part is specified, it is the center part
+ centre = createContents(s);
+ } else {
+ // Left part?
+ if (leftPos != -1) {
+ // We have a left part, find end of left part
+ int endLeftPos = s.length();
+ if (centrePos > leftPos) {
+ // Case centre part behind left part
+ endLeftPos = centrePos;
+ if (rightPos > leftPos && endLeftPos > rightPos) {
+ // LRC case
+ endLeftPos = rightPos;
+ } else {
+ // LCR case
+ }
+ } else {
+ // Case centre part before left part
+ if (rightPos > leftPos) {
+ // LR case
+ endLeftPos = rightPos;
+ } else {
+ // *L case
+ // Left pos is last
+
+
+ }
+ }
+ left = createContents(s.substring(leftPos + 2, endLeftPos));
+ }
+
+ // Right part?
+ if (rightPos != -1) {
+ // Find end of right part
+ int endRightPos = s.length();
+ if (centrePos > rightPos) {
+ // centre part behind right part
+ endRightPos = centrePos;
+ if (leftPos > rightPos && endRightPos > leftPos) {
+ // RLC case
+ endRightPos = leftPos;
+ } else {
+ // RCL case
+ }
+ } else {
+ if (leftPos > rightPos) {
+ // RL case
+ endRightPos = leftPos;
+ } else {
+ // *R case
+ // Right pos is last
+ }
+ }
+ right = createContents(s.substring(rightPos + 2, endRightPos));
+ }
+
+ // Centre part?
+ if (centrePos != -1) {
+ // Find end of centre part
+ int endCentrePos = s.length();
+ if (rightPos > centrePos) {
+ // right part behind centre part
+ endCentrePos = rightPos;
+ if (leftPos > centrePos && endCentrePos > leftPos) {
+ // CLR case
+ endCentrePos = leftPos;
+ } else {
+ // CRL case
+ }
+ } else {
+ if (leftPos > centrePos) {
+ // CL case
+ endCentrePos = leftPos;
+ } else {
+ // *C case
+ // Centre pos is last
+ }
+ }
+ centre = createContents(s.substring(centrePos + 2, endCentrePos));
+ }
+ }
+
+
+ if (left == null) {
+ left = createContents();
+ }
+
+ if (centre == null) {
+ centre = createContents();
+ }
+
+ if (right == null) {
+ right = createContents();
+ }
+ }
+
+ /**
+ * Retrieves a String
ified
+ * version of this object
+ *
+ * @return the header string
+ */
+ public String toString() {
+ StringBuffer hf = new StringBuffer();
+ if (!left.empty()) {
+ hf.append(LEFT_ALIGN);
+ hf.append(left.getContents());
+ }
+
+ if (!centre.empty()) {
+ hf.append(CENTRE);
+ hf.append(centre.getContents());
+ }
+
+ if (!right.empty()) {
+ hf.append(RIGHT_ALIGN);
+ hf.append(right.getContents());
+ }
+
+ return hf.toString();
+ }
+
+ /**
+ * Accessor for the contents which appear on the right hand side of the page
+ *
+ * @return the right aligned contents
+ */
+ protected Contents getRightText() {
+ return right;
+ }
+
+ /**
+ * Accessor for the contents which in the centre of the page
+ *
+ * @return the centrally aligned contents
+ */
+ protected Contents getCentreText() {
+ return centre;
+ }
+
+ /**
+ * Accessor for the contents which appear on the left hand side of the page
+ *
+ * @return the left aligned contents
+ */
+ protected Contents getLeftText() {
+ return left;
+ }
+
+ /**
+ * Clears the contents of the header/footer
+ */
+ protected void clear() {
+ left.clear();
+ right.clear();
+ centre.clear();
+ }
+
+ /**
+ * Creates internal class of the appropriate type
+ */
+ protected abstract Contents createContents();
+
+ /**
+ * Creates internal class of the appropriate type
+ */
+ protected abstract Contents createContents(String s);
+
+ /**
+ * Creates internal class of the appropriate type
+ */
+ protected abstract Contents createContents(Contents c);
+
+ /**
+ * The contents - a simple wrapper around a string buffer
+ */
+ protected static class Contents {
+ /**
+ * The buffer containing the header/footer string
+ */
+ private StringBuffer contents;
+
+ /**
+ * The constructor
+ */
+ protected Contents() {
+ contents = new StringBuffer();
+ }
+
+ /**
+ * Constructor used when reading worksheets. The string contains all
+ * the formatting (but not alignment characters
+ *
+ * @param s the format string
+ */
+ protected Contents(String s) {
+ contents = new StringBuffer(s);
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param copy the contents to copy
+ */
+ protected Contents(Contents copy) {
+ contents = new StringBuffer(copy.getContents());
+ }
+
+ /**
+ * Retrieves a String
ified
+ * version of this object
+ *
+ * @return the header string
+ */
+ protected String getContents() {
+ return contents != null ? contents.toString() : "";
+ }
+
+ /**
+ * Internal method which appends the text to the string buffer
+ *
+ * @param txt
+ */
+ private void appendInternal(String txt) {
+ if (contents == null) {
+ contents = new StringBuffer();
+ }
+
+ contents.append(txt);
+ }
+
+ /**
+ * Internal method which appends the text to the string buffer
+ *
+ * @param ch
+ */
+ private void appendInternal(char ch) {
+ if (contents == null) {
+ contents = new StringBuffer();
+ }
+
+ contents.append(ch);
+ }
+
+ /**
+ * Appends the text to the string buffer
+ *
+ * @param txt
+ */
+ protected void append(String txt) {
+ appendInternal(txt);
+ }
+
+ /**
+ * Turns bold printing on or off. Bold printing
+ * is initially off. Text subsequently appended to
+ * this object will be bolded until this method is
+ * called again.
+ */
+ protected void toggleBold() {
+ appendInternal(BOLD_TOGGLE);
+ }
+
+ /**
+ * Turns underline printing on or off. Underline printing
+ * is initially off. Text subsequently appended to
+ * this object will be underlined until this method is
+ * called again.
+ */
+ protected void toggleUnderline() {
+ appendInternal(UNDERLINE_TOGGLE);
+ }
+
+ /**
+ * Turns italics printing on or off. Italics printing
+ * is initially off. Text subsequently appended to
+ * this object will be italicized until this method is
+ * called again.
+ */
+ protected void toggleItalics() {
+ appendInternal(ITALICS_TOGGLE);
+ }
+
+ /**
+ * Turns strikethrough printing on or off. Strikethrough printing
+ * is initially off. Text subsequently appended to
+ * this object will be striked out until this method is
+ * called again.
+ */
+ protected void toggleStrikethrough() {
+ appendInternal(STRIKETHROUGH_TOGGLE);
+ }
+
+ /**
+ * Turns double-underline printing on or off. Double-underline printing
+ * is initially off. Text subsequently appended to
+ * this object will be double-underlined until this method is
+ * called again.
+ */
+ protected void toggleDoubleUnderline() {
+ appendInternal(DOUBLE_UNDERLINE_TOGGLE);
+ }
+
+ /**
+ * Turns superscript printing on or off. Superscript printing
+ * is initially off. Text subsequently appended to
+ * this object will be superscripted until this method is
+ * called again.
+ */
+ protected void toggleSuperScript() {
+ appendInternal(SUPERSCRIPT_TOGGLE);
+ }
+
+ /**
+ * Turns subscript printing on or off. Subscript printing
+ * is initially off. Text subsequently appended to
+ * this object will be subscripted until this method is
+ * called again.
+ */
+ protected void toggleSubScript() {
+ appendInternal(SUBSCRIPT_TOGGLE);
+ }
+
+ /**
+ * Turns outline printing on or off (Macintosh only).
+ * Outline printing is initially off. Text subsequently appended
+ * to this object will be outlined until this method is
+ * called again.
+ */
+ protected void toggleOutline() {
+ appendInternal(OUTLINE_TOGGLE);
+ }
+
+ /**
+ * Turns shadow printing on or off (Macintosh only).
+ * Shadow printing is initially off. Text subsequently appended
+ * to this object will be shadowed until this method is
+ * called again.
+ */
+ protected void toggleShadow() {
+ appendInternal(SHADOW_TOGGLE);
+ }
+
+ /**
+ * Sets the font of text subsequently appended to this
+ * object.. Previously appended text is not affected.
+ *
MsoDrawingRecord
value
+ * @param obj an ObjRecord
value
+ * @param dd the drawing data
+ * @param sp an int
value
+ * @param ep an int
value
+ * @param f a File
value
+ * @param ws the workbook settings
+ */
+ public Chart(MsoDrawingRecord mso,
+ ObjRecord obj,
+ DrawingData dd,
+ int sp, int ep, File f, WorkbookSettings ws) {
+ msoDrawingRecord = mso;
+ objRecord = obj;
+ startpos = sp;
+ endpos = ep;
+ file = f;
+ workbookSettings = ws;
+
+ // msoDrawingRecord is null if the entire sheet consists of just the
+ // chart. In this case, as there is only one drawing on the page,
+ // it isn't necessary to add to the drawing data record anyway
+ if (msoDrawingRecord != null) {
+ drawingData = dd;
+ drawingData.addData(msoDrawingRecord.getRecord().getData());
+ drawingNumber = drawingData.getNumDrawings() - 1;
+ }
+
+ initialized = false;
+
+ // Note: mso and obj values can be null if we are creating a chart
+ // which takes up an entire worksheet. Check that both are null or both
+ // not null though
+ Assert.verify((mso != null && obj != null) ||
+ (mso == null && obj == null));
+ }
+
+ /**
+ * Gets the entire binary record for the chart as a chunk of binary data
+ *
+ * @return the bytes
+ */
+ public byte[] getBytes() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return data;
+ }
+
+ /**
+ * Implementation of the EscherStream method
+ *
+ * @return the data
+ */
+ public byte[] getData() {
+ return msoDrawingRecord.getRecord().getData();
+ }
+
+ /**
+ * Initializes the charts byte data
+ */
+ private void initialize() {
+ data = file.read(startpos, endpos - startpos);
+ initialized = true;
+ }
+
+ /**
+ * Rationalizes the sheet's xf index mapping
+ *
+ * @param xfMapping the index mapping for XFRecords
+ * @param fontMapping the index mapping for fonts
+ * @param formatMapping the index mapping for formats
+ */
+ public void rationalize(IndexMapping xfMapping,
+ IndexMapping fontMapping,
+ IndexMapping formatMapping) {
+ if (!initialized) {
+ initialize();
+ }
+
+ // Read through the array, looking for the data types
+ // This is a total hack bodge for now - it will eventually need to be
+ // integrated properly
+ int pos = 0;
+ int code = 0;
+ int length = 0;
+ Type type = null;
+ while (pos < data.length) {
+ code = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ length = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+
+ type = Type.getType(code);
+
+ if (type == Type.FONTX) {
+ int fontind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind),
+ data, pos + 4);
+ } else if (type == Type.FBI) {
+ int fontind = IntegerHelper.getInt(data[pos + 12], data[pos + 13]);
+ IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind),
+ data, pos + 12);
+ } else if (type == Type.IFMT) {
+ int formind = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ IntegerHelper.getTwoBytes(formatMapping.getNewIndex(formind),
+ data, pos + 4);
+ } else if (type == Type.ALRUNS) {
+ int numRuns = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ int fontPos = pos + 6;
+ for (int i = 0; i < numRuns; i++) {
+ int fontind = IntegerHelper.getInt(data[fontPos + 2],
+ data[fontPos + 3]);
+ IntegerHelper.getTwoBytes(fontMapping.getNewIndex(fontind),
+ data, fontPos + 2);
+ fontPos += 4;
+ }
+ }
+
+ pos += length + 4;
+ }
+ }
+
+ /**
+ * Gets the SpContainer containing the charts drawing information
+ *
+ * @return the spContainer
+ */
+ EscherContainer getSpContainer() {
+ EscherContainer spContainer = drawingData.getSpContainer(drawingNumber);
+
+ return spContainer;
+ }
+
+ /**
+ * Accessor for the mso drawing record
+ *
+ * @return the drawing record
+ */
+ MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Accessor for the obj record
+ *
+ * @return the obj record
+ */
+ ObjRecord getObjRecord() {
+ return objRecord;
+ }
+}
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java
new file mode 100755
index 0000000..bb37622
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/CheckBox.java
@@ -0,0 +1,716 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2009 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import jxl.WorkbookSettings;
+import jxl.biff.ContinueRecord;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+/**
+ * Contains the various biff records used to copy a CheckBox (from the
+ * Form toolbox) between workbook
+ */
+public class CheckBox implements DrawingGroupObject {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(CheckBox.class);
+
+ /**
+ * The spContainer that was read in
+ */
+ private EscherContainer readSpContainer;
+
+ /**
+ * The spContainer that was generated
+ */
+ private EscherContainer spContainer;
+
+ /**
+ * The MsoDrawingRecord associated with the drawing
+ */
+ private MsoDrawingRecord msoDrawingRecord;
+
+ /**
+ * The ObjRecord associated with the drawing
+ */
+ private ObjRecord objRecord;
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized = false;
+
+ /**
+ * The object id, assigned by the drawing group
+ */
+ private int objectId;
+
+ /**
+ * The blip id
+ */
+ private int blipId;
+
+ /**
+ * The shape id
+ */
+ private int shapeId;
+
+ /**
+ * The column
+ */
+ private int column;
+
+ /**
+ * The row position of the image
+ */
+ private int row;
+
+ /**
+ * The width of the image in cells
+ */
+ private double width;
+
+ /**
+ * The height of the image in cells
+ */
+ private double height;
+
+ /**
+ * The number of places this drawing is referenced
+ */
+ private int referenceCount;
+
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+
+ /**
+ * Where this image came from (read, written or a copy)
+ */
+ private Origin origin;
+
+ /**
+ * The drawing group for all the images
+ */
+ private DrawingGroup drawingGroup;
+
+ /**
+ * The drawing data
+ */
+ private DrawingData drawingData;
+
+ /**
+ * The type of this drawing object
+ */
+ private ShapeType type;
+
+ /**
+ * The drawing position on the sheet
+ */
+ private int drawingNumber;
+
+ /**
+ * An mso drawing record, which sometimes appears
+ */
+ private MsoDrawingRecord mso;
+
+ /**
+ * The text object record
+ */
+ private TextObjectRecord txo;
+
+ /**
+ * Text data from the first continue record
+ */
+ private ContinueRecord text;
+
+ /**
+ * Formatting data from the second continue record
+ */
+ private ContinueRecord formatting;
+
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings workbookSettings;
+
+ /**
+ * Constructor used when reading images
+ *
+ * @param mso the drawing record
+ * @param obj the object record
+ * @param dd the drawing data for all drawings on this sheet
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ public CheckBox(MsoDrawingRecord mso, ObjRecord obj, DrawingData dd,
+ DrawingGroup dg, WorkbookSettings ws) {
+ drawingGroup = dg;
+ msoDrawingRecord = mso;
+ drawingData = dd;
+ objRecord = obj;
+ initialized = false;
+ workbookSettings = ws;
+ origin = Origin.READ;
+ drawingData.addData(msoDrawingRecord.getData());
+ drawingNumber = drawingData.getNumDrawings() - 1;
+ drawingGroup.addDrawing(this);
+
+ Assert.verify(mso != null && obj != null);
+
+ initialize();
+ }
+
+ /**
+ * Copy constructor used to copy drawings from read to write
+ *
+ * @param dgo the drawing group object
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ public CheckBox(DrawingGroupObject dgo,
+ DrawingGroup dg,
+ WorkbookSettings ws) {
+ CheckBox d = (CheckBox) dgo;
+ Assert.verify(d.origin == Origin.READ);
+ msoDrawingRecord = d.msoDrawingRecord;
+ objRecord = d.objRecord;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData = d.drawingData;
+ drawingGroup = dg;
+ drawingNumber = d.drawingNumber;
+ drawingGroup.addDrawing(this);
+ mso = d.mso;
+ txo = d.txo;
+ text = d.text;
+ formatting = d.formatting;
+ workbookSettings = ws;
+ }
+
+ /**
+ * Constructor invoked when writing images
+ */
+ public CheckBox() {
+ initialized = true;
+ origin = Origin.WRITE;
+ referenceCount = 1;
+ type = ShapeType.HOST_CONTROL;
+ }
+
+ /**
+ * Initializes the member variables from the Escher stream data
+ */
+ private void initialize() {
+ readSpContainer = drawingData.getSpContainer(drawingNumber);
+ Assert.verify(readSpContainer != null);
+
+ EscherRecord[] children = readSpContainer.getChildren();
+
+ Sp sp = (Sp) readSpContainer.getChildren()[0];
+ objectId = objRecord.getObjectId();
+ shapeId = sp.getShapeId();
+ type = ShapeType.getType(sp.getShapeType());
+
+ if (type == ShapeType.UNKNOWN) {
+ logger.warn("Unknown shape type");
+ }
+
+ ClientAnchor clientAnchor = null;
+ for (int i = 0; i < children.length && clientAnchor == null; i++) {
+ if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) {
+ clientAnchor = (ClientAnchor) children[i];
+ }
+ }
+
+ if (clientAnchor == null) {
+ logger.warn("Client anchor not found");
+ } else {
+ column = (int) clientAnchor.getX1();
+ row = (int) clientAnchor.getY1();
+ }
+
+ initialized = true;
+ }
+
+
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ public final void setObjectId(int objid, int bip, int sid) {
+ objectId = objid;
+ blipId = bip;
+ shapeId = sid;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public final int getObjectId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return objectId;
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the object id
+ */
+ public final int getShapeId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ public final int getBlipId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return blipId;
+ }
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ public MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ public EscherContainer getSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ if (origin == Origin.READ) {
+ return getReadSpContainer();
+ }
+
+ SpContainer spc = new SpContainer();
+ Sp sp = new Sp(type, shapeId, 2560);
+ spc.add(sp);
+ Opt opt = new Opt();
+ opt.addProperty(127, false, false, 17039620);
+ opt.addProperty(191, false, false, 524296);
+ opt.addProperty(511, false, false, 524288);
+ opt.addProperty(959, false, false, 131072);
+ // opt.addProperty(260, true, false, blipId);
+ // opt.addProperty(261, false, false, 36);
+ spc.add(opt);
+
+ ClientAnchor clientAnchor = new ClientAnchor(column,
+ row,
+ column + 1,
+ row + 1,
+ 0x1);
+ spc.add(clientAnchor);
+ ClientData clientData = new ClientData();
+ spc.add(clientData);
+
+ return spc;
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ public DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ public void setDrawingGroup(DrawingGroup dg) {
+ drawingGroup = dg;
+ }
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ public void setReferenceCount(int r) {
+ referenceCount = r;
+ }
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ public double getX() {
+ if (!initialized) {
+ initialize();
+ }
+ return column;
+ }
+
+ /**
+ * Sets the column position of this drawing. Used when inserting/removing
+ * columns from the spreadsheet
+ *
+ * @param x the column
+ */
+ public void setX(double x) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ column = (int) x;
+ }
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ public double getY() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return row;
+ }
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ public void setY(double y) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ row = (int) y;
+ }
+
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ public double getWidth() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return width;
+ }
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ public void setWidth(double w) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ width = w;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ public double getHeight() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return height;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ public void setHeight(double h) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ height = h;
+ }
+
+
+ /**
+ * Gets the SpContainer that was read in
+ *
+ * @return the read sp container
+ */
+ private EscherContainer getReadSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return readSpContainer;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageData() {
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ if (!initialized) {
+ initialize();
+ }
+
+ return drawingGroup.getImageData(blipId);
+ }
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ public ShapeType getType() {
+ return type;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageBytes() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ public String getImageFilePath() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Writes out the additional records for a combo box
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeAdditionalRecords(File outputFile) throws IOException {
+ if (origin == Origin.READ) {
+ outputFile.write(objRecord);
+
+ if (mso != null) {
+ outputFile.write(mso);
+ }
+ outputFile.write(txo);
+ outputFile.write(text);
+ if (formatting != null) {
+ outputFile.write(formatting);
+ }
+ return;
+ }
+
+ // Create the obj record
+ ObjRecord objrec = new ObjRecord(objectId,
+ ObjRecord.CHECKBOX);
+
+ outputFile.write(objrec);
+
+ logger.warn("Writing of additional records for checkboxes not " +
+ "implemented");
+ }
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ * Writes out all the note records
+ *
+ * @param outputFile the output file
+ */
+ public void writeTailRecords(File outputFile) {
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row
+ */
+ public int getRow() {
+ return 0;
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column
+ */
+ public int getColumn() {
+ return 0;
+ }
+
+ /**
+ * Hashing algorithm
+ *
+ * @return the hash code
+ */
+ public int hashCode() {
+ return getClass().getName().hashCode();
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return msoDrawingRecord.isFirst();
+ }
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over TXO and CONTINUE records and
+ * require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ public boolean isFormObject() {
+ return false;
+ }
+
+ /**
+ * Sets the text object
+ *
+ * @param t the text object record
+ */
+ public void setTextObject(TextObjectRecord t) {
+ txo = t;
+ }
+
+ /**
+ * Sets the text data
+ *
+ * @param t continuation record
+ */
+ public void setText(ContinueRecord t) {
+ text = t;
+ }
+
+ /**
+ * Sets the formatting
+ *
+ * @param t continue record
+ */
+ public void setFormatting(ContinueRecord t) {
+ formatting = t;
+ }
+
+ /**
+ * The drawing record
+ *
+ * @param d the drawing record
+ */
+ public void addMso(MsoDrawingRecord d) {
+ mso = d;
+ drawingData.addRawData(mso.getData());
+ }
+
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java
new file mode 100755
index 0000000..04e5e6e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Chunk.java
@@ -0,0 +1,40 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+class Chunk {
+ private final int pos;
+ private final int length;
+ private final ChunkType type;
+ private final byte[] data;
+
+ public Chunk(int p, int l, ChunkType ct, byte[] d) {
+ pos = p;
+ length = l;
+ type = ct;
+ data = new byte[length];
+ System.arraycopy(d, pos, data, 0, length);
+
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java
new file mode 100755
index 0000000..b0b0e03
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ChunkType.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.util.Arrays;
+
+/**
+ * Enumeration for the various chunk types
+ */
+class ChunkType {
+ public static ChunkType IHDR = new ChunkType(0x49, 0x48, 0x44, 0x52, "IHDR");
+ public static ChunkType IEND = new ChunkType(0x49, 0x45, 0x4e, 0x44, "IEND");
+ public static ChunkType PHYS = new ChunkType(0x70, 0x48, 0x59, 0x73, "pHYs");
+ public static ChunkType UNKNOWN = new ChunkType(0xff, 0xff, 0xff, 0xff, "UNKNOWN");
+ private static ChunkType[] chunkTypes = new ChunkType[0];
+ private final byte[] id;
+ private final String name;
+ private ChunkType(int d1, int d2, int d3, int d4, String n) {
+ id = new byte[]{(byte) d1, (byte) d2, (byte) d3, (byte) d4};
+ name = n;
+
+ ChunkType[] ct = new ChunkType[chunkTypes.length + 1];
+ System.arraycopy(chunkTypes, 0, ct, 0, chunkTypes.length);
+ ct[chunkTypes.length] = this;
+ chunkTypes = ct;
+ }
+
+ public static ChunkType getChunkType(byte d1, byte d2, byte d3, byte d4) {
+ byte[] cmp = new byte[]{d1, d2, d3, d4};
+
+ boolean found = false;
+ ChunkType chunk = ChunkType.UNKNOWN;
+
+ for (int i = 0; i < chunkTypes.length && !found; i++) {
+ if (Arrays.equals(chunkTypes[i].id, cmp)) {
+ chunk = chunkTypes[i];
+ found = true;
+ }
+ }
+
+ return chunk;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java
new file mode 100755
index 0000000..91ab320
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientAnchor.java
@@ -0,0 +1,201 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * The client anchor record
+ */
+class ClientAnchor extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ClientAnchor.class);
+
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * The properties
+ */
+ private final int properties;
+
+ /**
+ * The x1 position
+ */
+ private final double x1;
+
+ /**
+ * The y1 position
+ */
+ private final double y1;
+
+ /**
+ * The x2 position
+ */
+ private final double x2;
+
+ /**
+ * The y2 position
+ */
+ private final double y2;
+
+ /**
+ * Constructor
+ *
+ * @param erd the escher record data
+ */
+ public ClientAnchor(EscherRecordData erd) {
+ super(erd);
+ byte[] bytes = getBytes();
+
+ // The properties
+ properties = IntegerHelper.getInt(bytes[0], bytes[1]);
+
+ // The x1 cell
+ int x1Cell = IntegerHelper.getInt(bytes[2], bytes[3]);
+ int x1Fraction = IntegerHelper.getInt(bytes[4], bytes[5]);
+
+ x1 = x1Cell + (double) x1Fraction / (double) 1024;
+
+ // The y1 cell
+ int y1Cell = IntegerHelper.getInt(bytes[6], bytes[7]);
+ int y1Fraction = IntegerHelper.getInt(bytes[8], bytes[9]);
+
+ y1 = y1Cell + (double) y1Fraction / (double) 256;
+
+ // The x2 cell
+ int x2Cell = IntegerHelper.getInt(bytes[10], bytes[11]);
+ int x2Fraction = IntegerHelper.getInt(bytes[12], bytes[13]);
+
+ x2 = x2Cell + (double) x2Fraction / (double) 1024;
+
+ // The y1 cell
+ int y2Cell = IntegerHelper.getInt(bytes[14], bytes[15]);
+ int y2Fraction = IntegerHelper.getInt(bytes[16], bytes[17]);
+
+ y2 = y2Cell + (double) y2Fraction / (double) 256;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param x1 the x1 position
+ * @param y1 the y1 position
+ * @param x2 the x2 position
+ * @param y2 the y2 position
+ * @param props the anchor properties
+ */
+ public ClientAnchor(double x1, double y1, double x2, double y2, int props) {
+ super(EscherRecordType.CLIENT_ANCHOR);
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ properties = props;
+ }
+
+ /**
+ * Gets the client anchor data
+ *
+ * @return the data
+ */
+ byte[] getData() {
+ data = new byte[18];
+ IntegerHelper.getTwoBytes(properties, data, 0);
+
+ // The x1 cell
+ IntegerHelper.getTwoBytes((int) x1, data, 2);
+
+ // The x1 fraction into the cell 0-1024
+ int x1fraction = (int) ((x1 - (int) x1) * 1024);
+ IntegerHelper.getTwoBytes(x1fraction, data, 4);
+
+ // The y1 cell
+ IntegerHelper.getTwoBytes((int) y1, data, 6);
+
+ // The y1 fraction into the cell 0-256
+ int y1fraction = (int) ((y1 - (int) y1) * 256);
+ IntegerHelper.getTwoBytes(y1fraction, data, 8);
+
+ // The x2 cell
+ IntegerHelper.getTwoBytes((int) x2, data, 10);
+
+ // The x2 fraction into the cell 0-1024
+ int x2fraction = (int) ((x2 - (int) x2) * 1024);
+ IntegerHelper.getTwoBytes(x2fraction, data, 12);
+
+ // The y2 cell
+ IntegerHelper.getTwoBytes((int) y2, data, 14);
+
+ // The y2 fraction into the cell 0-256
+ int y2fraction = (int) ((y2 - (int) y2) * 256);
+ IntegerHelper.getTwoBytes(y2fraction, data, 16);
+
+ return setHeaderData(data);
+ }
+
+ /**
+ * Accessor for the x1 position
+ *
+ * @return the x1 position
+ */
+ double getX1() {
+ return x1;
+ }
+
+ /**
+ * Accessor for the y1 position
+ *
+ * @return the y1 position
+ */
+ double getY1() {
+ return y1;
+ }
+
+ /**
+ * Accessor for the x2 position
+ *
+ * @return the x2 position
+ */
+ double getX2() {
+ return x2;
+ }
+
+ /**
+ * Accessor for the y2 position
+ *
+ * @return the y2 position
+ */
+ double getY2() {
+ return y2;
+ }
+
+ /**
+ * Accessor for the anchor properties
+ */
+ int getProperties() {
+ return properties;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java
new file mode 100755
index 0000000..13c7839
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientData.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.common.Logger;
+
+/**
+ * The client data
+ */
+class ClientData extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ClientData.class);
+
+ /**
+ * The raw data
+ */
+ private byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param erd the record data
+ */
+ public ClientData(EscherRecordData erd) {
+ super(erd);
+ }
+
+ /**
+ * Constructor
+ */
+ public ClientData() {
+ super(EscherRecordType.CLIENT_DATA);
+ }
+
+ /**
+ * Accessor for the raw data
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ data = new byte[0];
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java
new file mode 100755
index 0000000..e614461
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ClientTextBox.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.common.Logger;
+
+/**
+ * ???
+ */
+class ClientTextBox extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ClientTextBox.class);
+
+ /**
+ * The raw data
+ */
+ private byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param erd
+ */
+ public ClientTextBox(EscherRecordData erd) {
+ super(erd);
+ }
+
+ /**
+ * Constructor
+ */
+ public ClientTextBox() {
+ super(EscherRecordType.CLIENT_TEXT_BOX);
+ }
+
+ /**
+ * Accessor for the raw data
+ *
+ * @return
+ */
+ byte[] getData() {
+ data = new byte[0];
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java
new file mode 100755
index 0000000..a8ba810
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ComboBox.java
@@ -0,0 +1,641 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import jxl.WorkbookSettings;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+/**
+ * Contains the various biff records used to copy a ComboBox (from the
+ * Form toolbox) between workbook
+ */
+public class ComboBox implements DrawingGroupObject {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ComboBox.class);
+
+ /**
+ * The spContainer that was read in
+ */
+ private EscherContainer readSpContainer;
+
+ /**
+ * The spContainer that was generated
+ */
+ private EscherContainer spContainer;
+
+ /**
+ * The MsoDrawingRecord associated with the drawing
+ */
+ private MsoDrawingRecord msoDrawingRecord;
+
+ /**
+ * The ObjRecord associated with the drawing
+ */
+ private ObjRecord objRecord;
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized = false;
+
+ /**
+ * The object id, assigned by the drawing group
+ */
+ private int objectId;
+
+ /**
+ * The blip id
+ */
+ private int blipId;
+
+ /**
+ * The shape id
+ */
+ private int shapeId;
+
+ /**
+ * The column
+ */
+ private int column;
+
+ /**
+ * The row position of the image
+ */
+ private int row;
+
+ /**
+ * The width of the image in cells
+ */
+ private double width;
+
+ /**
+ * The height of the image in cells
+ */
+ private double height;
+
+ /**
+ * The number of places this drawing is referenced
+ */
+ private int referenceCount;
+
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+
+ /**
+ * Where this image came from (read, written or a copy)
+ */
+ private Origin origin;
+
+ /**
+ * The drawing group for all the images
+ */
+ private DrawingGroup drawingGroup;
+
+ /**
+ * The drawing data
+ */
+ private DrawingData drawingData;
+
+ /**
+ * The type of this drawing object
+ */
+ private ShapeType type;
+
+ /**
+ * The drawing position on the sheet
+ */
+ private int drawingNumber;
+
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings workbookSettings;
+
+ /**
+ * Constructor used when reading images
+ *
+ * @param mso the drawing record
+ * @param obj the object record
+ * @param dd the drawing data for all drawings on this sheet
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ public ComboBox(MsoDrawingRecord mso, ObjRecord obj, DrawingData dd,
+ DrawingGroup dg, WorkbookSettings ws) {
+ drawingGroup = dg;
+ msoDrawingRecord = mso;
+ drawingData = dd;
+ objRecord = obj;
+ initialized = false;
+ workbookSettings = ws;
+ origin = Origin.READ;
+ drawingData.addData(msoDrawingRecord.getData());
+ drawingNumber = drawingData.getNumDrawings() - 1;
+ drawingGroup.addDrawing(this);
+
+ Assert.verify(mso != null && obj != null);
+
+ initialize();
+ }
+
+ /**
+ * Copy constructor used to copy drawings from read to write
+ *
+ * @param dgo the drawing group object
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ public ComboBox(DrawingGroupObject dgo,
+ DrawingGroup dg,
+ WorkbookSettings ws) {
+ ComboBox d = (ComboBox) dgo;
+ Assert.verify(d.origin == Origin.READ);
+ msoDrawingRecord = d.msoDrawingRecord;
+ objRecord = d.objRecord;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData = d.drawingData;
+ drawingGroup = dg;
+ drawingNumber = d.drawingNumber;
+ drawingGroup.addDrawing(this);
+ workbookSettings = ws;
+ }
+
+ /**
+ * Constructor invoked when writing images
+ */
+ public ComboBox() {
+ initialized = true;
+ origin = Origin.WRITE;
+ referenceCount = 1;
+ type = ShapeType.HOST_CONTROL;
+ }
+
+ /**
+ * Initializes the member variables from the Escher stream data
+ */
+ private void initialize() {
+ readSpContainer = drawingData.getSpContainer(drawingNumber);
+ Assert.verify(readSpContainer != null);
+
+ EscherRecord[] children = readSpContainer.getChildren();
+
+ Sp sp = (Sp) readSpContainer.getChildren()[0];
+ objectId = objRecord.getObjectId();
+ shapeId = sp.getShapeId();
+ type = ShapeType.getType(sp.getShapeType());
+
+ if (type == ShapeType.UNKNOWN) {
+ logger.warn("Unknown shape type");
+ }
+
+ ClientAnchor clientAnchor = null;
+ for (int i = 0; i < children.length && clientAnchor == null; i++) {
+ if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) {
+ clientAnchor = (ClientAnchor) children[i];
+ }
+ }
+
+ if (clientAnchor == null) {
+ logger.warn("Client anchor not found");
+ } else {
+ column = (int) clientAnchor.getX1();
+ row = (int) clientAnchor.getY1();
+ }
+
+ initialized = true;
+ }
+
+
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ public final void setObjectId(int objid, int bip, int sid) {
+ objectId = objid;
+ blipId = bip;
+ shapeId = sid;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public final int getObjectId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return objectId;
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the object id
+ */
+ public final int getShapeId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ public final int getBlipId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return blipId;
+ }
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ public MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ public EscherContainer getSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ if (origin == Origin.READ) {
+ return getReadSpContainer();
+ }
+
+ SpContainer spc = new SpContainer();
+ Sp sp = new Sp(type, shapeId, 2560);
+ spc.add(sp);
+ Opt opt = new Opt();
+ opt.addProperty(127, false, false, 17039620);
+ opt.addProperty(191, false, false, 524296);
+ opt.addProperty(511, false, false, 524288);
+ opt.addProperty(959, false, false, 131072);
+ // opt.addProperty(260, true, false, blipId);
+ // opt.addProperty(261, false, false, 36);
+ spc.add(opt);
+
+ ClientAnchor clientAnchor = new ClientAnchor(column,
+ row,
+ column + 1,
+ row + 1,
+ 0x1);
+ spc.add(clientAnchor);
+ ClientData clientData = new ClientData();
+ spc.add(clientData);
+
+ return spc;
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ public DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ public void setDrawingGroup(DrawingGroup dg) {
+ drawingGroup = dg;
+ }
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ public void setReferenceCount(int r) {
+ referenceCount = r;
+ }
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ public double getX() {
+ if (!initialized) {
+ initialize();
+ }
+ return column;
+ }
+
+ /**
+ * Sets the column position of this drawing. Used when inserting/removing
+ * columns from the spreadsheet
+ *
+ * @param x the column
+ */
+ public void setX(double x) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ column = (int) x;
+ }
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ public double getY() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return row;
+ }
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ public void setY(double y) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ row = (int) y;
+ }
+
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ public double getWidth() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return width;
+ }
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ public void setWidth(double w) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ width = w;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ public double getHeight() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return height;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ public void setHeight(double h) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ height = h;
+ }
+
+
+ /**
+ * Gets the SpContainer that was read in
+ *
+ * @return the read sp container
+ */
+ private EscherContainer getReadSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return readSpContainer;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageData() {
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ if (!initialized) {
+ initialize();
+ }
+
+ return drawingGroup.getImageData(blipId);
+ }
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ public ShapeType getType() {
+ return type;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageBytes() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ public String getImageFilePath() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Writes out the additional records for a combo box
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeAdditionalRecords(File outputFile) throws IOException {
+ if (origin == Origin.READ) {
+ outputFile.write(objRecord);
+ return;
+ }
+
+ // Create the obj record
+ ObjRecord objrec = new ObjRecord(objectId,
+ ObjRecord.COMBOBOX);
+
+ outputFile.write(objrec);
+ }
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ * Writes out all the note records
+ *
+ * @param outputFile the output file
+ */
+ public void writeTailRecords(File outputFile) {
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row
+ */
+ public int getRow() {
+ return 0;
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column
+ */
+ public int getColumn() {
+ return 0;
+ }
+
+ /**
+ * Hashing algorithm
+ *
+ * @return the hash code
+ */
+ public int hashCode() {
+ return getClass().getName().hashCode();
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return msoDrawingRecord.isFirst();
+ }
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over TXO and CONTINUE records and
+ * require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ public boolean isFormObject() {
+ return false;
+ }
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java
new file mode 100755
index 0000000..7bd5b29
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Comment.java
@@ -0,0 +1,829 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import jxl.WorkbookSettings;
+import jxl.biff.ContinueRecord;
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+/**
+ * Contains the various biff records used to insert a cell note into a
+ * worksheet
+ */
+public class Comment implements DrawingGroupObject {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Comment.class);
+
+ /**
+ * The spContainer that was read in
+ */
+ private EscherContainer readSpContainer;
+
+ /**
+ * The spContainer that was generated
+ */
+ private EscherContainer spContainer;
+
+ /**
+ * The MsoDrawingRecord associated with the drawing
+ */
+ private MsoDrawingRecord msoDrawingRecord;
+
+ /**
+ * The ObjRecord associated with the drawing
+ */
+ private ObjRecord objRecord;
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized = false;
+
+ /**
+ * The object id, assigned by the drawing group
+ */
+ private int objectId;
+
+ /**
+ * The blip id
+ */
+ private int blipId;
+
+ /**
+ * The shape id
+ */
+ private int shapeId;
+
+ /**
+ * The column
+ */
+ private int column;
+
+ /**
+ * The row position of the image
+ */
+ private int row;
+
+ /**
+ * The width of the image in cells
+ */
+ private double width;
+
+ /**
+ * The height of the image in cells
+ */
+ private double height;
+
+ /**
+ * The number of places this drawing is referenced
+ */
+ private int referenceCount;
+
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+
+ /**
+ * Where this image came from (read, written or a copy)
+ */
+ private Origin origin;
+
+ /**
+ * The drawing group for all the images
+ */
+ private DrawingGroup drawingGroup;
+
+ /**
+ * The drawing data
+ */
+ private DrawingData drawingData;
+
+ /**
+ * The type of this drawing object
+ */
+ private ShapeType type;
+
+ /**
+ * The drawing position on the sheet
+ */
+ private int drawingNumber;
+
+ /**
+ * An mso drawing record, which sometimes appears
+ */
+ private MsoDrawingRecord mso;
+
+ /**
+ * The text object record
+ */
+ private TextObjectRecord txo;
+
+ /**
+ * The note record
+ */
+ private NoteRecord note;
+
+ /**
+ * Text data from the first continue record
+ */
+ private ContinueRecord text;
+
+ /**
+ * Formatting data from the second continue record
+ */
+ private ContinueRecord formatting;
+
+ /**
+ * The comment text
+ */
+ private String commentText;
+
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings workbookSettings;
+
+ /**
+ * Constructor used when reading images
+ *
+ * @param msorec the drawing record
+ * @param obj the object record
+ * @param dd the drawing data for all drawings on this sheet
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ public Comment(MsoDrawingRecord msorec, ObjRecord obj, DrawingData dd,
+ DrawingGroup dg, WorkbookSettings ws) {
+ drawingGroup = dg;
+ msoDrawingRecord = msorec;
+ drawingData = dd;
+ objRecord = obj;
+ initialized = false;
+ workbookSettings = ws;
+ origin = Origin.READ;
+ drawingData.addData(msoDrawingRecord.getData());
+ drawingNumber = drawingData.getNumDrawings() - 1;
+ drawingGroup.addDrawing(this);
+
+ Assert.verify(msoDrawingRecord != null && objRecord != null);
+
+ if (!initialized) {
+ initialize();
+ }
+ }
+
+ /**
+ * Copy constructor used to copy drawings from read to write
+ *
+ * @param dgo the drawing group object
+ * @param dg the drawing group
+ * @param ws the workbook settings
+ */
+ /*protected*/
+ public Comment(DrawingGroupObject dgo,
+ DrawingGroup dg,
+ WorkbookSettings ws) {
+ Comment d = (Comment) dgo;
+ Assert.verify(d.origin == Origin.READ);
+ msoDrawingRecord = d.msoDrawingRecord;
+ objRecord = d.objRecord;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData = d.drawingData;
+ drawingGroup = dg;
+ drawingNumber = d.drawingNumber;
+ drawingGroup.addDrawing(this);
+ mso = d.mso;
+ txo = d.txo;
+ text = d.text;
+ formatting = d.formatting;
+ note = d.note;
+ width = d.width;
+ height = d.height;
+ workbookSettings = ws;
+ }
+
+ /**
+ * Constructor invoked when writing the images
+ *
+ * @param txt the comment text
+ * @param c the column
+ * @param r the row
+ */
+ public Comment(String txt, int c, int r) {
+ initialized = true;
+ origin = Origin.WRITE;
+ column = c;
+ row = r;
+ referenceCount = 1;
+ type = ShapeType.TEXT_BOX;
+ commentText = txt;
+ width = 3;
+ height = 4;
+ }
+
+ /**
+ * Initializes the member variables from the Escher stream data
+ */
+ private void initialize() {
+ readSpContainer = drawingData.getSpContainer(drawingNumber);
+ Assert.verify(readSpContainer != null);
+
+ EscherRecord[] children = readSpContainer.getChildren();
+
+ Sp sp = (Sp) readSpContainer.getChildren()[0];
+ objectId = objRecord.getObjectId();
+ shapeId = sp.getShapeId();
+ type = ShapeType.getType(sp.getShapeType());
+
+ if (type == ShapeType.UNKNOWN) {
+ logger.warn("Unknown shape type");
+ }
+
+ ClientAnchor clientAnchor = null;
+ for (int i = 0; i < children.length && clientAnchor == null; i++) {
+ if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) {
+ clientAnchor = (ClientAnchor) children[i];
+ }
+ }
+
+ if (clientAnchor == null) {
+ logger.warn("client anchor not found");
+ } else {
+ column = (int) clientAnchor.getX1() - 1;
+ row = (int) clientAnchor.getY1() + 1;
+ width = clientAnchor.getX2() - clientAnchor.getX1();
+ height = clientAnchor.getY2() - clientAnchor.getY1();
+ }
+
+ initialized = true;
+ }
+
+
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ public final void setObjectId(int objid, int bip, int sid) {
+ objectId = objid;
+ blipId = bip;
+ shapeId = sid;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public final int getObjectId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return objectId;
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the object id
+ */
+ public final int getShapeId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ public final int getBlipId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return blipId;
+ }
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ public MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ public EscherContainer getSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ if (origin == Origin.READ) {
+ return getReadSpContainer();
+ }
+
+ if (spContainer == null) {
+ spContainer = new SpContainer();
+ Sp sp = new Sp(type, shapeId, 2560);
+ spContainer.add(sp);
+ Opt opt = new Opt();
+ opt.addProperty(344, false, false, 0); // ?
+ opt.addProperty(385, false, false, 134217808); // fill colour
+ opt.addProperty(387, false, false, 134217808); // background colour
+ opt.addProperty(959, false, false, 131074); // hide
+ spContainer.add(opt);
+
+ ClientAnchor clientAnchor = new ClientAnchor(column + 1.3,
+ Math.max(0, row - 0.6),
+ column + 1.3 + width,
+ row + height,
+ 0x1);
+
+ spContainer.add(clientAnchor);
+
+ ClientData clientData = new ClientData();
+ spContainer.add(clientData);
+
+ ClientTextBox clientTextBox = new ClientTextBox();
+ spContainer.add(clientTextBox);
+ }
+
+ return spContainer;
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ public DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ public void setDrawingGroup(DrawingGroup dg) {
+ drawingGroup = dg;
+ }
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ public void setReferenceCount(int r) {
+ referenceCount = r;
+ }
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ public double getX() {
+ if (!initialized) {
+ initialize();
+ }
+ return column;
+ }
+
+ /**
+ * Sets the column position of this drawing. Used when inserting/removing
+ * columns from the spreadsheet
+ *
+ * @param x the column
+ */
+ public void setX(double x) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ column = (int) x;
+ }
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ public double getY() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return row;
+ }
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ public void setY(double y) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ row = (int) y;
+ }
+
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ public double getWidth() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return width;
+ }
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ public void setWidth(double w) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ width = w;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ public double getHeight() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return height;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ public void setHeight(double h) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ height = h;
+ }
+
+
+ /**
+ * Gets the SpContainer that was read in
+ *
+ * @return the read sp container
+ */
+ private EscherContainer getReadSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return readSpContainer;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageData() {
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ if (!initialized) {
+ initialize();
+ }
+
+ return drawingGroup.getImageData(blipId);
+ }
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ public ShapeType getType() {
+ return type;
+ }
+
+ /**
+ * Sets the text object
+ *
+ * @param t the text object
+ */
+ public void setTextObject(TextObjectRecord t) {
+ txo = t;
+ }
+
+ /**
+ * Sets the note object
+ *
+ * @param t the note record
+ */
+ public void setNote(NoteRecord t) {
+ note = t;
+ }
+
+ /**
+ * Sets the formatting
+ *
+ * @param t the formatting record
+ */
+ public void setFormatting(ContinueRecord t) {
+ formatting = t;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageBytes() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ public String getImageFilePath() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Adds an mso record to this object
+ *
+ * @param d the mso record
+ */
+ public void addMso(MsoDrawingRecord d) {
+ mso = d;
+ drawingData.addRawData(mso.getData());
+ }
+
+ /**
+ * Writes out the additional comment records
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeAdditionalRecords(File outputFile) throws IOException {
+ if (origin == Origin.READ) {
+ outputFile.write(objRecord);
+
+ if (mso != null) {
+ outputFile.write(mso);
+ }
+ outputFile.write(txo);
+ outputFile.write(text);
+ if (formatting != null) {
+ outputFile.write(formatting);
+ }
+ return;
+ }
+
+ // Create the obj record
+ ObjRecord objrec = new ObjRecord(objectId,
+ ObjRecord.EXCELNOTE);
+
+ outputFile.write(objrec);
+
+ // Create the mso data record. Write the text box record again,
+ // although it is already included in the SpContainer
+ ClientTextBox textBox = new ClientTextBox();
+ MsoDrawingRecord msod = new MsoDrawingRecord(textBox.getData());
+ outputFile.write(msod);
+
+ TextObjectRecord txorec = new TextObjectRecord(getText());
+ outputFile.write(txorec);
+
+ // Data for the first continue record
+ byte[] textData = new byte[commentText.length() * 2 + 1];
+ textData[0] = 0x1; // unicode indicator
+ StringHelper.getUnicodeBytes(commentText, textData, 1);
+ //StringHelper.getBytes(commentText, textData, 1);
+ ContinueRecord textContinue = new ContinueRecord(textData);
+ outputFile.write(textContinue);
+
+ // Data for the formatting runs
+
+ byte[] frData = new byte[16];
+
+ // First txo run (the user)
+ IntegerHelper.getTwoBytes(0, frData, 0); // index to the first character
+ IntegerHelper.getTwoBytes(0, frData, 2); // index to the font(default)
+ // Mandatory last txo run
+ IntegerHelper.getTwoBytes(commentText.length(), frData, 8);
+ IntegerHelper.getTwoBytes(0, frData, 10); // index to the font(default)
+
+ ContinueRecord frContinue = new ContinueRecord(frData);
+ outputFile.write(frContinue);
+ }
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ * Writes out all the note records
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeTailRecords(File outputFile) throws IOException {
+ if (origin == Origin.READ) {
+ outputFile.write(note);
+ return;
+ }
+
+ // The note record
+ NoteRecord noteRecord = new NoteRecord(column, row, objectId);
+ outputFile.write(noteRecord);
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row
+ */
+ public int getRow() {
+ return note.getRow();
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column
+ */
+ public int getColumn() {
+ return note.getColumn();
+ }
+
+ /**
+ * Accessor for the comment text
+ *
+ * @return the comment text
+ */
+ public String getText() {
+ if (commentText == null) {
+ Assert.verify(text != null);
+
+ byte[] td = text.getData();
+ if (td[0] == 0) {
+ commentText = StringHelper.getString
+ (td, td.length - 1, 1, workbookSettings);
+ } else {
+ commentText = StringHelper.getUnicodeString
+ (td, (td.length - 1) / 2, 1);
+ }
+ }
+
+ return commentText;
+ }
+
+ /**
+ * Sets the text data
+ *
+ * @param t the text data
+ */
+ public void setText(ContinueRecord t) {
+ text = t;
+ }
+
+ /**
+ * Hashing algorithm
+ *
+ * @return the hash code
+ */
+ public int hashCode() {
+ return commentText.hashCode();
+ }
+
+ /**
+ * Called when the comment text is changed during the sheet copy process
+ *
+ * @param t the new text
+ */
+ public void setCommentText(String t) {
+ commentText = t;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return msoDrawingRecord.isFirst();
+ }
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over several records and require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ public boolean isFormObject() {
+ return true;
+ }
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java
new file mode 100755
index 0000000..070d1a6
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dg.java
@@ -0,0 +1,105 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+
+/**
+ * The Drawing Group
+ */
+class Dg extends EscherAtom {
+ /**
+ * The data
+ */
+ private byte[] data;
+
+ /**
+ * The id of this drawing
+ */
+ private final int drawingId;
+
+ /**
+ * The number of shapes
+ */
+ private final int shapeCount;
+
+ /**
+ * The seed for drawing ids
+ */
+ private final int seed;
+
+ /**
+ * Constructor invoked when reading in an escher stream
+ *
+ * @param erd the escher record
+ */
+ public Dg(EscherRecordData erd) {
+ super(erd);
+ drawingId = getInstance();
+
+ byte[] bytes = getBytes();
+ shapeCount = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]);
+ seed = IntegerHelper.getInt(bytes[4], bytes[5], bytes[6], bytes[7]);
+ }
+
+ /**
+ * Constructor invoked when writing out an escher stream
+ *
+ * @param numDrawings the number of drawings
+ */
+ public Dg(int numDrawings) {
+ super(EscherRecordType.DG);
+ drawingId = 1;
+ shapeCount = numDrawings + 1;
+ seed = 1024 + shapeCount + 1;
+ setInstance(drawingId);
+ }
+
+ /**
+ * Gets the drawing id
+ *
+ * @return the drawing id
+ */
+ public int getDrawingId() {
+ return drawingId;
+ }
+
+ /**
+ * Gets the shape count
+ *
+ * @return the shape count
+ */
+ int getShapeCount() {
+ return shapeCount;
+ }
+
+ /**
+ * Used to generate the drawing data
+ *
+ * @return the data
+ */
+ byte[] getData() {
+ data = new byte[8];
+ IntegerHelper.getFourBytes(shapeCount, data, 0);
+ IntegerHelper.getFourBytes(seed, data, 4);
+
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java
new file mode 100755
index 0000000..1278cce
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DgContainer.java
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * A Dg Container
+ */
+class DgContainer extends EscherContainer {
+ /**
+ * Constructor
+ */
+ public DgContainer() {
+ super(EscherRecordType.DG_CONTAINER);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java
new file mode 100755
index 0000000..cae1897
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Dgg.java
@@ -0,0 +1,203 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.util.ArrayList;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * Dgg record
+ */
+class Dgg extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Dgg.class);
+
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * The number of clusters
+ */
+ private int numClusters;
+
+ /**
+ * The maximum shape id
+ */
+ private int maxShapeId;
+
+ /**
+ * The number of shapes saved
+ */
+ private final int shapesSaved;
+
+ /**
+ * The number of drawings saved
+ */
+ private final int drawingsSaved;
+
+ /**
+ * The clusters
+ */
+ private final ArrayList clusters;
+
+ /**
+ * Constructor
+ *
+ * @param erd the read in data
+ */
+ public Dgg(EscherRecordData erd) {
+ super(erd);
+ clusters = new ArrayList();
+ byte[] bytes = getBytes();
+ maxShapeId = IntegerHelper.getInt
+ (bytes[0], bytes[1], bytes[2], bytes[3]);
+ numClusters = IntegerHelper.getInt
+ (bytes[4], bytes[5], bytes[6], bytes[7]);
+ shapesSaved = IntegerHelper.getInt
+ (bytes[8], bytes[9], bytes[10], bytes[11]);
+ drawingsSaved = IntegerHelper.getInt
+ (bytes[12], bytes[13], bytes[14], bytes[15]);
+
+ int pos = 16;
+ for (int i = 0; i < numClusters; i++) {
+ int dgId = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]);
+ int sids = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3]);
+ Cluster c = new Cluster(dgId, sids);
+ clusters.add(c);
+ pos += 4;
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param numShapes the number of shapes
+ * @param numDrawings the number of drawings
+ */
+ public Dgg(int numShapes, int numDrawings) {
+ super(EscherRecordType.DGG);
+ shapesSaved = numShapes;
+ drawingsSaved = numDrawings;
+ clusters = new ArrayList();
+ }
+
+ /**
+ * Adds a cluster to this record
+ *
+ * @param dgid the id
+ * @param sids the sid
+ */
+ void addCluster(int dgid, int sids) {
+ Cluster c = new Cluster(dgid, sids);
+ clusters.add(c);
+ }
+
+ /**
+ * Gets the data for writing out
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ numClusters = clusters.size();
+ data = new byte[16 + numClusters * 4];
+
+ // The max shape id
+ IntegerHelper.getFourBytes(1024 + shapesSaved, data, 0);
+
+ // The number of clusters
+ IntegerHelper.getFourBytes(numClusters, data, 4);
+
+ // The number of shapes saved
+ IntegerHelper.getFourBytes(shapesSaved, data, 8);
+
+ // The number of drawings saved
+ // IntegerHelper.getFourBytes(drawingsSaved, data, 12);
+ IntegerHelper.getFourBytes(1, data, 12);
+
+ int pos = 16;
+ for (int i = 0; i < numClusters; i++) {
+ Cluster c = (Cluster) clusters.get(i);
+ IntegerHelper.getTwoBytes(c.drawingGroupId, data, pos);
+ IntegerHelper.getTwoBytes(c.shapeIdsUsed, data, pos + 2);
+ pos += 4;
+ }
+
+ return setHeaderData(data);
+ }
+
+ /**
+ * Accessor for the number of shapes saved
+ *
+ * @return the number of shapes saved
+ */
+ int getShapesSaved() {
+ return shapesSaved;
+ }
+
+ /**
+ * Accessor for the number of drawings saved
+ *
+ * @return the number of drawings saved
+ */
+ int getDrawingsSaved() {
+ return drawingsSaved;
+ }
+
+ /**
+ * Accessor for a particular cluster
+ *
+ * @param i the cluster number
+ * @return the cluster
+ */
+ Cluster getCluster(int i) {
+ return (Cluster) clusters.get(i);
+ }
+
+ /**
+ * The cluster structure
+ */
+ static final class Cluster {
+ /**
+ * The drawing group id
+ */
+ int drawingGroupId;
+
+ /**
+ * The something or other
+ */
+ int shapeIdsUsed;
+
+ /**
+ * Constructor
+ *
+ * @param dgId the drawing group id
+ * @param sids the sids
+ */
+ Cluster(int dgId, int sids) {
+ drawingGroupId = dgId;
+ shapeIdsUsed = sids;
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java
new file mode 100755
index 0000000..82d3236
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DggContainer.java
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Container for Dgg objects
+ */
+class DggContainer extends EscherContainer {
+ /**
+ * Constructor
+ */
+ public DggContainer() {
+ super(EscherRecordType.DGG_CONTAINER);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java
new file mode 100755
index 0000000..625730c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing.java
@@ -0,0 +1,990 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import jxl.CellView;
+import jxl.Image;
+import jxl.Sheet;
+import jxl.common.Assert;
+import jxl.common.LengthConverter;
+import jxl.common.LengthUnit;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+
+/**
+ * Contains the various biff records used to insert a drawing into a
+ * worksheet
+ */
+public class Drawing implements DrawingGroupObject, Image {
+ /**
+ * The default font size for columns
+ */
+ private static final double DEFAULT_FONT_SIZE = 10;
+ // The image anchor properties
+ public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS =
+ new ImageAnchorProperties(1);
+ public static ImageAnchorProperties MOVE_WITH_CELLS =
+ new ImageAnchorProperties(2);
+ public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS =
+ new ImageAnchorProperties(3);
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Drawing.class);
+ /**
+ * The spContainer that was read in
+ */
+ private EscherContainer readSpContainer;
+ /**
+ * The MsoDrawingRecord associated with the drawing
+ */
+ private MsoDrawingRecord msoDrawingRecord;
+ /**
+ * The ObjRecord associated with the drawing
+ */
+ private ObjRecord objRecord;
+ /**
+ * Initialized flag
+ */
+ private boolean initialized = false;
+ /**
+ * The file containing the image
+ */
+ private java.io.File imageFile;
+ /**
+ * The raw image data, used instead of an image file
+ */
+ private byte[] imageData;
+ /**
+ * The object id, assigned by the drawing group
+ */
+ private int objectId;
+ /**
+ * The blip id
+ */
+ private int blipId;
+ /**
+ * The column position of the image
+ */
+ private double x;
+ /**
+ * The row position of the image
+ */
+ private double y;
+ /**
+ * The width of the image in cells
+ */
+ private double width;
+ /**
+ * The height of the image in cells
+ */
+ private double height;
+ /**
+ * The number of places this drawing is referenced
+ */
+ private int referenceCount;
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+ /**
+ * Where this image came from (read, written or a copy)
+ */
+ private Origin origin;
+ /**
+ * The drawing group for all the images
+ */
+ private DrawingGroup drawingGroup;
+ /**
+ * The drawing data
+ */
+ private DrawingData drawingData;
+ /**
+ * The type of this drawing object
+ */
+ private ShapeType type;
+ /**
+ * The shape id
+ */
+ private int shapeId;
+ /**
+ * The drawing position on the sheet
+ */
+ private int drawingNumber;
+ /**
+ * A reference to the sheet containing this drawing. Used to calculate
+ * the drawing dimensions in pixels
+ */
+ private Sheet sheet;
+ /**
+ * Reader for the raw image data
+ */
+ private PNGReader pngReader;
+ /**
+ * The client anchor properties
+ */
+ private ImageAnchorProperties imageAnchorProperties;
+
+ /**
+ * Constructor used when reading images
+ *
+ * @param mso the drawing record
+ * @param obj the object record
+ * @param dd the drawing data for all drawings on this sheet
+ * @param dg the drawing group
+ */
+ public Drawing(MsoDrawingRecord mso,
+ ObjRecord obj,
+ DrawingData dd,
+ DrawingGroup dg,
+ Sheet s) {
+ drawingGroup = dg;
+ msoDrawingRecord = mso;
+ drawingData = dd;
+ objRecord = obj;
+ sheet = s;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData.addData(msoDrawingRecord.getData());
+ drawingNumber = drawingData.getNumDrawings() - 1;
+ drawingGroup.addDrawing(this);
+
+ Assert.verify(mso != null && obj != null);
+
+ initialize();
+ }
+
+ /**
+ * Copy constructor used to copy drawings from read to write
+ *
+ * @param dgo the drawing group object
+ * @param dg the drawing group
+ */
+ protected Drawing(DrawingGroupObject dgo, DrawingGroup dg) {
+ Drawing d = (Drawing) dgo;
+ Assert.verify(d.origin == Origin.READ);
+ msoDrawingRecord = d.msoDrawingRecord;
+ objRecord = d.objRecord;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData = d.drawingData;
+ drawingGroup = dg;
+ drawingNumber = d.drawingNumber;
+ drawingGroup.addDrawing(this);
+ }
+
+ /**
+ * Constructor invoked when writing the images
+ *
+ * @param x the column
+ * @param y the row
+ * @param w the width in cells
+ * @param h the height in cells
+ * @param image the image file
+ */
+ public Drawing(double x,
+ double y,
+ double w,
+ double h,
+ java.io.File image) {
+ imageFile = image;
+ initialized = true;
+ origin = Origin.WRITE;
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ referenceCount = 1;
+ imageAnchorProperties = MOVE_WITH_CELLS;
+ type = ShapeType.PICTURE_FRAME;
+ }
+
+ /**
+ * Constructor invoked when writing the images
+ *
+ * @param x the column
+ * @param y the row
+ * @param w the width in cells
+ * @param h the height in cells
+ * @param image the image data
+ */
+ public Drawing(double x,
+ double y,
+ double w,
+ double h,
+ byte[] image) {
+ imageData = image;
+ initialized = true;
+ origin = Origin.WRITE;
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ referenceCount = 1;
+ imageAnchorProperties = MOVE_WITH_CELLS;
+ type = ShapeType.PICTURE_FRAME;
+ }
+
+ /**
+ * Initializes the member variables from the Escher stream data
+ */
+ private void initialize() {
+ readSpContainer = drawingData.getSpContainer(drawingNumber);
+ Assert.verify(readSpContainer != null);
+
+ EscherRecord[] children = readSpContainer.getChildren();
+
+ Sp sp = (Sp) readSpContainer.getChildren()[0];
+ shapeId = sp.getShapeId();
+ objectId = objRecord.getObjectId();
+ type = ShapeType.getType(sp.getShapeType());
+
+ if (type == ShapeType.UNKNOWN) {
+ logger.warn("Unknown shape type");
+ }
+
+ Opt opt = (Opt) readSpContainer.getChildren()[1];
+
+ if (opt.getProperty(260) != null) {
+ blipId = opt.getProperty(260).value;
+ }
+
+ if (opt.getProperty(261) != null) {
+ imageFile = new java.io.File(opt.getProperty(261).stringValue);
+ } else {
+ if (type == ShapeType.PICTURE_FRAME) {
+ logger.warn("no filename property for drawing");
+ imageFile = new java.io.File(Integer.toString(blipId));
+ }
+ }
+
+ ClientAnchor clientAnchor = null;
+ for (int i = 0; i < children.length && clientAnchor == null; i++) {
+ if (children[i].getType() == EscherRecordType.CLIENT_ANCHOR) {
+ clientAnchor = (ClientAnchor) children[i];
+ }
+ }
+
+ if (clientAnchor == null) {
+ logger.warn("client anchor not found");
+ } else {
+ x = clientAnchor.getX1();
+ y = clientAnchor.getY1();
+ width = clientAnchor.getX2() - x;
+ height = clientAnchor.getY2() - y;
+ imageAnchorProperties = ImageAnchorProperties.getImageAnchorProperties
+ (clientAnchor.getProperties());
+ }
+
+ if (blipId == 0) {
+ logger.warn("linked drawings are not supported");
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Accessor for the image file
+ *
+ * @return the image file
+ */
+ public java.io.File getImageFile() {
+ return imageFile;
+ }
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ public String getImageFilePath() {
+ if (imageFile == null) {
+ // return the blip id, if it exists
+ return blipId != 0 ? Integer.toString(blipId) : "__new__image__";
+ }
+
+ return imageFile.getPath();
+ }
+
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ public final void setObjectId(int objid, int bip, int sid) {
+ objectId = objid;
+ blipId = bip;
+ shapeId = sid;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public final int getObjectId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return objectId;
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the shape id
+ */
+ public int getShapeId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ public final int getBlipId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return blipId;
+ }
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ public MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ public EscherContainer getSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ if (origin == Origin.READ) {
+ return getReadSpContainer();
+ }
+
+ SpContainer spContainer = new SpContainer();
+ Sp sp = new Sp(type, shapeId, 2560);
+ spContainer.add(sp);
+ Opt opt = new Opt();
+ opt.addProperty(260, true, false, blipId);
+
+ if (type == ShapeType.PICTURE_FRAME) {
+ String filePath = imageFile != null ? imageFile.getPath() : "";
+ opt.addProperty(261, true, true, filePath.length() * 2, filePath);
+ opt.addProperty(447, false, false, 65536);
+ opt.addProperty(959, false, false, 524288);
+ spContainer.add(opt);
+ }
+
+ ClientAnchor clientAnchor = new ClientAnchor
+ (x, y, x + width, y + height,
+ imageAnchorProperties.getValue());
+ spContainer.add(clientAnchor);
+ ClientData clientData = new ClientData();
+ spContainer.add(clientData);
+
+ return spContainer;
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ public DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ public void setDrawingGroup(DrawingGroup dg) {
+ drawingGroup = dg;
+ }
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ public void setReferenceCount(int r) {
+ referenceCount = r;
+ }
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ public double getX() {
+ if (!initialized) {
+ initialize();
+ }
+ return x;
+ }
+
+ /**
+ * Sets the column position of this drawing
+ *
+ * @param x the column
+ */
+ public void setX(double x) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ this.x = x;
+ }
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ public double getY() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return y;
+ }
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ public void setY(double y) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ this.y = y;
+ }
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ public double getWidth() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return width;
+ }
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ public void setWidth(double w) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ width = w;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ public double getHeight() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return height;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ public void setHeight(double h) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ height = h;
+ }
+
+ /**
+ * Gets the SpContainer that was read in
+ *
+ * @return the read sp container
+ */
+ private EscherContainer getReadSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return readSpContainer;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageData() {
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ if (!initialized) {
+ initialize();
+ }
+
+ return drawingGroup.getImageData(blipId);
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageBytes() throws IOException {
+ if (origin == Origin.READ || origin == Origin.READ_WRITE) {
+ return getImageData();
+ }
+
+ Assert.verify(origin == Origin.WRITE);
+
+ if (imageFile == null) {
+ Assert.verify(imageData != null);
+ return imageData;
+ }
+
+ byte[] data = new byte[(int) imageFile.length()];
+ FileInputStream fis = new FileInputStream(imageFile);
+ fis.read(data, 0, data.length);
+ fis.close();
+ return data;
+ }
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ public ShapeType getType() {
+ return type;
+ }
+
+ /**
+ * Writes any other records associated with this drawing group object
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeAdditionalRecords(File outputFile) throws IOException {
+ if (origin == Origin.READ) {
+ outputFile.write(objRecord);
+ return;
+ }
+
+ // Create the obj record
+ ObjRecord objrec = new ObjRecord(objectId,
+ ObjRecord.PICTURE);
+ outputFile.write(objrec);
+ }
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ * Does nothing here
+ *
+ * @param outputFile the output file
+ */
+ public void writeTailRecords(File outputFile) throws IOException {
+ // does nothing
+ }
+
+ /**
+ * Interface method
+ *
+ * @return the column number at which the image is positioned
+ */
+ public double getColumn() {
+ return getX();
+ }
+
+ /**
+ * Interface method
+ *
+ * @return the row number at which the image is positions
+ */
+ public double getRow() {
+ return getY();
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return msoDrawingRecord.isFirst();
+ }
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over TXO and CONTINUE records and
+ * require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ public boolean isFormObject() {
+ return false;
+ }
+
+ /**
+ * Removes a row
+ *
+ * @param r the row to be removed
+ */
+ public void removeRow(int r) {
+ if (y > r) {
+ setY(r);
+ }
+ }
+
+ /**
+ * Accessor for the image dimensions. See technotes for Bill's explanation
+ * of the calculation logic
+ *
+ * @return approximate drawing size in pixels
+ */
+ private double getWidthInPoints() {
+ if (sheet == null) {
+ logger.warn("calculating image width: sheet is null");
+ return 0;
+ }
+
+ // The start and end row numbers
+ int firstCol = (int) x;
+ int lastCol = (int) Math.ceil(x + width) - 1;
+
+ // **** MAGIC NUMBER ALERT ***
+ // multiply the point size of the font by 0.59 to give the point size
+ // I know of no explanation for this yet, other than that it seems to
+ // give the right answer
+
+ // Get the width of the image within the first col, allowing for
+ // fractional offsets
+ CellView cellView = sheet.getColumnView(firstCol);
+ int firstColWidth = cellView.getSize();
+ double firstColImageWidth = (1 - (x - firstCol)) * firstColWidth;
+ double pointSize = (cellView.getFormat() != null) ?
+ cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE;
+ double firstColWidthInPoints = firstColImageWidth * 0.59 * pointSize / 256;
+
+ // Get the height of the image within the last row, allowing for
+ // fractional offsets
+ int lastColWidth = 0;
+ double lastColImageWidth = 0;
+ double lastColWidthInPoints = 0;
+ if (lastCol != firstCol) {
+ cellView = sheet.getColumnView(lastCol);
+ lastColWidth = cellView.getSize();
+ lastColImageWidth = (x + width - lastCol) * lastColWidth;
+ pointSize = (cellView.getFormat() != null) ?
+ cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE;
+ lastColWidthInPoints = lastColImageWidth * 0.59 * pointSize / 256;
+ }
+
+ // Now get all the columns in between
+ double width = 0;
+ for (int i = 0; i < lastCol - firstCol - 1; i++) {
+ cellView = sheet.getColumnView(firstCol + 1 + i);
+ pointSize = (cellView.getFormat() != null) ?
+ cellView.getFormat().getFont().getPointSize() : DEFAULT_FONT_SIZE;
+ width += cellView.getSize() * 0.59 * pointSize / 256;
+ }
+
+ // Add on the first and last row contributions to get the height in twips
+ double widthInPoints = width +
+ firstColWidthInPoints + lastColWidthInPoints;
+
+ return widthInPoints;
+ }
+
+ /**
+ * Accessor for the image dimensions. See technotes for Bill's explanation
+ * of the calculation logic
+ *
+ * @return approximate drawing size in pixels
+ */
+ private double getHeightInPoints() {
+ if (sheet == null) {
+ logger.warn("calculating image height: sheet is null");
+ return 0;
+ }
+
+ // The start and end row numbers
+ int firstRow = (int) y;
+ int lastRow = (int) Math.ceil(y + height) - 1;
+
+ // Get the height of the image within the first row, allowing for
+ // fractional offsets
+ int firstRowHeight = sheet.getRowView(firstRow).getSize();
+ double firstRowImageHeight = (1 - (y - firstRow)) * firstRowHeight;
+
+ // Get the height of the image within the last row, allowing for
+ // fractional offsets
+ int lastRowHeight = 0;
+ double lastRowImageHeight = 0;
+ if (lastRow != firstRow) {
+ lastRowHeight = sheet.getRowView(lastRow).getSize();
+ lastRowImageHeight = (y + height - lastRow) * lastRowHeight;
+ }
+
+ // Now get all the rows in between
+ double height = 0;
+ for (int i = 0; i < lastRow - firstRow - 1; i++) {
+ height += sheet.getRowView(firstRow + 1 + i).getSize();
+ }
+
+ // Add on the first and last row contributions to get the height in twips
+ double heightInTwips = height + firstRowHeight + lastRowHeight;
+
+ // Now divide by the magic number to converts twips into pixels and
+ // return the value
+ double heightInPoints = heightInTwips / 20.0;
+
+ return heightInPoints;
+ }
+
+ /**
+ * Get the width of this image as rendered within Excel
+ *
+ * @param unit the unit of measurement
+ * @return the width of the image within Excel
+ */
+ public double getWidth(LengthUnit unit) {
+ double widthInPoints = getWidthInPoints();
+ return widthInPoints * LengthConverter.getConversionFactor
+ (LengthUnit.POINTS, unit);
+ }
+
+ /**
+ * Get the height of this image as rendered within Excel
+ *
+ * @param unit the unit of measurement
+ * @return the height of the image within Excel
+ */
+ public double getHeight(LengthUnit unit) {
+ double heightInPoints = getHeightInPoints();
+ return heightInPoints * LengthConverter.getConversionFactor
+ (LengthUnit.POINTS, unit);
+ }
+
+ /**
+ * Gets the width of the image. Note that this is the width of the
+ * underlying image, and does not take into account any size manipulations
+ * that may have occurred when the image was added into Excel
+ *
+ * @return the image width in pixels
+ */
+ public int getImageWidth() {
+ return getPngReader().getWidth();
+ }
+
+ /**
+ * Gets the height of the image. Note that this is the height of the
+ * underlying image, and does not take into account any size manipulations
+ * that may have occurred when the image was added into Excel
+ *
+ * @return the image width in pixels
+ */
+ public int getImageHeight() {
+ return getPngReader().getHeight();
+ }
+
+ /**
+ * Gets the horizontal resolution of the image, if that information
+ * is available.
+ *
+ * @return the number of dots per unit specified, if available, 0 otherwise
+ */
+ public double getHorizontalResolution(LengthUnit unit) {
+ int res = getPngReader().getHorizontalResolution();
+ return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit);
+ }
+
+ /**
+ * Gets the vertical resolution of the image, if that information
+ * is available.
+ *
+ * @return the number of dots per unit specified, if available, 0 otherwise
+ */
+ public double getVerticalResolution(LengthUnit unit) {
+ int res = getPngReader().getVerticalResolution();
+ return res / LengthConverter.getConversionFactor(LengthUnit.METRES, unit);
+ }
+
+ private PNGReader getPngReader() {
+ if (pngReader != null) {
+ return pngReader;
+ }
+
+ byte[] imdata = null;
+ if (origin == Origin.READ || origin == Origin.READ_WRITE) {
+ imdata = getImageData();
+ } else {
+ try {
+ imdata = getImageBytes();
+ } catch (IOException e) {
+ logger.warn("Could not read image file");
+ imdata = new byte[0];
+ }
+ }
+
+ pngReader = new PNGReader(imdata);
+ pngReader.read();
+ return pngReader;
+ }
+
+ /**
+ * Accessor for the anchor properties
+ */
+ protected ImageAnchorProperties getImageAnchor() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return imageAnchorProperties;
+ }
+
+ /**
+ * Accessor for the anchor properties
+ */
+ protected void setImageAnchor(ImageAnchorProperties iap) {
+ imageAnchorProperties = iap;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ // Enumeration type for the image anchor properties
+ protected static class ImageAnchorProperties {
+ private static ImageAnchorProperties[] o = new ImageAnchorProperties[0];
+ private final int value;
+
+ ImageAnchorProperties(int v) {
+ value = v;
+
+ ImageAnchorProperties[] oldArray = o;
+ o = new ImageAnchorProperties[oldArray.length + 1];
+ System.arraycopy(oldArray, 0, o, 0, oldArray.length);
+ o[oldArray.length] = this;
+ }
+
+ static ImageAnchorProperties getImageAnchorProperties(int val) {
+ ImageAnchorProperties iap = MOVE_AND_SIZE_WITH_CELLS;
+ int pos = 0;
+ while (pos < o.length) {
+ if (o[pos].getValue() == val) {
+ iap = o[pos];
+ break;
+ } else {
+ pos++;
+ }
+ }
+ return iap;
+ }
+
+ int getValue() {
+ return value;
+ }
+ }
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java
new file mode 100755
index 0000000..73a767f
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Drawing2.java
@@ -0,0 +1,629 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+
+/**
+ * Contains the various biff records used to insert a drawing into a
+ * worksheet. This type of image does not have an associated object
+ * record
+ */
+public class Drawing2 implements DrawingGroupObject {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Drawing.class);
+
+ /**
+ * The spContainer that was read in
+ */
+ private EscherContainer readSpContainer;
+
+ /**
+ * The MsoDrawingRecord associated with the drawing
+ */
+ private MsoDrawingRecord msoDrawingRecord;
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized = false;
+
+ /**
+ * The file containing the image
+ */
+ private java.io.File imageFile;
+
+ /**
+ * The raw image data, used instead of an image file
+ */
+ private byte[] imageData;
+
+ /**
+ * The object id, assigned by the drawing group
+ */
+ private int objectId;
+
+ /**
+ * The blip id
+ */
+ private int blipId;
+
+ /**
+ * The column position of the image
+ */
+ private double x;
+
+ /**
+ * The row position of the image
+ */
+ private double y;
+
+ /**
+ * The width of the image in cells
+ */
+ private double width;
+
+ /**
+ * The height of the image in cells
+ */
+ private double height;
+
+ /**
+ * The number of places this drawing is referenced
+ */
+ private int referenceCount;
+
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+
+ /**
+ * Where this image came from (read, written or a copy)
+ */
+ private Origin origin;
+
+ /**
+ * The drawing group for all the images
+ */
+ private DrawingGroup drawingGroup;
+
+ /**
+ * The drawing data
+ */
+ private DrawingData drawingData;
+
+ /**
+ * The type of this drawing object
+ */
+ private ShapeType type;
+
+ /**
+ * The shape id
+ */
+ private int shapeId;
+
+ /**
+ * The drawing position on the sheet
+ */
+ private int drawingNumber;
+
+
+ /**
+ * Constructor used when reading images
+ *
+ * @param mso the drawing record
+ * @param dd the drawing data for all drawings on this sheet
+ * @param dg the drawing group
+ */
+ public Drawing2(MsoDrawingRecord mso,
+ DrawingData dd,
+ DrawingGroup dg) {
+ drawingGroup = dg;
+ msoDrawingRecord = mso;
+ drawingData = dd;
+ initialized = false;
+ origin = Origin.READ;
+ // there is no drawing number associated with this drawing
+ drawingData.addRawData(msoDrawingRecord.getData());
+ drawingGroup.addDrawing(this);
+
+ Assert.verify(mso != null);
+
+ initialize();
+ }
+
+ /**
+ * Copy constructor used to copy drawings from read to write
+ *
+ * @param dgo the drawing group object
+ * @param dg the drawing group
+ */
+ protected Drawing2(DrawingGroupObject dgo, DrawingGroup dg) {
+ Drawing2 d = (Drawing2) dgo;
+ Assert.verify(d.origin == Origin.READ);
+ msoDrawingRecord = d.msoDrawingRecord;
+ initialized = false;
+ origin = Origin.READ;
+ drawingData = d.drawingData;
+ drawingGroup = dg;
+ drawingNumber = d.drawingNumber;
+ drawingGroup.addDrawing(this);
+ }
+
+ /**
+ * Constructor invoked when writing the images
+ *
+ * @param x the column
+ * @param y the row
+ * @param w the width in cells
+ * @param h the height in cells
+ * @param image the image file
+ */
+ public Drawing2(double x,
+ double y,
+ double w,
+ double h,
+ java.io.File image) {
+ imageFile = image;
+ initialized = true;
+ origin = Origin.WRITE;
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ referenceCount = 1;
+ type = ShapeType.PICTURE_FRAME;
+ }
+
+ /**
+ * Constructor invoked when writing the images
+ *
+ * @param x the column
+ * @param y the row
+ * @param w the width in cells
+ * @param h the height in cells
+ * @param image the image data
+ */
+ public Drawing2(double x,
+ double y,
+ double w,
+ double h,
+ byte[] image) {
+ imageData = image;
+ initialized = true;
+ origin = Origin.WRITE;
+ this.x = x;
+ this.y = y;
+ this.width = w;
+ this.height = h;
+ referenceCount = 1;
+ type = ShapeType.PICTURE_FRAME;
+ }
+
+ /**
+ * Initializes the member variables from the Escher stream data
+ */
+ private void initialize() {
+ initialized = true;
+ }
+
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ public final void setObjectId(int objid, int bip, int sid) {
+ objectId = objid;
+ blipId = bip;
+ shapeId = sid;
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ }
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public final int getObjectId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return objectId;
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the shape id
+ */
+ public int getShapeId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ public final int getBlipId() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return blipId;
+ }
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ public MsoDrawingRecord getMsoDrawingRecord() {
+ return msoDrawingRecord;
+ }
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ public EscherContainer getSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ Assert.verify(origin == Origin.READ);
+
+ return getReadSpContainer();
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ public DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ public void setDrawingGroup(DrawingGroup dg) {
+ drawingGroup = dg;
+ }
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ public void setReferenceCount(int r) {
+ referenceCount = r;
+ }
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ public double getX() {
+ if (!initialized) {
+ initialize();
+ }
+ return x;
+ }
+
+ /**
+ * Sets the column position of this drawing
+ *
+ * @param x the column
+ */
+ public void setX(double x) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ this.x = x;
+ }
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ public double getY() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return y;
+ }
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ public void setY(double y) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ this.y = y;
+ }
+
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ public double getWidth() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return width;
+ }
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ public void setWidth(double w) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ width = w;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ public double getHeight() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return height;
+ }
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ public void setHeight(double h) {
+ if (origin == Origin.READ) {
+ if (!initialized) {
+ initialize();
+ }
+ origin = Origin.READ_WRITE;
+ }
+
+ height = h;
+ }
+
+
+ /**
+ * Gets the SpContainer that was read in
+ *
+ * @return the read sp container
+ */
+ private EscherContainer getReadSpContainer() {
+ if (!initialized) {
+ initialize();
+ }
+
+ return readSpContainer;
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageData() {
+ Assert.verify(false);
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ if (!initialized) {
+ initialize();
+ }
+
+ return drawingGroup.getImageData(blipId);
+ }
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ public byte[] getImageBytes() throws IOException {
+ Assert.verify(false);
+ if (origin == Origin.READ || origin == Origin.READ_WRITE) {
+ return getImageData();
+ }
+
+ Assert.verify(origin == Origin.WRITE);
+
+ if (imageFile == null) {
+ Assert.verify(imageData != null);
+ return imageData;
+ }
+
+ byte[] data = new byte[(int) imageFile.length()];
+ FileInputStream fis = new FileInputStream(imageFile);
+ fis.read(data, 0, data.length);
+ fis.close();
+ return data;
+ }
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ public ShapeType getType() {
+ return type;
+ }
+
+ /**
+ * Writes any other records associated with this drawing group object
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeAdditionalRecords(File outputFile) throws IOException {
+ // no records to write
+ }
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ * Does nothing here
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeTailRecords(File outputFile) throws IOException {
+ // does nothing
+ }
+
+ /**
+ * Interface method
+ *
+ * @return the column number at which the image is positioned
+ */
+ public double getColumn() {
+ return getX();
+ }
+
+ /**
+ * Interface method
+ *
+ * @return the row number at which the image is positions
+ */
+ public double getRow() {
+ return getY();
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return msoDrawingRecord.isFirst();
+ }
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over TXO and CONTINUE records and
+ * require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ public boolean isFormObject() {
+ return false;
+ }
+
+ /**
+ * Removes a row
+ *
+ * @param r the row to be removed
+ */
+ public void removeRow(int r) {
+ if (y > r) {
+ setY(r);
+ }
+ }
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ public String getImageFilePath() {
+ Assert.verify(false);
+ return null;
+ }
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java
new file mode 100755
index 0000000..fa3b05c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingData.java
@@ -0,0 +1,206 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.util.ArrayList;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * Class used to concatenate all the data for the various drawing objects
+ * into one continuous stream
+ */
+public class DrawingData implements EscherStream {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(DrawingData.class);
+
+ /**
+ * The drawing data
+ */
+ private byte[] drawingData;
+
+ /**
+ * The number of drawings
+ */
+ private int numDrawings;
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized;
+
+ /**
+ * The spgr container. The contains the SpContainer for each drawing
+ */
+ private EscherRecord[] spContainers;
+
+ /**
+ * Constructor
+ */
+ public DrawingData() {
+ numDrawings = 0;
+ drawingData = null;
+ initialized = false;
+ }
+
+ /**
+ * Initialization
+ */
+ private void initialize() {
+ EscherRecordData er = new EscherRecordData(this, 0);
+ Assert.verify(er.isContainer());
+
+ EscherContainer dgContainer = new EscherContainer(er);
+ EscherRecord[] children = dgContainer.getChildren();
+
+ children = dgContainer.getChildren();
+ // Dg dg = (Dg) children[0];
+
+ EscherContainer spgrContainer = null;
+
+ for (int i = 0; i < children.length && spgrContainer == null; i++) {
+ EscherRecord child = children[i];
+ if (child.getType() == EscherRecordType.SPGR_CONTAINER) {
+ spgrContainer = (EscherContainer) child;
+ }
+ }
+ Assert.verify(spgrContainer != null);
+
+ EscherRecord[] spgrChildren = spgrContainer.getChildren();
+
+ // See if any of the spgrChildren are SpgrContainer
+ boolean nestedContainers = false;
+ for (int i = 0; i < spgrChildren.length && !nestedContainers; i++) {
+ if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) {
+ nestedContainers = true;
+ }
+ }
+
+ // If there are no nested containers, simply set the spContainer list
+ // to be the list of children
+ if (!nestedContainers) {
+ spContainers = spgrChildren;
+ } else {
+ // Go through the hierarchy and dig out all the Sp containers
+ ArrayList sps = new ArrayList();
+ getSpContainers(spgrContainer, sps);
+ spContainers = new EscherRecord[sps.size()];
+ spContainers = (EscherRecord[]) sps.toArray(spContainers);
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Gets the sp container from the internal data
+ *
+ * @param spgrContainer the spgr container
+ * @param sps the list of sp records
+ */
+ private void getSpContainers(EscherContainer spgrContainer, ArrayList sps) {
+ EscherRecord[] spgrChildren = spgrContainer.getChildren();
+ for (int i = 0; i < spgrChildren.length; i++) {
+ if (spgrChildren[i].getType() == EscherRecordType.SP_CONTAINER) {
+ sps.add(spgrChildren[i]);
+ } else if (spgrChildren[i].getType() == EscherRecordType.SPGR_CONTAINER) {
+ getSpContainers((EscherContainer) spgrChildren[i], sps);
+ } else {
+ logger.warn("Spgr Containers contains a record other than Sp/Spgr " +
+ "containers");
+ }
+ }
+ }
+
+ /**
+ * Adds the byte stream to the drawing data
+ *
+ * @param data the data to add
+ */
+ public void addData(byte[] data) {
+ addRawData(data);
+ numDrawings++;
+ }
+
+ /**
+ * Adds the data to the array without incrementing the drawing number.
+ * This is used by comments, which for some bizarre and inexplicable
+ * reason split out the data
+ *
+ * @param data the data to add
+ */
+ public void addRawData(byte[] data) {
+ if (drawingData == null) {
+ drawingData = data;
+ return;
+ }
+
+ // Resize the array
+ byte[] newArray = new byte[drawingData.length + data.length];
+ System.arraycopy(drawingData, 0, newArray, 0, drawingData.length);
+ System.arraycopy(data, 0, newArray, drawingData.length, data.length);
+ drawingData = newArray;
+
+ // Dirty up this object
+ initialized = false;
+ }
+
+ /**
+ * Accessor for the number of drawings
+ *
+ * @return the current count of drawings
+ */
+ final int getNumDrawings() {
+ return numDrawings;
+ }
+
+ /**
+ * Gets the sp container for the specified drawing number
+ *
+ * @param drawingNum the drawing number for which to return the spContainer
+ * @return the spcontainer
+ */
+ EscherContainer getSpContainer(int drawingNum) {
+ if (!initialized) {
+ initialize();
+ }
+
+ if ((drawingNum + 1) >= spContainers.length) {
+ throw new DrawingDataException();
+ }
+
+ EscherContainer spContainer =
+ (EscherContainer) spContainers[drawingNum + 1];
+
+ Assert.verify(spContainer != null);
+
+ return spContainer;
+ }
+
+ /**
+ * Gets the data which was read in for the drawings
+ *
+ * @return the drawing data
+ */
+ public byte[] getData() {
+ return drawingData;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java
new file mode 100755
index 0000000..8ee2358
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingDataException.java
@@ -0,0 +1,35 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Checked exception thrown when the drawing data is corrupt eg. when
+ * the drawing number exceeds the number of SpContainers. This exception
+ * is handled within the drawing package, and usually causes drawings to be
+ * disabled for the remainder of the workbook
+ */
+public class DrawingDataException extends RuntimeException {
+ private static final String message =
+ "Drawing number exceeds available SpContainers";
+
+ DrawingDataException() {
+ super(message);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java
new file mode 100755
index 0000000..8a91a33
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroup.java
@@ -0,0 +1,534 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.read.biff.Record;
+import jxl.write.biff.File;
+
+/**
+ * This class contains the Excel picture data in Escher format for the
+ * entire workbook
+ */
+public class DrawingGroup implements EscherStream {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(DrawingGroup.class);
+
+ /**
+ * The escher data read in from file
+ */
+ private byte[] drawingData;
+
+ /**
+ * The top level escher container
+ */
+ private EscherContainer escherData;
+
+ /**
+ * The Bstore container, which contains all the drawing data
+ */
+ private BStoreContainer bstoreContainer;
+
+ /**
+ * The initialized flag
+ */
+ private boolean initialized;
+
+ /**
+ * The list of user added drawings
+ */
+ private final ArrayList drawings;
+
+ /**
+ * The number of blips
+ */
+ private int numBlips;
+
+ /**
+ * The number of charts
+ */
+ private int numCharts;
+
+ /**
+ * The number of shape ids used on the second Dgg cluster
+ */
+ private int drawingGroupId;
+
+ /**
+ * Flag which indicates that at least one of the drawings has been omitted
+ * from the worksheet
+ */
+ private boolean drawingsOmitted;
+
+ /**
+ * The origin of this drawing group
+ */
+ private Origin origin;
+
+ /**
+ * A hash map of images keyed on the file path, containing the
+ * reference count
+ */
+ private final HashMap imageFiles;
+
+ /**
+ * A count of the next available object id
+ */
+ private int maxObjectId;
+
+ /**
+ * The maximum shape id so far encountered
+ */
+ private int maxShapeId;
+
+ /**
+ * Constructor
+ *
+ * @param o the origin of this drawing group
+ */
+ public DrawingGroup(Origin o) {
+ origin = o;
+ initialized = o == Origin.WRITE;
+ drawings = new ArrayList();
+ imageFiles = new HashMap();
+ drawingsOmitted = false;
+ maxObjectId = 1;
+ maxShapeId = 1024;
+ }
+
+ /**
+ * Copy constructor
+ * Uses a shallow copy for most things, since as soon as anything
+ * is changed, the drawing group is invalidated and all the data blocks
+ * regenerated
+ *
+ * @param dg the drawing group to copy
+ */
+ public DrawingGroup(DrawingGroup dg) {
+ drawingData = dg.drawingData;
+ escherData = dg.escherData;
+ bstoreContainer = dg.bstoreContainer;
+ initialized = dg.initialized;
+ drawingData = dg.drawingData;
+ escherData = dg.escherData;
+ bstoreContainer = dg.bstoreContainer;
+ numBlips = dg.numBlips;
+ numCharts = dg.numCharts;
+ drawingGroupId = dg.drawingGroupId;
+ drawingsOmitted = dg.drawingsOmitted;
+ origin = dg.origin;
+ imageFiles = (HashMap) dg.imageFiles.clone();
+ maxObjectId = dg.maxObjectId;
+ maxShapeId = dg.maxShapeId;
+
+ // Create this as empty, because all drawings will get added later
+ // as part of the sheet copy process
+ drawings = new ArrayList();
+ }
+
+ /**
+ * /**
+ * Adds in a drawing group record to this drawing group. The binary
+ * data is extracted from the drawing group and added to a single
+ * byte array
+ *
+ * @param mso the drawing group record to add
+ */
+ public void add(MsoDrawingGroupRecord mso) {
+ addData(mso.getData());
+ }
+
+ /**
+ * Adds a continue record to this drawing group. the binary data is
+ * extracted and appended to the byte array
+ *
+ * @param cont the continue record
+ */
+ public void add(Record cont) {
+ addData(cont.getData());
+ }
+
+ /**
+ * Adds the mso record data to the drawing data
+ *
+ * @param msodata the raw mso data
+ */
+ private void addData(byte[] msodata) {
+ if (drawingData == null) {
+ drawingData = new byte[msodata.length];
+ System.arraycopy(msodata, 0, drawingData, 0, msodata.length);
+ return;
+ }
+
+ // Grow the array
+ byte[] newdata = new byte[drawingData.length + msodata.length];
+ System.arraycopy(drawingData, 0, newdata, 0, drawingData.length);
+ System.arraycopy(msodata, 0, newdata, drawingData.length, msodata.length);
+ drawingData = newdata;
+ }
+
+ /**
+ * Adds a drawing to the drawing group
+ *
+ * @param d the drawing to add
+ */
+ final void addDrawing(DrawingGroupObject d) {
+ drawings.add(d);
+ maxObjectId = Math.max(maxObjectId, d.getObjectId());
+ maxShapeId = Math.max(maxShapeId, d.getShapeId());
+ }
+
+ /**
+ * Adds a chart to the drawing group
+ *
+ * @param c the chart
+ */
+ public void add(Chart c) {
+ numCharts++;
+ }
+
+ /**
+ * Adds a drawing from the public, writable interface
+ *
+ * @param d the drawing to add
+ */
+ public void add(DrawingGroupObject d) {
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ BStoreContainer bsc = getBStoreContainer(); // force initialization
+ Dgg dgg = (Dgg) escherData.getChildren()[0];
+ drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1;
+ numBlips = bsc != null ? bsc.getNumBlips() : 0;
+
+ if (bsc != null) {
+ Assert.verify(numBlips == bsc.getNumBlips());
+ }
+ }
+
+ if (!(d instanceof Drawing)) {
+ // Assign a new object id and add it to the list
+ // drawings.add(d);
+ maxObjectId++;
+ maxShapeId++;
+ d.setDrawingGroup(this);
+ d.setObjectId(maxObjectId, numBlips + 1, maxShapeId);
+ if (drawings.size() > maxObjectId) {
+ logger.warn("drawings length " + drawings.size() +
+ " exceeds the max object id " + maxObjectId);
+ }
+ // numBlips++;
+ return;
+ }
+
+ Drawing drawing = (Drawing) d;
+
+ // See if this is referenced elsewhere
+ Drawing refImage =
+ (Drawing) imageFiles.get(d.getImageFilePath());
+
+ if (refImage == null) {
+ // There are no other references to this drawing, so assign
+ // a new object id and put it on the hash map
+ maxObjectId++;
+ maxShapeId++;
+ drawings.add(drawing);
+ drawing.setDrawingGroup(this);
+ drawing.setObjectId(maxObjectId, numBlips + 1, maxShapeId);
+ numBlips++;
+ imageFiles.put(drawing.getImageFilePath(), drawing);
+ } else {
+ // This drawing is used elsewhere in the workbook. Increment the
+ // reference count on the drawing, and set the object id of the drawing
+ // passed in
+ refImage.setReferenceCount(refImage.getReferenceCount() + 1);
+ drawing.setDrawingGroup(this);
+ drawing.setObjectId(refImage.getObjectId(),
+ refImage.getBlipId(),
+ refImage.getShapeId());
+ }
+ }
+
+ /**
+ * Interface method to remove a drawing from the group
+ *
+ * @param d the drawing to remove
+ */
+ public void remove(DrawingGroupObject d) {
+ // Unless there are real images or some such, it is possible that
+ // a BStoreContainer will not be present. In that case simply return
+ if (getBStoreContainer() == null) {
+ return;
+ }
+
+ if (origin == Origin.READ) {
+ origin = Origin.READ_WRITE;
+ numBlips = getBStoreContainer().getNumBlips();
+ Dgg dgg = (Dgg) escherData.getChildren()[0];
+ drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1;
+ }
+
+ // Get the blip
+ EscherRecord[] children = getBStoreContainer().getChildren();
+ BlipStoreEntry bse = (BlipStoreEntry) children[d.getBlipId() - 1];
+
+ bse.dereference();
+
+ if (bse.getReferenceCount() == 0) {
+ // Remove the blip
+ getBStoreContainer().remove(bse);
+
+ // Adjust blipId on the other blips
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject drawing = (DrawingGroupObject) i.next();
+
+ if (drawing.getBlipId() > d.getBlipId()) {
+ drawing.setObjectId(drawing.getObjectId(),
+ drawing.getBlipId() - 1,
+ drawing.getShapeId());
+ }
+ }
+
+ numBlips--;
+ }
+ }
+
+
+ /**
+ * Initializes the drawing data from the escher record read in
+ */
+ private void initialize() {
+ EscherRecordData er = new EscherRecordData(this, 0);
+
+ Assert.verify(er.isContainer());
+
+ escherData = new EscherContainer(er);
+
+ Assert.verify(escherData.getLength() == drawingData.length);
+ Assert.verify(escherData.getType() == EscherRecordType.DGG_CONTAINER);
+
+ initialized = true;
+ }
+
+ /**
+ * Gets hold of the BStore container from the Escher data
+ *
+ * @return the BStore container
+ */
+ private BStoreContainer getBStoreContainer() {
+ if (bstoreContainer == null) {
+ if (!initialized) {
+ initialize();
+ }
+
+ EscherRecord[] children = escherData.getChildren();
+ if (children.length > 1 &&
+ children[1].getType() == EscherRecordType.BSTORE_CONTAINER) {
+ bstoreContainer = (BStoreContainer) children[1];
+ }
+ }
+
+ return bstoreContainer;
+ }
+
+ /**
+ * Gets hold of the binary data
+ *
+ * @return the data
+ */
+ public byte[] getData() {
+ return drawingData;
+ }
+
+ /**
+ * Writes the drawing group to the output file
+ *
+ * @param outputFile the file to write to
+ * @throws IOException
+ */
+ public void write(File outputFile) throws IOException {
+ if (origin == Origin.WRITE) {
+ DggContainer dggContainer = new DggContainer();
+
+ Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips);
+
+ dgg.addCluster(1, 0);
+ dgg.addCluster(numBlips + 1, 0);
+
+ dggContainer.add(dgg);
+
+ int drawingsAdded = 0;
+ BStoreContainer bstoreCont = new BStoreContainer();
+
+ // Create a blip entry for each drawing
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ Object o = i.next();
+ if (o instanceof Drawing) {
+ Drawing d = (Drawing) o;
+ BlipStoreEntry bse = new BlipStoreEntry(d);
+
+ bstoreCont.add(bse);
+ drawingsAdded++;
+ }
+ }
+ if (drawingsAdded > 0) {
+ bstoreCont.setNumBlips(drawingsAdded);
+ dggContainer.add(bstoreCont);
+ }
+
+ Opt opt = new Opt();
+
+ dggContainer.add(opt);
+
+ SplitMenuColors splitMenuColors = new SplitMenuColors();
+ dggContainer.add(splitMenuColors);
+
+ drawingData = dggContainer.getData();
+ } else if (origin == Origin.READ_WRITE) {
+ DggContainer dggContainer = new DggContainer();
+
+ Dgg dgg = new Dgg(numBlips + numCharts + 1, numBlips);
+
+ dgg.addCluster(1, 0);
+ dgg.addCluster(drawingGroupId + numBlips + 1, 0);
+
+ dggContainer.add(dgg);
+
+ BStoreContainer bstoreCont = new BStoreContainer();
+ bstoreCont.setNumBlips(numBlips);
+
+ // Create a blip entry for each drawing that was read in
+ BStoreContainer readBStoreContainer = getBStoreContainer();
+
+ if (readBStoreContainer != null) {
+ EscherRecord[] children = readBStoreContainer.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ BlipStoreEntry bse = (BlipStoreEntry) children[i];
+ bstoreCont.add(bse);
+ }
+ }
+
+ // Create a blip entry for each drawing that has been added
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject dgo = (DrawingGroupObject) i.next();
+ if (dgo instanceof Drawing) {
+ Drawing d = (Drawing) dgo;
+ if (d.getOrigin() == Origin.WRITE) {
+ BlipStoreEntry bse = new BlipStoreEntry(d);
+ bstoreCont.add(bse);
+ }
+ }
+ }
+
+ dggContainer.add(bstoreCont);
+
+ Opt opt = new Opt();
+
+ opt.addProperty(191, false, false, 524296);
+ opt.addProperty(385, false, false, 134217737);
+ opt.addProperty(448, false, false, 134217792);
+
+ dggContainer.add(opt);
+
+ SplitMenuColors splitMenuColors = new SplitMenuColors();
+ dggContainer.add(splitMenuColors);
+
+ drawingData = dggContainer.getData();
+ }
+
+ MsoDrawingGroupRecord msodg = new MsoDrawingGroupRecord(drawingData);
+ outputFile.write(msodg);
+ }
+
+ /**
+ * Accessor for the number of blips in the drawing group
+ *
+ * @return the number of blips
+ */
+ final int getNumberOfBlips() {
+ return numBlips;
+ }
+
+ /**
+ * Gets the drawing data for the given blip id. Called by the Drawing
+ * object
+ *
+ * @param blipId the blipId
+ * @return the drawing data
+ */
+ byte[] getImageData(int blipId) {
+ numBlips = getBStoreContainer().getNumBlips();
+
+ Assert.verify(blipId <= numBlips);
+ Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
+
+ // Get the blip
+ EscherRecord[] children = getBStoreContainer().getChildren();
+ BlipStoreEntry bse = (BlipStoreEntry) children[blipId - 1];
+
+ return bse.getImageData();
+ }
+
+ /**
+ * Indicates that at least one of the drawings has been omitted from
+ * the worksheet
+ *
+ * @param mso the mso record
+ * @param obj the obj record
+ */
+ public void setDrawingsOmitted(MsoDrawingRecord mso, ObjRecord obj) {
+ drawingsOmitted = true;
+
+ if (obj != null) {
+ maxObjectId = Math.max(maxObjectId, obj.getObjectId());
+ }
+ }
+
+ /**
+ * Accessor for the drawingsOmitted flag
+ *
+ * @return TRUE if a drawing has been omitted, FALSE otherwise
+ */
+ public boolean hasDrawingsOmitted() {
+ return drawingsOmitted;
+ }
+
+ /**
+ * Updates this with the appropriate data from the drawing group passed in
+ * This is called during the copy process: this is first initialised as
+ * an empty object, but during the copy, the source DrawingGroup may
+ * change. After the copy process, this method is then called to update
+ * the relevant fields. Unfortunately, the copy process required the
+ * presence of a drawing group
+ *
+ * @param dg the drawing group containing the updated data
+ */
+ public void updateData(DrawingGroup dg) {
+ drawingsOmitted = dg.drawingsOmitted;
+ maxObjectId = dg.maxObjectId;
+ maxShapeId = dg.maxShapeId;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java
new file mode 100755
index 0000000..10ede04
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/DrawingGroupObject.java
@@ -0,0 +1,231 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import jxl.write.biff.File;
+
+/**
+ * Interface for the various object types that can be added to a drawing
+ * group
+ */
+public interface DrawingGroupObject {
+ /**
+ * Sets the object id. Invoked by the drawing group when the object is
+ * added to id
+ *
+ * @param objid the object id
+ * @param bip the blip id
+ * @param sid the shape id
+ */
+ void setObjectId(int objid, int bip, int sid);
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ int getObjectId();
+
+ /**
+ * Accessor for the blip id
+ *
+ * @return the blip id
+ */
+ int getBlipId();
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the shape id
+ */
+ int getShapeId();
+
+
+ /**
+ * Gets the drawing record which was read in
+ *
+ * @return the drawing record
+ */
+ MsoDrawingRecord getMsoDrawingRecord();
+
+ /**
+ * Creates the main Sp container for the drawing
+ *
+ * @return the SP container
+ */
+ EscherContainer getSpContainer();
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ DrawingGroup getDrawingGroup();
+
+ /**
+ * Sets the drawing group for this drawing. Called by the drawing group
+ * when this drawing is added to it
+ *
+ * @param dg the drawing group
+ */
+ void setDrawingGroup(DrawingGroup dg);
+
+ /**
+ * Gets the origin of this drawing
+ *
+ * @return where this drawing came from
+ */
+ Origin getOrigin();
+
+ /**
+ * Accessor for the reference count on this drawing
+ *
+ * @return the reference count
+ */
+ int getReferenceCount();
+
+ /**
+ * Sets the new reference count on the drawing
+ *
+ * @param r the new reference count
+ */
+ void setReferenceCount(int r);
+
+ /**
+ * Accessor for the column of this drawing
+ *
+ * @return the column
+ */
+ double getX();
+
+ /**
+ * Sets the column position of this drawing
+ *
+ * @param x the column
+ */
+ void setX(double x);
+
+ /**
+ * Accessor for the row of this drawing
+ *
+ * @return the row
+ */
+ double getY();
+
+ /**
+ * Accessor for the row of the drawing
+ *
+ * @param y the row
+ */
+ void setY(double y);
+
+ /**
+ * Accessor for the width of this drawing
+ *
+ * @return the number of columns spanned by this image
+ */
+ double getWidth();
+
+ /**
+ * Accessor for the width
+ *
+ * @param w the number of columns to span
+ */
+ void setWidth(double w);
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @return the number of rows spanned by this image
+ */
+ double getHeight();
+
+ /**
+ * Accessor for the height of this drawing
+ *
+ * @param h the number of rows spanned by this image
+ */
+ void setHeight(double h);
+
+
+ /**
+ * Accessor for the type
+ *
+ * @return the type
+ */
+ ShapeType getType();
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ byte[] getImageData();
+
+ /**
+ * Accessor for the image data
+ *
+ * @return the image data
+ */
+ byte[] getImageBytes() throws IOException;
+
+ /**
+ * Accessor for the image file path. Normally this is the absolute path
+ * of a file on the directory system, but if this drawing was constructed
+ * using an byte[] then the blip id is returned
+ *
+ * @return the image file path, or the blip id
+ */
+ String getImageFilePath();
+
+ /**
+ * Writes any other records associated with this drawing group object
+ */
+ void writeAdditionalRecords(File outputFile) throws IOException;
+
+ /**
+ * Writes any records that need to be written after all the drawing group
+ * objects have been written
+ */
+ void writeTailRecords(File outputFile) throws IOException;
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ boolean isFirst();
+
+ /**
+ * Queries whether this object is a form object. Form objects have their
+ * drawings records spread over TXO and CONTINUE records and
+ * require special handling
+ *
+ * @return TRUE if this is a form object, FALSE otherwise
+ */
+ boolean isFormObject();
+
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java
new file mode 100755
index 0000000..611a444
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherAtom.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.common.Logger;
+
+/**
+ * Class for atoms. This may be instantiated as is for unknown/uncared about
+ * atoms, or subclassed if we have some semantic interest in the contents
+ */
+class EscherAtom extends EscherRecord {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(EscherAtom.class);
+
+ /**
+ * Constructor
+ *
+ * @param erd the escher record data
+ */
+ public EscherAtom(EscherRecordData erd) {
+ super(erd);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param type the type
+ */
+ protected EscherAtom(EscherRecordType type) {
+ super(type);
+ }
+
+ /**
+ * Gets the data for writing
+ *
+ * @return the data
+ */
+ byte[] getData() {
+ logger.warn("escher atom getData called on object of type " +
+ getClass().getName() + " code " +
+ Integer.toString(getType().getValue(), 16));
+ return null;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java
new file mode 100755
index 0000000..2f9a9cc
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherContainer.java
@@ -0,0 +1,177 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.common.Logger;
+
+/**
+ * An escher container. This record may contain other escher containers or
+ * atoms
+ */
+class EscherContainer extends EscherRecord {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(EscherContainer.class);
+
+ /**
+ * Initialized flag
+ */
+ private boolean initialized;
+
+
+ /**
+ * The children of this container
+ */
+ private final ArrayList children;
+
+ /**
+ * Constructor
+ *
+ * @param erd the raw data
+ */
+ public EscherContainer(EscherRecordData erd) {
+ super(erd);
+ initialized = false;
+ children = new ArrayList();
+ }
+
+ /**
+ * Constructor used when writing out escher data
+ *
+ * @param type the type
+ */
+ protected EscherContainer(EscherRecordType type) {
+ super(type);
+ setContainer(true);
+ children = new ArrayList();
+ }
+
+ /**
+ * Accessor for the children of this container
+ *
+ * @return the children
+ */
+ public EscherRecord[] getChildren() {
+ if (!initialized) {
+ initialize();
+ }
+
+ Object[] ca = children.toArray(new EscherRecord[children.size()]);
+
+ return (EscherRecord[]) ca;
+ }
+
+ /**
+ * Adds a child to this container
+ *
+ * @param child the item to add
+ */
+ public void add(EscherRecord child) {
+ children.add(child);
+ }
+
+ /**
+ * Removes a child from this container
+ *
+ * @param child the item to remove
+ */
+ public void remove(EscherRecord child) {
+ boolean result = children.remove(child);
+ }
+
+ /**
+ * Initialization
+ */
+ private void initialize() {
+ int curpos = getPos() + HEADER_LENGTH;
+ int endpos = Math.min(getPos() + getLength(), getStreamLength());
+
+ EscherRecord newRecord = null;
+
+ while (curpos < endpos) {
+ EscherRecordData erd = new EscherRecordData(getEscherStream(), curpos);
+
+ EscherRecordType type = erd.getType();
+ if (type == EscherRecordType.DGG) {
+ newRecord = new Dgg(erd);
+ } else if (type == EscherRecordType.DG) {
+ newRecord = new Dg(erd);
+ } else if (type == EscherRecordType.BSTORE_CONTAINER) {
+ newRecord = new BStoreContainer(erd);
+ } else if (type == EscherRecordType.SPGR_CONTAINER) {
+ newRecord = new SpgrContainer(erd);
+ } else if (type == EscherRecordType.SP_CONTAINER) {
+ newRecord = new SpContainer(erd);
+ } else if (type == EscherRecordType.SPGR) {
+ newRecord = new Spgr(erd);
+ } else if (type == EscherRecordType.SP) {
+ newRecord = new Sp(erd);
+ } else if (type == EscherRecordType.CLIENT_ANCHOR) {
+ newRecord = new ClientAnchor(erd);
+ } else if (type == EscherRecordType.CLIENT_DATA) {
+ newRecord = new ClientData(erd);
+ } else if (type == EscherRecordType.BSE) {
+ newRecord = new BlipStoreEntry(erd);
+ } else if (type == EscherRecordType.OPT) {
+ newRecord = new Opt(erd);
+ } else if (type == EscherRecordType.SPLIT_MENU_COLORS) {
+ newRecord = new SplitMenuColors(erd);
+ } else if (type == EscherRecordType.CLIENT_TEXT_BOX) {
+ newRecord = new ClientTextBox(erd);
+ } else {
+ newRecord = new EscherAtom(erd);
+ }
+
+ children.add(newRecord);
+ curpos += newRecord.getLength();
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Gets the data for this container (and all of its children recursively
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ if (!initialized) {
+ initialize();
+ }
+
+ byte[] data = new byte[0];
+ for (Iterator i = children.iterator(); i.hasNext(); ) {
+ EscherRecord er = (EscherRecord) i.next();
+ byte[] childData = er.getData();
+
+ if (childData != null) {
+ byte[] newData = new byte[data.length + childData.length];
+ System.arraycopy(data, 0, newData, 0, data.length);
+ System.arraycopy(childData, 0, newData, data.length, childData.length);
+ data = newData;
+ }
+ }
+
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java
new file mode 100755
index 0000000..dfcb7dd
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherDisplay.java
@@ -0,0 +1,189 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * Class used to display a complete hierarchically organized Escher stream
+ * The whole thing is dumped to System.out
+ *
+ * This class is only used as a debugging tool
+ */
+public class EscherDisplay {
+ /**
+ * The escher stream
+ */
+ private final EscherStream stream;
+
+ /**
+ * The writer
+ */
+ private final BufferedWriter writer;
+
+ /**
+ * Constructor
+ *
+ * @param s the stream
+ * @param bw the writer
+ */
+ public EscherDisplay(EscherStream s, BufferedWriter bw) {
+ stream = s;
+ writer = bw;
+ }
+
+ /**
+ * Display the formatted escher stream
+ *
+ * @throws IOException
+ */
+ public void display() throws IOException {
+ EscherRecordData er = new EscherRecordData(stream, 0);
+ EscherContainer ec = new EscherContainer(er);
+ displayContainer(ec, 0);
+ }
+
+ /**
+ * Displays the escher container as text
+ *
+ * @param ec the escher container
+ * @param level the indent level
+ * @throws IOException
+ */
+ private void displayContainer(EscherContainer ec, int level)
+ throws IOException {
+ displayRecord(ec, level);
+
+ // Display the contents of the container
+ level++;
+
+ EscherRecord[] children = ec.getChildren();
+
+ for (int i = 0; i < children.length; i++) {
+ EscherRecord er = children[i];
+ if (er.getEscherData().isContainer()) {
+ displayContainer((EscherContainer) er, level);
+ } else {
+ displayRecord(er, level);
+ }
+ }
+ }
+
+ /**
+ * Displays an escher record
+ *
+ * @param er the record to display
+ * @param level the amount of indentation
+ * @throws IOException
+ */
+ private void displayRecord(EscherRecord er, int level)
+ throws IOException {
+ indent(level);
+
+ EscherRecordType type = er.getType();
+
+ // Display the code
+ writer.write(Integer.toString(type.getValue(), 16));
+ writer.write(" - ");
+
+ // Display the name
+ if (type == EscherRecordType.DGG_CONTAINER) {
+ writer.write("Dgg Container");
+ writer.newLine();
+ } else if (type == EscherRecordType.BSTORE_CONTAINER) {
+ writer.write("BStore Container");
+ writer.newLine();
+ } else if (type == EscherRecordType.DG_CONTAINER) {
+ writer.write("Dg Container");
+ writer.newLine();
+ } else if (type == EscherRecordType.SPGR_CONTAINER) {
+ writer.write("Spgr Container");
+ writer.newLine();
+ } else if (type == EscherRecordType.SP_CONTAINER) {
+ writer.write("Sp Container");
+ writer.newLine();
+ } else if (type == EscherRecordType.DGG) {
+ writer.write("Dgg");
+ writer.newLine();
+ } else if (type == EscherRecordType.BSE) {
+ writer.write("Bse");
+ writer.newLine();
+ } else if (type == EscherRecordType.DG) {
+ Dg dg = new Dg(er.getEscherData());
+ writer.write("Dg: drawing id " + dg.getDrawingId() +
+ " shape count " + dg.getShapeCount());
+ writer.newLine();
+ } else if (type == EscherRecordType.SPGR) {
+ writer.write("Spgr");
+ writer.newLine();
+ } else if (type == EscherRecordType.SP) {
+ Sp sp = new Sp(er.getEscherData());
+ writer.write("Sp: shape id " + sp.getShapeId() +
+ " shape type " + sp.getShapeType());
+ writer.newLine();
+ } else if (type == EscherRecordType.OPT) {
+ Opt opt = new Opt(er.getEscherData());
+ Opt.Property p260 = opt.getProperty(260);
+ Opt.Property p261 = opt.getProperty(261);
+ writer.write("Opt (value, stringValue): ");
+ if (p260 != null) {
+ writer.write("260: " +
+ p260.value + ", " +
+ p260.stringValue +
+ ";");
+ }
+ if (p261 != null) {
+ writer.write("261: " +
+ p261.value + ", " +
+ p261.stringValue +
+ ";");
+ }
+ writer.newLine();
+ } else if (type == EscherRecordType.CLIENT_ANCHOR) {
+ writer.write("Client Anchor");
+ writer.newLine();
+ } else if (type == EscherRecordType.CLIENT_DATA) {
+ writer.write("Client Data");
+ writer.newLine();
+ } else if (type == EscherRecordType.CLIENT_TEXT_BOX) {
+ writer.write("Client Text Box");
+ writer.newLine();
+ } else if (type == EscherRecordType.SPLIT_MENU_COLORS) {
+ writer.write("Split Menu Colors");
+ writer.newLine();
+ } else {
+ writer.write("???");
+ writer.newLine();
+ }
+ }
+
+ /**
+ * Indents to the amount specified by the level
+ *
+ * @param level the level
+ * @throws IOException
+ */
+ private void indent(int level) throws IOException {
+ for (int i = 0; i < level * 2; i++) {
+ writer.write(' ');
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java
new file mode 100755
index 0000000..8961dec
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecord.java
@@ -0,0 +1,179 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.common.Logger;
+
+/**
+ * The base class for all escher records. This class contains
+ * the jxl.common.header data and is basically a wrapper for the EscherRecordData
+ * object
+ */
+abstract class EscherRecord {
+ /**
+ * The length of the escher header on all records
+ */
+ protected static final int HEADER_LENGTH = 8;
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(EscherRecord.class);
+ //protected EscherRecordData data;
+ /**
+ * The escher data
+ */
+ private final EscherRecordData data;
+
+ /**
+ * Constructor
+ *
+ * @param erd the data
+ */
+ protected EscherRecord(EscherRecordData erd) {
+ data = erd;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param type the type
+ */
+ protected EscherRecord(EscherRecordType type) {
+ data = new EscherRecordData(type);
+ }
+
+ /**
+ * Identifies whether this item is a container
+ *
+ * @param cont TRUE if this is a container, FALSE otherwise
+ */
+ protected void setContainer(boolean cont) {
+ data.setContainer(cont);
+ }
+
+ /**
+ * Gets the entire length of the record, including the header
+ *
+ * @return the length of the record, including the header data
+ */
+ public int getLength() {
+ return data.getLength() + HEADER_LENGTH;
+ }
+
+ /**
+ * Accessor for the escher stream
+ *
+ * @return the escher stream
+ */
+ protected final EscherStream getEscherStream() {
+ return data.getEscherStream();
+ }
+
+ /**
+ * The position of this escher record in the stream
+ *
+ * @return the position
+ */
+ protected final int getPos() {
+ return data.getPos();
+ }
+
+ /**
+ * Accessor for the instance
+ *
+ * @return the instance
+ */
+ protected final int getInstance() {
+ return data.getInstance();
+ }
+
+ /**
+ * Sets the instance number when writing out the escher data
+ *
+ * @param i the instance
+ */
+ protected final void setInstance(int i) {
+ data.setInstance(i);
+ }
+
+ /**
+ * Sets the version when writing out the escher data
+ *
+ * @param v the version
+ */
+ protected final void setVersion(int v) {
+ data.setVersion(v);
+ }
+
+ /**
+ * Accessor for the escher type
+ *
+ * @return the type
+ */
+ public EscherRecordType getType() {
+ return data.getType();
+ }
+
+ /**
+ * Abstract method used to retrieve the generated escher data when writing
+ * out image information
+ *
+ * @return the escher data
+ */
+ abstract byte[] getData();
+
+ /**
+ * Prepends the standard header data to the first eight bytes of the array
+ * and returns it
+ *
+ * @param d the data
+ * @return the binary data
+ */
+ final byte[] setHeaderData(byte[] d) {
+ return data.setHeaderData(d);
+ }
+
+ /**
+ * Gets the data that was read in, excluding the header data
+ *
+ * @return the bytes read in, excluding the header data
+ */
+ byte[] getBytes() {
+ return data.getBytes();
+ }
+
+ /**
+ * Accessor for the stream length
+ *
+ * @return the stream length
+ */
+ protected int getStreamLength() {
+ return data.getStreamLength();
+ }
+
+ /**
+ * Used by the EscherDisplay class to retrieve the data
+ *
+ * @return the data
+ */
+ protected EscherRecordData getEscherData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java
new file mode 100755
index 0000000..cf9adcb
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordData.java
@@ -0,0 +1,285 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+
+/**
+ * A single record from an Escher stream. Basically this a container for
+ * the header data for each Escher record
+ */
+final class EscherRecordData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(EscherRecordData.class);
+
+ /**
+ * The byte position of this record in the escher stream
+ */
+ private int pos;
+
+ /**
+ * The instance value
+ */
+ private int instance;
+
+ /**
+ * The version value
+ */
+ private int version;
+
+ /**
+ * The record id
+ */
+ private final int recordId;
+
+ /**
+ * The length of the record, excluding the 8 byte header
+ */
+ private int length;
+
+ /**
+ * The length of the stream
+ */
+ private int streamLength;
+
+ /**
+ * Indicates whether this record is a container
+ */
+ private boolean container;
+
+ /**
+ * The type of this record
+ */
+ private EscherRecordType type;
+
+ /**
+ * A handle back to the drawing group, which contains the entire escher
+ * stream byte data
+ */
+ private EscherStream escherStream;
+
+ /**
+ * Constructor
+ *
+ * @param dg the escher stream data
+ * @param p the current position in the stream
+ */
+ public EscherRecordData(EscherStream dg, int p) {
+ escherStream = dg;
+ pos = p;
+ byte[] data = escherStream.getData();
+
+ streamLength = data.length;
+
+ // First two bytes contain instance and version
+ int value = IntegerHelper.getInt(data[pos], data[pos + 1]);
+
+ // Instance value is the first 12 bits
+ instance = (value & 0xfff0) >> 4;
+
+ // Version is the last four bits
+ version = value & 0xf;
+
+ // Bytes 2 and 3 are the record id
+ recordId = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+
+ // Length is bytes 4,5,6 and 7
+ length = IntegerHelper.getInt(data[pos + 4], data[pos + 5],
+ data[pos + 6], data[pos + 7]);
+
+ container = version == 0x0f;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param t the type of the escher record
+ */
+ public EscherRecordData(EscherRecordType t) {
+ type = t;
+ recordId = type.getValue();
+ }
+
+ /**
+ * Determines whether this record is a container
+ *
+ * @return TRUE if this is a container, FALSE otherwise
+ */
+ public boolean isContainer() {
+ return container;
+ }
+
+ /**
+ * Sets whether or not this is a container - called when writing
+ * out an escher stream
+ *
+ * @param c TRUE if this is a container, FALSE otherwise
+ */
+ void setContainer(boolean c) {
+ container = c;
+ }
+
+ /**
+ * Accessor for the length, excluding the 8 byte header
+ *
+ * @return the length excluding the 8 byte header
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Called when writing to set the length of this record
+ *
+ * @param l the length
+ */
+ void setLength(int l) {
+ length = l;
+ }
+
+ /**
+ * Accessor for the record id
+ *
+ * @return the record id
+ */
+ public int getRecordId() {
+ return recordId;
+ }
+
+ /**
+ * Accessor for the drawing group stream
+ *
+ * @return the drawing group stream
+ */
+ EscherStream getDrawingGroup() {
+ return escherStream;
+ }
+
+ /**
+ * Gets the position in the stream
+ *
+ * @return the position in the stream
+ */
+ int getPos() {
+ return pos;
+ }
+
+ /**
+ * Gets the escher type of this record
+ *
+ * @return the escher type
+ */
+ EscherRecordType getType() {
+ if (type == null) {
+ type = EscherRecordType.getType(recordId);
+ }
+
+ return type;
+ }
+
+ /**
+ * Gets the instance value
+ *
+ * @return the instance value
+ */
+ int getInstance() {
+ return instance;
+ }
+
+ /**
+ * Called from the subclass when writing to set the instance value
+ *
+ * @param inst the instance
+ */
+ void setInstance(int inst) {
+ instance = inst;
+ }
+
+ /**
+ * Called when writing to set the version of this record
+ *
+ * @param v the version
+ */
+ void setVersion(int v) {
+ version = v;
+ }
+
+ /**
+ * Adds the 8 byte header data on the value data passed in, returning
+ * the modified data
+ *
+ * @param d the value data
+ * @return the value data with the header information
+ */
+ byte[] setHeaderData(byte[] d) {
+ byte[] data = new byte[d.length + 8];
+ System.arraycopy(d, 0, data, 8, d.length);
+
+ if (container) {
+ version = 0x0f;
+ }
+
+ // First two bytes contain instance and version
+ int value = instance << 4;
+ value |= version;
+ IntegerHelper.getTwoBytes(value, data, 0);
+
+ // Bytes 2 and 3 are the record id
+ IntegerHelper.getTwoBytes(recordId, data, 2);
+
+ // Length is bytes 4,5,6 and 7
+ IntegerHelper.getFourBytes(d.length, data, 4);
+
+ return data;
+ }
+
+ /**
+ * Accessor for the header stream
+ *
+ * @return the escher stream
+ */
+ EscherStream getEscherStream() {
+ return escherStream;
+ }
+
+ /**
+ * Gets the data that was read in, excluding the header data
+ *
+ * @return the value data that was read in
+ */
+ byte[] getBytes() {
+ byte[] d = new byte[length];
+ System.arraycopy(escherStream.getData(), pos + 8, d, 0, length);
+ return d;
+ }
+
+ /**
+ * Accessor for the stream length
+ *
+ * @return the stream length
+ */
+ int getStreamLength() {
+ return streamLength;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java
new file mode 100755
index 0000000..d230edd
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherRecordType.java
@@ -0,0 +1,101 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Enumeration class for Escher record types
+ */
+final class EscherRecordType {
+ public static final EscherRecordType UNKNOWN = new EscherRecordType(0x0);
+ public static final EscherRecordType DGG_CONTAINER =
+ new EscherRecordType(0xf000);
+ public static final EscherRecordType BSTORE_CONTAINER =
+ new EscherRecordType(0xf001);
+ public static final EscherRecordType DG_CONTAINER =
+ new EscherRecordType(0xf002);
+ public static final EscherRecordType SPGR_CONTAINER =
+ new EscherRecordType(0xf003);
+ public static final EscherRecordType SP_CONTAINER =
+ new EscherRecordType(0xf004);
+ public static final EscherRecordType DGG = new EscherRecordType(0xf006);
+ public static final EscherRecordType BSE = new EscherRecordType(0xf007);
+ public static final EscherRecordType DG = new EscherRecordType(0xf008);
+ public static final EscherRecordType SPGR = new EscherRecordType(0xf009);
+ public static final EscherRecordType SP = new EscherRecordType(0xf00a);
+ public static final EscherRecordType OPT = new EscherRecordType(0xf00b);
+ public static final EscherRecordType CLIENT_ANCHOR =
+ new EscherRecordType(0xf010);
+ public static final EscherRecordType CLIENT_DATA =
+ new EscherRecordType(0xf011);
+ public static final EscherRecordType CLIENT_TEXT_BOX =
+ new EscherRecordType(0xf00d);
+ public static final EscherRecordType SPLIT_MENU_COLORS =
+ new EscherRecordType(0xf11e);
+ /**
+ * All escher types
+ */
+ private static EscherRecordType[] types = new EscherRecordType[0];
+ /**
+ * The code of the item within the escher stream
+ */
+ private final int value;
+ /**
+ * Constructor
+ *
+ * @param val the escher record value
+ */
+ private EscherRecordType(int val) {
+ value = val;
+
+ EscherRecordType[] newtypes = new EscherRecordType[types.length + 1];
+ System.arraycopy(types, 0, newtypes, 0, types.length);
+ newtypes[types.length] = this;
+ types = newtypes;
+ }
+
+ /**
+ * Accessor to get the item from a particular value
+ *
+ * @param val the escher record value
+ * @return the type corresponding to val, or UNKNOWN if a match could not
+ * be found
+ */
+ public static EscherRecordType getType(int val) {
+ EscherRecordType type = UNKNOWN;
+
+ for (int i = 0; i < types.length; i++) {
+ if (val == types[i].value) {
+ type = types[i];
+ break;
+ }
+ }
+
+ return type;
+ }
+
+ /**
+ * Accessor for the escher record value
+ *
+ * @return the escher record value
+ */
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java
new file mode 100755
index 0000000..6e7a13e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/EscherStream.java
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Interface implemented by records which contain escher byte streams
+ */
+interface EscherStream {
+ /**
+ * Method to access the escher data
+ *
+ * @return the escher data
+ */
+ byte[] getData();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java
new file mode 100755
index 0000000..06835a1
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingGroupRecord.java
@@ -0,0 +1,68 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2001 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.read.biff.Record;
+
+/**
+ * A record which merely holds the MSODRAWINGGROUP data. Used when copying
+ * files which contain images
+ */
+public class MsoDrawingGroupRecord extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructs this object from the raw data
+ *
+ * @param t the raw data
+ */
+ public MsoDrawingGroupRecord(Record t) {
+ super(t);
+ data = t.getData();
+ }
+
+ /**
+ * Constructor
+ *
+ * @param d the data
+ */
+ MsoDrawingGroupRecord(byte[] d) {
+ super(Type.MSODRAWINGGROUP);
+ data = d;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java
new file mode 100755
index 0000000..8fc1390
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/MsoDrawingRecord.java
@@ -0,0 +1,108 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2001 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Logger;
+import jxl.read.biff.Record;
+
+/**
+ * A record which merely holds the MSODRAWING data. Used when copying files
+ * which contain images
+ */
+public class MsoDrawingRecord extends WritableRecordData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(MsoDrawingRecord.class);
+
+ /**
+ * Flag to indicate whether this is the first drawing on the sheet
+ * - needed for copying
+ */
+ private boolean first;
+ /**
+ * The raw drawing data which was read in
+ */
+ private final byte[] data;
+
+ /**
+ * Constructs this object from the raw data
+ *
+ * @param t the raw data
+ */
+ public MsoDrawingRecord(Record t) {
+ super(t);
+ data = getRecord().getData();
+ first = false;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param d the drawing data
+ */
+ public MsoDrawingRecord(byte[] d) {
+ super(Type.MSODRAWING);
+ data = d;
+ first = false;
+ }
+
+ /**
+ * Expose the protected function
+ *
+ * @return the raw record data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public Record getRecord() {
+ return super.getRecord();
+ }
+
+ /**
+ * Sets the flag to indicate that this is the first drawing on the sheet
+ */
+ public void setFirst() {
+ first = true;
+ }
+
+ /**
+ * Accessor for the first drawing on the sheet. This is used when
+ * copying unmodified sheets to indicate that this drawing contains
+ * the first time Escher gubbins
+ *
+ * @return TRUE if this MSORecord is the first drawing on the sheet
+ */
+ public boolean isFirst() {
+ return first;
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java
new file mode 100755
index 0000000..6faff5d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/NoteRecord.java
@@ -0,0 +1,157 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Logger;
+import jxl.read.biff.Record;
+
+/**
+ * A Note (TXO) record which contains the information for comments
+ */
+public class NoteRecord extends WritableRecordData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(NoteRecord.class);
+
+ /**
+ * The raw drawing data which was read in
+ */
+ private byte[] data;
+
+ /**
+ * The row
+ */
+ private int row;
+
+ /**
+ * The column
+ */
+ private int column;
+
+ /**
+ * The object id
+ */
+ private int objectId;
+
+ /**
+ * Constructs this object from the raw data
+ *
+ * @param t the raw data
+ */
+ public NoteRecord(Record t) {
+ super(t);
+ data = getRecord().getData();
+ row = IntegerHelper.getInt(data[0], data[1]);
+ column = IntegerHelper.getInt(data[2], data[3]);
+ objectId = IntegerHelper.getInt(data[6], data[7]);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param d the drawing data
+ */
+ public NoteRecord(byte[] d) {
+ super(Type.NOTE);
+ data = d;
+ }
+
+ /**
+ * Constructor used when writing a Note
+ *
+ * @param c the column
+ * @param r the row
+ * @param id the object id
+ */
+ public NoteRecord(int c, int r, int id) {
+ super(Type.NOTE);
+ row = r;
+ column = c;
+ objectId = id;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public byte[] getData() {
+ if (data != null) {
+ return data;
+ }
+
+ String author = "";
+ data = new byte[8 + author.length() + 4];
+
+ // the row
+ IntegerHelper.getTwoBytes(row, data, 0);
+
+ // the column
+ IntegerHelper.getTwoBytes(column, data, 2);
+
+ // the object id
+ IntegerHelper.getTwoBytes(objectId, data, 6);
+
+ // the length of the string
+ IntegerHelper.getTwoBytes(author.length(), data, 8);
+
+ // the string
+ // StringHelper.getBytes(author, data, 11);
+
+ // data[data.length-1]=(byte)0x24;
+
+ return data;
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row
+ */
+ int getRow() {
+ return row;
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column
+ */
+ int getColumn() {
+ return column;
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return the object id
+ */
+ public int getObjectId() {
+ return objectId;
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java
new file mode 100755
index 0000000..9b4168d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ObjRecord.java
@@ -0,0 +1,382 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2001 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.read.biff.Record;
+
+/**
+ * A record which merely holds the OBJ data. Used when copying files which
+ * contain images
+ */
+public class ObjRecord extends WritableRecordData {
+ // The object types
+ public static final ObjType GROUP = new ObjType(0x0, "Group");
+ public static final ObjType LINE = new ObjType(0x01, "Line");
+ public static final ObjType RECTANGLE = new ObjType(0x02, "Rectangle");
+ public static final ObjType OVAL = new ObjType(0x03, "Oval");
+ public static final ObjType ARC = new ObjType(0x04, "Arc");
+ public static final ObjType CHART = new ObjType(0x05, "Chart");
+ public static final ObjType TEXT = new ObjType(0x06, "Text");
+ public static final ObjType BUTTON = new ObjType(0x07, "Button");
+ public static final ObjType PICTURE = new ObjType(0x08, "Picture");
+ public static final ObjType POLYGON = new ObjType(0x09, "Polygon");
+ public static final ObjType CHECKBOX = new ObjType(0x0b, "Checkbox");
+ public static final ObjType OPTION = new ObjType(0x0c, "Option");
+ public static final ObjType EDITBOX = new ObjType(0x0d, "Edit Box");
+ public static final ObjType LABEL = new ObjType(0x0e, "Label");
+ public static final ObjType DIALOGUEBOX = new ObjType(0x0f, "Dialogue Box");
+ public static final ObjType SPINBOX = new ObjType(0x10, "Spin Box");
+ public static final ObjType SCROLLBAR = new ObjType(0x11, "Scrollbar");
+ public static final ObjType LISTBOX = new ObjType(0x12, "List Box");
+ public static final ObjType GROUPBOX = new ObjType(0x13, "Group Box");
+ public static final ObjType COMBOBOX = new ObjType(0x14, "Combo Box");
+ public static final ObjType MSOFFICEDRAWING = new ObjType
+ (0x1e, "MS Office Drawing");
+ public static final ObjType FORMCONTROL =
+ new ObjType(0x14, "Form Combo Box");
+ public static final ObjType EXCELNOTE =
+ new ObjType(0x19, "Excel Note");
+ public static final ObjType UNKNOWN = new ObjType(0xff, "Unknown");
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ObjRecord.class);
+ // Field sub records
+ private static final int COMMON_DATA_LENGTH = 22;
+ private static final int CLIPBOARD_FORMAT_LENGTH = 6;
+ private static final int PICTURE_OPTION_LENGTH = 6;
+ private static final int NOTE_STRUCTURE_LENGTH = 26;
+ private static final int COMBOBOX_STRUCTURE_LENGTH = 44;
+ private static final int END_LENGTH = 4;
+ /**
+ * The object type
+ */
+ private final ObjType type;
+ /**
+ * Indicates whether this record was read in
+ */
+ private boolean read;
+ /**
+ * The object id
+ */
+ private final int objectId;
+ /**
+ * Constructs this object from the raw data
+ *
+ * @param t the raw data
+ */
+ public ObjRecord(Record t) {
+ super(t);
+ byte[] data = t.getData();
+ int objtype = IntegerHelper.getInt(data[4], data[5]);
+ read = true;
+ type = ObjType.getType(objtype);
+
+ if (type == UNKNOWN) {
+ logger.warn("unknown object type code " + objtype);
+ }
+
+ objectId = IntegerHelper.getInt(data[6], data[7]);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param objId the object id
+ * @param t the object type
+ */
+ ObjRecord(int objId, ObjType t) {
+ super(Type.OBJ);
+ objectId = objId;
+ type = t;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public byte[] getData() {
+ if (read) {
+ return getRecord().getData();
+ }
+
+ if (type == PICTURE || type == CHART) {
+ return getPictureData();
+ } else if (type == EXCELNOTE) {
+ return getNoteData();
+ } else if (type == COMBOBOX) {
+ return getComboBoxData();
+ } else {
+ Assert.verify(false);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the ObjRecord subrecords for a picture
+ *
+ * @return the binary data for the picture
+ */
+ private byte[] getPictureData() {
+ int dataLength = COMMON_DATA_LENGTH +
+ CLIPBOARD_FORMAT_LENGTH +
+ PICTURE_OPTION_LENGTH +
+ END_LENGTH;
+ int pos = 0;
+ byte[] data = new byte[dataLength];
+
+ // The jxl.common.data
+ // record id
+ IntegerHelper.getTwoBytes(0x15, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2);
+
+ // object type
+ IntegerHelper.getTwoBytes(type.value, data, pos + 4);
+
+ // object id
+ IntegerHelper.getTwoBytes(objectId, data, pos + 6);
+
+ // the options
+ IntegerHelper.getTwoBytes(0x6011, data, pos + 8);
+ pos += COMMON_DATA_LENGTH;
+
+ // The clipboard format
+ // record id
+ IntegerHelper.getTwoBytes(0x7, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(CLIPBOARD_FORMAT_LENGTH - 4, data, pos + 2);
+
+ // the data
+ IntegerHelper.getTwoBytes(0xffff, data, pos + 4);
+ pos += CLIPBOARD_FORMAT_LENGTH;
+
+ // Picture option flags
+ // record id
+ IntegerHelper.getTwoBytes(0x8, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(PICTURE_OPTION_LENGTH - 4, data, pos + 2);
+
+ // the data
+ IntegerHelper.getTwoBytes(0x1, data, pos + 4);
+ pos += CLIPBOARD_FORMAT_LENGTH;
+
+ // End record id
+ IntegerHelper.getTwoBytes(0x0, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2);
+
+ // the data
+ pos += END_LENGTH;
+
+ return data;
+ }
+
+ /**
+ * Gets the ObjRecord subrecords for a note
+ *
+ * @return the note data
+ */
+ private byte[] getNoteData() {
+ int dataLength = COMMON_DATA_LENGTH +
+ NOTE_STRUCTURE_LENGTH +
+ END_LENGTH;
+ int pos = 0;
+ byte[] data = new byte[dataLength];
+
+ // The jxl.common.data
+ // record id
+ IntegerHelper.getTwoBytes(0x15, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2);
+
+ // object type
+ IntegerHelper.getTwoBytes(type.value, data, pos + 4);
+
+ // object id
+ IntegerHelper.getTwoBytes(objectId, data, pos + 6);
+
+ // the options
+ IntegerHelper.getTwoBytes(0x4011, data, pos + 8);
+ pos += COMMON_DATA_LENGTH;
+
+ // The note structure
+ // record id
+ IntegerHelper.getTwoBytes(0xd, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(NOTE_STRUCTURE_LENGTH - 4, data, pos + 2);
+
+ // the data
+ pos += NOTE_STRUCTURE_LENGTH;
+
+ // End
+ // record id
+ IntegerHelper.getTwoBytes(0x0, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2);
+
+ // the data
+ pos += END_LENGTH;
+
+ return data;
+ }
+
+ /**
+ * Gets the ObjRecord subrecords for a combo box
+ *
+ * @return returns the binary data for a combo box
+ */
+ private byte[] getComboBoxData() {
+ int dataLength = COMMON_DATA_LENGTH +
+ COMBOBOX_STRUCTURE_LENGTH +
+ END_LENGTH;
+ int pos = 0;
+ byte[] data = new byte[dataLength];
+
+ // The jxl.common.data
+ // record id
+ IntegerHelper.getTwoBytes(0x15, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(COMMON_DATA_LENGTH - 4, data, pos + 2);
+
+ // object type
+ IntegerHelper.getTwoBytes(type.value, data, pos + 4);
+
+ // object id
+ IntegerHelper.getTwoBytes(objectId, data, pos + 6);
+
+ // the options
+ IntegerHelper.getTwoBytes(0x0, data, pos + 8);
+ pos += COMMON_DATA_LENGTH;
+
+ // The combo box structure
+ // record id
+ IntegerHelper.getTwoBytes(0xc, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(0x14, data, pos + 2);
+
+ // the data
+ data[pos + 14] = 0x01;
+ data[pos + 16] = 0x04;
+ data[pos + 20] = 0x10;
+ data[pos + 24] = 0x13;
+ data[pos + 26] = (byte) 0xee;
+ data[pos + 27] = 0x1f;
+ data[pos + 30] = 0x04;
+ data[pos + 34] = 0x01;
+ data[pos + 35] = 0x06;
+ data[pos + 38] = 0x02;
+ data[pos + 40] = 0x08;
+ data[pos + 42] = 0x40;
+
+ pos += COMBOBOX_STRUCTURE_LENGTH;
+
+ // End
+ // record id
+ IntegerHelper.getTwoBytes(0x0, data, pos);
+
+ // record length
+ IntegerHelper.getTwoBytes(END_LENGTH - 4, data, pos + 2);
+
+ // the data
+ pos += END_LENGTH;
+
+ return data;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public Record getRecord() {
+ return super.getRecord();
+ }
+
+ /**
+ * Accessor for the object type
+ *
+ * @return the object type
+ */
+ public ObjType getType() {
+ return type;
+ }
+
+ /**
+ * Accessor for the object id
+ *
+ * @return accessor for the object id
+ */
+ public int getObjectId() {
+ return objectId;
+ }
+
+ /**
+ * Object type enumeration
+ */
+ private static final class ObjType {
+ private static ObjType[] types = new ObjType[0];
+ public int value;
+ public String desc;
+
+ ObjType(int v, String d) {
+ value = v;
+ desc = d;
+
+ ObjType[] oldtypes = types;
+ types = new ObjType[types.length + 1];
+ System.arraycopy(oldtypes, 0, types, 0, oldtypes.length);
+ types[oldtypes.length] = this;
+ }
+
+ public static ObjType getType(int val) {
+ ObjType retval = UNKNOWN;
+ for (int i = 0; i < types.length && retval == UNKNOWN; i++) {
+ if (types[i].value == val) {
+ retval = types[i];
+ }
+ }
+ return retval;
+ }
+
+ public String toString() {
+ return desc;
+ }
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java
new file mode 100755
index 0000000..da22f30
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Opt.java
@@ -0,0 +1,236 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.common.Logger;
+
+/**
+ * An options record in the escher stream
+ */
+class Opt extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Opt.class);
+
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * The number of properties
+ */
+ private int numProperties;
+
+ /**
+ * The list of properties
+ */
+ private ArrayList properties;
+
+ /**
+ * Constructor
+ *
+ * @param erd the escher record data
+ */
+ public Opt(EscherRecordData erd) {
+ super(erd);
+ numProperties = getInstance();
+ readProperties();
+ }
+
+ /**
+ * Constructor
+ */
+ public Opt() {
+ super(EscherRecordType.OPT);
+ properties = new ArrayList();
+ setVersion(3);
+ }
+
+ /**
+ * Reads the properties
+ */
+ private void readProperties() {
+ properties = new ArrayList();
+ int pos = 0;
+ byte[] bytes = getBytes();
+
+ for (int i = 0; i < numProperties; i++) {
+ int val = IntegerHelper.getInt(bytes[pos], bytes[pos + 1]);
+ int id = val & 0x3fff;
+ int value = IntegerHelper.getInt(bytes[pos + 2], bytes[pos + 3],
+ bytes[pos + 4], bytes[pos + 5]);
+ Property p = new Property(id,
+ (val & 0x4000) != 0,
+ (val & 0x8000) != 0,
+ value);
+ pos += 6;
+ properties.add(p);
+ }
+
+ for (Iterator i = properties.iterator(); i.hasNext(); ) {
+ Property p = (Property) i.next();
+ if (p.complex) {
+ p.stringValue = StringHelper.getUnicodeString(bytes, p.value / 2,
+ pos);
+ pos += p.value;
+ }
+ }
+ }
+
+ /**
+ * Accessor for the binary data
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ numProperties = properties.size();
+ setInstance(numProperties);
+
+ data = new byte[numProperties * 6];
+ int pos = 0;
+
+ // Add in the root data
+ for (Iterator i = properties.iterator(); i.hasNext(); ) {
+ Property p = (Property) i.next();
+ int val = p.id & 0x3fff;
+
+ if (p.blipId) {
+ val |= 0x4000;
+ }
+
+ if (p.complex) {
+ val |= 0x8000;
+ }
+
+ IntegerHelper.getTwoBytes(val, data, pos);
+ IntegerHelper.getFourBytes(p.value, data, pos + 2);
+ pos += 6;
+ }
+
+ // Add in any complex data
+ for (Iterator i = properties.iterator(); i.hasNext(); ) {
+ Property p = (Property) i.next();
+
+ if (p.complex && p.stringValue != null) {
+ byte[] newData =
+ new byte[data.length + p.stringValue.length() * 2];
+ System.arraycopy(data, 0, newData, 0, data.length);
+ StringHelper.getUnicodeBytes(p.stringValue, newData, data.length);
+ data = newData;
+ }
+ }
+
+ return setHeaderData(data);
+ }
+
+ /**
+ * Adds a property into the options
+ *
+ * @param id the property id
+ * @param blip the blip id
+ * @param complex whether it's a complex property
+ * @param val the value
+ */
+ void addProperty(int id, boolean blip, boolean complex, int val) {
+ Property p = new Property(id, blip, complex, val);
+ properties.add(p);
+ }
+
+ /**
+ * Adds a property into the options
+ *
+ * @param id the property id
+ * @param blip the blip id
+ * @param complex whether it's a complex property
+ * @param val the value
+ * @param s the value string
+ */
+ void addProperty(int id, boolean blip, boolean complex, int val, String s) {
+ Property p = new Property(id, blip, complex, val, s);
+ properties.add(p);
+ }
+
+ /**
+ * Accessor for the property
+ *
+ * @param id the property id
+ * @return the property
+ */
+ Property getProperty(int id) {
+ boolean found = false;
+ Property p = null;
+ for (Iterator i = properties.iterator(); i.hasNext() && !found; ) {
+ p = (Property) i.next();
+ if (p.id == id) {
+ found = true;
+ }
+ }
+ return found ? p : null;
+ }
+
+ /**
+ * Properties enumeration inner class
+ */
+ final static class Property {
+ int id;
+ boolean blipId;
+ boolean complex;
+ int value;
+ String stringValue;
+
+ /**
+ * Constructor
+ *
+ * @param i the property id
+ * @param bl the blip id
+ * @param co complex flag
+ * @param v the value
+ */
+ public Property(int i, boolean bl, boolean co, int v) {
+ id = i;
+ blipId = bl;
+ complex = co;
+ value = v;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param i the property id
+ * @param bl the blip id
+ * @param co complex flag
+ * @param v the value
+ * @param s the property string
+ */
+ public Property(int i, boolean bl, boolean co, int v, String s) {
+ id = i;
+ blipId = bl;
+ complex = co;
+ value = v;
+ stringValue = s;
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java
new file mode 100755
index 0000000..2777aa3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Origin.java
@@ -0,0 +1,34 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Enumeration type for the origin of a drawing object
+ */
+public final class Origin {
+ public static final Origin READ = new Origin();
+ public static final Origin WRITE = new Origin();
+ public static final Origin READ_WRITE = new Origin();
+ /**
+ * Constructor
+ */
+ private Origin() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java
new file mode 100755
index 0000000..d83ec68
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/PNGReader.java
@@ -0,0 +1,136 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Arrays;
+
+public class PNGReader {
+ private static final byte[] PNG_MAGIC_NUMBER = new byte[]
+ {(byte) 0x89, (byte) 0x50, (byte) 0x4e, (byte) 0x47,
+ (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a};
+ private final byte[] pngData;
+ private Chunk ihdr;
+ private Chunk phys;
+ private int pixelWidth;
+ private int pixelHeight;
+ private int verticalResolution;
+ private int horizontalResolution;
+ private int resolutionUnit;
+
+ public PNGReader(byte[] data) {
+ pngData = data;
+ }
+
+ public static void main(String[] args) {
+ try {
+ File f = new File(args[0]);
+ int size = (int) f.length();
+
+ byte[] data = new byte[size];
+
+ FileInputStream fis = new FileInputStream(f);
+ fis.read(data);
+ fis.close();
+ PNGReader reader = new PNGReader(data);
+ reader.read();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ void read() {
+ // Verify the magic data
+ byte[] header = new byte[PNG_MAGIC_NUMBER.length];
+ System.arraycopy(pngData, 0, header, 0, header.length);
+ boolean pngFile = Arrays.equals(PNG_MAGIC_NUMBER, header);
+ if (!pngFile) {
+ return;
+ }
+
+ int pos = 8;
+ while (pos < pngData.length) {
+ int length = getInt(pngData[pos],
+ pngData[pos + 1],
+ pngData[pos + 2],
+ pngData[pos + 3]);
+ ChunkType chunkType = ChunkType.getChunkType(pngData[pos + 4],
+ pngData[pos + 5],
+ pngData[pos + 6],
+ pngData[pos + 7]);
+
+ if (chunkType == ChunkType.IHDR) {
+ ihdr = new Chunk(pos + 8, length, chunkType, pngData);
+ } else if (chunkType == ChunkType.PHYS) {
+ phys = new Chunk(pos + 8, length, chunkType, pngData);
+ }
+
+ pos += length + 12;
+ }
+
+ // Get the width and height from the ihdr
+ byte[] ihdrData = ihdr.getData();
+ pixelWidth = getInt(ihdrData[0], ihdrData[1], ihdrData[2], ihdrData[3]);
+ pixelHeight = getInt(ihdrData[4], ihdrData[5], ihdrData[6], ihdrData[7]);
+
+ if (phys != null) {
+ byte[] physData = phys.getData();
+ resolutionUnit = physData[8];
+ horizontalResolution = getInt(physData[0], physData[1],
+ physData[2], physData[3]);
+ verticalResolution = getInt(physData[4], physData[5],
+ physData[6], physData[7]);
+ }
+ }
+
+ // Gets the big-Endian integer
+ private int getInt(byte d1, byte d2, byte d3, byte d4) {
+ int i1 = d1 & 0xff;
+ int i2 = d2 & 0xff;
+ int i3 = d3 & 0xff;
+ int i4 = d4 & 0xff;
+
+ int val = i1 << 24 |
+ i2 << 16 |
+ i3 << 8 |
+ i4;
+
+ return val;
+ }
+
+ public int getHeight() {
+ return pixelHeight;
+ }
+
+ public int getWidth() {
+ return pixelWidth;
+ }
+
+ public int getHorizontalResolution() {
+ // only return if the resolution unit is in metres
+ return resolutionUnit == 1 ? horizontalResolution : 0;
+ }
+
+ public int getVerticalResolution() {
+ // only return if the resolution unit is in metres
+ return resolutionUnit == 1 ? verticalResolution : 0;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java
new file mode 100755
index 0000000..42a2494
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/ShapeType.java
@@ -0,0 +1,79 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Enumerations for the shape type
+ */
+final class ShapeType {
+ public static final ShapeType MIN = new ShapeType(0);
+ public static final ShapeType PICTURE_FRAME = new ShapeType(75);
+ public static final ShapeType HOST_CONTROL = new ShapeType(201);
+ public static final ShapeType TEXT_BOX = new ShapeType(202);
+ public static final ShapeType UNKNOWN = new ShapeType(-1);
+ /**
+ * The list of shape types
+ */
+ private static ShapeType[] types = new ShapeType[0];
+ /**
+ * The value
+ */
+ private final int value;
+ /**
+ * Constructor
+ *
+ * @param v the value
+ */
+ ShapeType(int v) {
+ value = v;
+
+ ShapeType[] old = types;
+ types = new ShapeType[types.length + 1];
+ System.arraycopy(old, 0, types, 0, old.length);
+ types[old.length] = this;
+ }
+
+ /**
+ * Gets the shape type given the value
+ *
+ * @param v the value
+ * @return the shape type for the value
+ */
+ static ShapeType getType(int v) {
+ ShapeType st = UNKNOWN;
+ boolean found = false;
+ for (int i = 0; i < types.length && !found; i++) {
+ if (types[i].value == v) {
+ found = true;
+ st = types[i];
+ }
+ }
+ return st;
+ }
+
+ /**
+ * Accessor for the value
+ *
+ * @return the value
+ */
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java
new file mode 100755
index 0000000..7779a49
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SheetDrawingWriter.java
@@ -0,0 +1,432 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.WorkbookSettings;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+import jxl.write.biff.File;
+
+/**
+ * Handles the writing out of the different charts and images on a sheet.
+ * Called by the SheetWriter object
+ */
+public class SheetDrawingWriter {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(SheetDrawingWriter.class);
+
+ /**
+ * The drawings on the sheet
+ */
+ private ArrayList drawings;
+
+ /**
+ * Flag indicating whether the drawings on the sheet were modified
+ */
+ private boolean drawingsModified;
+
+ /**
+ * The charts on the sheet
+ */
+ private Chart[] charts;
+
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings workbookSettings;
+
+ /**
+ * Constructor
+ *
+ * @param ws the workbook settings
+ */
+ public SheetDrawingWriter(WorkbookSettings ws) {
+ charts = new Chart[0];
+ }
+
+ /**
+ * The drawings on the sheet
+ *
+ * @param dr the list of drawings
+ * @param mod flag indicating whether the drawings have been tampered with
+ */
+ public void setDrawings(ArrayList dr, boolean mod) {
+ drawings = dr;
+ drawingsModified = mod;
+ }
+
+ /**
+ * Writes out the MsoDrawing records and Obj records for each image
+ * and chart on the sheet
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void write(File outputFile) throws IOException {
+ // If there are no drawings or charts on this sheet then exit
+ if (drawings.size() == 0 && charts.length == 0) {
+ return;
+ }
+
+ // See if any drawing has been modified
+ boolean modified = drawingsModified;
+ int numImages = drawings.size();
+
+ for (Iterator i = drawings.iterator(); i.hasNext() && !modified; ) {
+ DrawingGroupObject d = (DrawingGroupObject) i.next();
+ if (d.getOrigin() != Origin.READ) {
+ modified = true;
+ }
+ }
+
+ // If the drawing order has been muddled at all, then we'll need
+ // to regenerate the Escher drawing data
+ if (numImages > 0 && !modified) {
+ DrawingGroupObject d2 = (DrawingGroupObject) drawings.get(0);
+ if (!d2.isFirst()) {
+ modified = true;
+ }
+ }
+
+ // Check to see if this sheet consists of just a single chart. If so
+ // there is no MsoDrawingRecord, so write out the data and exit
+ if (numImages == 0 &&
+ charts.length == 1 &&
+ charts[0].getMsoDrawingRecord() == null) {
+ modified = false; // this sheet has not been modified
+ }
+
+ // If no drawing has been modified, then simply write them straight out
+ // again and exit
+ if (!modified) {
+ writeUnmodified(outputFile);
+ return;
+ }
+
+ Object[] spContainerData = new Object[numImages + charts.length];
+ int length = 0;
+ EscherContainer firstSpContainer = null;
+
+ // Get all the spContainer byte data from the drawings
+ // and store in an array
+ for (int i = 0; i < numImages; i++) {
+ DrawingGroupObject drawing = (DrawingGroupObject) drawings.get(i);
+
+ EscherContainer spc = drawing.getSpContainer();
+
+ if (spc != null) {
+ byte[] data = spc.getData();
+ spContainerData[i] = data;
+
+ if (i == 0) {
+ firstSpContainer = spc;
+ } else {
+ length += data.length;
+ }
+ }
+ }
+
+ // Get all the spContainer bytes from the charts and add to the array
+ for (int i = 0; i < charts.length; i++) {
+ EscherContainer spContainer = charts[i].getSpContainer();
+ byte[] data = spContainer.getBytes(); //use getBytes instead of getData
+ data = spContainer.setHeaderData(data);
+ spContainerData[i + numImages] = data;
+
+ if (i == 0 && numImages == 0) {
+ firstSpContainer = spContainer;
+ } else {
+ length += data.length;
+ }
+ }
+
+ // Put the generalised stuff around the first item
+ DgContainer dgContainer = new DgContainer();
+ Dg dg = new Dg(numImages + charts.length);
+ dgContainer.add(dg);
+
+ SpgrContainer spgrContainer = new SpgrContainer();
+
+ SpContainer spContainer = new SpContainer();
+ Spgr spgr = new Spgr();
+ spContainer.add(spgr);
+ Sp sp = new Sp(ShapeType.MIN, 1024, 5);
+ spContainer.add(sp);
+ spgrContainer.add(spContainer);
+
+ spgrContainer.add(firstSpContainer);
+
+ dgContainer.add(spgrContainer);
+
+ byte[] firstMsoData = dgContainer.getData();
+
+ // Adjust the length of the DgContainer
+ int len = IntegerHelper.getInt(firstMsoData[4],
+ firstMsoData[5],
+ firstMsoData[6],
+ firstMsoData[7]);
+ IntegerHelper.getFourBytes(len + length, firstMsoData, 4);
+
+ // Adjust the length of the SpgrContainer
+ len = IntegerHelper.getInt(firstMsoData[28],
+ firstMsoData[29],
+ firstMsoData[30],
+ firstMsoData[31]);
+ IntegerHelper.getFourBytes(len + length, firstMsoData, 28);
+
+ // Now write out each MsoDrawing record
+
+ // First MsoRecord
+ // test hack for form objects, to remove the ClientTextBox record
+ // from the end of the SpContainer
+ if (numImages > 0 &&
+ ((DrawingGroupObject) drawings.get(0)).isFormObject()) {
+ byte[] msodata2 = new byte[firstMsoData.length - 8];
+ System.arraycopy(firstMsoData, 0, msodata2, 0, msodata2.length);
+ firstMsoData = msodata2;
+ }
+
+ MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData);
+ outputFile.write(msoDrawingRecord);
+
+ if (numImages > 0) {
+ DrawingGroupObject firstDrawing = (DrawingGroupObject) drawings.get(0);
+ firstDrawing.writeAdditionalRecords(outputFile);
+ } else {
+ // first image is a chart
+ Chart chart = charts[0];
+ ObjRecord objRecord = chart.getObjRecord();
+ outputFile.write(objRecord);
+ outputFile.write(chart);
+ }
+
+ // Now do all the others
+ for (int i = 1; i < spContainerData.length; i++) {
+ byte[] bytes = (byte[]) spContainerData[i];
+
+ // test hack for form objects, to remove the ClientTextBox record
+ // from the end of the SpContainer
+ if (i < numImages &&
+ ((DrawingGroupObject) drawings.get(i)).isFormObject()) {
+ byte[] bytes2 = new byte[bytes.length - 8];
+ System.arraycopy(bytes, 0, bytes2, 0, bytes2.length);
+ bytes = bytes2;
+ }
+
+ msoDrawingRecord = new MsoDrawingRecord(bytes);
+ outputFile.write(msoDrawingRecord);
+
+ if (i < numImages) {
+ // Write anything else the object needs
+ DrawingGroupObject d = (DrawingGroupObject) drawings.get(i);
+ d.writeAdditionalRecords(outputFile);
+ } else {
+ Chart chart = charts[i - numImages];
+ ObjRecord objRecord = chart.getObjRecord();
+ outputFile.write(objRecord);
+ outputFile.write(chart);
+ }
+ }
+
+ // Write any tail records that need to be written
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject dgo2 = (DrawingGroupObject) i.next();
+ dgo2.writeTailRecords(outputFile);
+ }
+ }
+
+ /**
+ * Writes out the drawings and the charts if nothing has been modified
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ private void writeUnmodified(File outputFile) throws IOException {
+ if (charts.length == 0 && drawings.size() == 0) {
+ // No drawings or charts
+ return;
+ } else if (charts.length == 0 && drawings.size() != 0) {
+ // If there are no charts, then write out the drawings and return
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject d = (DrawingGroupObject) i.next();
+ outputFile.write(d.getMsoDrawingRecord());
+ d.writeAdditionalRecords(outputFile);
+ }
+
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject d = (DrawingGroupObject) i.next();
+ d.writeTailRecords(outputFile);
+ }
+ return;
+ } else if (drawings.size() == 0 && charts.length != 0) {
+ // If there are no drawings, then write out the charts and return
+ Chart curChart = null;
+ for (int i = 0; i < charts.length; i++) {
+ curChart = charts[i];
+ if (curChart.getMsoDrawingRecord() != null) {
+ outputFile.write(curChart.getMsoDrawingRecord());
+ }
+
+ if (curChart.getObjRecord() != null) {
+ outputFile.write(curChart.getObjRecord());
+ }
+
+ outputFile.write(curChart);
+ }
+
+ return;
+ }
+
+ // There are both charts and drawings - the output
+ // drawing group records will need
+ // to be re-jigged in order to write the drawings out first, then the
+ // charts
+ int numDrawings = drawings.size();
+ int length = 0;
+ EscherContainer[] spContainers =
+ new EscherContainer[numDrawings + charts.length];
+ boolean[] isFormObject = new boolean[numDrawings + charts.length];
+
+ for (int i = 0; i < numDrawings; i++) {
+ DrawingGroupObject d = (DrawingGroupObject) drawings.get(i);
+ spContainers[i] = d.getSpContainer();
+
+ if (i > 0) {
+ length += spContainers[i].getLength();
+ }
+
+ if (d.isFormObject()) {
+ isFormObject[i] = true;
+ }
+ }
+
+ for (int i = 0; i < charts.length; i++) {
+ spContainers[i + numDrawings] = charts[i].getSpContainer();
+ length += spContainers[i + numDrawings].getLength();
+ }
+
+ // Put the generalised stuff around the first item
+ DgContainer dgContainer = new DgContainer();
+ Dg dg = new Dg(numDrawings + charts.length);
+ dgContainer.add(dg);
+
+ SpgrContainer spgrContainer = new SpgrContainer();
+
+ SpContainer spContainer = new SpContainer();
+ Spgr spgr = new Spgr();
+ spContainer.add(spgr);
+ Sp sp = new Sp(ShapeType.MIN, 1024, 5);
+ spContainer.add(sp);
+ spgrContainer.add(spContainer);
+
+ spgrContainer.add(spContainers[0]);
+
+ dgContainer.add(spgrContainer);
+
+ byte[] firstMsoData = dgContainer.getData();
+
+ // Adjust the length of the DgContainer
+ int len = IntegerHelper.getInt(firstMsoData[4],
+ firstMsoData[5],
+ firstMsoData[6],
+ firstMsoData[7]);
+ IntegerHelper.getFourBytes(len + length, firstMsoData, 4);
+
+ // Adjust the length of the SpgrContainer
+ len = IntegerHelper.getInt(firstMsoData[28],
+ firstMsoData[29],
+ firstMsoData[30],
+ firstMsoData[31]);
+ IntegerHelper.getFourBytes(len + length, firstMsoData, 28);
+
+ // Now write out each MsoDrawing record and object record
+
+ // Hack to remove the last eight bytes (text box escher record)
+ // from the container
+ if (isFormObject[0] == true) {
+ byte[] cbytes = new byte[firstMsoData.length - 8];
+ System.arraycopy(firstMsoData, 0, cbytes, 0, cbytes.length);
+ firstMsoData = cbytes;
+ }
+
+ // First MsoRecord
+ MsoDrawingRecord msoDrawingRecord = new MsoDrawingRecord(firstMsoData);
+ outputFile.write(msoDrawingRecord);
+
+ DrawingGroupObject dgo = (DrawingGroupObject) drawings.get(0);
+ dgo.writeAdditionalRecords(outputFile);
+
+ // Now do all the others
+ for (int i = 1; i < spContainers.length; i++) {
+ byte[] bytes = spContainers[i].getBytes();
+ byte[] bytes2 = spContainers[i].setHeaderData(bytes);
+
+ // Hack to remove the last eight bytes (text box escher record)
+ // from the container
+ if (isFormObject[i] == true) {
+ byte[] cbytes = new byte[bytes2.length - 8];
+ System.arraycopy(bytes2, 0, cbytes, 0, cbytes.length);
+ bytes2 = cbytes;
+ }
+
+ msoDrawingRecord = new MsoDrawingRecord(bytes2);
+ outputFile.write(msoDrawingRecord);
+
+ if (i < numDrawings) {
+ dgo = (DrawingGroupObject) drawings.get(i);
+ dgo.writeAdditionalRecords(outputFile);
+ } else {
+ Chart chart = charts[i - numDrawings];
+ ObjRecord objRecord = chart.getObjRecord();
+ outputFile.write(objRecord);
+ outputFile.write(chart);
+ }
+ }
+
+ // Write any tail records that need to be written
+ for (Iterator i = drawings.iterator(); i.hasNext(); ) {
+ DrawingGroupObject dgo2 = (DrawingGroupObject) i.next();
+ dgo2.writeTailRecords(outputFile);
+ }
+ }
+
+ /**
+ * Accessor for the charts on the sheet
+ *
+ * @return the charts
+ */
+ public Chart[] getCharts() {
+ return charts;
+ }
+
+ /**
+ * Sets the charts on the sheet
+ *
+ * @param ch the charts
+ */
+ public void setCharts(Chart[] ch) {
+ charts = ch;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java
new file mode 100755
index 0000000..de311e8
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Sp.java
@@ -0,0 +1,113 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * The Sp escher atom
+ */
+class Sp extends EscherAtom {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Sp.class);
+
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * The shape type
+ */
+ private final int shapeType;
+
+ /**
+ * The shape id
+ */
+ private final int shapeId;
+
+ /**
+ * The Sp persistence flags
+ */
+ private final int persistenceFlags;
+
+ /**
+ * Constructor
+ *
+ * @param erd the entity record data
+ */
+ public Sp(EscherRecordData erd) {
+ super(erd);
+ shapeType = getInstance();
+ byte[] bytes = getBytes();
+ shapeId = IntegerHelper.getInt(bytes[0], bytes[1], bytes[2], bytes[3]);
+ persistenceFlags = IntegerHelper.getInt(bytes[4], bytes[5],
+ bytes[6], bytes[7]);
+ }
+
+ /**
+ * Constructor - used when writing
+ *
+ * @param st the shape type
+ * @param sid the shape id
+ * @param p persistence flags
+ */
+ public Sp(ShapeType st, int sid, int p) {
+ super(EscherRecordType.SP);
+ setVersion(2);
+ shapeType = st.getValue();
+ shapeId = sid;
+ persistenceFlags = p;
+ setInstance(shapeType);
+ }
+
+ /**
+ * Accessor for the shape id
+ *
+ * @return the shape id
+ */
+ int getShapeId() {
+ return shapeId;
+ }
+
+ /**
+ * Accessor for the shape type
+ *
+ * @return the shape type
+ */
+ int getShapeType() {
+ return shapeType;
+ }
+
+ /**
+ * Gets the data
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ data = new byte[8];
+ IntegerHelper.getFourBytes(shapeId, data, 0);
+ IntegerHelper.getFourBytes(persistenceFlags, data, 4);
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java
new file mode 100755
index 0000000..7a01f0d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpContainer.java
@@ -0,0 +1,41 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Shape contianer - Contains the data for this particular shape
+ */
+class SpContainer extends EscherContainer {
+ /**
+ * Constructor
+ */
+ public SpContainer() {
+ super(EscherRecordType.SP_CONTAINER);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param erd the escher record data
+ */
+ public SpContainer(EscherRecordData erd) {
+ super(erd);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java
new file mode 100755
index 0000000..6a74121
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/Spgr.java
@@ -0,0 +1,57 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * The SpGr escher atom
+ */
+class Spgr extends EscherAtom {
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param erd the raw escher record data
+ */
+ public Spgr(EscherRecordData erd) {
+ super(erd);
+ }
+
+ /**
+ * Constructor
+ */
+ public Spgr() {
+ super(EscherRecordType.SPGR);
+ setVersion(1);
+ data = new byte[16];
+ }
+
+ /**
+ * Gets the binary data
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java
new file mode 100755
index 0000000..bc680b3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SpgrContainer.java
@@ -0,0 +1,48 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.common.Logger;
+
+/**
+ * An Spgr container record in an escher stream
+ */
+class SpgrContainer extends EscherContainer {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(SpgrContainer.class);
+
+ /**
+ * Constructor used when writing
+ */
+ public SpgrContainer() {
+ super(EscherRecordType.SPGR_CONTAINER);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param erd the escher record data read in
+ */
+ public SpgrContainer(EscherRecordData erd) {
+ super(erd);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java
new file mode 100755
index 0000000..d780ca3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/SplitMenuColors.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+/**
+ * Split menu colours escher record
+ */
+class SplitMenuColors extends EscherAtom {
+ /**
+ * The binary data
+ */
+ private byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param erd escher record data
+ */
+ public SplitMenuColors(EscherRecordData erd) {
+ super(erd);
+ }
+
+ /**
+ * Constructor
+ */
+ public SplitMenuColors() {
+ super(EscherRecordType.SPLIT_MENU_COLORS);
+ setVersion(0);
+ setInstance(4);
+
+ data = new byte[]
+ {(byte) 0x0d, (byte) 0x00, (byte) 0x00, (byte) 0x08,
+ (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x08,
+ (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x08,
+ (byte) 0xf7, (byte) 0x00, (byte) 0x00, (byte) 0x10};
+ }
+
+ /**
+ * The binary data
+ *
+ * @return the binary data
+ */
+ byte[] getData() {
+ return setHeaderData(data);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java b/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java
new file mode 100755
index 0000000..61bf58b
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/drawing/TextObjectRecord.java
@@ -0,0 +1,123 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.drawing;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Logger;
+import jxl.read.biff.Record;
+
+/**
+ * A TextObject (TXO) record which contains the information for comments
+ */
+public class TextObjectRecord extends WritableRecordData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(TextObjectRecord.class);
+
+ /**
+ * The raw drawing data which was read in
+ */
+ private byte[] data;
+
+ /**
+ * The text
+ */
+ private int textLength;
+
+ /**
+ * Constructor invoked when writing out this object
+ *
+ * @param t the text string
+ */
+ TextObjectRecord(String t) {
+ super(Type.TXO);
+
+ textLength = t.length();
+ }
+
+ /**
+ * Constructs this object from the raw data
+ *
+ * @param t the raw data
+ */
+ public TextObjectRecord(Record t) {
+ super(t);
+ data = getRecord().getData();
+ textLength = IntegerHelper.getInt(data[10], data[11]);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param d the drawing data
+ */
+ public TextObjectRecord(byte[] d) {
+ super(Type.TXO);
+ data = d;
+ }
+
+ /**
+ * Gets the text length. Used to determine if there is any data held
+ * in the following continue records
+ *
+ * @return the length of the text
+ */
+ public int getTextLength() {
+ return textLength;
+ }
+
+ /**
+ * Expose the protected function to the SheetImpl in this package
+ *
+ * @return the raw record data
+ */
+ public byte[] getData() {
+ if (data != null) {
+ return data;
+ }
+
+ data = new byte[18];
+
+ // the options
+ int options = 0;
+ options |= (0x1 << 1); // horizontal alignment - left
+ options |= (0x1 << 4); // vertical alignment - top
+ options |= (0x1 << 9); // lock text
+ IntegerHelper.getTwoBytes(options, data, 0);
+
+ // the rotation
+ // no rotation
+
+ // Length of text
+ IntegerHelper.getTwoBytes(textLength, data, 10);
+
+ // Length of formatting runs
+ IntegerHelper.getTwoBytes(0x10, data, 12);
+
+ return data;
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java
new file mode 100755
index 0000000..df142a8
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Add.java
@@ -0,0 +1,59 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Add extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Add() {
+ }
+
+ /**
+ * Gets the symbol for string displays
+ *
+ * @return the symbol
+ */
+ public String getSymbol() {
+ return "+";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.ADD;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 4;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java
new file mode 100755
index 0000000..e98ee9b
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Area.java
@@ -0,0 +1,383 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A nested class to hold range information
+ */
+class Area extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Area.class);
+
+ /**
+ * The first column
+ */
+ private int columnFirst;
+
+ /**
+ * The first row
+ */
+ private int rowFirst;
+
+ /**
+ * The last column
+ */
+ private int columnLast;
+
+ /**
+ * The last row
+ */
+ private int rowLast;
+
+ /**
+ * Indicates whether the first column is a relative or absolute reference
+ */
+ private boolean columnFirstRelative;
+
+ /**
+ * Indicates whether the first row is a relative or absolute reference
+ */
+ private boolean rowFirstRelative;
+
+ /**
+ * Indicates whether the last column is a relative or absolute reference
+ */
+ private boolean columnLastRelative;
+
+ /**
+ * Indicates whether the last row is a relative or absolute reference
+ */
+ private boolean rowLastRelative;
+
+ /**
+ * Constructor
+ */
+ Area() {
+ }
+
+ /**
+ * Constructor invoked when parsing a string formula
+ *
+ * @param s the string to parse
+ */
+ Area(String s) {
+ int seppos = s.indexOf(":");
+ Assert.verify(seppos != -1);
+ String startcell = s.substring(0, seppos);
+ String endcell = s.substring(seppos + 1);
+
+ columnFirst = CellReferenceHelper.getColumn(startcell);
+ rowFirst = CellReferenceHelper.getRow(startcell);
+ columnLast = CellReferenceHelper.getColumn(endcell);
+ rowLast = CellReferenceHelper.getRow(endcell);
+
+ columnFirstRelative = CellReferenceHelper.isColumnRelative(startcell);
+ rowFirstRelative = CellReferenceHelper.isRowRelative(startcell);
+ columnLastRelative = CellReferenceHelper.isColumnRelative(endcell);
+ rowLastRelative = CellReferenceHelper.isRowRelative(endcell);
+ }
+
+ /**
+ * Accessor for the first column
+ *
+ * @return the first column
+ */
+ int getFirstColumn() {
+ return columnFirst;
+ }
+
+ /**
+ * Accessor for the first row
+ *
+ * @return the first row
+ */
+ int getFirstRow() {
+ return rowFirst;
+ }
+
+ /**
+ * Accessor for the last column
+ *
+ * @return the last column
+ */
+ int getLastColumn() {
+ return columnLast;
+ }
+
+ /**
+ * Accessor for the last row
+ *
+ * @return the last row
+ */
+ int getLastRow() {
+ return rowLast;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ rowFirst = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ rowLast = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+ int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ columnFirst = columnMask & 0x00ff;
+ columnFirstRelative = ((columnMask & 0x4000) != 0);
+ rowFirstRelative = ((columnMask & 0x8000) != 0);
+ columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]);
+ columnLast = columnMask & 0x00ff;
+ columnLastRelative = ((columnMask & 0x4000) != 0);
+ rowLastRelative = ((columnMask & 0x8000) != 0);
+
+ return 8;
+ }
+
+ /**
+ * Gets the string representation of this item
+ *
+ * @param buf the string buffer
+ */
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf);
+ buf.append(':');
+ CellReferenceHelper.getCellReference(columnLast, rowLast, buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[9];
+ data[0] = !useAlternateCode() ? Token.AREA.getCode() :
+ Token.AREA.getCode2();
+
+ IntegerHelper.getTwoBytes(rowFirst, data, 1);
+ IntegerHelper.getTwoBytes(rowLast, data, 3);
+
+ int grcol = columnFirst;
+
+ // Set the row/column relative bits if applicable
+ if (rowFirstRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnFirstRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 5);
+
+ grcol = columnLast;
+
+ // Set the row/column relative bits if applicable
+ if (rowLastRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnLastRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 7);
+
+ return data;
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ if (columnFirstRelative) {
+ columnFirst += colAdjust;
+ }
+
+ if (columnLastRelative) {
+ columnLast += colAdjust;
+ }
+
+ if (rowFirstRelative) {
+ rowFirst += rowAdjust;
+ }
+
+ if (rowLastRelative) {
+ rowLast += rowAdjust;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (col <= columnFirst) {
+ columnFirst++;
+ }
+
+ if (col <= columnLast) {
+ columnLast++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (col < columnFirst) {
+ columnFirst--;
+ }
+
+ if (col <= columnLast) {
+ columnLast--;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (rowLast == 0xffff) {
+ // area applies to the whole column, so nothing to do
+ return;
+ }
+
+ if (row <= rowFirst) {
+ rowFirst++;
+ }
+
+ if (row <= rowLast) {
+ rowLast++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (rowLast == 0xffff) {
+ // area applies to the whole column, so nothing to do
+ return;
+ }
+
+ if (row < rowFirst) {
+ rowFirst--;
+ }
+
+ if (row <= rowLast) {
+ rowLast--;
+ }
+ }
+
+ /**
+ * Used by subclasses columns/row range to set the range information
+ *
+ * @param colFirst the first column
+ * @param colLast the last column
+ * @param rwFirst the first row
+ * @param rwLast the last row
+ * @param colFirstRel flag indicating whether the first column is relative
+ * @param colLastRel flag indicating whether the last column is relative
+ * @param rowFirstRel flag indicating whether the first row is relative
+ * @param rowLastRel flag indicating whether the last row is relative
+ */
+ protected void setRangeData(int colFirst,
+ int colLast,
+ int rwFirst,
+ int rwLast,
+ boolean colFirstRel,
+ boolean colLastRel,
+ boolean rowFirstRel,
+ boolean rowLastRel) {
+ columnFirst = colFirst;
+ columnLast = colLast;
+ rowFirst = rwFirst;
+ rowLast = rwLast;
+ columnFirstRelative = colFirstRel;
+ columnLastRelative = colLastRel;
+ rowFirstRelative = rowFirstRel;
+ rowLastRelative = rowLastRel;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing here
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java
new file mode 100755
index 0000000..219a4c1
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Area3d.java
@@ -0,0 +1,433 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A nested class to hold range information
+ */
+class Area3d extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Area3d.class);
+
+ /**
+ * The sheet
+ */
+ private int sheet;
+
+ /**
+ * The first column
+ */
+ private int columnFirst;
+
+ /**
+ * The first row
+ */
+ private int rowFirst;
+
+ /**
+ * The last column
+ */
+ private int columnLast;
+
+ /**
+ * The last row
+ */
+ private int rowLast;
+
+ /**
+ * Indicates whether the first column is relative
+ */
+ private boolean columnFirstRelative;
+
+ /**
+ * Indicates whether the first row is relative
+ */
+ private boolean rowFirstRelative;
+
+ /**
+ * Indicates whether the last column is relative
+ */
+ private boolean columnLastRelative;
+
+ /**
+ * Indicates whether the last row is relative
+ */
+ private boolean rowLastRelative;
+
+ /**
+ * A handle to the workbook
+ */
+ private final ExternalSheet workbook;
+
+ /**
+ * Constructor
+ *
+ * @param es the external sheet
+ */
+ Area3d(ExternalSheet es) {
+ workbook = es;
+ }
+
+ /**
+ * Constructor invoked when parsing a string formula
+ *
+ * @param s the string to parse
+ * @param es the external sheet
+ * @throws FormulaException
+ */
+ Area3d(String s, ExternalSheet es) throws FormulaException {
+ workbook = es;
+ int seppos = s.lastIndexOf(":");
+ Assert.verify(seppos != -1);
+ String endcell = s.substring(seppos + 1);
+
+ // Get the the start cell details
+ int sep = s.indexOf('!');
+ String cellString = s.substring(sep + 1, seppos);
+ columnFirst = CellReferenceHelper.getColumn(cellString);
+ rowFirst = CellReferenceHelper.getRow(cellString);
+
+ // Get the sheet index
+ String sheetName = s.substring(0, sep);
+
+ // Remove single quotes, if they exist
+ if (sheetName.charAt(0) == '\'' &&
+ sheetName.charAt(sheetName.length() - 1) == '\'') {
+ sheetName = sheetName.substring(1, sheetName.length() - 1);
+ }
+
+ sheet = es.getExternalSheetIndex(sheetName);
+
+ if (sheet < 0) {
+ throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND,
+ sheetName);
+ }
+
+ // Get the last cell index
+ columnLast = CellReferenceHelper.getColumn(endcell);
+ rowLast = CellReferenceHelper.getRow(endcell);
+
+ columnFirstRelative = true;
+ rowFirstRelative = true;
+ columnLastRelative = true;
+ rowLastRelative = true;
+ }
+
+ /**
+ * Accessor for the first column
+ *
+ * @return the first column
+ */
+ int getFirstColumn() {
+ return columnFirst;
+ }
+
+ /**
+ * Accessor for the first row
+ *
+ * @return the first row
+ */
+ int getFirstRow() {
+ return rowFirst;
+ }
+
+ /**
+ * Accessor for the last column
+ *
+ * @return the last column
+ */
+ int getLastColumn() {
+ return columnLast;
+ }
+
+ /**
+ * Accessor for the last row
+ *
+ * @return the last row
+ */
+ int getLastRow() {
+ return rowLast;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ sheet = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ rowFirst = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+ rowLast = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ int columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]);
+ columnFirst = columnMask & 0x00ff;
+ columnFirstRelative = ((columnMask & 0x4000) != 0);
+ rowFirstRelative = ((columnMask & 0x8000) != 0);
+ columnMask = IntegerHelper.getInt(data[pos + 8], data[pos + 9]);
+ columnLast = columnMask & 0x00ff;
+ columnLastRelative = ((columnMask & 0x4000) != 0);
+ rowLastRelative = ((columnMask & 0x8000) != 0);
+
+ return 10;
+ }
+
+ /**
+ * Gets the string version of this area
+ *
+ * @param buf the area to populate
+ */
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference
+ (sheet, columnFirst, rowFirst, workbook, buf);
+ buf.append(':');
+ CellReferenceHelper.getCellReference(columnLast, rowLast, buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[11];
+ data[0] = Token.AREA3D.getCode();
+
+ IntegerHelper.getTwoBytes(sheet, data, 1);
+
+ IntegerHelper.getTwoBytes(rowFirst, data, 3);
+ IntegerHelper.getTwoBytes(rowLast, data, 5);
+
+ int grcol = columnFirst;
+
+ // Set the row/column relative bits if applicable
+ if (rowFirstRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnFirstRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 7);
+
+ grcol = columnLast;
+
+ // Set the row/column relative bits if applicable
+ if (rowLastRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnLastRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 9);
+
+ return data;
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ if (columnFirstRelative) {
+ columnFirst += colAdjust;
+ }
+
+ if (columnLastRelative) {
+ columnLast += colAdjust;
+ }
+
+ if (rowFirstRelative) {
+ rowFirst += rowAdjust;
+ }
+
+ if (rowLastRelative) {
+ rowLast += rowAdjust;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (columnFirst >= col) {
+ columnFirst++;
+ }
+
+ if (columnLast >= col) {
+ columnLast++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (col < columnFirst) {
+ columnFirst--;
+ }
+
+ if (col <= columnLast) {
+ columnLast--;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (rowLast == 0xffff) {
+ // area applies to the whole column, so nothing to do
+ return;
+ }
+
+ if (row <= rowFirst) {
+ rowFirst++;
+ }
+
+ if (row <= rowLast) {
+ rowLast++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (rowLast == 0xffff) {
+ // area applies to the whole column, so nothing to do
+ return;
+ }
+
+ if (row < rowFirst) {
+ rowFirst--;
+ }
+
+ if (row <= rowLast) {
+ rowLast--;
+ }
+ }
+
+ /**
+ * Used by subclasses columns/row range to set the range information
+ *
+ * @param sht the sheet containing the area
+ * @param colFirst the first column
+ * @param colLast the last column
+ * @param rwFirst the first row
+ * @param rwLast the last row
+ * @param colFirstRel flag indicating whether the first column is relative
+ * @param colLastRel flag indicating whether the last column is relative
+ * @param rowFirstRel flag indicating whether the first row is relative
+ * @param rowLastRel flag indicating whether the last row is relative
+ */
+ protected void setRangeData(int sht,
+ int colFirst,
+ int colLast,
+ int rwFirst,
+ int rwLast,
+ boolean colFirstRel,
+ boolean colLastRel,
+ boolean rowFirstRel,
+ boolean rowLastRel) {
+ sheet = sht;
+ columnFirst = colFirst;
+ columnLast = colLast;
+ rowFirst = rwFirst;
+ rowLast = rwLast;
+ columnFirstRelative = colFirstRel;
+ columnLastRelative = colLastRel;
+ rowFirstRelative = rowFirstRel;
+ rowLastRelative = rowLastRel;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ */
+ void handleImportedCellReferences() {
+ setInvalid();
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java
new file mode 100755
index 0000000..636e741
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ArgumentSeparator.java
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A dummy token used when parsing strings in order to indicate the
+ * separation of parameters
+ */
+class ArgumentSeparator extends StringParseItem {
+ /**
+ * Constructor
+ */
+ public ArgumentSeparator() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java
new file mode 100755
index 0000000..bde9c23
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Attribute.java
@@ -0,0 +1,478 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.WorkbookSettings;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * A special attribute control token - typically either a SUM function
+ * or an IF function
+ */
+class Attribute extends Operator implements ParsedThing {
+ private static final int SUM_MASK = 0x10;
+ private static final int IF_MASK = 0x02;
+ private static final int CHOOSE_MASK = 0x04;
+ private static final int GOTO_MASK = 0x08;
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Attribute.class);
+ /**
+ * The options used by the attribute
+ */
+ private int options;
+ /**
+ * The word contained in this attribute
+ */
+ private int word;
+ /**
+ * The workbook settings
+ */
+ private final WorkbookSettings settings;
+ /**
+ * If this attribute is an IF functions, sets the associated if conditions
+ */
+ private VariableArgFunction ifConditions;
+
+ /**
+ * Constructor
+ *
+ * @param ws the workbook settings
+ */
+ public Attribute(WorkbookSettings ws) {
+ settings = ws;
+ }
+
+ /**
+ * Constructor for use when this is called when parsing a string
+ *
+ * @param sf the built in function
+ * @param ws the workbook settings
+ */
+ public Attribute(StringFunction sf, WorkbookSettings ws) {
+ settings = ws;
+
+ if (sf.getFunction(settings) == Function.SUM) {
+ options |= SUM_MASK;
+ } else if (sf.getFunction(settings) == Function.IF) {
+ options |= IF_MASK;
+ }
+ }
+
+ /**
+ * Sets the if conditions for this attribute, if it represents an IF function
+ *
+ * @param vaf a VariableArgFunction
value
+ */
+ void setIfConditions(VariableArgFunction vaf) {
+ ifConditions = vaf;
+
+ // Sometimes there is not Attribute token, so we need to create
+ // an attribute out of thin air. In that case, make sure the if mask
+ options |= IF_MASK;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ options = data[pos];
+ word = IntegerHelper.getInt(data[pos + 1], data[pos + 2]);
+
+ if (!isChoose()) {
+ return 3;
+ }
+
+ // word contains the number of jumps by index.
+ // and there is an additional final jump to the choose function itself.
+ return 3 + (word + 1) * 2;
+ }
+
+ /**
+ * Queries whether this attribute is a function
+ *
+ * @return TRUE if this is a function, FALSE otherwise
+ */
+ public boolean isFunction() {
+ return (options & (SUM_MASK | IF_MASK)) != 0;
+ }
+
+ /**
+ * Queries whether this attribute is a sum
+ *
+ * @return TRUE if this is SUM, FALSE otherwise
+ */
+ public boolean isSum() {
+ return (options & SUM_MASK) != 0;
+ }
+
+ /**
+ * Queries whether this attribute is an IF
+ *
+ * @return TRUE if this is an IF, FALSE otherwise
+ */
+ public boolean isIf() {
+ return (options & IF_MASK) != 0;
+ }
+
+ /**
+ * Queries whether this attribute is a goto
+ *
+ * @return TRUE if this is a goto, FALSE otherwise
+ */
+ public boolean isGoto() {
+ return (options & GOTO_MASK) != 0;
+ }
+
+ /**
+ * Queries whether this attribute is a CHOOSE
+ *
+ * @return TRUE if this is a CHOOSE, FALSE otherwise
+ */
+ public boolean isChoose() {
+ return (options & CHOOSE_MASK) != 0;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ *
+ * @param s the token stack
+ */
+ public void getOperands(Stack s) {
+ if ((options & SUM_MASK) != 0) {
+ ParseItem o1 = (ParseItem) s.pop();
+ add(o1);
+ } else if ((options & IF_MASK) != 0) {
+ ParseItem o1 = (ParseItem) s.pop();
+ add(o1);
+ }
+ }
+
+ /**
+ * Gets the string version of the attribute
+ *
+ * @param buf the buffer to populate
+ */
+ public void getString(StringBuffer buf) {
+ if ((options & SUM_MASK) != 0) {
+ ParseItem[] operands = getOperands();
+ buf.append(Function.SUM.getName(settings));
+ buf.append('(');
+ operands[0].getString(buf);
+ buf.append(')');
+ } else if ((options & IF_MASK) != 0) {
+ buf.append(Function.IF.getName(settings));
+ buf.append('(');
+
+ ParseItem[] operands = ifConditions.getOperands();
+
+ // Operands are in the correct order for IFs
+ for (int i = 0; i < operands.length - 1; i++) {
+ operands[i].getString(buf);
+ buf.append(',');
+ }
+ operands[operands.length - 1].getString(buf);
+ buf.append(')');
+ }
+ }
+
+ /**
+ * Gets the token representation of this item in RPN. The Attribute
+ * token is a special case, which overrides anything useful we could do
+ * in the base class
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[0];
+ if (isSum()) {
+ // Get the data for the operands
+ ParseItem[] operands = getOperands();
+
+ // Get the operands in reverse order to get the RPN
+ for (int i = operands.length - 1; i >= 0; i--) {
+ byte[] opdata = operands[i].getBytes();
+
+ // Grow the array
+ byte[] newdata = new byte[data.length + opdata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(opdata, 0, newdata, data.length, opdata.length);
+ data = newdata;
+ }
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = Token.ATTRIBUTE.getCode();
+ newdata[data.length + 1] = SUM_MASK;
+ data = newdata;
+ } else if (isIf()) {
+ return getIf();
+ }
+
+ return data;
+ }
+
+ /**
+ * Gets the associated if conditions with this attribute
+ *
+ * @return the associated if conditions
+ */
+ private byte[] getIf() {
+ ParseItem[] operands = ifConditions.getOperands();
+
+ // The position of the offset to the false portion of the expression
+ int falseOffsetPos = 0;
+ int gotoEndPos = 0;
+ int numArgs = operands.length;
+
+ // First, write out the conditions
+ byte[] data = operands[0].getBytes();
+
+ // Grow the array by three and write out the optimized if attribute
+ int pos = data.length;
+ byte[] newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ data = newdata;
+ data[pos] = Token.ATTRIBUTE.getCode();
+ data[pos + 1] = 0x2;
+ falseOffsetPos = pos + 2;
+
+ // Get the true portion of the expression and add it to the array
+ byte[] truedata = operands[1].getBytes();
+ newdata = new byte[data.length + truedata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(truedata, 0, newdata, data.length, truedata.length);
+ data = newdata;
+
+ // Grow the array by three and write out the goto end attribute
+ pos = data.length;
+ newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ data = newdata;
+ data[pos] = Token.ATTRIBUTE.getCode();
+ data[pos + 1] = 0x8;
+ gotoEndPos = pos + 2;
+
+ // If the false condition exists, then add that to the array
+ if (numArgs > 2) {
+ // Set the offset to the false expression to be the current position
+ IntegerHelper.getTwoBytes(data.length - falseOffsetPos - 2,
+ data, falseOffsetPos);
+
+ // Copy in the false expression
+ byte[] falsedata = operands[numArgs - 1].getBytes();
+ newdata = new byte[data.length + falsedata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(falsedata, 0, newdata, data.length, falsedata.length);
+ data = newdata;
+
+ // Write the goto to skip over the varargs token
+ pos = data.length;
+ newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ data = newdata;
+ data[pos] = Token.ATTRIBUTE.getCode();
+ data[pos + 1] = 0x8;
+ data[pos + 2] = 0x3;
+ }
+
+ // Grow the array and write out the varargs function
+ pos = data.length;
+ newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ data = newdata;
+ data[pos] = Token.FUNCTIONVARARG.getCode();
+ data[pos + 1] = (byte) numArgs;
+ data[pos + 2] = 1;
+ data[pos + 3] = 0; // indicates the end of the expression
+
+ // Position the final offsets
+ int endPos = data.length - 1;
+
+ if (numArgs < 3) {
+ // Set the offset to the false expression to be the current position
+ IntegerHelper.getTwoBytes(endPos - falseOffsetPos - 5,
+ data, falseOffsetPos);
+ }
+
+ // Set the offset after the true expression
+ IntegerHelper.getTwoBytes(endPos - gotoEndPos - 2,
+ data, gotoEndPos);
+
+ return data;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnInserted(sheetIndex, col, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnRemoved(sheetIndex, col, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted1
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowInserted(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowRemoved(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = null;
+
+ if (isIf()) {
+ operands = ifConditions.getOperands();
+ } else {
+ operands = getOperands();
+ }
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].handleImportedCellReferences();
+ }
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java
new file mode 100755
index 0000000..0f1edb2
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BinaryOperator.java
@@ -0,0 +1,210 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.common.Logger;
+
+/**
+ * A cell reference in a formula
+ */
+abstract class BinaryOperator extends Operator implements ParsedThing {
+ // The logger
+ private static final Logger logger = Logger.getLogger(BinaryOperator.class);
+
+ /**
+ * Constructor
+ */
+ public BinaryOperator() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ return 0;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ *
+ * @param s the token stack
+ */
+ public void getOperands(Stack s) {
+ ParseItem o1 = (ParseItem) s.pop();
+ ParseItem o2 = (ParseItem) s.pop();
+
+ add(o1);
+ add(o2);
+ }
+
+ /**
+ * Gets the string version of this binary operator
+ *
+ * @param buf a the string buffer
+ */
+ public void getString(StringBuffer buf) {
+ ParseItem[] operands = getOperands();
+ operands[1].getString(buf);
+ buf.append(getSymbol());
+ operands[0].getString(buf);
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = getOperands();
+ operands[1].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[1].columnInserted(sheetIndex, col, currentSheet);
+ operands[0].columnInserted(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[1].columnRemoved(sheetIndex, col, currentSheet);
+ operands[0].columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[1].rowInserted(sheetIndex, row, currentSheet);
+ operands[0].rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[1].rowRemoved(sheetIndex, row, currentSheet);
+ operands[0].rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ // Get the data for the operands
+ ParseItem[] operands = getOperands();
+ byte[] data = new byte[0];
+
+ // Get the operands in reverse order to get the RPN
+ for (int i = operands.length - 1; i >= 0; i--) {
+ byte[] opdata = operands[i].getBytes();
+
+ // Grow the array
+ byte[] newdata = new byte[data.length + opdata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(opdata, 0, newdata, data.length, opdata.length);
+ data = newdata;
+ }
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 1];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = getToken().getCode();
+
+ return newdata;
+ }
+
+ /**
+ * Abstract method which gets the binary operator string symbol
+ *
+ * @return the string symbol for this token
+ */
+ abstract String getSymbol();
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ abstract Token getToken();
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ operands[0].handleImportedCellReferences();
+ operands[1].handleImportedCellReferences();
+ }
+
+}
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java
new file mode 100755
index 0000000..b6abddc
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BooleanValue.java
@@ -0,0 +1,90 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A boolean operand in a formula
+ */
+class BooleanValue extends Operand implements ParsedThing {
+ /**
+ * The boolean value
+ */
+ private boolean value;
+
+ /**
+ * Constructor
+ */
+ public BooleanValue() {
+ }
+
+ /**
+ * Constructor used when parsing a string formula
+ *
+ * @param s the string token, including quote marks
+ */
+ public BooleanValue(String s) {
+ // remove the quotes
+ value = Boolean.valueOf(s).booleanValue();
+ }
+
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ value = data[pos] == 1;
+ return 1;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[2];
+ data[0] = Token.BOOL.getCode();
+ data[1] = (byte) (value == true ? 1 : 0);
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append((new Boolean(value)));
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java
new file mode 100755
index 0000000..5d20c76
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/BuiltInFunction.java
@@ -0,0 +1,263 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.WorkbookSettings;
+import jxl.biff.IntegerHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A built in function in a formula
+ */
+class BuiltInFunction extends Operator implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(BuiltInFunction.class);
+
+ /**
+ * The function
+ */
+ private Function function;
+
+ /**
+ * The workbook settings
+ */
+ private final WorkbookSettings settings;
+
+ /**
+ * Constructor
+ *
+ * @param ws the workbook settings
+ */
+ public BuiltInFunction(WorkbookSettings ws) {
+ settings = ws;
+ }
+
+ /**
+ * Constructor used when parsing a formula from a string
+ *
+ * @param f the function
+ * @param ws the workbook settings
+ */
+ public BuiltInFunction(Function f, WorkbookSettings ws) {
+ function = f;
+ settings = ws;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ int index = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ function = Function.getFunction(index);
+ Assert.verify(function != Function.UNKNOWN, "function code " + index);
+ return 2;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ *
+ * @param s the token stack
+ */
+ public void getOperands(Stack s) {
+ // parameters are in the correct order, god damn them
+ ParseItem[] items = new ParseItem[function.getNumArgs()];
+ // modified in 2.4.3
+ for (int i = function.getNumArgs() - 1; i >= 0; i--) {
+ ParseItem pi = (ParseItem) s.pop();
+
+ items[i] = pi;
+ }
+
+ for (int i = 0; i < function.getNumArgs(); i++) {
+ add(items[i]);
+ }
+ }
+
+ /**
+ * Gets the string for this functions
+ *
+ * @param buf the buffer to append
+ */
+ public void getString(StringBuffer buf) {
+ buf.append(function.getName(settings));
+ buf.append('(');
+
+ int numArgs = function.getNumArgs();
+
+ if (numArgs > 0) {
+ ParseItem[] operands = getOperands();
+
+ // arguments are in the same order they were specified
+ operands[0].getString(buf);
+
+ for (int i = 1; i < numArgs; i++) {
+ buf.append(',');
+ operands[i].getString(buf);
+ }
+ }
+
+ buf.append(')');
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = getOperands();
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnInserted(sheetIndex, col, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnRemoved(sheetIndex, col, currentSheet);
+ }
+ }
+
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowInserted(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowRemoved(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].handleImportedCellReferences();
+ }
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ // Get the data for the operands
+ ParseItem[] operands = getOperands();
+ byte[] data = new byte[0];
+
+ for (int i = 0; i < operands.length; i++) {
+ byte[] opdata = operands[i].getBytes();
+
+ // Grow the array
+ byte[] newdata = new byte[data.length + opdata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(opdata, 0, newdata, data.length, opdata.length);
+ data = newdata;
+ }
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 3];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = !useAlternateCode() ? Token.FUNCTION.getCode() :
+ Token.FUNCTION.getCode2();
+ IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length + 1);
+
+ return newdata;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java
new file mode 100755
index 0000000..5baf15a
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference.java
@@ -0,0 +1,268 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.Cell;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * A cell reference in a formula
+ */
+class CellReference extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(CellReference.class);
+
+ /**
+ * Indicates whether the column reference is relative or absolute
+ */
+ private boolean columnRelative;
+
+ /**
+ * Indicates whether the row reference is relative or absolute
+ */
+ private boolean rowRelative;
+
+ /**
+ * The column reference
+ */
+ private int column;
+
+ /**
+ * The row reference
+ */
+ private int row;
+
+ /**
+ * The cell containing the formula. Stored in order to determine
+ * relative cell values
+ */
+ private Cell relativeTo;
+
+ /**
+ * Constructor
+ *
+ * @param rt the cell containing the formula
+ */
+ public CellReference(Cell rt) {
+ relativeTo = rt;
+ }
+
+ /**
+ * Constructor
+ */
+ public CellReference() {
+ }
+
+ /**
+ * Constructor invoked when parsing a text string
+ *
+ * @param s the string being parsed
+ */
+ public CellReference(String s) {
+ column = CellReferenceHelper.getColumn(s);
+ row = CellReferenceHelper.getRow(s);
+ columnRelative = CellReferenceHelper.isColumnRelative(s);
+ rowRelative = CellReferenceHelper.isRowRelative(s);
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ row = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ int columnMask = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+ column = columnMask & 0x00ff;
+ columnRelative = ((columnMask & 0x4000) != 0);
+ rowRelative = ((columnMask & 0x8000) != 0);
+
+ return 4;
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row
+ */
+ public int getRow() {
+ return row;
+ }
+
+ /**
+ * Gets the cell reference as a string for this item
+ *
+ * @param buf the string buffer to populate
+ */
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference(column, !columnRelative,
+ row, !rowRelative,
+ buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[5];
+ data[0] = !useAlternateCode() ? Token.REF.getCode() :
+ Token.REF.getCode2();
+
+ IntegerHelper.getTwoBytes(row, data, 1);
+
+ int grcol = column;
+
+ // Set the row/column relative bits if applicable
+ if (rowRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 3);
+
+ return data;
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ if (columnRelative) {
+ column += colAdjust;
+ }
+
+ if (rowRelative) {
+ row += rowAdjust;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (column >= col) {
+ column++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (column >= col) {
+ column--;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param r the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int r, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (row >= r) {
+ row++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param r the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int r, boolean currentSheet) {
+ if (!currentSheet) {
+ return;
+ }
+
+ if (row >= r) {
+ row--;
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Flags the formula as invalid
+ * Does nothing here
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java
new file mode 100755
index 0000000..0815fad
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReference3d.java
@@ -0,0 +1,299 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.Cell;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * A 3d cell reference in a formula
+ */
+class CellReference3d extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(CellReference3d.class);
+
+ /**
+ * Indicates whether the column reference is relative or absolute
+ */
+ private boolean columnRelative;
+
+ /**
+ * Indicates whether the row reference is relative or absolute
+ */
+ private boolean rowRelative;
+
+ /**
+ * The column reference
+ */
+ private int column;
+
+ /**
+ * The row reference
+ */
+ private int row;
+
+ /**
+ * The cell containing the formula. Stored in order to determine
+ * relative cell values
+ */
+ private Cell relativeTo;
+
+ /**
+ * The sheet which the reference is present on
+ */
+ private int sheet;
+
+ /**
+ * A handle to the container of the external sheets ie. the workbook
+ */
+ private final ExternalSheet workbook;
+
+ /**
+ * Constructor
+ *
+ * @param rt the cell containing the formula
+ * @param w the list of external sheets
+ */
+ public CellReference3d(Cell rt, ExternalSheet w) {
+ relativeTo = rt;
+ workbook = w;
+ }
+
+ /**
+ * Constructs this object from a string
+ *
+ * @param s the string
+ * @param w the external sheet
+ * @throws FormulaException
+ */
+ public CellReference3d(String s, ExternalSheet w) throws FormulaException {
+ workbook = w;
+ columnRelative = true;
+ rowRelative = true;
+
+ // Get the cell details
+ int sep = s.indexOf('!');
+ String cellString = s.substring(sep + 1);
+ column = CellReferenceHelper.getColumn(cellString);
+ row = CellReferenceHelper.getRow(cellString);
+
+ // Get the sheet index
+ String sheetName = s.substring(0, sep);
+
+ // Remove single quotes, if they exist
+ if (sheetName.charAt(0) == '\'' &&
+ sheetName.charAt(sheetName.length() - 1) == '\'') {
+ sheetName = sheetName.substring(1, sheetName.length() - 1);
+ }
+ sheet = w.getExternalSheetIndex(sheetName);
+
+ if (sheet < 0) {
+ throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND,
+ sheetName);
+ }
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ sheet = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ row = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+ int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ column = columnMask & 0x00ff;
+ columnRelative = ((columnMask & 0x4000) != 0);
+ rowRelative = ((columnMask & 0x8000) != 0);
+
+ return 6;
+ }
+
+ /**
+ * Accessor for the column
+ *
+ * @return the column number
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * Accessor for the row
+ *
+ * @return the row number
+ */
+ public int getRow() {
+ return row;
+ }
+
+ /**
+ * Gets the string version of this cell reference
+ *
+ * @param buf the buffer to append to
+ */
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference(sheet, column, !columnRelative,
+ row, !rowRelative,
+ workbook, buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[7];
+ data[0] = Token.REF3D.getCode();
+
+ IntegerHelper.getTwoBytes(sheet, data, 1);
+ IntegerHelper.getTwoBytes(row, data, 3);
+
+ int grcol = column;
+
+ // Set the row/column relative bits if applicable
+ if (rowRelative) {
+ grcol |= 0x8000;
+ }
+
+ if (columnRelative) {
+ grcol |= 0x4000;
+ }
+
+ IntegerHelper.getTwoBytes(grcol, data, 5);
+
+ return data;
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ if (columnRelative) {
+ column += colAdjust;
+ }
+
+ if (rowRelative) {
+ row += rowAdjust;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (column >= col) {
+ column++;
+ }
+ }
+
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (column >= col) {
+ column--;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param r the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int r, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (row >= r) {
+ row++;
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param r the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int r, boolean currentSheet) {
+ if (sheetIndex != sheet) {
+ return;
+ }
+
+ if (row >= r) {
+ row--;
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Flags the formula as invalid
+ */
+ void handleImportedCellReferences() {
+ setInvalid();
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java
new file mode 100755
index 0000000..5bed91f
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CellReferenceError.java
@@ -0,0 +1,82 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.common.Logger;
+
+/**
+ * An cell reference error which occurs in a formula
+ */
+class CellReferenceError extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(CellReferenceError.class);
+
+ /**
+ * Constructor
+ */
+ public CellReferenceError() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ // the data is unused - just return the four bytes
+
+ return 4;
+ }
+
+ /**
+ * Gets the cell reference as a string for this item
+ *
+ * @param buf the string buffer to populate
+ */
+ public void getString(StringBuffer buf) {
+ buf.append(FormulaErrorCode.REF.getDescription());
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[5];
+ data[0] = Token.REFERR.getCode();
+
+ // bytes 1-5 are unused
+
+ return data;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java b/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java
new file mode 100755
index 0000000..b4b3b37
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/CloseParentheses.java
@@ -0,0 +1,31 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * The close of parentheses token
+ */
+class CloseParentheses extends StringParseItem {
+ /**
+ * Constructor
+ */
+ public CloseParentheses() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java
new file mode 100755
index 0000000..317201e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange.java
@@ -0,0 +1,80 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.CellReferenceHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A class to hold range information across two entire columns
+ */
+class ColumnRange extends Area {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ColumnRange.class);
+
+ /**
+ * Constructor
+ */
+ ColumnRange() {
+ super();
+ }
+
+ /**
+ * Constructor invoked when parsing a string formula
+ *
+ * @param s the string to parse
+ */
+ ColumnRange(String s) {
+ int seppos = s.indexOf(":");
+ Assert.verify(seppos != -1);
+ String startcell = s.substring(0, seppos);
+ String endcell = s.substring(seppos + 1);
+
+ int columnFirst = CellReferenceHelper.getColumn(startcell);
+ int rowFirst = 0;
+ int columnLast = CellReferenceHelper.getColumn(endcell);
+ int rowLast = 0xffff;
+
+ boolean columnFirstRelative =
+ CellReferenceHelper.isColumnRelative(startcell);
+ boolean rowFirstRelative = false;
+ boolean columnLastRelative = CellReferenceHelper.isColumnRelative(endcell);
+ boolean rowLastRelative = false;
+
+ setRangeData(columnFirst, columnLast,
+ rowFirst, rowLast,
+ columnFirstRelative, columnLastRelative,
+ rowFirstRelative, rowLastRelative);
+ }
+
+ /**
+ * Gets the string representation of this item
+ *
+ * @param buf the string buffer
+ */
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getColumnReference(getFirstColumn(), buf);
+ buf.append(':');
+ CellReferenceHelper.getColumnReference(getLastColumn(), buf);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java
new file mode 100755
index 0000000..85fa32e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ColumnRange3d.java
@@ -0,0 +1,122 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.CellReferenceHelper;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A nested class to hold range information
+ */
+class ColumnRange3d extends Area3d {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ColumnRange3d.class);
+
+ /**
+ * A handle to the workbook
+ */
+ private final ExternalSheet workbook;
+
+ /**
+ * The sheet number
+ */
+ private int sheet;
+
+ /**
+ * Constructor
+ *
+ * @param es the external sheet
+ */
+ ColumnRange3d(ExternalSheet es) {
+ super(es);
+ workbook = es;
+ }
+
+ /**
+ * Constructor invoked when parsing a string formula
+ *
+ * @param s the string to parse
+ * @param es the external sheet
+ * @throws FormulaException
+ */
+ ColumnRange3d(String s, ExternalSheet es) throws FormulaException {
+ super(es);
+ workbook = es;
+ int seppos = s.lastIndexOf(":");
+ Assert.verify(seppos != -1);
+ String startcell = s.substring(0, seppos);
+ String endcell = s.substring(seppos + 1);
+
+ // Get the the start cell details
+ int sep = s.indexOf('!');
+ String cellString = s.substring(sep + 1, seppos);
+ int columnFirst = CellReferenceHelper.getColumn(cellString);
+ int rowFirst = 0;
+
+ // Get the sheet index
+ String sheetName = s.substring(0, sep);
+ int sheetNamePos = sheetName.lastIndexOf(']');
+
+ // Remove single quotes, if they exist
+ if (sheetName.charAt(0) == '\'' &&
+ sheetName.charAt(sheetName.length() - 1) == '\'') {
+ sheetName = sheetName.substring(1, sheetName.length() - 1);
+ }
+
+ sheet = es.getExternalSheetIndex(sheetName);
+
+ if (sheet < 0) {
+ throw new FormulaException(FormulaException.SHEET_REF_NOT_FOUND,
+ sheetName);
+ }
+
+ // Get the last cell index
+ int columnLast = CellReferenceHelper.getColumn(endcell);
+ int rowLast = 0xffff;
+
+ boolean columnFirstRelative = true;
+ boolean rowFirstRelative = true;
+ boolean columnLastRelative = true;
+ boolean rowLastRelative = true;
+
+ setRangeData(sheet, columnFirst, columnLast, rowFirst, rowLast,
+ columnFirstRelative, rowFirstRelative,
+ columnLastRelative, rowLastRelative);
+ }
+
+ /**
+ * Gets the string representation of this column range
+ *
+ * @param buf the string buffer to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append('\'');
+ buf.append(workbook.getExternalSheetName(sheet));
+ buf.append('\'');
+ buf.append('!');
+
+ CellReferenceHelper.getColumnReference(getFirstColumn(), buf);
+ buf.append(':');
+ CellReferenceHelper.getColumnReference(getLastColumn(), buf);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java
new file mode 100755
index 0000000..fdcd748
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Concatenate.java
@@ -0,0 +1,59 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Concatenate extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Concatenate() {
+ }
+
+ /**
+ * Gets the string representation of this symbol
+ *
+ * @return the concatenation symbol
+ */
+ public String getSymbol() {
+ return "&";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.CONCAT;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java
new file mode 100755
index 0000000..ecfc299
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Divide.java
@@ -0,0 +1,60 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Divide extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Divide() {
+ }
+
+ /**
+ * Gets the string representation of this symbol
+ *
+ * @return the symbol
+ */
+ public String getSymbol() {
+ return "/";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.DIVIDE;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java
new file mode 100755
index 0000000..c3e0cb3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/DoubleValue.java
@@ -0,0 +1,112 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.DoubleHelper;
+import jxl.common.Logger;
+
+/**
+ * A cell reference in a formula
+ */
+class DoubleValue extends NumberValue implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(DoubleValue.class);
+
+ /**
+ * The value of this double in the formula
+ */
+ private double value;
+
+ /**
+ * Constructor
+ */
+ public DoubleValue() {
+ }
+
+ /**
+ * Constructor - invoked when writing an integer value that's out
+ * of range for a short
+ *
+ * @param v the double value
+ */
+ DoubleValue(double v) {
+ value = v;
+ }
+
+ /**
+ * Constructor for a double value when reading from a string
+ *
+ * @param s the string representation of this token
+ */
+ public DoubleValue(String s) {
+ try {
+ value = Double.parseDouble(s);
+ } catch (NumberFormatException e) {
+ logger.warn(e, e);
+ value = 0;
+ }
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ value = DoubleHelper.getIEEEDouble(data, pos);
+
+ return 8;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[9];
+ data[0] = Token.DOUBLE.getCode();
+
+ DoubleHelper.getIEEEBytes(value, data, 1);
+
+ return data;
+ }
+
+ /**
+ * The double value
+ *
+ * @return the value
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java
new file mode 100755
index 0000000..407a516
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Equal.java
@@ -0,0 +1,61 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Equal extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Equal() {
+ }
+
+ /**
+ * Gets the string for this symbol
+ *
+ * @return the symbol string
+ */
+ public String getSymbol() {
+ return "=";
+ }
+
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.EQUAL;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java
new file mode 100755
index 0000000..f90f55b
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ErrorConstant.java
@@ -0,0 +1,89 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * An error constant
+ */
+class ErrorConstant extends Operand implements ParsedThing {
+ /**
+ * The error
+ */
+ private FormulaErrorCode error;
+
+ /**
+ * Constructor
+ */
+ public ErrorConstant() {
+ }
+
+ /**
+ * Constructor
+ *
+ * @param s the error constant
+ */
+ public ErrorConstant(String s) {
+ error = FormulaErrorCode.getErrorCode(s);
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ int code = data[pos];
+ error = FormulaErrorCode.getErrorCode(code);
+ return 1;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[2];
+ data[0] = Token.ERR.getCode();
+ data[1] = (byte) error.getCode();
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append(error.getDescription());
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java
new file mode 100755
index 0000000..f0838a0
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ExternalSheet.java
@@ -0,0 +1,76 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.read.biff.BOFRecord;
+
+/**
+ * Interface which exposes the methods needed by formulas
+ * to access external sheet records
+ */
+public interface ExternalSheet {
+ /**
+ * Gets the name of the external sheet specified by the index
+ *
+ * @param index the external sheet index
+ * @return the name of the external sheet
+ */
+ String getExternalSheetName(int index);
+
+ /**
+ * Gets the index of the first external sheet for the name
+ *
+ * @param sheetName the name of the external sheet
+ * @return the index of the external sheet with the specified name
+ */
+ int getExternalSheetIndex(String sheetName);
+
+ /**
+ * Gets the index of the first external sheet for the name
+ *
+ * @param index the external sheet index
+ * @return the sheet index of the external sheet index
+ */
+ int getExternalSheetIndex(int index);
+
+ /**
+ * Gets the index of the last external sheet for the name
+ *
+ * @param sheetName the name of the external sheet
+ * @return the index of the external sheet with the specified name
+ */
+ int getLastExternalSheetIndex(String sheetName);
+
+ /**
+ * Gets the index of the first external sheet for the name
+ *
+ * @param index the external sheet index
+ * @return the sheet index of the external sheet index
+ */
+ int getLastExternalSheetIndex(int index);
+
+ /**
+ * Parsing of formulas is only supported for a subset of the available
+ * biff version, so we need to test to see if this version is acceptable
+ *
+ * @return the BOF record
+ */
+ BOFRecord getWorkbookBof();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java
new file mode 100755
index 0000000..4500bef
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaErrorCode.java
@@ -0,0 +1,127 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Enumeration for formula error codes
+ */
+public class FormulaErrorCode {
+ public static final FormulaErrorCode UNKNOWN =
+ new FormulaErrorCode(0xff, "?");
+ public static final FormulaErrorCode NULL =
+ new FormulaErrorCode(0x0, "#NULL!");
+ public static final FormulaErrorCode DIV0 =
+ new FormulaErrorCode(0x7, "#DIV/0!");
+ public static final FormulaErrorCode VALUE =
+ new FormulaErrorCode(0xf, "#VALUE!");
+ public static final FormulaErrorCode REF =
+ new FormulaErrorCode(0x17, "#REF!");
+ public static final FormulaErrorCode NAME =
+ new FormulaErrorCode(0x1d, "#NAME?");
+ public static final FormulaErrorCode NUM = new FormulaErrorCode(0x24,
+ "#NUM!");
+ public static final FormulaErrorCode NA = new FormulaErrorCode(0x2a,
+ "#N/A!");
+ /**
+ * The list of error codes
+ */
+ private static FormulaErrorCode[] codes = new FormulaErrorCode[0];
+ /**
+ * The error code
+ */
+ private final int errorCode;
+ /**
+ * The description
+ */
+ private final String description;
+ /**
+ * Constructor
+ *
+ * @param code the code
+ * @param desc the description
+ */
+ FormulaErrorCode(int code, String desc) {
+ errorCode = code;
+ description = desc;
+ FormulaErrorCode[] newcodes = new FormulaErrorCode[codes.length + 1];
+ System.arraycopy(codes, 0, newcodes, 0, codes.length);
+ newcodes[codes.length] = this;
+ codes = newcodes;
+ }
+
+ /**
+ * Gets the error type given just the code
+ *
+ * @param code the code to lookup
+ * @return the error type
+ */
+ public static FormulaErrorCode getErrorCode(int code) {
+ boolean found = false;
+ FormulaErrorCode ec = UNKNOWN;
+ for (int i = 0; i < codes.length && !found; i++) {
+ if (codes[i].errorCode == code) {
+ found = true;
+ ec = codes[i];
+ }
+ }
+ return ec;
+ }
+
+ /**
+ * Gets the error type given the string value
+ *
+ * @param code the code to lookup
+ * @return the error type
+ */
+ public static FormulaErrorCode getErrorCode(String code) {
+ boolean found = false;
+ FormulaErrorCode ec = UNKNOWN;
+
+ if (code == null || code.length() == 0) {
+ return ec;
+ }
+
+ for (int i = 0; i < codes.length && !found; i++) {
+ if (codes[i].description.equals(code)) {
+ found = true;
+ ec = codes[i];
+ }
+ }
+ return ec;
+ }
+
+ /**
+ * Accessor for the code
+ *
+ * @return the code
+ */
+ public int getCode() {
+ return errorCode;
+ }
+
+ /**
+ * Accessor for the description
+ *
+ * @return the description
+ */
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java
new file mode 100755
index 0000000..3597d80
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaException.java
@@ -0,0 +1,122 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.JXLException;
+
+/**
+ * Exception thrown when parsing a formula
+ */
+public class FormulaException extends JXLException {
+ /**
+ *
+ */
+ public static final FormulaMessage BIFF8_SUPPORTED =
+ new FormulaMessage("Only biff8 formulas are supported");
+ /**
+ *
+ */
+ static final FormulaMessage UNRECOGNIZED_TOKEN =
+ new FormulaMessage("Unrecognized token");
+
+ /**
+ *
+ */
+ static final FormulaMessage UNRECOGNIZED_FUNCTION =
+ new FormulaMessage("Unrecognized function");
+ /**
+ *
+ */
+ static final FormulaMessage LEXICAL_ERROR =
+ new FormulaMessage("Lexical error: ");
+ /**
+ *
+ */
+ static final FormulaMessage INCORRECT_ARGUMENTS =
+ new FormulaMessage("Incorrect arguments supplied to function");
+ /**
+ *
+ */
+ static final FormulaMessage SHEET_REF_NOT_FOUND =
+ new FormulaMessage("Could not find sheet");
+ /**
+ *
+ */
+ static final FormulaMessage CELL_NAME_NOT_FOUND =
+ new FormulaMessage("Could not find named cell");
+
+ /**
+ * Constructs this exception with the specified message
+ *
+ * @param m the message
+ */
+ public FormulaException(FormulaMessage m) {
+ super(m.message);
+ }
+
+
+ /**
+ * Constructs this exception with the specified message
+ *
+ * @param m the message
+ * @param val the value
+ */
+ public FormulaException(FormulaMessage m, int val) {
+ super(m.message + " " + val);
+ }
+
+ /**
+ * Constructs this exception with the specified message
+ *
+ * @param m the message
+ * @param val the value
+ */
+ public FormulaException(FormulaMessage m, String val) {
+ super(m.message + " " + val);
+ }
+
+ /**
+ * Inner class containing the message
+ */
+ private static class FormulaMessage {
+ /**
+ * The message
+ */
+ private final String message;
+
+ /**
+ * Constructs this exception with the specified message
+ *
+ * @param m the message
+ */
+ FormulaMessage(String m) {
+ message = m;
+ }
+
+ /**
+ * Accessor for the message
+ *
+ * @return the message
+ */
+ public String getMessage() {
+ return message;
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java
new file mode 100755
index 0000000..59749ae
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FormulaParser.java
@@ -0,0 +1,239 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the7 Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.Cell;
+import jxl.WorkbookSettings;
+import jxl.biff.WorkbookMethods;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * Parses the formula passed in (either as parsed strings or as a string)
+ * into a tree of operators and operands
+ */
+public class FormulaParser {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(FormulaParser.class);
+
+ /**
+ * The formula parser. The object implementing this interface will either
+ * parse tokens or strings
+ */
+ private final Parser parser;
+
+ /**
+ * Constructor which creates the parse tree out of tokens
+ *
+ * @param tokens the list of parsed tokens
+ * @param rt the cell containing the formula
+ * @param es a handle to the external sheet
+ * @param nt a handle to the name table
+ * @param ws the workbook settings
+ * @param pc the parse context
+ * @throws FormulaException
+ */
+ public FormulaParser(byte[] tokens,
+ Cell rt,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws)
+ throws FormulaException {
+ // A null workbook bof means that it is a writable workbook and therefore
+ // must be biff8
+ if (es.getWorkbookBof() != null &&
+ !es.getWorkbookBof().isBiff8()) {
+ throw new FormulaException(FormulaException.BIFF8_SUPPORTED);
+ }
+ Assert.verify(nt != null);
+ parser = new TokenFormulaParser(tokens, rt, es, nt, ws,
+ ParseContext.DEFAULT);
+ }
+
+ /**
+ * Constructor which creates the parse tree out of tokens
+ *
+ * @param tokens the list of parsed tokens
+ * @param rt the cell containing the formula
+ * @param es a handle to the external sheet
+ * @param nt a handle to the name table
+ * @param ws the workbook settings
+ * @param pc the parse context
+ * @throws FormulaException
+ */
+ public FormulaParser(byte[] tokens,
+ Cell rt,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws,
+ ParseContext pc)
+ throws FormulaException {
+ // A null workbook bof means that it is a writable workbook and therefore
+ // must be biff8
+ if (es.getWorkbookBof() != null &&
+ !es.getWorkbookBof().isBiff8()) {
+ throw new FormulaException(FormulaException.BIFF8_SUPPORTED);
+ }
+ Assert.verify(nt != null);
+ parser = new TokenFormulaParser(tokens, rt, es, nt, ws, pc);
+ }
+
+ /**
+ * Constructor which creates the parse tree out of the string
+ *
+ * @param form the formula string
+ * @param es the external sheet handle
+ * @param nt the name table
+ * @param ws the workbook settings
+ */
+ public FormulaParser(String form,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws) {
+ parser = new StringFormulaParser(form, es, nt, ws,
+ ParseContext.DEFAULT);
+ }
+
+ /**
+ * Constructor which creates the parse tree out of the string
+ *
+ * @param form the formula string
+ * @param es the external sheet handle
+ * @param nt the name table
+ * @param ws the workbook settings
+ * @param pc the context of the parse
+ */
+ public FormulaParser(String form,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws,
+ ParseContext pc) {
+ parser = new StringFormulaParser(form, es, nt, ws, pc);
+ }
+
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ parser.adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Parses the formula into a parse tree
+ *
+ * @throws FormulaException
+ */
+ public void parse() throws FormulaException {
+ parser.parse();
+ }
+
+ /**
+ * Gets the formula as a string
+ *
+ * @return the formula as a string
+ * @throws FormulaException
+ */
+ public String getFormula() throws FormulaException {
+ return parser.getFormula();
+ }
+
+ /**
+ * Gets the bytes for the formula. This takes into account any
+ * token mapping necessary because of shared formulas
+ *
+ * @return the bytes in RPN
+ */
+ public byte[] getBytes() {
+ return parser.getBytes();
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ parser.columnInserted(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ parser.columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ parser.rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ parser.rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ *
+ * @return TRUE if the formula is valid import, FALSE otherwise
+ */
+ public boolean handleImportedCellReferences() {
+ return parser.handleImportedCellReferences();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java
new file mode 100755
index 0000000..8d220d7
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Function.java
@@ -0,0 +1,694 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.WorkbookSettings;
+import jxl.common.Logger;
+
+/**
+ * An enumeration detailing the Excel function codes
+ */
+final class Function {
+ public static final Function COUNT =
+ new Function(0x0, "count", 0xff);
+ public static final Function ATTRIBUTE = new Function(0x1, "", 0xff);
+ public static final Function ISNA =
+ new Function(0x2, "isna", 1);
+ public static final Function ISERROR =
+ new Function(0x3, "iserror", 1);
+ public static final Function SUM =
+ new Function(0x4, "sum", 0xff);
+ public static final Function AVERAGE =
+ new Function(0x5, "average", 0xff);
+ public static final Function MIN =
+ new Function(0x6, "min", 0xff);
+ public static final Function MAX =
+ new Function(0x7, "max", 0xff);
+ public static final Function ROW =
+ new Function(0x8, "row", 0xff);
+ public static final Function COLUMN =
+ new Function(0x9, "column", 0xff);
+ public static final Function NA =
+ new Function(0xa, "na", 0);
+ public static final Function NPV =
+ new Function(0xb, "npv", 0xff);
+ public static final Function STDEV =
+ new Function(0xc, "stdev", 0xff);
+ public static final Function DOLLAR =
+ new Function(0xd, "dollar", 2);
+
+ // The functions
+ public static final Function FIXED =
+ new Function(0xe, "fixed", 0xff);
+ public static final Function SIN =
+ new Function(0xf, "sin", 1);
+ public static final Function COS =
+ new Function(0x10, "cos", 1);
+ public static final Function TAN =
+ new Function(0x11, "tan", 1);
+ public static final Function ATAN =
+ new Function(0x12, "atan", 1);
+ public static final Function PI =
+ new Function(0x13, "pi", 0);
+ public static final Function SQRT =
+ new Function(0x14, "sqrt", 1);
+ public static final Function EXP =
+ new Function(0x15, "exp", 1);
+ public static final Function LN =
+ new Function(0x16, "ln", 1);
+ public static final Function LOG10 =
+ new Function(0x17, "log10", 1);
+ public static final Function ABS =
+ new Function(0x18, "abs", 1);
+ public static final Function INT =
+ new Function(0x19, "int", 1);
+ public static final Function SIGN =
+ new Function(0x1a, "sign", 1);
+ public static final Function ROUND =
+ new Function(0x1b, "round", 2);
+ public static final Function LOOKUP =
+ new Function(0x1c, "lookup", 2);
+ public static final Function INDEX =
+ new Function(0x1d, "index", 3);
+ public static final Function REPT = new Function(0x1e, "rept", 2);
+ public static final Function MID =
+ new Function(0x1f, "mid", 3);
+ public static final Function LEN =
+ new Function(0x20, "len", 1);
+ public static final Function VALUE =
+ new Function(0x21, "value", 1);
+ public static final Function TRUE =
+ new Function(0x22, "true", 0);
+ public static final Function FALSE =
+ new Function(0x23, "false", 0);
+ public static final Function AND =
+ new Function(0x24, "and", 0xff);
+ public static final Function OR =
+ new Function(0x25, "or", 0xff);
+ public static final Function NOT =
+ new Function(0x26, "not", 1);
+ public static final Function MOD =
+ new Function(0x27, "mod", 2);
+ public static final Function DCOUNT =
+ new Function(0x28, "dcount", 3);
+ public static final Function DSUM =
+ new Function(0x29, "dsum", 3);
+ public static final Function DAVERAGE =
+ new Function(0x2a, "daverage", 3);
+ public static final Function DMIN =
+ new Function(0x2b, "dmin", 3);
+ public static final Function DMAX =
+ new Function(0x2c, "dmax", 3);
+ public static final Function DSTDEV =
+ new Function(0x2d, "dstdev", 3);
+ public static final Function VAR =
+ new Function(0x2e, "var", 0xff);
+ public static final Function DVAR =
+ new Function(0x2f, "dvar", 3);
+ public static final Function TEXT =
+ new Function(0x30, "text", 2);
+ public static final Function LINEST =
+ new Function(0x31, "linest", 0xff);
+ public static final Function TREND =
+ new Function(0x32, "trend", 0xff);
+ public static final Function LOGEST =
+ new Function(0x33, "logest", 0xff);
+ public static final Function GROWTH =
+ new Function(0x34, "growth", 0xff);
+ //public static final Function GOTO = new Function(0x35, "GOTO",);
+ //public static final Function HALT = new Function(0x36, "HALT",);
+ public static final Function PV =
+ new Function(0x38, "pv", 0xff);
+ public static final Function FV =
+ new Function(0x39, "fv", 0xff);
+ public static final Function NPER =
+ new Function(0x3a, "nper", 0xff);
+ public static final Function PMT =
+ new Function(0x3b, "pmt", 0xff);
+ public static final Function RATE =
+ new Function(0x3c, "rate", 0xff);
+ //public static final Function MIRR = new Function(0x3d, "MIRR",);
+ //public static final Function IRR = new Function(0x3e, "IRR",);
+ public static final Function RAND =
+ new Function(0x3f, "rand", 0);
+ public static final Function MATCH =
+ new Function(0x40, "match", 3);
+ public static final Function DATE =
+ new Function(0x41, "date", 3);
+ public static final Function TIME =
+ new Function(0x42, "time", 3);
+ public static final Function DAY =
+ new Function(0x43, "day", 1);
+ public static final Function MONTH =
+ new Function(0x44, "month", 1);
+ public static final Function YEAR =
+ new Function(0x45, "year", 1);
+ public static final Function WEEKDAY =
+ new Function(0x46, "weekday", 2);
+ public static final Function HOUR =
+ new Function(0x47, "hour", 1);
+ public static final Function MINUTE =
+ new Function(0x48, "minute", 1);
+ public static final Function SECOND =
+ new Function(0x49, "second", 1);
+ public static final Function NOW =
+ new Function(0x4a, "now", 0);
+ public static final Function AREAS =
+ new Function(0x4b, "areas", 0xff);
+ public static final Function ROWS =
+ new Function(0x4c, "rows", 1);
+ public static final Function COLUMNS =
+ new Function(0x4d, "columns", 0xff);
+ public static final Function OFFSET =
+ new Function(0x4e, "offset", 0xff);
+ //public static final Function ABSREF = new Function(0x4f, "ABSREF",);
+ //public static final Function RELREF = new Function(0x50, "RELREF",);
+ //public static final Function ARGUMENT = new Function(0x51,"ARGUMENT",);
+ public static final Function SEARCH = new Function(0x52, "search", 0xff);
+ public static final Function TRANSPOSE =
+ new Function(0x53, "transpose", 0xff);
+ public static final Function ERROR =
+ new Function(0x54, "error", 1);
+ //public static final Function STEP = new Function(0x55, "STEP",);
+ public static final Function TYPE =
+ new Function(0x56, "type", 1);
+ //public static final Function ECHO = new Function(0x57, "ECHO",);
+ //public static final Function SETNAME = new Function(0x58, "SETNAME",);
+ //public static final Function CALLER = new Function(0x59, "CALLER",);
+ //public static final Function DEREF = new Function(0x5a, "DEREF",);
+ //public static final Function WINDOWS = new Function(0x5b, "WINDOWS",);
+ //public static final Function SERIES = new Function(0x5c, "SERIES",);
+ //public static final Function DOCUMENTS = new Function(0x5d,"DOCUMENTS",);
+ //public static final Function ACTIVECELL = new Function(0x5e,"ACTIVECELL",);
+ //public static final Function SELECTION = new Function(0x5f,"SELECTION",);
+ //public static final Function RESULT = new Function(0x60, "RESULT",);
+ public static final Function ATAN2 =
+ new Function(0x61, "atan2", 1);
+ public static final Function ASIN =
+ new Function(0x62, "asin", 1);
+ public static final Function ACOS =
+ new Function(0x63, "acos", 1);
+ public static final Function CHOOSE =
+ new Function(0x64, "choose", 0xff);
+ public static final Function HLOOKUP =
+ new Function(0x65, "hlookup", 0xff);
+ public static final Function VLOOKUP =
+ new Function(0x66, "vlookup", 0xff);
+ //public static final Function LINKS = new Function(0x67, "LINKS",);
+ //public static final Function INPUT = new Function(0x68, "INPUT",);
+ public static final Function ISREF =
+ new Function(0x69, "isref", 1);
+ //public static final Function GETFORMULA = new Function(0x6a,"GETFORMULA",);
+ //public static final Function GETNAME = new Function(0x6b, "GETNAME",);
+ //public static final Function SETVALUE = new Function(0x6c,"SETVALUE",);
+ public static final Function LOG =
+ new Function(0x6d, "log", 0xff);
+ //public static final Function EXEC = new Function(0x6e, "EXEC",);
+ public static final Function CHAR =
+ new Function(0x6f, "char", 1);
+ public static final Function LOWER =
+ new Function(0x70, "lower", 1);
+ public static final Function UPPER =
+ new Function(0x71, "upper", 1);
+ public static final Function PROPER =
+ new Function(0x72, "proper", 1);
+ public static final Function LEFT =
+ new Function(0x73, "left", 0xff);
+ public static final Function RIGHT =
+ new Function(0x74, "right", 0xff);
+ public static final Function EXACT =
+ new Function(0x75, "exact", 2);
+ public static final Function TRIM =
+ new Function(0x76, "trim", 1);
+ public static final Function REPLACE =
+ new Function(0x77, "replace", 4);
+ public static final Function SUBSTITUTE =
+ new Function(0x78, "substitute", 0xff);
+ public static final Function CODE =
+ new Function(0x79, "code", 1);
+ //public static final Function NAMES = new Function(0x7a, "NAMES",);
+ //public static final Function DIRECTORY = new Function(0x7b,"DIRECTORY",);
+ public static final Function FIND =
+ new Function(0x7c, "find", 0xff);
+ public static final Function CELL =
+ new Function(0x7d, "cell", 2);
+ public static final Function ISERR =
+ new Function(0x7e, "iserr", 1);
+ public static final Function ISTEXT =
+ new Function(0x7f, "istext", 1);
+ public static final Function ISNUMBER =
+ new Function(0x80, "isnumber", 1);
+ public static final Function ISBLANK =
+ new Function(0x81, "isblank", 1);
+ public static final Function T =
+ new Function(0x82, "t", 1);
+ public static final Function N =
+ new Function(0x83, "n", 1);
+ //public static final Function FOPEN = new Function(0x84, "FOPEN",);
+ //public static final Function FCLOSE = new Function(0x85, "FCLOSE",);
+ //public static final Function FSIZE = new Function(0x86, "FSIZE",);
+ //public static final Function FREADLN = new Function(0x87, "FREADLN",);
+ //public static final Function FREAD = new Function(0x88, "FREAD",);
+ //public static final Function FWRITELN = new Function(0x89,"FWRITELN",);
+ //public static final Function FWRITE = new Function(0x8a, "FWRITE",);
+ //public static final Function FPOS = new Function(0x8b, "FPOS",);
+ public static final Function DATEVALUE =
+ new Function(0x8c, "datevalue", 1);
+ public static final Function TIMEVALUE =
+ new Function(0x8d, "timevalue", 1);
+ public static final Function SLN =
+ new Function(0x8e, "sln", 3);
+ public static final Function SYD =
+ new Function(0x8f, "syd", 3);
+ public static final Function DDB =
+ new Function(0x90, "ddb", 0xff);
+ //public static final Function GETDEF = new Function(0x91, "GETDEF",);
+ //public static final Function REFTEXT = new Function(0x92, "REFTEXT",);
+ //public static final Function TEXTREF = new Function(0x93, "TEXTREF",);
+ public static final Function INDIRECT =
+ new Function(0x94, "indirect", 0xff);
+ //public static final Function REGISTER = new Function(0x95,"REGISTER",);
+ //public static final Function CALL = new Function(0x96, "CALL",);
+ //public static final Function ADDBAR = new Function(0x97, "ADDBAR",);
+ //public static final Function ADDMENU = new Function(0x98, "ADDMENU",);
+ //public static final Function ADDCOMMAND =
+ // new Function(0x99,"ADDCOMMAND",);
+ //public static final Function ENABLECOMMAND =
+ // new Function(0x9a,"ENABLECOMMAND",);
+ //public static final Function CHECKCOMMAND =
+ // new Function(0x9b,"CHECKCOMMAND",);
+ //public static final Function RENAMECOMMAND =
+ // new Function(0x9c,"RENAMECOMMAND",);
+ //public static final Function SHOWBAR = new Function(0x9d, "SHOWBAR",);
+ //public static final Function DELETEMENU =
+ // new Function(0x9e,"DELETEMENU",);
+ //public static final Function DELETECOMMAND =
+ // new Function(0x9f,"DELETECOMMAND",);
+ //public static final Function GETCHARTITEM =
+ // new Function(0xa0,"GETCHARTITEM",);
+ //public static final Function DIALOGBOX = new Function(0xa1,"DIALOGBOX",);
+ public static final Function CLEAN =
+ new Function(0xa2, "clean", 1);
+ public static final Function MDETERM =
+ new Function(0xa3, "mdeterm", 0xff);
+ public static final Function MINVERSE =
+ new Function(0xa4, "minverse", 0xff);
+ public static final Function MMULT =
+ new Function(0xa5, "mmult", 0xff);
+ public static final Function IPMT =
+ new Function(0xa7, "ipmt", 0xff);
+ public static final Function PPMT =
+ new Function(0xa8, "ppmt", 0xff);
+ public static final Function COUNTA =
+ new Function(0xa9, "counta", 0xff);
+ public static final Function PRODUCT =
+ new Function(0xb7, "product", 0xff);
+ public static final Function FACT =
+ new Function(0xb8, "fact", 1);
+ //public static final Function GETCELL = new Function(0xb9, "GETCELL",);
+ //public static final Function GETWORKSPACE =
+ // new Function(0xba,"GETWORKSPACE",);
+ //public static final Function GETWINDOW = new Function(0xbb,"GETWINDOW",);
+ //public static final Function GETDOCUMENT =
+ // new Function(0xbc,"GETDOCUMENT",);
+ public static final Function DPRODUCT =
+ new Function(0xbd, "dproduct", 3);
+ public static final Function ISNONTEXT =
+ new Function(0xbe, "isnontext", 1);
+ //public static final Function GETNOTE = new Function(0xbf, "GETNOTE",);
+ //public static final Function NOTE = new Function(0xc0, "NOTE",);
+ public static final Function STDEVP =
+ new Function(0xc1, "stdevp", 0xff);
+ public static final Function VARP =
+ new Function(0xc2, "varp", 0xff);
+ public static final Function DSTDEVP =
+ new Function(0xc3, "dstdevp", 0xff);
+ public static final Function DVARP =
+ new Function(0xc4, "dvarp", 0xff);
+ public static final Function TRUNC =
+ new Function(0xc5, "trunc", 0xff);
+ public static final Function ISLOGICAL =
+ new Function(0xc6, "islogical", 1);
+ public static final Function DCOUNTA =
+ new Function(0xc7, "dcounta", 0xff);
+ //public static final Function FILES = new Function(0xa6, "FILES",
+ public static final Function FINDB =
+ new Function(0xcd, "findb", 0xff);
+ public static final Function SEARCHB =
+ new Function(0xce, "searchb", 3);
+ public static final Function REPLACEB =
+ new Function(0xcf, "replaceb", 4);
+ public static final Function LEFTB =
+ new Function(0xd0, "leftb", 0xff);
+ public static final Function RIGHTB =
+ new Function(0xd1, "rightb", 0xff);
+ public static final Function MIDB =
+ new Function(0xd2, "midb", 3);
+ public static final Function LENB =
+ new Function(0xd3, "lenb", 1);
+ public static final Function ROUNDUP =
+ new Function(0xd4, "roundup", 2);
+ public static final Function ROUNDDOWN =
+ new Function(0xd5, "rounddown", 2);
+ public static final Function RANK =
+ new Function(0xd8, "rank", 0xff);
+ public static final Function ADDRESS =
+ new Function(0xdb, "address", 0xff);
+ public static final Function AYS360 =
+ new Function(0xdc, "days360", 0xff);
+ public static final Function ODAY =
+ new Function(0xdd, "today", 0);
+ public static final Function VDB =
+ new Function(0xde, "vdb", 0xff);
+ public static final Function MEDIAN =
+ new Function(0xe3, "median", 0xff);
+ public static final Function SUMPRODUCT =
+ new Function(0xe4, "sumproduct", 0xff);
+ public static final Function SINH =
+ new Function(0xe5, "sinh", 1);
+ public static final Function COSH =
+ new Function(0xe6, "cosh", 1);
+ public static final Function TANH =
+ new Function(0xe7, "tanh", 1);
+ public static final Function ASINH =
+ new Function(0xe8, "asinh", 1);
+ public static final Function ACOSH =
+ new Function(0xe9, "acosh", 1);
+ public static final Function ATANH =
+ new Function(0xea, "atanh", 1);
+ public static final Function INFO =
+ new Function(0xf4, "info", 1);
+ public static final Function AVEDEV =
+ new Function(0x10d, "avedev", 0XFF);
+ public static final Function BETADIST =
+ new Function(0x10e, "betadist", 0XFF);
+ public static final Function GAMMALN =
+ new Function(0x10f, "gammaln", 1);
+ public static final Function BETAINV =
+ new Function(0x110, "betainv", 0XFF);
+ public static final Function BINOMDIST =
+ new Function(0x111, "binomdist", 4);
+ public static final Function CHIDIST =
+ new Function(0x112, "chidist", 2);
+ public static final Function CHIINV =
+ new Function(0x113, "chiinv", 2);
+ public static final Function COMBIN =
+ new Function(0x114, "combin", 2);
+ public static final Function CONFIDENCE =
+ new Function(0x115, "confidence", 3);
+ public static final Function CRITBINOM =
+ new Function(0x116, "critbinom", 3);
+ public static final Function EVEN =
+ new Function(0x117, "even", 1);
+ public static final Function EXPONDIST =
+ new Function(0x118, "expondist", 3);
+ public static final Function FDIST =
+ new Function(0x119, "fdist", 3);
+ public static final Function FINV =
+ new Function(0x11a, "finv", 3);
+ public static final Function FISHER =
+ new Function(0x11b, "fisher", 1);
+ public static final Function FISHERINV =
+ new Function(0x11c, "fisherinv", 1);
+ public static final Function FLOOR =
+ new Function(0x11d, "floor", 2);
+ public static final Function GAMMADIST =
+ new Function(0x11e, "gammadist", 4);
+ public static final Function GAMMAINV =
+ new Function(0x11f, "gammainv", 3);
+ public static final Function CEILING =
+ new Function(0x120, "ceiling", 2);
+ public static final Function HYPGEOMDIST =
+ new Function(0x121, "hypgeomdist", 4);
+ public static final Function LOGNORMDIST =
+ new Function(0x122, "lognormdist", 3);
+ public static final Function LOGINV =
+ new Function(0x123, "loginv", 3);
+ public static final Function NEGBINOMDIST =
+ new Function(0x124, "negbinomdist", 3);
+ public static final Function NORMDIST =
+ new Function(0x125, "normdist", 4);
+ public static final Function NORMSDIST =
+ new Function(0x126, "normsdist", 1);
+ public static final Function NORMINV =
+ new Function(0x127, "norminv", 3);
+ public static final Function NORMSINV =
+ new Function(0x128, "normsinv", 1);
+ public static final Function STANDARDIZE =
+ new Function(0x129, "standardize", 3);
+ public static final Function ODD =
+ new Function(0x12a, "odd", 1);
+ public static final Function PERMUT =
+ new Function(0x12b, "permut", 2);
+ public static final Function POISSON =
+ new Function(0x12c, "poisson", 3);
+ public static final Function TDIST =
+ new Function(0x12d, "tdist", 3);
+ public static final Function WEIBULL =
+ new Function(0x12e, "weibull", 4);
+ public static final Function SUMXMY2 =
+ new Function(303, "sumxmy2", 0xff);
+ public static final Function SUMX2MY2 =
+ new Function(304, "sumx2my2", 0xff);
+ public static final Function SUMX2PY2 =
+ new Function(305, "sumx2py2", 0xff);
+ public static final Function CHITEST =
+ new Function(0x132, "chitest", 0xff);
+ public static final Function CORREL =
+ new Function(0x133, "correl", 0xff);
+ public static final Function COVAR =
+ new Function(0x134, "covar", 0xff);
+ public static final Function FORECAST =
+ new Function(0x135, "forecast", 0xff);
+ public static final Function FTEST =
+ new Function(0x136, "ftest", 0xff);
+ public static final Function INTERCEPT =
+ new Function(0x137, "intercept", 0xff);
+ public static final Function PEARSON =
+ new Function(0x138, "pearson", 0xff);
+ public static final Function RSQ =
+ new Function(0x139, "rsq", 0xff);
+ public static final Function STEYX =
+ new Function(0x13a, "steyx", 0xff);
+ public static final Function SLOPE =
+ new Function(0x13b, "slope", 2);
+ public static final Function TTEST =
+ new Function(0x13c, "ttest", 0xff);
+ public static final Function PROB =
+ new Function(0x13d, "prob", 0xff);
+ public static final Function DEVSQ =
+ new Function(0x13e, "devsq", 0xff);
+ public static final Function GEOMEAN =
+ new Function(0x13f, "geomean", 0xff);
+ public static final Function HARMEAN =
+ new Function(0x140, "harmean", 0xff);
+ public static final Function SUMSQ =
+ new Function(0x141, "sumsq", 0xff);
+ public static final Function KURT =
+ new Function(0x142, "kurt", 0xff);
+ public static final Function SKEW =
+ new Function(0x143, "skew", 0xff);
+ public static final Function ZTEST =
+ new Function(0x144, "ztest", 0xff);
+ public static final Function LARGE =
+ new Function(0x145, "large", 0xff);
+ public static final Function SMALL =
+ new Function(0x146, "small", 0xff);
+ public static final Function QUARTILE =
+ new Function(0x147, "quartile", 0xff);
+ public static final Function PERCENTILE =
+ new Function(0x148, "percentile", 0xff);
+ public static final Function PERCENTRANK =
+ new Function(0x149, "percentrank", 0xff);
+ public static final Function MODE =
+ new Function(0x14a, "mode", 0xff);
+ public static final Function TRIMMEAN =
+ new Function(0x14b, "trimmean", 0xff);
+ public static final Function TINV =
+ new Function(0x14c, "tinv", 2);
+ public static final Function CONCATENATE =
+ new Function(0x150, "concatenate", 0xff);
+ public static final Function POWER =
+ new Function(0x151, "power", 2);
+ public static final Function RADIANS =
+ new Function(0x156, "radians", 1);
+ public static final Function DEGREES =
+ new Function(0x157, "degrees", 1);
+ public static final Function SUBTOTAL =
+ new Function(0x158, "subtotal", 0xff);
+ public static final Function SUMIF =
+ new Function(0x159, "sumif", 0xff);
+ public static final Function COUNTIF =
+ new Function(0x15a, "countif", 2);
+ public static final Function COUNTBLANK =
+ new Function(0x15b, "countblank", 1);
+ public static final Function HYPERLINK =
+ new Function(0x167, "hyperlink", 2);
+ public static final Function AVERAGEA =
+ new Function(0x169, "averagea", 0xff);
+ public static final Function MAXA =
+ new Function(0x16a, "maxa", 0xff);
+ public static final Function MINA =
+ new Function(0x16b, "mina", 0xff);
+ public static final Function STDEVPA =
+ new Function(0x16c, "stdevpa", 0xff);
+ public static final Function VARPA =
+ new Function(0x16d, "varpa", 0xff);
+ public static final Function STDEVA =
+ new Function(0x16e, "stdeva", 0xff);
+ public static final Function VARA =
+ new Function(0x16f, "vara", 0xff);
+ // If token. This is not an excel assigned number, but one made up
+ // in order that the if command may be recognized
+ public static final Function IF =
+ new Function(0xfffe, "if", 0xff);
+ // Unknown token
+ public static final Function UNKNOWN = new Function(0xffff, "", 0);
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Function.class);
+ /**
+ * All available functions. This attribute is package protected in order
+ * to enable the FunctionNames to initialize
+ */
+ private static Function[] functions = new Function[0];
+ /**
+ * The code which applies to this function
+ */
+ private final int code;
+ /**
+ * The property name of this function
+ */
+ private final String name;
+ /**
+ * The number of args this function expects
+ */
+ private final int numArgs;
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ * @param s the string
+ * @param a the number of arguments
+ */
+ private Function(int v, String s, int a) {
+ code = v;
+ name = s;
+ numArgs = a;
+
+ // Grow the array
+ Function[] newarray = new Function[functions.length + 1];
+ System.arraycopy(functions, 0, newarray, 0, functions.length);
+ newarray[functions.length] = this;
+ functions = newarray;
+ }
+
+ /**
+ * Gets the type object from its integer value
+ *
+ * @param v the function value
+ * @return the function
+ */
+ public static Function getFunction(int v) {
+ Function f = null;
+
+ for (int i = 0; i < functions.length; i++) {
+ if (functions[i].code == v) {
+ f = functions[i];
+ break;
+ }
+ }
+
+ return f != null ? f : UNKNOWN;
+ }
+
+ /**
+ * Gets the type object from its string value. Used when parsing strings
+ *
+ * @param v the function name
+ * @param ws the workbook settings
+ * @return the function
+ */
+ public static Function getFunction(String v, WorkbookSettings ws) {
+ FunctionNames fn = ws.getFunctionNames();
+ Function f = fn.getFunction(v);
+ return f != null ? f : UNKNOWN;
+ }
+
+ /**
+ * Accessor for all the functions, used by the internationalization
+ * work around
+ *
+ * @return all the functions
+ */
+ static Function[] getFunctions() {
+ return functions;
+ }
+
+ /**
+ * Standard hash code method
+ *
+ * @return the hash code
+ */
+ public int hashCode() {
+ return code;
+ }
+
+ /**
+ * Gets the function code - used when generating token data
+ *
+ * @return the code
+ */
+ int getCode() {
+ return code;
+ }
+
+ /**
+ * Gets the property name. Used by the FunctionNames object when initializing
+ * the locale specific names
+ *
+ * @return the property name for this function
+ */
+ String getPropertyName() {
+ return name;
+ }
+
+ /**
+ * Gets the function name
+ *
+ * @param ws the workbook settings
+ * @return the function name
+ */
+ String getName(WorkbookSettings ws) {
+ FunctionNames fn = ws.getFunctionNames();
+ return fn.getName(this);
+ }
+
+ /**
+ * Gets the number of arguments for this function
+ *
+ * @return the number of arguments
+ */
+ int getNumArgs() {
+ return numArgs;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java b/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java
new file mode 100755
index 0000000..d648b4d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/FunctionNames.java
@@ -0,0 +1,97 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import jxl.common.Logger;
+
+/**
+ * A class which contains the function names for the current workbook. The
+ * function names can potentially vary from workbook to workbook depending
+ * on the locale
+ */
+public class FunctionNames {
+ /**
+ * The logger class
+ */
+ private static final Logger logger = Logger.getLogger(FunctionNames.class);
+
+ /**
+ * A hash mapping keyed on the function and returning its locale specific
+ * name
+ */
+ private final HashMap names;
+
+ /**
+ * A hash mapping keyed on the locale specific name and returning the
+ * function
+ */
+ private final HashMap functions;
+
+ /**
+ * Constructor
+ *
+ * @param l the locale
+ */
+ public FunctionNames(Locale l) {
+ ResourceBundle rb = ResourceBundle.getBundle("functions", l);
+ Function[] allfunctions = Function.getFunctions();
+ names = new HashMap(allfunctions.length);
+ functions = new HashMap(allfunctions.length);
+
+ // Iterate through all the functions, adding them to the hash maps
+ Function f = null;
+ String n = null;
+ String propname = null;
+ for (int i = 0; i < allfunctions.length; i++) {
+ f = allfunctions[i];
+ propname = f.getPropertyName();
+
+ n = propname.length() != 0 ? rb.getString(propname) : null;
+
+ if (n != null) {
+ names.put(f, n);
+ functions.put(n, f);
+ }
+ }
+ }
+
+ /**
+ * Gets the function for the specified name
+ *
+ * @param s the string
+ * @return the function
+ */
+ Function getFunction(String s) {
+ return (Function) functions.get(s);
+ }
+
+ /**
+ * Gets the name for the function
+ *
+ * @param f the function
+ * @return the string
+ */
+ String getName(Function f) {
+ return (String) names.get(f);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java
new file mode 100755
index 0000000..798f3d2
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterEqual.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class GreaterEqual extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public GreaterEqual() {
+ }
+
+ public String getSymbol() {
+ return ">=";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.GREATER_EQUAL;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java
new file mode 100755
index 0000000..0cadd98
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/GreaterThan.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class GreaterThan extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public GreaterThan() {
+ }
+
+ public String getSymbol() {
+ return ">";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.GREATER_THAN;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java
new file mode 100755
index 0000000..e8e5e20
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/IntegerValue.java
@@ -0,0 +1,118 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * A cell reference in a formula
+ */
+class IntegerValue extends NumberValue implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(IntegerValue.class);
+
+ /**
+ * The value of this integer
+ */
+ private double value;
+
+ /**
+ * Flag which indicates whether or not this integer is out range
+ */
+ private final boolean outOfRange;
+
+ /**
+ * Constructor
+ */
+ public IntegerValue() {
+ outOfRange = false;
+ }
+
+ /**
+ * Constructor for an integer value being read from a string
+ */
+ public IntegerValue(String s) {
+ try {
+ value = Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ logger.warn(e, e);
+ value = 0;
+ }
+
+ short v = (short) value;
+ outOfRange = (value != v);
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ value = IntegerHelper.getInt(data[pos], data[pos + 1]);
+
+ return 2;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[3];
+ data[0] = Token.INTEGER.getCode();
+
+ IntegerHelper.getTwoBytes((int) value, data, 1);
+
+ return data;
+ }
+
+ /**
+ * Accessor for the value
+ *
+ * @return the value
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /**
+ * Accessor for the out of range flag
+ *
+ * @return TRUE if the value is out of range for an excel integer
+ */
+ boolean isOutOfRange() {
+ return outOfRange;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java
new file mode 100755
index 0000000..10dea8c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/LessEqual.java
@@ -0,0 +1,66 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class LessEqual extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public LessEqual() {
+ }
+
+ public String getSymbol() {
+ return "<=";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.LESS_EQUAL;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java b/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java
new file mode 100755
index 0000000..5e11973
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/LessThan.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class LessThan extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public LessThan() {
+ }
+
+ public String getSymbol() {
+ return "<";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.LESS_THAN;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java
new file mode 100755
index 0000000..67b9ed0
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MemArea.java
@@ -0,0 +1,67 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2007 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.IntegerHelper;
+
+/**
+ * Indicates that the function doesn't evaluate to a constant reference
+ */
+class MemArea extends SubExpression {
+ /**
+ * Constructor
+ */
+ public MemArea() {
+ }
+
+ public void getString(StringBuffer buf) {
+ ParseItem[] subExpression = getSubExpression();
+
+ if (subExpression.length == 1) {
+ subExpression[0].getString(buf);
+ } else if (subExpression.length == 2) {
+ subExpression[1].getString(buf);
+ buf.append(':');
+ subExpression[0].getString(buf);
+ }
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ // For mem areas, the first four bytes are not used
+ setLength(IntegerHelper.getInt(data[pos + 4], data[pos + 5]));
+ return 6;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java
new file mode 100755
index 0000000..12c1842
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MemFunc.java
@@ -0,0 +1,46 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Indicates that the function doesn't evaluate to a constant reference
+ */
+class MemFunc extends SubExpression {
+ /**
+ * Constructor
+ */
+ public MemFunc() {
+ }
+
+ public void getString(StringBuffer sb) {
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java
new file mode 100755
index 0000000..a741512
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Minus.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.common.Logger;
+
+/**
+ * Ambiguously defined minus operator, used as a place holder when parsing
+ * string formulas. At this stage it could be either
+ * a unary or binary operator - the string parser will deduce which and
+ * create the appropriate type
+ */
+class Minus extends StringOperator {
+ // The logger
+ private static final Logger logger = Logger.getLogger(StringOperator.class);
+
+ /**
+ * Constructor
+ */
+ public Minus() {
+ super();
+ }
+
+ /**
+ * Abstract method which gets the binary version of this operator
+ */
+ Operator getBinaryOperator() {
+ return new Subtract();
+ }
+
+ /**
+ * Abstract method which gets the unary version of this operator
+ */
+ Operator getUnaryOperator() {
+ return new UnaryMinus();
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java b/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java
new file mode 100755
index 0000000..8f61caf
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/MissingArg.java
@@ -0,0 +1,73 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Represents a missing argument in an argument list
+ */
+class MissingArg extends Operand implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public MissingArg() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position.
+ * A missing argument contains no associated data
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ return 0;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[1];
+ data[0] = Token.MISSING_ARG.getCode();
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java
new file mode 100755
index 0000000..12a4362
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Multiply.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Multiply extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Multiply() {
+ }
+
+ public String getSymbol() {
+ return "*";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.MULTIPLY;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java
new file mode 100755
index 0000000..082c3d9
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Name.java
@@ -0,0 +1,72 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A name operand
+ */
+class Name extends Operand implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Name() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ return 6;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[6];
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append("[Name record not implemented]");
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Sets this formula to invalid
+ */
+ void handleImportedCellReferences() {
+ setInvalid();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java
new file mode 100755
index 0000000..1d8de91
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NameRange.java
@@ -0,0 +1,137 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.NameRangeException;
+import jxl.biff.WorkbookMethods;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * A name operand
+ */
+class NameRange extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(NameRange.class);
+
+ /**
+ * A handle to the name table
+ */
+ private final WorkbookMethods nameTable;
+
+ /**
+ * The string name
+ */
+ private String name;
+
+ /**
+ * The index into the name table
+ */
+ private int index;
+
+ /**
+ * Constructor
+ */
+ public NameRange(WorkbookMethods nt) {
+ nameTable = nt;
+ Assert.verify(nameTable != null);
+ }
+
+ /**
+ * Constructor when parsing a string via the api
+ *
+ * @param nm the name string
+ * @param nt the name table
+ */
+ public NameRange(String nm, WorkbookMethods nt) throws FormulaException {
+ name = nm;
+ nameTable = nt;
+
+ index = nameTable.getNameIndex(name);
+
+ if (index < 0) {
+ throw new FormulaException(FormulaException.CELL_NAME_NOT_FOUND, name);
+ }
+
+ index += 1; // indexes are 1-based
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) throws FormulaException {
+ try {
+ index = IntegerHelper.getInt(data[pos], data[pos + 1]);
+
+ name = nameTable.getName(index - 1); // ilbl is 1-based
+
+ return 4;
+ } catch (NameRangeException e) {
+ throw new FormulaException(FormulaException.CELL_NAME_NOT_FOUND, "");
+ }
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[5];
+
+ data[0] = Token.NAMED_RANGE.getValueCode();
+
+ if (getParseContext() == ParseContext.DATA_VALIDATION) {
+ data[0] = Token.NAMED_RANGE.getReferenceCode();
+ }
+
+ IntegerHelper.getTwoBytes(index, data, 1);
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append(name);
+ }
+
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Flags the formula as invalid
+ */
+ void handleImportedCellReferences() {
+ setInvalid();
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java
new file mode 100755
index 0000000..8750c36
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NotEqual.java
@@ -0,0 +1,55 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class NotEqual extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public NotEqual() {
+ }
+
+ public String getSymbol() {
+ return "<>";
+ }
+
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.NOT_EQUAL;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java
new file mode 100755
index 0000000..4ebfa5d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/NumberValue.java
@@ -0,0 +1,34 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A constant numerical value in a formula
+ */
+abstract class NumberValue extends Operand implements ParsedThing {
+ protected NumberValue() {
+ }
+
+ public abstract double getValue();
+
+ public void getString(StringBuffer buf) {
+ buf.append(getValue());
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java b/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java
new file mode 100755
index 0000000..6e709b1
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/OpenParentheses.java
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A dummy token used when parsing strings in order to indicate the
+ * opening of some parenthesees
+ */
+class OpenParentheses extends StringParseItem {
+ /**
+ * Constructor
+ */
+ public OpenParentheses() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java
new file mode 100755
index 0000000..a3198c5
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Operand.java
@@ -0,0 +1,102 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * An operand in the parse tree. Operands represent leaf nodes in the
+ * tree, and may not have children
+ * Operands include numerical values, cell references and ranges
+ */
+abstract class Operand extends ParseItem {
+ /**
+ * Constructor
+ */
+ public Operand() {
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java
new file mode 100755
index 0000000..77faf2b
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Operator.java
@@ -0,0 +1,84 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+
+/**
+ * An operator is a node in a parse tree. Its children can be other
+ * operators or operands
+ * Arithmetic operators and functions are all considered operators
+ */
+abstract class Operator extends ParseItem {
+ /**
+ * The items which this operator manipulates. There will be at most two
+ */
+ private ParseItem[] operands;
+
+ /**
+ * Constructor
+ */
+ public Operator() {
+ operands = new ParseItem[0];
+ }
+
+ /**
+ * Tells the operands to use the alternate code
+ */
+ protected void setOperandAlternateCode() {
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].setAlternateCode();
+ }
+ }
+
+ /**
+ * Adds operands to this item
+ */
+ protected void add(ParseItem n) {
+ n.setParent(this);
+
+ // Grow the array
+ ParseItem[] newOperands = new ParseItem[operands.length + 1];
+ System.arraycopy(operands, 0, newOperands, 0, operands.length);
+ newOperands[operands.length] = n;
+ operands = newOperands;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ */
+ public abstract void getOperands(Stack s);
+
+ /**
+ * Gets the operands ie. the children of the node
+ */
+ protected ParseItem[] getOperands() {
+ return operands;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ abstract int getPrecedence();
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java
new file mode 100755
index 0000000..8083dfb
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Parenthesis.java
@@ -0,0 +1,179 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+
+/**
+ * A cell reference in a formula
+ */
+class Parenthesis extends Operator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Parenthesis() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ return 0;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ */
+ public void getOperands(Stack s) {
+ ParseItem pi = (ParseItem) s.pop();
+
+ add(pi);
+ }
+
+ public void getString(StringBuffer buf) {
+ ParseItem[] operands = getOperands();
+ buf.append('(');
+ operands[0].getString(buf);
+ buf.append(')');
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = getOperands();
+ operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].columnInserted(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ operands[0].handleImportedCellReferences();
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.PARENTHESIS;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ // Get the data for the operands
+ ParseItem[] operands = getOperands();
+ byte[] data = operands[0].getBytes();
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 1];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = getToken().getCode();
+
+ return newdata;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 4;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java
new file mode 100755
index 0000000..7f75326
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseContext.java
@@ -0,0 +1,34 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Enumeration type for the context in which this formula is being parsed
+ */
+public class ParseContext {
+ public static ParseContext DEFAULT = new ParseContext();
+ public static ParseContext DATA_VALIDATION = new ParseContext();
+
+ /**
+ * Private constructor
+ */
+ private ParseContext() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java
new file mode 100755
index 0000000..a560ecf
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParseItem.java
@@ -0,0 +1,231 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.common.Logger;
+
+/**
+ * Abstract base class for an item in a formula parse tree
+ */
+abstract class ParseItem {
+ // The logger
+ private static final Logger logger = Logger.getLogger(ParseItem.class);
+
+ /**
+ * The parent of this parse item
+ */
+ private ParseItem parent;
+
+ /**
+ * Volatile flag
+ */
+ private boolean volatileFunction;
+
+ /**
+ * Indicates that the alternative token code should be used
+ *
+ * @deprecated - use the ParseContext now
+ */
+ private boolean alternateCode;
+
+ /**
+ * Indicates that an alternative token code should be used
+ */
+ private ParseContext parseContext;
+
+ /**
+ * Indicates whether this tree represents a valid formula or not. If not
+ * the parser replaces it with a valid one
+ */
+ private boolean valid;
+
+ /**
+ * Constructor
+ */
+ public ParseItem() {
+ volatileFunction = false;
+ alternateCode = false;
+ valid = true;
+ parseContext = ParseContext.DEFAULT;
+ }
+
+ /**
+ * Called by this class to initialize the parent
+ */
+ protected void setParent(ParseItem p) {
+ parent = p;
+ }
+
+ /**
+ * Sets the volatile flag and ripples all the way up the parse tree
+ */
+ protected void setVolatile() {
+ volatileFunction = true;
+ if (parent != null && !parent.isVolatile()) {
+ parent.setVolatile();
+ }
+ }
+
+ /**
+ * Sets the invalid flag and ripples all the way up the parse tree
+ */
+ protected final void setInvalid() {
+ valid = false;
+ if (parent != null) {
+ parent.setInvalid();
+ }
+ }
+
+ /**
+ * Accessor for the volatile function
+ *
+ * @return TRUE if the formula is volatile, FALSE otherwise
+ */
+ final boolean isVolatile() {
+ return volatileFunction;
+ }
+
+ /**
+ * Accessor for the volatile function
+ *
+ * @return TRUE if the formula is volatile, FALSE otherwise
+ */
+ final boolean isValid() {
+ return valid;
+ }
+
+ /**
+ * Gets the string representation of this item
+ *
+ * @param ws the workbook settings
+ */
+ abstract void getString(StringBuffer buf);
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ abstract byte[] getBytes();
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ abstract void adjustRelativeCellReferences(int colAdjust, int rowAdjust);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ abstract void columnInserted(int sheetIndex, int col, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ abstract void columnRemoved(int sheetIndex, int col, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ abstract void rowInserted(int sheetIndex, int row, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ abstract void rowRemoved(int sheetIndex, int row, boolean currentSheet);
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ */
+ abstract void handleImportedCellReferences();
+
+ /**
+ * Tells the operands to use the alternate code
+ *
+ * @deprecated - use setParseContext now
+ */
+ protected void setAlternateCode() {
+ alternateCode = true;
+ }
+
+ /**
+ * Accessor for the alternate code flag
+ *
+ * @return TRUE to use the alternate code, FALSE otherwise
+ * @deprecated - use setParseContext now
+ */
+ protected final boolean useAlternateCode() {
+ return alternateCode;
+ }
+
+ /**
+ * Accessor for the alternate code flag
+ *
+ * @return the parse context
+ */
+ protected final ParseContext getParseContext() {
+ return parseContext;
+ }
+
+ /**
+ * Tells the operands to use the alternate code
+ *
+ * @pc the parse context
+ */
+ protected void setParseContext(ParseContext pc) {
+ parseContext = pc;
+ }
+
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java b/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java
new file mode 100755
index 0000000..30cba98
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/ParsedThing.java
@@ -0,0 +1,34 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * An interface for an excel ptg
+ */
+interface ParsedThing {
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ int read(byte[] data, int pos) throws FormulaException;
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java
new file mode 100755
index 0000000..da9ce6e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Parser.java
@@ -0,0 +1,113 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Interface used by the two different types of formula parser
+ */
+interface Parser {
+ /**
+ * Parses the formula
+ *
+ * @throws FormulaException if an error occurs
+ */
+ void parse() throws FormulaException;
+
+ /**
+ * Gets the string version of the formula
+ *
+ * @return the formula as a string
+ */
+ String getFormula();
+
+ /**
+ * Gets the bytes for the formula. This takes into account any
+ * token mapping necessary because of shared formulas
+ *
+ * @return the bytes in RPN
+ */
+ byte[] getBytes();
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified.
+ *
+ * @param colAdjust
+ * @param rowAdjust
+ */
+ void adjustRelativeCellReferences(int colAdjust, int rowAdjust);
+
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param row the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet);
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param row the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet);
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ *
+ * @return TRUE if the formula is valid import, FALSE otherwise
+ */
+ boolean handleImportedCellReferences();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java
new file mode 100755
index 0000000..da9aecb
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Percent.java
@@ -0,0 +1,70 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Percent extends UnaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Percent() {
+ }
+
+ public String getSymbol() {
+ return "%";
+ }
+
+ public void getString(StringBuffer buf) {
+ ParseItem[] operands = getOperands();
+ operands[0].getString(buf);
+ buf.append(getSymbol());
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ operands[0].handleImportedCellReferences();
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.PERCENT;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java
new file mode 100755
index 0000000..f32c781
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Plus.java
@@ -0,0 +1,58 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * Ambiguously defined plus operator, used as a place holder when parsing
+ * string formulas. At this stage it could be either
+ * a unary or binary operator - the string parser will deduce which and
+ * create the appropriate type
+ */
+class Plus extends StringOperator {
+ /**
+ * Constructor
+ */
+ public Plus() {
+ super();
+ }
+
+ /**
+ * Abstract method which gets the binary version of this operator
+ */
+ Operator getBinaryOperator() {
+ return new Add();
+ }
+
+ /**
+ * Abstract method which gets the unary version of this operator
+ */
+ Operator getUnaryOperator() {
+ return new UnaryPlus();
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java
new file mode 100755
index 0000000..ef7c01a
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Power.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Power extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Power() {
+ }
+
+ public String getSymbol() {
+ return "^";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.POWER;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 1;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java
new file mode 100755
index 0000000..5514862
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/RangeSeparator.java
@@ -0,0 +1,81 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.biff.IntegerHelper;
+
+/**
+ * A "holding" token for a range separator. This token gets instantiated
+ * when the lexical analyzer can't distinguish a range cleanly, eg in the
+ * case where where one of the identifiers of the range is a formula
+ */
+class RangeSeparator extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public RangeSeparator() {
+ }
+
+ public String getSymbol() {
+ return ":";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.RANGE;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 1;
+ }
+
+ /**
+ * Overrides the getBytes() method in the base class and prepends the
+ * memFunc token
+ *
+ * @return the bytes
+ */
+ byte[] getBytes() {
+ setVolatile();
+ setOperandAlternateCode();
+
+ byte[] funcBytes = super.getBytes();
+
+ byte[] bytes = new byte[funcBytes.length + 3];
+ System.arraycopy(funcBytes, 0, bytes, 3, funcBytes.length);
+
+ // Indicate the mem func
+ bytes[0] = Token.MEM_FUNC.getCode();
+ IntegerHelper.getTwoBytes(funcBytes.length, bytes, 1);
+
+ return bytes;
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java
new file mode 100755
index 0000000..cfe9bf6
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaArea.java
@@ -0,0 +1,152 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.Cell;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+
+/**
+ * A cell reference in a formula
+ */
+class SharedFormulaArea extends Operand implements ParsedThing {
+ private int columnFirst;
+ private int rowFirst;
+ private int columnLast;
+ private int rowLast;
+
+ private boolean columnFirstRelative;
+ private boolean rowFirstRelative;
+ private boolean columnLastRelative;
+ private boolean rowLastRelative;
+
+ /**
+ * The cell containing the formula. Stored in order to determine
+ * relative cell values
+ */
+ private final Cell relativeTo;
+
+ /**
+ * Constructor
+ *
+ * @param the cell the formula is relative to
+ */
+ public SharedFormulaArea(Cell rt) {
+ relativeTo = rt;
+ }
+
+ int getFirstColumn() {
+ return columnFirst;
+ }
+
+ int getFirstRow() {
+ return rowFirst;
+ }
+
+ int getLastColumn() {
+ return columnLast;
+ }
+
+ int getLastRow() {
+ return rowLast;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ // Preserve signage on column and row values, because they will
+ // probably be relative
+
+ rowFirst = IntegerHelper.getShort(data[pos], data[pos + 1]);
+ rowLast = IntegerHelper.getShort(data[pos + 2], data[pos + 3]);
+
+ int columnMask = IntegerHelper.getInt(data[pos + 4], data[pos + 5]);
+ columnFirst = columnMask & 0x00ff;
+ columnFirstRelative = ((columnMask & 0x4000) != 0);
+ rowFirstRelative = ((columnMask & 0x8000) != 0);
+
+ if (columnFirstRelative) {
+ columnFirst = relativeTo.getColumn() + columnFirst;
+ }
+
+ if (rowFirstRelative) {
+ rowFirst = relativeTo.getRow() + rowFirst;
+ }
+
+ columnMask = IntegerHelper.getInt(data[pos + 6], data[pos + 7]);
+ columnLast = columnMask & 0x00ff;
+
+ columnLastRelative = ((columnMask & 0x4000) != 0);
+ rowLastRelative = ((columnMask & 0x8000) != 0);
+
+ if (columnLastRelative) {
+ columnLast = relativeTo.getColumn() + columnLast;
+ }
+
+ if (rowLastRelative) {
+ rowLast = relativeTo.getRow() + rowLast;
+ }
+
+
+ return 8;
+ }
+
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference(columnFirst, rowFirst, buf);
+ buf.append(':');
+ CellReferenceHelper.getCellReference(columnLast, rowLast, buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[9];
+ data[0] = Token.AREA.getCode();
+
+ // Use absolute references for columns, so don't bother about
+ // the col relative/row relative bits
+ IntegerHelper.getTwoBytes(rowFirst, data, 1);
+ IntegerHelper.getTwoBytes(rowLast, data, 3);
+ IntegerHelper.getTwoBytes(columnFirst, data, 5);
+ IntegerHelper.getTwoBytes(columnLast, data, 7);
+
+ return data;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+
+}
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java
new file mode 100755
index 0000000..b6f2755
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SharedFormulaCellReference.java
@@ -0,0 +1,155 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.Cell;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+
+/**
+ * A cell reference in a formula
+ */
+class SharedFormulaCellReference extends Operand implements ParsedThing {
+ // The logger
+ private static final Logger logger =
+ Logger.getLogger(SharedFormulaCellReference.class);
+
+ /**
+ * Indicates whether the column reference is relative or absolute
+ */
+ private boolean columnRelative;
+
+ /**
+ * Indicates whether the row reference is relative or absolute
+ */
+ private boolean rowRelative;
+
+ /**
+ * The column reference
+ */
+ private int column;
+
+ /**
+ * The row reference
+ */
+ private int row;
+
+ /**
+ * The cell containing the formula. Stored in order to determine
+ * relative cell values
+ */
+ private final Cell relativeTo;
+
+ /**
+ * Constructor
+ *
+ * @param the cell the formula is relative to
+ */
+ public SharedFormulaCellReference(Cell rt) {
+ relativeTo = rt;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ // Preserve signage on column and row values, because they will
+ // probably be relative
+ row = IntegerHelper.getShort(data[pos], data[pos + 1]);
+
+ int columnMask = IntegerHelper.getInt(data[pos + 2], data[pos + 3]);
+
+ column = (byte) (columnMask & 0xff);
+ columnRelative = ((columnMask & 0x4000) != 0);
+ rowRelative = ((columnMask & 0x8000) != 0);
+
+ if (columnRelative && relativeTo != null) {
+ column = relativeTo.getColumn() + column;
+ }
+
+ if (rowRelative && relativeTo != null) {
+ row = relativeTo.getRow() + row;
+ }
+
+ return 4;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public int getRow() {
+ return row;
+ }
+
+ public void getString(StringBuffer buf) {
+ CellReferenceHelper.getCellReference(column, row, buf);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[5];
+ data[0] = Token.REF.getCode();
+
+ IntegerHelper.getTwoBytes(row, data, 1);
+
+ int columnMask = column;
+
+ if (columnRelative) {
+ columnMask |= 0x4000;
+ }
+
+ if (rowRelative) {
+ columnMask |= 0x8000;
+ }
+
+ IntegerHelper.getTwoBytes(columnMask, data, 3);
+
+ return data;
+ }
+
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+}
+
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java
new file mode 100755
index 0000000..10e3116
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFormulaParser.java
@@ -0,0 +1,501 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+import jxl.WorkbookSettings;
+import jxl.biff.WorkbookMethods;
+import jxl.common.Logger;
+
+/**
+ * Parses a string formula into a parse tree
+ */
+class StringFormulaParser implements Parser {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(StringFormulaParser.class);
+
+ /**
+ * The formula string passed to this object
+ */
+ private final String formula;
+
+ /**
+ * The parsed formula string, as retrieved from the parse tree
+ */
+ private String parsedFormula;
+
+ /**
+ * The parse tree
+ */
+ private ParseItem root;
+
+ /**
+ * The stack argument used when parsing a function in order to
+ * pass multiple arguments back to the calling method
+ */
+ private Stack arguments;
+
+ /**
+ * The workbook settings
+ */
+ private final WorkbookSettings settings;
+
+ /**
+ * A handle to the external sheet
+ */
+ private final ExternalSheet externalSheet;
+
+ /**
+ * A handle to the name table
+ */
+ private final WorkbookMethods nameTable;
+
+ /**
+ * The parse context
+ */
+ private final ParseContext parseContext;
+
+ /**
+ * Constructor
+ *
+ * @param f
+ * @param ws
+ */
+ public StringFormulaParser(String f,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws,
+ ParseContext pc) {
+ formula = f;
+ settings = ws;
+ externalSheet = es;
+ nameTable = nt;
+ parseContext = pc;
+ }
+
+ /**
+ * Parses the list of tokens
+ *
+ * @throws FormulaException
+ */
+ public void parse() throws FormulaException {
+ ArrayList tokens = getTokens();
+
+ Iterator i = tokens.iterator();
+
+ root = parseCurrent(i);
+ }
+
+ /**
+ * Recursively parses the token array. Recursion is used in order
+ * to evaluate parentheses and function arguments
+ *
+ * @param i an iterator of tokens
+ * @return the root node of the current parse stack
+ * @throws FormulaException if an error occurs
+ */
+ private ParseItem parseCurrent(Iterator i) throws FormulaException {
+ Stack stack = new Stack();
+ Stack operators = new Stack();
+ Stack args = null; // we usually don't need this
+
+ boolean parenthesesClosed = false;
+ ParseItem lastParseItem = null;
+
+ while (i.hasNext() && !parenthesesClosed) {
+ ParseItem pi = (ParseItem) i.next();
+ pi.setParseContext(parseContext);
+
+ if (pi instanceof Operand) {
+ handleOperand((Operand) pi, stack);
+ } else if (pi instanceof StringFunction) {
+ handleFunction((StringFunction) pi, i, stack);
+ } else if (pi instanceof Operator) {
+ Operator op = (Operator) pi;
+
+ // See if the operator is a binary or unary operator
+ // It is a unary operator either if the stack is empty, or if
+ // the last thing off the stack was another operator
+ if (op instanceof StringOperator) {
+ StringOperator sop = (StringOperator) op;
+ if (stack.isEmpty() || lastParseItem instanceof Operator) {
+ op = sop.getUnaryOperator();
+ } else {
+ op = sop.getBinaryOperator();
+ }
+ }
+
+ if (operators.empty()) {
+ // nothing much going on, so do nothing for the time being
+ operators.push(op);
+ } else {
+ Operator operator = (Operator) operators.peek();
+
+ // If the last operator has a higher precedence then add this to
+ // the operator stack and wait
+ if (op.getPrecedence() < operator.getPrecedence()) {
+ operators.push(op);
+ } else if (op.getPrecedence() == operator.getPrecedence() &&
+ op instanceof UnaryOperator) {
+ // The operators are of equal precedence, but because it is a
+ // unary operator the operand isn't available yet, so put it on
+ // the stack
+ operators.push(op);
+ } else {
+ // The operator is of a lower precedence so we can sort out
+ // some of the items on the stack
+ operators.pop(); // remove the operator from the stack
+ operator.getOperands(stack);
+ stack.push(operator);
+ operators.push(op);
+ }
+ }
+ } else if (pi instanceof ArgumentSeparator) {
+ // Clean up any remaining items on this stack
+ while (!operators.isEmpty()) {
+ Operator o = (Operator) operators.pop();
+ o.getOperands(stack);
+ stack.push(o);
+ }
+
+ // Add it to the argument stack. Create the argument stack
+ // if necessary. Items will be stored on the argument stack in
+ // reverse order
+ if (args == null) {
+ args = new Stack();
+ }
+
+ args.push(stack.pop());
+ stack.clear();
+ } else if (pi instanceof OpenParentheses) {
+ ParseItem pi2 = parseCurrent(i);
+ Parenthesis p = new Parenthesis();
+ pi2.setParent(p);
+ p.add(pi2);
+ stack.push(p);
+ } else if (pi instanceof CloseParentheses) {
+ parenthesesClosed = true;
+ }
+
+ lastParseItem = pi;
+ }
+
+ while (!operators.isEmpty()) {
+ Operator o = (Operator) operators.pop();
+ o.getOperands(stack);
+ stack.push(o);
+ }
+
+ ParseItem rt = !stack.empty() ? (ParseItem) stack.pop() : null;
+
+ // if the argument stack is not null, then add it to that stack
+ // as well for good measure
+ if (args != null && rt != null) {
+ args.push(rt);
+ }
+
+ arguments = args;
+
+ if (!stack.empty() || !operators.empty()) {
+ logger.warn("Formula " + formula +
+ " has a non-empty parse stack");
+ }
+
+ return rt;
+ }
+
+ /**
+ * Gets the list of lexical tokens using the generated lexical analyzer
+ *
+ * @return the list of tokens
+ * @throws FormulaException if an error occurs
+ */
+ private ArrayList getTokens() throws FormulaException {
+ ArrayList tokens = new ArrayList();
+
+ StringReader sr = new StringReader(formula);
+ Yylex lex = new Yylex(sr);
+ lex.setExternalSheet(externalSheet);
+ lex.setNameTable(nameTable);
+ try {
+ ParseItem pi = lex.yylex();
+ while (pi != null) {
+ tokens.add(pi);
+ pi = lex.yylex();
+ }
+ } catch (IOException e) {
+ logger.warn(e.toString());
+ } catch (Error e) {
+ throw new FormulaException(FormulaException.LEXICAL_ERROR,
+ formula + " at char " + lex.getPos());
+ }
+
+ return tokens;
+ }
+
+ /**
+ * Gets the formula as a string. Uses the parse tree to do this, and
+ * does not simply return whatever string was passed in
+ */
+ public String getFormula() {
+ if (parsedFormula == null) {
+ StringBuffer sb = new StringBuffer();
+ root.getString(sb);
+ parsedFormula = sb.toString();
+ }
+
+ return parsedFormula;
+ }
+
+ /**
+ * Gets the bytes for the formula
+ *
+ * @return the bytes in RPN
+ */
+ public byte[] getBytes() {
+ byte[] bytes = root.getBytes();
+
+ if (root.isVolatile()) {
+ byte[] newBytes = new byte[bytes.length + 4];
+ System.arraycopy(bytes, 0, newBytes, 4, bytes.length);
+ newBytes[0] = Token.ATTRIBUTE.getCode();
+ newBytes[1] = (byte) 0x1;
+ bytes = newBytes;
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Handles the case when parsing a string when a token is a function
+ *
+ * @param sf the string function
+ * @param i the token iterator
+ * @param stack the parse tree stack
+ * @throws FormulaException if an error occurs
+ */
+ private void handleFunction(StringFunction sf, Iterator i,
+ Stack stack)
+ throws FormulaException {
+ ParseItem pi2 = parseCurrent(i);
+
+ // If the function is unknown, then throw an error
+ if (sf.getFunction(settings) == Function.UNKNOWN) {
+ throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION);
+ }
+
+ // First check for possible optimized functions and possible
+ // use of the Attribute token
+ if (sf.getFunction(settings) == Function.SUM && arguments == null) {
+ // this is handled by an attribute
+ Attribute a = new Attribute(sf, settings);
+ a.add(pi2);
+ stack.push(a);
+ return;
+ }
+
+ if (sf.getFunction(settings) == Function.IF) {
+ // this is handled by an attribute
+ Attribute a = new Attribute(sf, settings);
+
+ // Add in the if conditions as a var arg function in
+ // the correct order
+ VariableArgFunction vaf = new VariableArgFunction(settings);
+ int numargs = arguments.size();
+ for (int j = 0; j < numargs; j++) {
+ ParseItem pi3 = (ParseItem) arguments.get(j);
+ vaf.add(pi3);
+ }
+
+ a.setIfConditions(vaf);
+ stack.push(a);
+ return;
+ }
+
+ // Function cannot be optimized. See if it is a variable argument
+ // function or not
+ if (sf.getFunction(settings).getNumArgs() == 0xff) {
+ // If the arg stack has not been initialized, it means
+ // that there was only one argument, which is the
+ // returned parse item
+ if (arguments == null) {
+ int numArgs = pi2 != null ? 1 : 0;
+ VariableArgFunction vaf = new VariableArgFunction
+ (sf.getFunction(settings), numArgs, settings);
+
+ if (pi2 != null) {
+ vaf.add(pi2);
+ }
+
+ stack.push(vaf);
+ } else {
+ // Add the args to the function in the correct order
+ int numargs = arguments.size();
+ VariableArgFunction vaf = new VariableArgFunction
+ (sf.getFunction(settings), numargs, settings);
+
+ ParseItem[] args = new ParseItem[numargs];
+ for (int j = 0; j < numargs; j++) {
+ ParseItem pi3 = (ParseItem) arguments.pop();
+ args[numargs - j - 1] = pi3;
+ }
+
+ for (int j = 0; j < args.length; j++) {
+ vaf.add(args[j]);
+ }
+ stack.push(vaf);
+ arguments.clear();
+ arguments = null;
+ }
+ return;
+ }
+
+ // Function is a standard built in function
+ BuiltInFunction bif = new BuiltInFunction(sf.getFunction(settings),
+ settings);
+
+ int numargs = sf.getFunction(settings).getNumArgs();
+ if (numargs == 1) {
+ // only one item which is the returned ParseItem
+ bif.add(pi2);
+ } else {
+ if ((arguments == null && numargs != 0) ||
+ (arguments != null && numargs != arguments.size())) {
+ throw new FormulaException(FormulaException.INCORRECT_ARGUMENTS);
+ }
+ // multiple arguments so go to the arguments stack.
+ // Unlike the variable argument function, the args are
+ // stored in reverse order
+ for (int j = 0; j < numargs; j++) {
+ ParseItem pi3 = (ParseItem) arguments.get(j);
+ bif.add(pi3);
+ }
+ }
+ stack.push(bif);
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ root.adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ root.columnInserted(sheetIndex, col, currentSheet);
+ }
+
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ root.columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param row the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ root.rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param row the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ root.rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Handles operands by pushing them onto the stack
+ *
+ * @param o operand
+ * @param stack stack
+ */
+ private void handleOperand(Operand o, Stack stack) {
+ if (!(o instanceof IntegerValue)) {
+ stack.push(o);
+ return;
+ }
+
+ if (o instanceof IntegerValue) {
+ IntegerValue iv = (IntegerValue) o;
+ if (!iv.isOutOfRange()) {
+ stack.push(iv);
+ } else {
+ // convert to a double
+ DoubleValue dv = new DoubleValue(iv.getValue());
+ stack.push(dv);
+ }
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ *
+ * @return TRUE if the formula is valid import, FALSE otherwise
+ */
+ public boolean handleImportedCellReferences() {
+ root.handleImportedCellReferences();
+ return root.isValid();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java
new file mode 100755
index 0000000..1eb2feb
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringFunction.java
@@ -0,0 +1,66 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.WorkbookSettings;
+import jxl.common.Logger;
+
+/**
+ * Class used to hold a function when reading it in from a string. At this
+ * stage it is unknown whether it is a BuiltInFunction or a VariableArgFunction
+ */
+class StringFunction extends StringParseItem {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(StringFunction.class);
+
+ /**
+ * The function
+ */
+ private Function function;
+
+ /**
+ * The function string
+ */
+ private final String functionString;
+
+ /**
+ * Constructor
+ *
+ * @param s the lexically parsed stirng
+ */
+ StringFunction(String s) {
+ functionString = s.substring(0, s.length() - 1);
+ }
+
+ /**
+ * Accessor for the function
+ *
+ * @param ws the workbook settings
+ * @return the function
+ */
+ Function getFunction(WorkbookSettings ws) {
+ if (function == null) {
+ function = Function.getFunction(functionString, ws);
+ }
+ return function;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java
new file mode 100755
index 0000000..f2da274
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringOperator.java
@@ -0,0 +1,148 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.common.Assert;
+
+/**
+ * Ambiguously defined operator, used as a place holder when parsing
+ * string formulas. At this stage it could be either
+ * a unary or binary operator - the string parser will deduce which and
+ * create the appropriate type
+ */
+abstract class StringOperator extends Operator {
+ /**
+ * Constructor
+ */
+ protected StringOperator() {
+ super();
+ }
+
+ /**
+ * Gets the operands for this operator from the stack. Does nothing
+ * here
+ */
+ public void getOperands(Stack s) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Gets the precedence for this operator. Does nothing here
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ Assert.verify(false);
+ return 0;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN. Does nothing here
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ Assert.verify(false);
+ return null;
+ }
+
+ /**
+ * Gets the string representation of this item
+ */
+ void getString(StringBuffer buf) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ Assert.verify(false);
+ }
+
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ Assert.verify(false);
+ }
+
+ /**
+ * Abstract method which gets the binary version of this operator
+ */
+ abstract Operator getBinaryOperator();
+
+ /**
+ * Abstract method which gets the unary version of this operator
+ */
+ abstract Operator getUnaryOperator();
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java
new file mode 100755
index 0000000..a64cb8d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringParseItem.java
@@ -0,0 +1,118 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A dummy implementation used for typing information when tokens
+ * are read when parsing strings. These are then stored by the parser before
+ * being re-stored as the appropriate RPN syntactic equivalent
+ */
+class StringParseItem extends ParseItem {
+ /**
+ * Constructor
+ */
+ protected StringParseItem() {
+ }
+
+ /**
+ * Gets the string representation of this item. Does nothing here
+ *
+ * @param buf
+ */
+ void getString(StringBuffer buf) {
+ }
+
+ /**
+ * Gets the token representation of this item in RPN. Does nothing here
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ return new byte[0];
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ }
+
+ /**
+ * Default behaviour is to do nothing
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java b/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java
new file mode 100755
index 0000000..61edacc
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/StringValue.java
@@ -0,0 +1,121 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import jxl.WorkbookSettings;
+import jxl.biff.StringHelper;
+import jxl.common.Logger;
+
+
+/**
+ * A string constant operand in a formula
+ */
+class StringValue extends Operand implements ParsedThing {
+ /**
+ * The logger
+ */
+ private final static Logger logger = Logger.getLogger(StringValue.class);
+
+ /**
+ * The string value
+ */
+ private String value;
+
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings settings;
+
+ /**
+ * Constructor
+ */
+ public StringValue(WorkbookSettings ws) {
+ settings = ws;
+ }
+
+ /**
+ * Constructor used when parsing a string formula
+ *
+ * @param s the string token, including quote marks
+ */
+ public StringValue(String s) {
+ // remove the quotes
+ value = s;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ int length = data[pos] & 0xff;
+ int consumed = 2;
+
+ if ((data[pos + 1] & 0x01) == 0) {
+ value = StringHelper.getString(data, length, pos + 2, settings);
+ consumed += length;
+ } else {
+ value = StringHelper.getUnicodeString(data, length, pos + 2);
+ consumed += length * 2;
+ }
+
+ return consumed;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ byte[] data = new byte[value.length() * 2 + 3];
+ data[0] = Token.STRING.getCode();
+ data[1] = (byte) (value.length());
+ data[2] = 0x01;
+ StringHelper.getUnicodeBytes(value, data, 3);
+
+ return data;
+ }
+
+ /**
+ * Abstract method implementation to get the string equivalent of this
+ * token
+ *
+ * @param buf the string to append to
+ */
+ public void getString(StringBuffer buf) {
+ buf.append("\"");
+ buf.append(value);
+ buf.append("\"");
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing
+ */
+ void handleImportedCellReferences() {
+ }
+
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java b/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java
new file mode 100755
index 0000000..4260dac
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/SubExpression.java
@@ -0,0 +1,105 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2007 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.biff.IntegerHelper;
+
+/**
+ * Base class for those tokens which encapsulate a subexpression
+ */
+abstract class SubExpression extends Operand implements ParsedThing {
+ /**
+ * The number of bytes in the subexpression
+ */
+ private int length;
+
+ /**
+ * The sub expression
+ */
+ private ParseItem[] subExpression;
+
+ /**
+ * Constructor
+ */
+ protected SubExpression() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ length = IntegerHelper.getInt(data[pos], data[pos + 1]);
+ return 2;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ */
+ public void getOperands(Stack s) {
+ }
+
+ /**
+ * Gets the token representation of this item in RPN. The Attribute
+ * token is a special case, which overrides anything useful we could do
+ * in the base class
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ return null;
+ }
+
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 5;
+ }
+
+ /**
+ * Accessor for the length
+ *
+ * @return the length of the subexpression
+ */
+ public int getLength() {
+ return length;
+ }
+
+ protected final void setLength(int l) {
+ length = l;
+ }
+
+ protected ParseItem[] getSubExpression() {
+ return subExpression;
+ }
+
+ public void setSubExpression(ParseItem[] pi) {
+ subExpression = pi;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java
new file mode 100755
index 0000000..0d60f9c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Subtract.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class Subtract extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Subtract() {
+ }
+
+ public String getSymbol() {
+ return "-";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.SUBTRACT;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 4;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java
new file mode 100755
index 0000000..a87dfb2
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Token.java
@@ -0,0 +1,197 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.HashMap;
+
+/**
+ * An enumeration detailing the Excel parsed tokens
+ * A particular token may be associated with more than one token code
+ */
+class Token {
+ // Operands
+ public static final Token REF = new Token(0x44, 0x24, 0x64);
+ public static final Token REF3D = new Token(0x5a, 0x3a, 0x7a);
+ public static final Token MISSING_ARG = new Token(0x16);
+ public static final Token STRING = new Token(0x17);
+ public static final Token ERR = new Token(0x1c);
+ public static final Token BOOL = new Token(0x1d);
+ public static final Token INTEGER = new Token(0x1e);
+ public static final Token DOUBLE = new Token(0x1f);
+ public static final Token REFERR = new Token(0x2a, 0x4a, 0x6a);
+ public static final Token REFV = new Token(0x2c, 0x4c, 0x6c);
+ public static final Token AREAV = new Token(0x2d, 0x4d, 0x6d);
+ public static final Token MEM_AREA = new Token(0x26, 0x46, 0x66);
+ public static final Token AREA = new Token(0x25, 0x65, 0x45);
+ public static final Token NAMED_RANGE = new Token(0x23, 0x43, 0x63);
+ //need 0x23 for data validation references
+ public static final Token NAME = new Token(0x39, 0x59);
+ public static final Token AREA3D = new Token(0x3b, 0x5b);
+ // Unary Operators
+ public static final Token UNARY_PLUS = new Token(0x12);
+ public static final Token UNARY_MINUS = new Token(0x13);
+ public static final Token PERCENT = new Token(0x14);
+ public static final Token PARENTHESIS = new Token(0x15);
+ // Binary Operators
+ public static final Token ADD = new Token(0x3);
+ public static final Token SUBTRACT = new Token(0x4);
+ public static final Token MULTIPLY = new Token(0x5);
+ public static final Token DIVIDE = new Token(0x6);
+ public static final Token POWER = new Token(0x7);
+ public static final Token CONCAT = new Token(0x8);
+ public static final Token LESS_THAN = new Token(0x9);
+ public static final Token LESS_EQUAL = new Token(0xa);
+ public static final Token EQUAL = new Token(0xb);
+ public static final Token GREATER_EQUAL = new Token(0xc);
+ public static final Token GREATER_THAN = new Token(0xd);
+ public static final Token NOT_EQUAL = new Token(0xe);
+ public static final Token UNION = new Token(0x10);
+ public static final Token RANGE = new Token(0x11);
+ // Functions
+ public static final Token FUNCTION = new Token(0x41, 0x21, 0x61);
+ public static final Token FUNCTIONVARARG = new Token(0x42, 0x22, 0x62);
+ // Control
+ public static final Token ATTRIBUTE = new Token(0x19);
+ public static final Token MEM_FUNC = new Token(0x29, 0x49, 0x69);
+ // Unknown token
+ public static final Token UNKNOWN = new Token(0xffff);
+ /**
+ * All available tokens, keyed on value
+ */
+ private static final HashMap tokens = new HashMap(20);
+ /**
+ * The array of values which apply to this token
+ */
+ public final int[] value;
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ */
+ private Token(int v) {
+ value = new int[]{v};
+
+ tokens.put(new Integer(v), this);
+ }
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ */
+ private Token(int v1, int v2) {
+ value = new int[]{v1, v2};
+
+ tokens.put(new Integer(v1), this);
+ tokens.put(new Integer(v2), this);
+ }
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ */
+ private Token(int v1, int v2, int v3) {
+ value = new int[]{v1, v2, v3};
+
+ tokens.put(new Integer(v1), this);
+ tokens.put(new Integer(v2), this);
+ tokens.put(new Integer(v3), this);
+ }
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ */
+ private Token(int v1, int v2, int v3, int v4) {
+ value = new int[]{v1, v2, v3, v4};
+
+ tokens.put(new Integer(v1), this);
+ tokens.put(new Integer(v2), this);
+ tokens.put(new Integer(v3), this);
+ tokens.put(new Integer(v4), this);
+ }
+ /**
+ * Constructor
+ * Sets the token value and adds this token to the array of all token
+ *
+ * @param v the biff code for the token
+ */
+ private Token(int v1, int v2, int v3, int v4, int v5) {
+ value = new int[]{v1, v2, v3, v4, v5};
+
+ tokens.put(new Integer(v1), this);
+ tokens.put(new Integer(v2), this);
+ tokens.put(new Integer(v3), this);
+ tokens.put(new Integer(v4), this);
+ tokens.put(new Integer(v5), this);
+ }
+
+ /**
+ * Gets the type object from its integer value
+ */
+ public static Token getToken(int v) {
+ Token t = (Token) tokens.get(new Integer(v));
+
+ return t != null ? t : UNKNOWN;
+ }
+
+ /**
+ * Gets the token code for the specified token
+ *
+ * @return the token code. This is the first item in the array
+ */
+ public byte getCode() {
+ return (byte) value[0];
+ }
+
+ /**
+ * Gets the reference token code for the specified token. This is always
+ * the first on the list
+ *
+ * @return the token code. This is the first item in the array
+ */
+ public byte getReferenceCode() {
+ return (byte) value[0];
+ }
+
+ /**
+ * Gets the an alternative token code for the specified token
+ * Used for certain types of volatile function
+ *
+ * @return the token code
+ */
+ public byte getCode2() {
+ return (byte) (value.length > 0 ? value[1] : value[0]);
+ }
+
+ /**
+ * Gets the value token code for the specified token. This is always
+ * the second item on the list
+ *
+ * @return the token code
+ */
+ public byte getValueCode() {
+ return (byte) (value.length > 0 ? value[1] : value[0]);
+ }
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java b/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java
new file mode 100755
index 0000000..03e09a0
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/TokenFormulaParser.java
@@ -0,0 +1,474 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.Cell;
+import jxl.WorkbookSettings;
+import jxl.biff.WorkbookMethods;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * Parses the excel ptgs into a parse tree
+ */
+class TokenFormulaParser implements Parser {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(TokenFormulaParser.class);
+
+ /**
+ * The Excel ptgs
+ */
+ private final byte[] tokenData;
+
+ /**
+ * The cell containing the formula. This is used in order to determine
+ * relative cell values
+ */
+ private final Cell relativeTo;
+
+ /**
+ * The current position within the array
+ */
+ private int pos;
+
+ /**
+ * The parse tree
+ */
+ private ParseItem root;
+
+ /**
+ * The hash table of items that have been parsed
+ */
+ private Stack tokenStack;
+
+ /**
+ * A reference to the workbook which holds the external sheet
+ * information
+ */
+ private final ExternalSheet workbook;
+
+ /**
+ * A reference to the name table
+ */
+ private final WorkbookMethods nameTable;
+
+ /**
+ * The workbook settings
+ */
+ private final WorkbookSettings settings;
+
+ /**
+ * The parse context
+ */
+ private final ParseContext parseContext;
+
+ /**
+ * Constructor
+ */
+ public TokenFormulaParser(byte[] data,
+ Cell c,
+ ExternalSheet es,
+ WorkbookMethods nt,
+ WorkbookSettings ws,
+ ParseContext pc) {
+ tokenData = data;
+ pos = 0;
+ relativeTo = c;
+ workbook = es;
+ nameTable = nt;
+ tokenStack = new Stack();
+ settings = ws;
+ parseContext = pc;
+
+ Assert.verify(nameTable != null);
+ }
+
+ /**
+ * Parses the sublist of tokens. In most cases this will equate to
+ * the full list
+ *
+ * @throws FormulaException
+ */
+ public void parse() throws FormulaException {
+ parseSubExpression(tokenData.length);
+
+ // Finally, there should be one thing left on the stack. Get that
+ // and add it to the root node
+ root = (ParseItem) tokenStack.pop();
+
+ Assert.verify(tokenStack.empty());
+
+ }
+
+ /**
+ * Parses the sublist of tokens. In most cases this will equate to
+ * the full list
+ *
+ * @param len the length of the subexpression to parse
+ * @throws FormulaException
+ */
+ private void parseSubExpression(int len) throws FormulaException {
+ int tokenVal = 0;
+ Token t = null;
+
+ // Indicates that we are parsing the incredibly complicated and
+ // hacky if construct that MS saw fit to include, the gits
+ Stack ifStack = new Stack();
+
+ // The end position of the sub-expression
+ int endpos = pos + len;
+
+ while (pos < endpos) {
+ tokenVal = tokenData[pos];
+ pos++;
+
+ t = Token.getToken(tokenVal);
+
+ if (t == Token.UNKNOWN) {
+ throw new FormulaException
+ (FormulaException.UNRECOGNIZED_TOKEN, tokenVal);
+ }
+
+ Assert.verify(t != Token.UNKNOWN);
+
+ // Operands
+ if (t == Token.REF) {
+ CellReference cr = new CellReference(relativeTo);
+ pos += cr.read(tokenData, pos);
+ tokenStack.push(cr);
+ } else if (t == Token.REFERR) {
+ CellReferenceError cr = new CellReferenceError();
+ pos += cr.read(tokenData, pos);
+ tokenStack.push(cr);
+ } else if (t == Token.ERR) {
+ ErrorConstant ec = new ErrorConstant();
+ pos += ec.read(tokenData, pos);
+ tokenStack.push(ec);
+ } else if (t == Token.REFV) {
+ SharedFormulaCellReference cr =
+ new SharedFormulaCellReference(relativeTo);
+ pos += cr.read(tokenData, pos);
+ tokenStack.push(cr);
+ } else if (t == Token.REF3D) {
+ CellReference3d cr = new CellReference3d(relativeTo, workbook);
+ pos += cr.read(tokenData, pos);
+ tokenStack.push(cr);
+ } else if (t == Token.AREA) {
+ Area a = new Area();
+ pos += a.read(tokenData, pos);
+ tokenStack.push(a);
+ } else if (t == Token.AREAV) {
+ SharedFormulaArea a = new SharedFormulaArea(relativeTo);
+ pos += a.read(tokenData, pos);
+ tokenStack.push(a);
+ } else if (t == Token.AREA3D) {
+ Area3d a = new Area3d(workbook);
+ pos += a.read(tokenData, pos);
+ tokenStack.push(a);
+ } else if (t == Token.NAME) {
+ Name n = new Name();
+ pos += n.read(tokenData, pos);
+ n.setParseContext(parseContext);
+ tokenStack.push(n);
+ } else if (t == Token.NAMED_RANGE) {
+ NameRange nr = new NameRange(nameTable);
+ pos += nr.read(tokenData, pos);
+ nr.setParseContext(parseContext);
+ tokenStack.push(nr);
+ } else if (t == Token.INTEGER) {
+ IntegerValue i = new IntegerValue();
+ pos += i.read(tokenData, pos);
+ tokenStack.push(i);
+ } else if (t == Token.DOUBLE) {
+ DoubleValue d = new DoubleValue();
+ pos += d.read(tokenData, pos);
+ tokenStack.push(d);
+ } else if (t == Token.BOOL) {
+ BooleanValue bv = new BooleanValue();
+ pos += bv.read(tokenData, pos);
+ tokenStack.push(bv);
+ } else if (t == Token.STRING) {
+ StringValue sv = new StringValue(settings);
+ pos += sv.read(tokenData, pos);
+ tokenStack.push(sv);
+ } else if (t == Token.MISSING_ARG) {
+ MissingArg ma = new MissingArg();
+ pos += ma.read(tokenData, pos);
+ tokenStack.push(ma);
+ }
+
+ // Unary Operators
+ else if (t == Token.UNARY_PLUS) {
+ UnaryPlus up = new UnaryPlus();
+ pos += up.read(tokenData, pos);
+ addOperator(up);
+ } else if (t == Token.UNARY_MINUS) {
+ UnaryMinus um = new UnaryMinus();
+ pos += um.read(tokenData, pos);
+ addOperator(um);
+ } else if (t == Token.PERCENT) {
+ Percent p = new Percent();
+ pos += p.read(tokenData, pos);
+ addOperator(p);
+ }
+
+ // Binary Operators
+ else if (t == Token.SUBTRACT) {
+ Subtract s = new Subtract();
+ pos += s.read(tokenData, pos);
+ addOperator(s);
+ } else if (t == Token.ADD) {
+ Add s = new Add();
+ pos += s.read(tokenData, pos);
+ addOperator(s);
+ } else if (t == Token.MULTIPLY) {
+ Multiply s = new Multiply();
+ pos += s.read(tokenData, pos);
+ addOperator(s);
+ } else if (t == Token.DIVIDE) {
+ Divide s = new Divide();
+ pos += s.read(tokenData, pos);
+ addOperator(s);
+ } else if (t == Token.CONCAT) {
+ Concatenate c = new Concatenate();
+ pos += c.read(tokenData, pos);
+ addOperator(c);
+ } else if (t == Token.POWER) {
+ Power p = new Power();
+ pos += p.read(tokenData, pos);
+ addOperator(p);
+ } else if (t == Token.LESS_THAN) {
+ LessThan lt = new LessThan();
+ pos += lt.read(tokenData, pos);
+ addOperator(lt);
+ } else if (t == Token.LESS_EQUAL) {
+ LessEqual lte = new LessEqual();
+ pos += lte.read(tokenData, pos);
+ addOperator(lte);
+ } else if (t == Token.GREATER_THAN) {
+ GreaterThan gt = new GreaterThan();
+ pos += gt.read(tokenData, pos);
+ addOperator(gt);
+ } else if (t == Token.GREATER_EQUAL) {
+ GreaterEqual gte = new GreaterEqual();
+ pos += gte.read(tokenData, pos);
+ addOperator(gte);
+ } else if (t == Token.NOT_EQUAL) {
+ NotEqual ne = new NotEqual();
+ pos += ne.read(tokenData, pos);
+ addOperator(ne);
+ } else if (t == Token.EQUAL) {
+ Equal e = new Equal();
+ pos += e.read(tokenData, pos);
+ addOperator(e);
+ } else if (t == Token.PARENTHESIS) {
+ Parenthesis p = new Parenthesis();
+ pos += p.read(tokenData, pos);
+ addOperator(p);
+ }
+
+ // Functions
+ else if (t == Token.ATTRIBUTE) {
+ Attribute a = new Attribute(settings);
+ pos += a.read(tokenData, pos);
+
+ if (a.isSum()) {
+ addOperator(a);
+ } else if (a.isIf()) {
+ // Add it to a special stack for ifs
+ ifStack.push(a);
+ }
+ } else if (t == Token.FUNCTION) {
+ BuiltInFunction bif = new BuiltInFunction(settings);
+ pos += bif.read(tokenData, pos);
+
+ addOperator(bif);
+ } else if (t == Token.FUNCTIONVARARG) {
+ VariableArgFunction vaf = new VariableArgFunction(settings);
+ pos += vaf.read(tokenData, pos);
+
+ if (vaf.getFunction() != Function.ATTRIBUTE) {
+ addOperator(vaf);
+ } else {
+ // This is part of an IF function. Get the operands, but then
+ // add it to the top of the if stack
+ vaf.getOperands(tokenStack);
+
+ Attribute ifattr = null;
+ if (ifStack.empty()) {
+ ifattr = new Attribute(settings);
+ } else {
+ ifattr = (Attribute) ifStack.pop();
+ }
+
+ ifattr.setIfConditions(vaf);
+ tokenStack.push(ifattr);
+ }
+ }
+
+ // Other things
+ else if (t == Token.MEM_FUNC) {
+ MemFunc memFunc = new MemFunc();
+ handleMemoryFunction(memFunc);
+ } else if (t == Token.MEM_AREA) {
+ MemArea memArea = new MemArea();
+ handleMemoryFunction(memArea);
+ }
+ }
+ }
+
+ /**
+ * Handles a memory function
+ */
+ private void handleMemoryFunction(SubExpression subxp)
+ throws FormulaException {
+ pos += subxp.read(tokenData, pos);
+
+ // Create new tokenStack for the sub expression
+ Stack oldStack = tokenStack;
+ tokenStack = new Stack();
+
+ parseSubExpression(subxp.getLength());
+
+ ParseItem[] subexpr = new ParseItem[tokenStack.size()];
+ int i = 0;
+ while (!tokenStack.isEmpty()) {
+ subexpr[i] = (ParseItem) tokenStack.pop();
+ i++;
+ }
+
+ subxp.setSubExpression(subexpr);
+
+ tokenStack = oldStack;
+ tokenStack.push(subxp);
+ }
+
+ /**
+ * Adds the specified operator to the parse tree, taking operands off
+ * the stack as appropriate
+ */
+ private void addOperator(Operator o) {
+ // Get the operands off the stack
+ o.getOperands(tokenStack);
+
+ // Add this operator onto the stack
+ tokenStack.push(o);
+ }
+
+ /**
+ * Gets the formula as a string
+ */
+ public String getFormula() {
+ StringBuffer sb = new StringBuffer();
+ root.getString(sb);
+ return sb.toString();
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ root.adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Gets the bytes for the formula. This takes into account any
+ * token mapping necessary because of shared formulas
+ *
+ * @return the bytes in RPN
+ */
+ public byte[] getBytes() {
+ return root.getBytes();
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ root.columnInserted(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ root.columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param row the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ root.rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param row the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ public void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ root.rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ *
+ * @return TRUE if the formula is valid import, FALSE otherwise
+ */
+ public boolean handleImportedCellReferences() {
+ root.handleImportedCellReferences();
+ return root.isValid();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java
new file mode 100755
index 0000000..c57f628
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryMinus.java
@@ -0,0 +1,62 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class UnaryMinus extends UnaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public UnaryMinus() {
+ }
+
+ public String getSymbol() {
+ return "-";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.UNARY_MINUS;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 2;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java
new file mode 100755
index 0000000..9e2d144
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryOperator.java
@@ -0,0 +1,180 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+
+/**
+ * A cell reference in a formula
+ */
+abstract class UnaryOperator extends Operator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public UnaryOperator() {
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ */
+ public int read(byte[] data, int pos) {
+ return 0;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ */
+ public void getOperands(Stack s) {
+ ParseItem o1 = (ParseItem) s.pop();
+
+ add(o1);
+ }
+
+ /**
+ * Gets the string
+ *
+ * @param buf
+ */
+ public void getString(StringBuffer buf) {
+ ParseItem[] operands = getOperands();
+ buf.append(getSymbol());
+ operands[0].getString(buf);
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = getOperands();
+ operands[0].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].columnInserted(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].columnRemoved(sheetIndex, col, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].rowInserted(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ operands[0].rowRemoved(sheetIndex, row, currentSheet);
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ // Get the data for the operands
+ ParseItem[] operands = getOperands();
+ byte[] data = operands[0].getBytes();
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 1];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = getToken().getCode();
+
+ return newdata;
+ }
+
+ /**
+ * Abstract method which gets the binary operator string symbol
+ *
+ * @return the string symbol for this token
+ */
+ abstract String getSymbol();
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ abstract Token getToken();
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ operands[0].handleImportedCellReferences();
+ }
+
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java
new file mode 100755
index 0000000..0c3ff91
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/UnaryPlus.java
@@ -0,0 +1,62 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A cell reference in a formula
+ */
+class UnaryPlus extends UnaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public UnaryPlus() {
+ }
+
+ public String getSymbol() {
+ return "+";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.UNARY_PLUS;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 2;
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java
new file mode 100755
index 0000000..63665af
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Union.java
@@ -0,0 +1,54 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2005 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * A union of two cell ranges - the "," operator
+ */
+class Union extends BinaryOperator implements ParsedThing {
+ /**
+ * Constructor
+ */
+ public Union() {
+ }
+
+ public String getSymbol() {
+ return ",";
+ }
+
+ /**
+ * Abstract method which gets the token for this operator
+ *
+ * @return the string symbol for this token
+ */
+ Token getToken() {
+ return Token.UNION;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 4;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java b/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java
new file mode 100755
index 0000000..21134b8
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/VariableArgFunction.java
@@ -0,0 +1,311 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+import java.util.Stack;
+import jxl.WorkbookSettings;
+import jxl.biff.IntegerHelper;
+import jxl.common.Logger;
+
+/**
+ * A built in function in a formula. These functions take a variable
+ * number of arguments, such as a range (eg. SUM etc)
+ */
+class VariableArgFunction extends Operator implements ParsedThing {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(VariableArgFunction.class);
+
+ /**
+ * The function
+ */
+ private Function function;
+
+ /**
+ * The number of arguments
+ */
+ private int arguments;
+
+ /**
+ * Flag which indicates whether this was initialized from the client
+ * api or from an excel sheet
+ */
+ private final boolean readFromSheet;
+
+ /**
+ * The workbooks settings
+ */
+ private final WorkbookSettings settings;
+
+ /**
+ * Constructor
+ */
+ public VariableArgFunction(WorkbookSettings ws) {
+ readFromSheet = true;
+ settings = ws;
+ }
+
+ /**
+ * Constructor used when parsing a function from a string
+ *
+ * @param f the function
+ * @param a the number of arguments
+ */
+ public VariableArgFunction(Function f, int a, WorkbookSettings ws) {
+ function = f;
+ arguments = a;
+ readFromSheet = false;
+ settings = ws;
+ }
+
+ /**
+ * Reads the ptg data from the array starting at the specified position
+ *
+ * @param data the RPN array
+ * @param pos the current position in the array, excluding the ptg identifier
+ * @return the number of bytes read
+ * @throws FormulaException
+ */
+ public int read(byte[] data, int pos) throws FormulaException {
+ arguments = data[pos];
+ int index = IntegerHelper.getInt(data[pos + 1], data[pos + 2]);
+ function = Function.getFunction(index);
+
+ if (function == Function.UNKNOWN) {
+ throw new FormulaException(FormulaException.UNRECOGNIZED_FUNCTION,
+ index);
+ }
+
+ return 3;
+ }
+
+ /**
+ * Gets the operands for this operator from the stack
+ */
+ public void getOperands(Stack s) {
+ // parameters are in the correct order, god damn them
+ ParseItem[] items = new ParseItem[arguments];
+
+ for (int i = arguments - 1; i >= 0; i--) {
+ ParseItem pi = (ParseItem) s.pop();
+
+ items[i] = pi;
+ }
+
+ for (int i = 0; i < arguments; i++) {
+ add(items[i]);
+ }
+ }
+
+ public void getString(StringBuffer buf) {
+ buf.append(function.getName(settings));
+ buf.append('(');
+
+ if (arguments > 0) {
+ ParseItem[] operands = getOperands();
+ if (readFromSheet) {
+ // arguments are in the same order they were specified
+ operands[0].getString(buf);
+
+ for (int i = 1; i < arguments; i++) {
+ buf.append(',');
+ operands[i].getString(buf);
+ }
+ } else {
+ // arguments are stored in the reverse order to which they
+ // were specified, so iterate through them backwards
+ operands[arguments - 1].getString(buf);
+
+ for (int i = arguments - 2; i >= 0; i--) {
+ buf.append(',');
+ operands[i].getString(buf);
+ }
+ }
+ }
+
+ buf.append(')');
+ }
+
+ /**
+ * Adjusts all the relative cell references in this formula by the
+ * amount specified. Used when copying formulas
+ *
+ * @param colAdjust the amount to add on to each relative cell reference
+ * @param rowAdjust the amount to add on to each relative row reference
+ */
+ public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) {
+ ParseItem[] operands = getOperands();
+
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].adjustRelativeCellReferences(colAdjust, rowAdjust);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnInserted(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnInserted(sheetIndex, col, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the column was removed
+ * @param col the column number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void columnRemoved(int sheetIndex, int col, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].columnRemoved(sheetIndex, col, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowInserted(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowInserted(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Tells
+ * the formula parser to update all of its cell references beyond this
+ * column
+ *
+ * @param sheetIndex the sheet on which the row was removed
+ * @param row the row number which was removed
+ * @param currentSheet TRUE if this formula is on the sheet in which the
+ * column was inserted, FALSE otherwise
+ */
+ void rowRemoved(int sheetIndex, int row, boolean currentSheet) {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].rowRemoved(sheetIndex, row, currentSheet);
+ }
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ * Does nothing, as operators don't have cell references
+ */
+ void handleImportedCellReferences() {
+ ParseItem[] operands = getOperands();
+ for (int i = 0; i < operands.length; i++) {
+ operands[i].handleImportedCellReferences();
+ }
+ }
+
+ /**
+ * Gets the underlying function
+ */
+ Function getFunction() {
+ return function;
+ }
+
+ /**
+ * Gets the token representation of this item in RPN
+ *
+ * @return the bytes applicable to this formula
+ */
+ byte[] getBytes() {
+ handleSpecialCases();
+
+ // Get the data for the operands - in the correct order
+ ParseItem[] operands = getOperands();
+ byte[] data = new byte[0];
+
+ for (int i = 0; i < operands.length; i++) {
+ byte[] opdata = operands[i].getBytes();
+
+ // Grow the array
+ byte[] newdata = new byte[data.length + opdata.length];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ System.arraycopy(opdata, 0, newdata, data.length, opdata.length);
+ data = newdata;
+ }
+
+ // Add on the operator byte
+ byte[] newdata = new byte[data.length + 4];
+ System.arraycopy(data, 0, newdata, 0, data.length);
+ newdata[data.length] = !useAlternateCode() ?
+ Token.FUNCTIONVARARG.getCode() : Token.FUNCTIONVARARG.getCode2();
+ newdata[data.length + 1] = (byte) arguments;
+ IntegerHelper.getTwoBytes(function.getCode(), newdata, data.length + 2);
+
+ return newdata;
+ }
+
+ /**
+ * Gets the precedence for this operator. Operator precedents run from
+ * 1 to 5, one being the highest, 5 being the lowest
+ *
+ * @return the operator precedence
+ */
+ int getPrecedence() {
+ return 3;
+ }
+
+ /**
+ * Handles functions which form a special case
+ */
+ private void handleSpecialCases() {
+ // Handle the array functions. Tell all the Area records to
+ // use their alternative token code
+ if (function == Function.SUMPRODUCT) {
+ // Get the data for the operands - in reverse order
+ ParseItem[] operands = getOperands();
+
+ for (int i = operands.length - 1; i >= 0; i--) {
+ if (operands[i] instanceof Area) {
+ operands[i].setAlternateCode();
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java b/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java
new file mode 100755
index 0000000..13b2113
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/biff/formula/Yylex.java
@@ -0,0 +1,837 @@
+/* The following code was generated by JFlex 1.4.1 on 24/10/09 14:10 */
+
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.biff.formula;
+
+/**
+ * This file is generated by JLex. Do not alter the contents of this file
+ * because changes will be overridden
+ */
+
+import jxl.biff.WorkbookMethods;
+
+
+/**
+ * This class is a scanner generated by
+ * JFlex 1.4.1
+ * on 24/10/09 14:10 from the specification file
+ * xlformula.flex
+ */
+class Yylex {
+
+ /**
+ * This character denotes the end of file
+ */
+ public static final int YYEOF = -1;
+ /**
+ * lexical states
+ */
+ public static final int YYSTRING = 1;
+ public static final int YYINITIAL = 0;
+ /**
+ * initial size of the lookahead buffer
+ */
+ private static final int ZZ_BUFFERSIZE = 16384;
+ /**
+ * Translates characters to character classes
+ */
+ private static final String ZZ_CMAP_PACKED =
+ "\10\0\3\25\25\0\1\25\1\24\1\21\1\26\1\10\2\0\1\22" +
+ "\1\5\1\6\1\41\1\37\1\4\1\40\1\7\1\33\1\34\11\2" +
+ "\1\3\1\0\1\44\1\43\1\42\1\36\1\0\1\16\2\1\1\30" +
+ "\1\14\1\15\2\1\1\31\2\1\1\17\1\35\1\27\3\1\1\12" +
+ "\1\20\1\11\1\13\1\32\4\1\4\0\1\23\1\0\32\1\uff85\0";
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final char[] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
+ private static final String ZZ_ACTION_PACKED_0 =
+ "\1\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7" +
+ "\1\0\2\2\1\10\1\0\1\11\1\0\1\12\1\13" +
+ "\1\14\1\15\1\16\1\17\1\20\1\1\1\21\1\2" +
+ "\1\22\1\0\1\23\1\0\1\2\3\0\2\2\5\0" +
+ "\1\24\1\25\1\26\1\2\1\0\1\27\1\0\1\22" +
+ "\2\0\1\30\1\0\2\2\10\0\1\27\1\0\1\31" +
+ "\1\0\1\32\10\0\1\33\2\0\1\31\2\0\1\34" +
+ "\4\0\1\35\3\0\1\35\1\0\1\36\1\0";
+ /**
+ * Translates DFA states to action switch labels.
+ */
+ private static final int[] ZZ_ACTION = zzUnpackAction();
+ private static final String ZZ_ROWMAP_PACKED_0 =
+ "\0\0\0\45\0\112\0\157\0\224\0\224\0\224\0\224" +
+ "\0\271\0\336\0\u0103\0\224\0\u0128\0\224\0\u014d\0\224" +
+ "\0\224\0\224\0\224\0\u0172\0\224\0\u0197\0\u01bc\0\224" +
+ "\0\u01e1\0\u0206\0\u022b\0\224\0\u0250\0\u0275\0\u029a\0\u02bf" +
+ "\0\u02e4\0\u0309\0\u032e\0\u0353\0\u0378\0\u039d\0\u03c2\0\u03e7" +
+ "\0\224\0\224\0\224\0\u040c\0\u0431\0\u0456\0\u047b\0\u04a0" +
+ "\0\u04c5\0\u04ea\0\u02bf\0\u050f\0\u0534\0\u0559\0\u057e\0\u05a3" +
+ "\0\u05c8\0\u05ed\0\u0612\0\u0637\0\u065c\0\u0681\0\224\0\u06a6" +
+ "\0\u06cb\0\u06cb\0\u040c\0\u06f0\0\u0715\0\u073a\0\u075f\0\u0784" +
+ "\0\u07a9\0\u07ce\0\u07f3\0\u0818\0\u0818\0\u083d\0\u0862\0\u0887" +
+ "\0\u08ac\0\224\0\u08d1\0\u08f6\0\u091b\0\u0940\0\u0965\0\u098a" +
+ "\0\u09af\0\u09d4\0\224\0\u09f9\0\u0a1e\0\u0a1e";
+ /**
+ * Translates a state to a row index in the transition table
+ */
+ private static final int[] ZZ_ROWMAP = zzUnpackRowMap();
+ private static final String ZZ_TRANS_PACKED_0 =
+ "\1\0\1\3\1\4\1\5\1\6\1\7\1\10\1\0" +
+ "\1\11\1\12\3\3\1\13\3\3\1\14\1\15\2\0" +
+ "\1\16\1\17\4\3\1\20\1\4\1\3\1\0\1\21" +
+ "\1\22\1\23\1\24\1\25\1\26\21\27\1\30\23\27" +
+ "\1\0\1\31\1\32\1\33\1\0\1\34\2\0\1\35" +
+ "\10\31\2\0\1\36\1\37\2\0\4\31\1\0\1\32" +
+ "\1\31\11\0\1\4\4\0\1\40\24\0\1\4\56\0" +
+ "\1\41\7\0\10\41\6\0\4\41\2\0\1\41\10\0" +
+ "\1\31\1\32\1\33\1\0\1\34\2\0\1\35\1\31" +
+ "\1\42\6\31\2\0\1\36\1\37\2\0\4\31\1\0" +
+ "\1\32\1\31\10\0\1\31\1\32\1\33\1\0\1\34" +
+ "\2\0\1\35\5\31\1\43\2\31\2\0\1\36\1\37" +
+ "\2\0\4\31\1\0\1\32\1\31\7\0\22\15\1\44" +
+ "\22\15\12\0\1\45\14\0\1\46\1\47\1\0\1\50" +
+ "\55\0\1\51\43\0\1\52\1\53\1\0\21\27\1\0" +
+ "\23\27\1\0\1\54\1\32\1\33\1\0\1\34\2\0" +
+ "\1\35\10\54\2\0\1\36\1\37\2\0\4\54\1\0" +
+ "\1\32\1\54\10\0\1\36\1\32\1\55\5\0\10\36" +
+ "\2\0\1\36\3\0\4\36\1\0\1\32\1\36\10\0" +
+ "\1\56\6\0\1\57\10\56\6\0\4\56\2\0\1\56" +
+ "\11\0\1\60\31\0\1\60\11\0\2\36\6\0\10\36" +
+ "\2\0\1\36\3\0\4\36\1\0\2\36\10\0\1\61" +
+ "\6\0\1\62\10\61\6\0\4\61\2\0\1\61\11\0" +
+ "\1\63\31\0\1\63\11\0\1\64\1\60\1\33\4\0" +
+ "\1\35\10\64\6\0\4\64\1\0\1\60\1\64\10\0" +
+ "\1\54\1\32\1\33\1\0\1\34\2\0\1\35\2\54" +
+ "\1\65\5\54\2\0\1\36\1\37\2\0\4\54\1\0" +
+ "\1\32\1\54\10\0\1\54\1\32\1\33\1\0\1\34" +
+ "\2\0\1\35\6\54\1\66\1\54\2\0\1\36\1\37" +
+ "\2\0\4\54\1\0\1\32\1\54\33\0\1\67\34\0" +
+ "\1\70\43\0\1\71\2\0\1\72\57\0\1\73\31\0" +
+ "\1\74\27\0\1\54\1\36\2\0\1\34\3\0\10\54" +
+ "\2\0\1\36\1\37\2\0\4\54\1\0\1\36\1\54" +
+ "\10\0\1\75\6\0\1\76\10\75\6\0\4\75\2\0" +
+ "\1\75\10\0\1\77\7\0\10\77\6\0\4\77\2\0" +
+ "\1\77\10\0\1\56\7\0\10\56\6\0\4\56\2\0" +
+ "\1\56\11\0\1\60\1\55\30\0\1\60\11\0\1\100" +
+ "\1\101\5\0\1\102\10\100\6\0\4\100\1\0\1\101" +
+ "\1\100\10\0\1\61\7\0\10\61\6\0\4\61\2\0" +
+ "\1\61\11\0\1\60\1\33\4\0\1\35\23\0\1\60" +
+ "\11\0\1\54\1\36\2\0\1\34\3\0\3\54\1\103" +
+ "\4\54\2\0\1\36\1\37\2\0\4\54\1\0\1\36" +
+ "\1\54\10\0\1\54\1\36\2\0\1\34\3\0\7\54" +
+ "\1\65\2\0\1\36\1\37\2\0\4\54\1\0\1\36" +
+ "\1\54\10\0\1\104\6\0\1\105\10\104\6\0\4\104" +
+ "\2\0\1\104\24\0\1\106\46\0\1\107\15\0\1\106" +
+ "\44\0\1\110\41\0\1\111\31\0\1\112\26\0\1\113" +
+ "\1\114\5\0\1\115\10\113\6\0\4\113\1\0\1\114" +
+ "\1\113\10\0\1\75\7\0\10\75\6\0\4\75\2\0" +
+ "\1\75\11\0\1\101\5\0\1\102\23\0\1\101\12\0" +
+ "\1\101\31\0\1\101\11\0\1\116\1\117\1\120\4\0" +
+ "\1\121\10\116\6\0\4\116\1\0\1\117\1\116\10\0" +
+ "\1\104\7\0\10\104\6\0\4\104\2\0\1\104\33\0" +
+ "\1\122\37\0\1\106\41\0\1\123\63\0\1\124\24\0" +
+ "\1\125\33\0\1\114\5\0\1\115\23\0\1\114\12\0" +
+ "\1\114\31\0\1\114\12\0\1\117\1\120\4\0\1\121" +
+ "\23\0\1\117\12\0\1\117\1\126\30\0\1\117\11\0" +
+ "\1\127\6\0\1\130\10\127\6\0\4\127\2\0\1\127" +
+ "\11\0\1\117\31\0\1\117\46\0\1\122\42\0\1\106" +
+ "\24\0\1\106\31\0\1\131\6\0\1\132\10\131\6\0" +
+ "\4\131\2\0\1\131\10\0\1\133\7\0\10\133\6\0" +
+ "\4\133\2\0\1\133\10\0\1\127\7\0\10\127\6\0" +
+ "\4\127\2\0\1\127\10\0\1\134\1\135\5\0\1\136" +
+ "\10\134\6\0\4\134\1\0\1\135\1\134\10\0\1\131" +
+ "\7\0\10\131\6\0\4\131\2\0\1\131\11\0\1\135" +
+ "\5\0\1\136\23\0\1\135\12\0\1\135\31\0\1\135" +
+ "\10\0";
+ /**
+ * The transition table of the DFA
+ */
+ private static final int[] ZZ_TRANS = zzUnpackTrans();
+ /* error codes */
+ private static final int ZZ_UNKNOWN_ERROR = 0;
+ private static final int ZZ_NO_MATCH = 1;
+ private static final int ZZ_PUSHBACK_2BIG = 2;
+ /* error messages for the codes above */
+ private static final String[] ZZ_ERROR_MSG = {
+ "Unkown internal scanner error",
+ "Error: could not match input",
+ "Error: pushback value was too large"
+ };
+ private static final String ZZ_ATTRIBUTE_PACKED_0 =
+ "\1\0\3\1\4\11\1\0\2\1\1\11\1\0\1\11" +
+ "\1\0\4\11\1\1\1\11\2\1\1\11\2\1\1\0" +
+ "\1\11\1\0\1\1\3\0\2\1\5\0\3\11\1\1" +
+ "\1\0\1\1\1\0\1\1\2\0\1\1\1\0\2\1" +
+ "\10\0\1\11\1\0\1\1\1\0\1\1\10\0\1\1" +
+ "\2\0\1\1\2\0\1\11\4\0\1\1\3\0\1\11" +
+ "\1\0\1\1\1\0";
+ /**
+ * ZZ_ATTRIBUTE[aState] contains the attributes of state aState
+ */
+ private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute();
+ /**
+ * the input device
+ */
+ private java.io.Reader zzReader;
+ /**
+ * the current state of the DFA
+ */
+ private int zzState;
+ /**
+ * the current lexical state
+ */
+ private int zzLexicalState = YYINITIAL;
+ /**
+ * this buffer contains the current text to be matched and is
+ * the source of the yytext() string
+ */
+ private char[] zzBuffer = new char[ZZ_BUFFERSIZE];
+ /**
+ * the textposition at the last accepting state
+ */
+ private int zzMarkedPos;
+ /**
+ * the textposition at the last state to be included in yytext
+ */
+ private int zzPushbackPos;
+ /**
+ * the current text position in the buffer
+ */
+ private int zzCurrentPos;
+ /**
+ * startRead marks the beginning of the yytext() string in the buffer
+ */
+ private int zzStartRead;
+ /**
+ * endRead marks the last character in the buffer, that has been read
+ * from input
+ */
+ private int zzEndRead;
+ /**
+ * number of newlines encountered up to the start of the matched text
+ */
+ private int yyline;
+ /**
+ * the number of characters up to the start of the matched text
+ */
+ private int yychar;
+ /**
+ * the number of characters from the last newline up to the start of the
+ * matched text
+ */
+ private int yycolumn;
+ /**
+ * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+ */
+ private boolean zzAtBOL = true;
+ /**
+ * zzAtEOF == true <=> the scanner is at the EOF
+ */
+ private boolean zzAtEOF;
+ private boolean emptyString;
+ private ExternalSheet externalSheet;
+ private WorkbookMethods nameTable;
+
+ /**
+ * Creates a new scanner
+ * There is also a java.io.InputStream version of this constructor.
+ *
+ * @param in the java.io.Reader to read input from.
+ */
+ Yylex(java.io.Reader in) {
+ this.zzReader = in;
+ }
+
+ /**
+ * Creates a new scanner.
+ * There is also java.io.Reader version of this constructor.
+ *
+ * @param in the java.io.Inputstream to read input from.
+ */
+ Yylex(java.io.InputStream in) {
+ this(new java.io.InputStreamReader(in));
+ }
+
+ private static int[] zzUnpackAction() {
+ int[] result = new int[94];
+ int offset = 0;
+ offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAction(String packed, int offset, int[] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+ private static int[] zzUnpackRowMap() {
+ int[] result = new int[94];
+ int offset = 0;
+ offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackRowMap(String packed, int offset, int[] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int high = packed.charAt(i++) << 16;
+ result[j++] = high | packed.charAt(i++);
+ }
+ return j;
+ }
+
+ private static int[] zzUnpackTrans() {
+ int[] result = new int[2627];
+ int offset = 0;
+ offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackTrans(String packed, int offset, int[] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ value--;
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+ private static int[] zzUnpackAttribute() {
+ int[] result = new int[94];
+ int offset = 0;
+ offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAttribute(String packed, int offset, int[] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+ /**
+ * Unpacks the compressed character translation table.
+ *
+ * @param packed the packed character translation table
+ * @return the unpacked character translation table
+ */
+ private static char[] zzUnpackCMap(String packed) {
+ char[] map = new char[0x10000];
+ int i = 0; /* index in packed string */
+ int j = 0; /* index in unpacked array */
+ while (i < 100) {
+ int count = packed.charAt(i++);
+ char value = packed.charAt(i++);
+ do map[j++] = value; while (--count > 0);
+ }
+ return map;
+ }
+
+ /* user code: */
+ int getPos() {
+ return yychar;
+ }
+
+ void setExternalSheet(ExternalSheet es) {
+ externalSheet = es;
+ }
+
+ void setNameTable(WorkbookMethods nt) {
+ nameTable = nt;
+ }
+
+ /**
+ * Refills the input buffer.
+ *
+ * @return false
, iff there was new input.
+ * @throws java.io.IOException if any I/O-Error occurs
+ */
+ private boolean zzRefill() throws java.io.IOException {
+
+ /* first: make room (if you can) */
+ if (zzStartRead > 0) {
+ System.arraycopy(zzBuffer, zzStartRead,
+ zzBuffer, 0,
+ zzEndRead - zzStartRead);
+
+ /* translate stored positions */
+ zzEndRead -= zzStartRead;
+ zzCurrentPos -= zzStartRead;
+ zzMarkedPos -= zzStartRead;
+ zzPushbackPos -= zzStartRead;
+ zzStartRead = 0;
+ }
+
+ /* is the buffer big enough? */
+ if (zzCurrentPos >= zzBuffer.length) {
+ /* if not: blow it up */
+ char[] newBuffer = new char[zzCurrentPos * 2];
+ System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length);
+ zzBuffer = newBuffer;
+ }
+
+ /* finally: fill the buffer with new input */
+ int numRead = zzReader.read(zzBuffer, zzEndRead,
+ zzBuffer.length - zzEndRead);
+
+ if (numRead < 0) {
+ return true;
+ } else {
+ zzEndRead += numRead;
+ return false;
+ }
+ }
+
+
+ /**
+ * Closes the input stream.
+ */
+ public final void yyclose() throws java.io.IOException {
+ zzAtEOF = true; /* indicate end of file */
+ zzEndRead = zzStartRead; /* invalidate buffer */
+
+ if (zzReader != null)
+ zzReader.close();
+ }
+
+
+ /**
+ * Resets the scanner to read from a new input stream.
+ * Does not close the old reader.
+ *
+ * All internal variables are reset, the old input stream + * cannot be reused (internal buffer is discarded and lost). + * Lexical state is set to ZZ_INITIAL. + * + * @param reader the new input stream + */ + public final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = zzPushbackPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final String yytext() { + return new String(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead); + } + + + /** + * Returns the character at position pos from the + * matched text. + *
+ * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBuffer[zzStartRead + pos]; + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos - zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + *
+ * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + *
+ * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + *
+ * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if (number > yylength()) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @throws java.io.IOException if any I/O-Error occurs + */ + public ParseItem yylex() throws java.io.IOException, FormulaException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char[] zzBufferL = zzBuffer; + char[] zzCMapL = ZZ_CMAP; + + int[] zzTransL = ZZ_TRANS; + int[] zzRowMapL = ZZ_ROWMAP; + int[] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + yychar += zzMarkedPosL - zzStartRead; + + boolean zzR = false; + for (zzCurrentPosL = zzStartRead; zzCurrentPosL < zzMarkedPosL; + zzCurrentPosL++) { + switch (zzBufferL[zzCurrentPosL]) { + case '\u000B': + case '\u000C': + case '\u0085': + case '\u2028': + case '\u2029': + yyline++; + zzR = false; + break; + case '\r': + yyline++; + zzR = true; + break; + case '\n': + if (zzR) + zzR = false; + else { + yyline++; + } + break; + default: + zzR = false; + } + } + + if (zzR) { + // peek one character ahead if it is \n (if we have counted one line too much) + boolean zzPeek; + if (zzMarkedPosL < zzEndReadL) + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + else if (zzAtEOF) + zzPeek = false; + else { + boolean eof = zzRefill(); + zzEndReadL = zzEndRead; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + if (eof) + zzPeek = false; + else + zzPeek = zzBufferL[zzMarkedPosL] == '\n'; + } + if (zzPeek) yyline--; + } + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = zzLexicalState; + + + zzForAction: + { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[zzRowMapL[zzState] + zzCMapL[zzInput]]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ((zzAttributes & 1) == 1) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ((zzAttributes & 8) == 8) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 12: { + return new Minus(); + } + case 31: + break; + case 7: { + return new CloseParentheses(); + } + case 32: + break; + case 3: { + return new IntegerValue(yytext()); + } + case 33: + break; + case 24: { + return new DoubleValue(yytext()); + } + case 34: + break; + case 29: { + return new ColumnRange3d(yytext(), externalSheet); + } + case 35: + break; + case 4: { + return new RangeSeparator(); + } + case 36: + break; + case 10: { + return new Divide(); + } + case 37: + break; + case 25: { + return new CellReference3d(yytext(), externalSheet); + } + case 38: + break; + case 26: { + return new BooleanValue(yytext()); + } + case 39: + break; + case 15: { + return new Equal(); + } + case 40: + break; + case 17: { + yybegin(YYINITIAL); + if (emptyString) return new StringValue(""); + } + case 41: + break; + case 8: { + emptyString = true; + yybegin(YYSTRING); + } + case 42: + break; + case 21: { + return new NotEqual(); + } + case 43: + break; + case 22: { + return new LessEqual(); + } + case 44: + break; + case 16: { + return new LessThan(); + } + case 45: + break; + case 5: { + return new ArgumentSeparator(); + } + case 46: + break; + case 30: { + return new Area3d(yytext(), externalSheet); + } + case 47: + break; + case 14: { + return new GreaterThan(); + } + case 48: + break; + case 18: { + return new CellReference(yytext()); + } + case 49: + break; + case 20: { + return new GreaterEqual(); + } + case 50: + break; + case 27: { + return new Area(yytext()); + } + case 51: + break; + case 23: { + return new ColumnRange(yytext()); + } + case 52: + break; + case 1: { + emptyString = false; + return new StringValue(yytext()); + } + case 53: + break; + case 2: { + return new NameRange(yytext(), nameTable); + } + case 54: + break; + case 19: { + return new StringFunction(yytext()); + } + case 55: + break; + case 11: { + return new Plus(); + } + case 56: + break; + case 28: { + return new ErrorConstant(yytext()); + } + case 57: + break; + case 9: { + } + case 58: + break; + case 13: { + return new Multiply(); + } + case 59: + break; + case 6: { + return new OpenParentheses(); + } + case 60: + break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/datastructures-xslx/src/main/java/jxl/common/Assert.java b/datastructures-xslx/src/main/java/jxl/common/Assert.java new file mode 100755 index 0000000..82a47b7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/Assert.java @@ -0,0 +1,56 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +/** + * Simple assertion mechanism for use during development + */ +public final class Assert { + /** + * Throws an AssertionFailed exception if the specified condition is + * false + * + * @param condition The assertion condition which must be true + */ + public static void verify(boolean condition) { + if (!condition) { + throw new AssertionFailed(); + } + } + + /** + * If the condition evaluates to false, an AssertionFailed is thrown + * + * @param message A message thrown with the failed assertion + * @param condition If this evaluates to false, an AssertionFailed is thrown + */ + public static void verify(boolean condition, String message) { + if (!condition) { + throw new AssertionFailed(message); + } + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java b/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java new file mode 100755 index 0000000..cb0b88e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/AssertionFailed.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + + +package jxl.common; + +/** + * An exception thrown when an assert (from the Assert class) fails + */ +public class AssertionFailed extends RuntimeException { + /** + * Default constructor + * Prints the stack trace + */ + public AssertionFailed() { + super(); + printStackTrace(); + } + + /** + * Constructor with message + * Prints the stack trace + * + * @param s Message thrown with the assertion + */ + public AssertionFailed(String s) { + super(s); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java b/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java new file mode 100755 index 0000000..b9032ea --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/BaseUnit.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +public class BaseUnit { + private final int index; + + protected BaseUnit(int ind) { + index = ind; + } + + protected int getIndex() { + return index; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java b/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java new file mode 100755 index 0000000..72773b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/LengthConverter.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +public class LengthConverter { + private static final double[][] factors = + new double[LengthUnit.getCount()][LengthUnit.getCount()]; + + static { + // The identity factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.POINTS.getIndex()] = 1; + factors[LengthUnit.METRES.getIndex()][LengthUnit.METRES.getIndex()] = 1; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 1; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.INCHES.getIndex()] = 1; + + // The points conversion factors + factors[LengthUnit.POINTS.getIndex()][LengthUnit.METRES.getIndex()] = 0.00035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 0.035277777778; + factors[LengthUnit.POINTS.getIndex()][LengthUnit.INCHES.getIndex()] = 0.013888888889; + + // The metres conversion factors + factors[LengthUnit.METRES.getIndex()][LengthUnit.POINTS.getIndex()] = 2877.84; + factors[LengthUnit.METRES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 100; + factors[LengthUnit.METRES.getIndex()][LengthUnit.INCHES.getIndex()] = 39.37; + + // The centimetres conversion factors + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.POINTS.getIndex()] = 28.34643; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.METRES.getIndex()] = 0.01; + factors[LengthUnit.CENTIMETRES.getIndex()][LengthUnit.INCHES.getIndex()] = 0.3937; + + // The inches conversion factors + factors[LengthUnit.INCHES.getIndex()][LengthUnit.POINTS.getIndex()] = 72; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.METRES.getIndex()] = 0.0254; + factors[LengthUnit.INCHES.getIndex()][LengthUnit.CENTIMETRES.getIndex()] = 2.54; + } + + public static double getConversionFactor(LengthUnit from, LengthUnit to) { + return factors[from.getIndex()][to.getIndex()]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java b/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java new file mode 100755 index 0000000..8fd29e8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/LengthUnit.java @@ -0,0 +1,39 @@ +/********************************************************************* + * + * Copyright (C) 2006 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + + +/** + * Enumeration for units + */ +public class LengthUnit extends BaseUnit { + public static LengthUnit POINTS = new LengthUnit(); + public static LengthUnit METRES = new LengthUnit(); + public static LengthUnit CENTIMETRES = new LengthUnit(); + public static LengthUnit INCHES = new LengthUnit(); + private static int count = 0; + private LengthUnit() { + super(count++); + } + + public static int getCount() { + return count; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/Logger.java b/datastructures-xslx/src/main/java/jxl/common/Logger.java new file mode 100755 index 0000000..562dde1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/Logger.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common; + +import java.security.AccessControlException; + +/** + * Abstract wrapper class for the logging interface of choice. + * The methods declared here are the same as those for the log4j + */ +public abstract class Logger { + /** + * The singleton logger + */ + private static Logger logger = null; + + /** + * Constructor + */ + protected Logger() { + } + + /** + * Factory method to return the logger + */ + public static final Logger getLogger(Class cl) { + if (logger == null) { + initializeLogger(); + } + + return logger.getLoggerImpl(cl); + } + + /** + * Initializes the logger in a thread safe manner + */ + private synchronized static void initializeLogger() { + if (logger != null) { + return; + } + + String loggerName = jxl.common.log.LoggerName.NAME; + + try { + // First see if there was anything defined at run time + loggerName = System.getProperty("logger"); + + if (loggerName == null) { + // Get the logger name from the compiled in logger + loggerName = jxl.common.log.LoggerName.NAME; + } + + logger = (Logger) Class.forName(loggerName).newInstance(); + } catch (IllegalAccessException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (InstantiationException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (AccessControlException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } catch (ClassNotFoundException e) { + logger = new jxl.common.log.SimpleLogger(); + logger.warn("Could not instantiate logger " + loggerName + + " using default"); + } + } + + /** + * Log a debug message + */ + public abstract void debug(Object message); + + /** + * Log a debug message and exception + */ + public abstract void debug(Object message, Throwable t); + + /** + * Log an error message + */ + public abstract void error(Object message); + + /** + * Log an error message object and exception + */ + public abstract void error(Object message, Throwable t); + + /** + * Log a fatal message + */ + public abstract void fatal(Object message); + + /** + * Log a fatal message and exception + */ + public abstract void fatal(Object message, Throwable t); + + /** + * Log an information message + */ + public abstract void info(Object message); + + /** + * Logs an information message and an exception + */ + public abstract void info(Object message, Throwable t); + + /** + * Log a warning message object + */ + public abstract void warn(Object message); + + /** + * Log a warning message with exception + */ + public abstract void warn(Object message, Throwable t); + + /** + * Accessor to the logger implementation + */ + protected abstract Logger getLoggerImpl(Class cl); + + /** + * Empty implementation of the suppressWarnings. Subclasses may + * or may not override this method. This method is included + * primarily for backwards support of the jxl.nowarnings property, and + * is used only by the SimpleLogger + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) { + // default implementation does nothing + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java b/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java new file mode 100755 index 0000000..b594936 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/LoggerName.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class LoggerName { + public final static String NAME = LoggerName.class.getName(); +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java new file mode 100755 index 0000000..0599470 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLogger.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +import jxl.common.Logger; + +/** + * The default logger. Simple writes everything out to stdout or stderr + */ +public class SimpleLogger extends Logger { + /** + * Flag to indicate whether or not warnings should be suppressed + */ + private boolean suppressWarnings; + + /** + * Constructor + */ + public SimpleLogger() { + suppressWarnings = false; + } + + /** + * Log a debug message + */ + public void debug(Object message) { + if (!suppressWarnings) { + System.out.print("Debug: "); + System.out.println(message); + } + } + + /** + * Log a debug message and exception + */ + public void debug(Object message, Throwable t) { + if (!suppressWarnings) { + System.out.print("Debug: "); + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log an error message + */ + public void error(Object message) { + System.err.print("Error: "); + System.err.println(message); + } + + /** + * Log an error message object and exception + */ + public void error(Object message, Throwable t) { + System.err.print("Error: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log a fatal message + */ + public void fatal(Object message) { + System.err.print("Fatal: "); + System.err.println(message); + } + + /** + * Log a fatal message and exception + */ + public void fatal(Object message, Throwable t) { + System.err.print("Fatal: "); + System.err.println(message); + t.printStackTrace(); + } + + /** + * Log an information message + */ + public void info(Object message) { + if (!suppressWarnings) { + System.out.println(message); + } + } + + /** + * Logs an information message and an exception + */ + + public void info(Object message, Throwable t) { + if (!suppressWarnings) { + System.out.println(message); + t.printStackTrace(); + } + } + + /** + * Log a warning message object + */ + public void warn(Object message) { + if (!suppressWarnings) { + System.err.print("Warning: "); + System.err.println(message); + } + } + + /** + * Log a warning message with exception + */ + public void warn(Object message, Throwable t) { + if (!suppressWarnings) { + System.err.print("Warning: "); + System.err.println(message); + t.printStackTrace(); + } + } + + /** + * Accessor to the logger implementation + */ + protected Logger getLoggerImpl(Class c) { + return this; + } + + /** + * Overrides the method in the base class to suppress warnings - it can + * be set using the system property jxl.nowarnings. + * This method was originally present in the WorkbookSettings bean, + * but has been moved to the logger class. This means it is now present + * when the JVM is initialized, and subsequent to change it on + * a Workbook by Workbook basis will prove fruitless + * + * @param w suppression flag + */ + public void setSuppressWarnings(boolean w) { + suppressWarnings = w; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java new file mode 100755 index 0000000..0f3ce0b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/common/log/SimpleLoggerName.java @@ -0,0 +1,29 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.common.log; + +/** + * Static structure containing the class name of the logger. This may + * be overwritten at build time if loggers other than the default, + * no-dependency logger are required + */ +public class SimpleLoggerName { + public final static String NAME = SimpleLoggerName.class.getName(); +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Alignment.java b/datastructures-xslx/src/main/java/jxl/format/Alignment.java new file mode 100755 index 0000000..6c5cb5b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Alignment.java @@ -0,0 +1,115 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + */ +public /*final*/ class Alignment { + /** + * The standard alignment + */ + public static Alignment GENERAL = new Alignment(0, "general"); + /** + * Data cells with this alignment will appear at the left hand edge of the + * cell + */ + public static Alignment LEFT = new Alignment(1, "left"); + /** + * Data in cells with this alignment will be centred + */ + public static Alignment CENTRE = new Alignment(2, "centre"); + /** + * Data in cells with this alignment will be right aligned + */ + public static Alignment RIGHT = new Alignment(3, "right"); + /** + * Data in cells with this alignment will fill the cell + */ + public static Alignment FILL = new Alignment(4, "fill"); + /** + * Data in cells with this alignment will be justified + */ + public static Alignment JUSTIFY = new Alignment(5, "justify"); + /** + * The list of alignments + */ + private static Alignment[] alignments = new Alignment[0]; + /** + * The internal numerical repreentation of the alignment + */ + private final int value; + /** + * The string description of this alignment + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param string + */ + protected Alignment(int val, String s) { + value = val; + string = s; + + Alignment[] oldaligns = alignments; + alignments = new Alignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Alignment getAlignment(int val) { + for (int i = 0; i < alignments.length; i++) { + if (alignments[i].getValue() == val) { + return alignments[i]; + } + } + + return GENERAL; + } + + /** + * Gets the value of this alignment. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description of this alignment + * + * @return the string description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java b/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java new file mode 100755 index 0000000..f4bbe6d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/BoldStyle.java @@ -0,0 +1,75 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class containing the various bold styles for data + */ +public /*final*/ class BoldStyle { + /** + * Normal style + */ + public static final BoldStyle NORMAL = new BoldStyle(0x190, "Normal"); + /** + * Emboldened style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc, "Bold"); + /** + * The bold weight + */ + private final int value; + /** + * The description + */ + private final String string; + + /** + * Constructor + * + * @param val + */ + protected BoldStyle(int val, String s) { + value = val; + string = s; + } + + /** + * Gets the value of the bold weight. This is the value that will be + * written to the generated Excel file. + * + * @return the bold weight + */ + public int getValue() { + return value; + } + + /** + * Gets the string description of the bold style + */ + public String getDescription() { + return string; + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Border.java b/datastructures-xslx/src/main/java/jxl/format/Border.java new file mode 100755 index 0000000..ad63acf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Border.java @@ -0,0 +1,50 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * The location of a border + */ +public /*final*/ class Border { + public final static Border NONE = new Border("none"); + public final static Border ALL = new Border("all"); + public final static Border TOP = new Border("top"); + public final static Border BOTTOM = new Border("bottom"); + public final static Border LEFT = new Border("left"); + public final static Border RIGHT = new Border("right"); + /** + * The string description + */ + private final String string; + /** + * Constructor + */ + protected Border(String s) { + string = s; + } + + /** + * Gets the description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java b/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java new file mode 100755 index 0000000..23b81d5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/BorderLineStyle.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * The border line style + */ +public /*final*/ class BorderLineStyle { + public final static BorderLineStyle NONE + = new BorderLineStyle(0, "none"); + public final static BorderLineStyle THIN + = new BorderLineStyle(1, "thin"); + public final static BorderLineStyle MEDIUM + = new BorderLineStyle(2, "medium"); + public final static BorderLineStyle DASHED + = new BorderLineStyle(3, "dashed"); + public final static BorderLineStyle DOTTED + = new BorderLineStyle(4, "dotted"); + public final static BorderLineStyle THICK + = new BorderLineStyle(5, "thick"); + public final static BorderLineStyle DOUBLE + = new BorderLineStyle(6, "double"); + public final static BorderLineStyle HAIR + = new BorderLineStyle(7, "hair"); + public final static BorderLineStyle MEDIUM_DASHED + = new BorderLineStyle(8, "medium dashed"); + public final static BorderLineStyle DASH_DOT + = new BorderLineStyle(9, "dash dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT + = new BorderLineStyle(0xa, "medium dash dot"); + public final static BorderLineStyle DASH_DOT_DOT + = new BorderLineStyle(0xb, "Dash dot dot"); + public final static BorderLineStyle MEDIUM_DASH_DOT_DOT + = new BorderLineStyle(0xc, "Medium dash dot dot"); + public final static BorderLineStyle SLANTED_DASH_DOT + = new BorderLineStyle(0xd, "Slanted dash dot"); + /** + * The list of alignments + */ + private static BorderLineStyle[] styles = new BorderLineStyle[0]; + /** + * The value + */ + private final int value; + /** + * The string description + */ + private final String string; + /** + * Constructor + */ + protected BorderLineStyle(int val, String s) { + value = val; + string = s; + + BorderLineStyle[] oldstyles = styles; + styles = new BorderLineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static BorderLineStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NONE; + } + + /** + * Gets the value for this line style + * + * @return the value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/CellFormat.java b/datastructures-xslx/src/main/java/jxl/format/CellFormat.java new file mode 100755 index 0000000..da96fa9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/CellFormat.java @@ -0,0 +1,142 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Interface for cell formats + */ +public interface CellFormat { + /** + * Gets the format used by this format + * + * @return the format + */ + Format getFormat(); + + /** + * Gets the font information used by this format + * + * @return the font + */ + Font getFont(); + + /** + * Gets whether or not the contents of this cell are wrapped + * + * @return TRUE if this cell's contents are wrapped, FALSE otherwise + */ + boolean getWrap(); + + /** + * Gets the horizontal cell alignment + * + * @return the alignment + */ + Alignment getAlignment(); + + /** + * Gets the vertical cell alignment + * + * @return the alignment + */ + VerticalAlignment getVerticalAlignment(); + + /** + * Gets the orientation + * + * @return the orientation + */ + Orientation getOrientation(); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + BorderLineStyle getBorder(Border border); + + /** + * Gets the line style for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + BorderLineStyle getBorderLine(Border border); + + /** + * Gets the colour for the given cell border + * If a border type of ALL or NONE is specified, then a line style of + * NONE is returned + * If the specified cell does not have an associated line style, then + * the colour the line would be is still returned + * + * @param border the cell border we are interested in + * @return the line style of the specified border + */ + Colour getBorderColour(Border border); + + /** + * Determines if this cell format has any borders at all. Used to + * set the new borders when merging a group of cells + * + * @return TRUE if this cell has any borders, FALSE otherwise + */ + boolean hasBorders(); + + /** + * Gets the background colour used by this cell + * + * @return the foreground colour + */ + Colour getBackgroundColour(); + + /** + * Gets the pattern used by this cell format + * + * @return the background pattern + */ + Pattern getPattern(); + + /** + * Gets the indentation of the cell text + * + * @return the indentation + */ + int getIndentation(); + + /** + * Gets the shrink to fit flag + * + * @return TRUE if this format is shrink to fit, FALSE otherise + */ + boolean isShrinkToFit(); + + /** + * Accessor for whether a particular cell is locked + * + * @return TRUE if this cell is locked, FALSE otherwise + */ + boolean isLocked(); +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Colour.java b/datastructures-xslx/src/main/java/jxl/format/Colour.java new file mode 100755 index 0000000..b38934d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Colour.java @@ -0,0 +1,237 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + */ +public /*final*/ class Colour { + // Major colours + public final static Colour UNKNOWN = + new Colour(0x7fee, "unknown", 0, 0, 0); + public final static Colour BLACK = + new Colour(0x7fff, "black", 0, 0, 0); + public final static Colour WHITE = + new Colour(0x9, "white", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND1 + = new Colour(0x0, "default background", 0xff, 0xff, 0xff); + public final static Colour DEFAULT_BACKGROUND + = new Colour(0xc0, "default background", 0xff, 0xff, 0xff); + public final static Colour PALETTE_BLACK = + new Colour(0x8, "black", 0x1, 0, 0); + // Other colours + public final static Colour RED = new Colour(0xa, "red", 0xff, 0, 0); + public final static Colour BRIGHT_GREEN = new Colour(0xb, "bright green", 0, 0xff, 0); + public final static Colour BLUE = new Colour(0xc, "blue", 0, 0, 0xff); + public final static Colour YELLOW = new Colour(0xd, "yellow", 0xff, 0xff, 0); + public final static Colour PINK = new Colour(0xe, "pink", 0xff, 0, 0xff); + public final static Colour TURQUOISE = new Colour(0xf, "turquoise", 0, 0xff, 0xff); + public final static Colour DARK_RED = new Colour(0x10, "dark red", 0x80, 0, 0); + public final static Colour GREEN = new Colour(0x11, "green", 0, 0x80, 0); + public final static Colour DARK_BLUE = new Colour(0x12, "dark blue", 0, 0, 0x80); + public final static Colour DARK_YELLOW = new Colour(0x13, "dark yellow", 0x80, 0x80, 0); + public final static Colour VIOLET = new Colour(0x14, "violet", 0x80, 0x80, 0); + public final static Colour TEAL = new Colour(0x15, "teal", 0, 0x80, 0x80); + public final static Colour GREY_25_PERCENT = new Colour(0x16, "grey 25%", 0xc0, 0xc0, 0xc0); + // the first item in the colour palette + public final static Colour GREY_50_PERCENT = new Colour(0x17, "grey 50%", 0x80, 0x80, 0x80); + public final static Colour PERIWINKLE = new Colour(0x18, "periwinkle%", 0x99, 0x99, 0xff); + public final static Colour PLUM2 = new Colour(0x19, "plum", 0x99, 0x33, 0x66); + public final static Colour IVORY = new Colour(0x1a, "ivory", 0xff, 0xff, 0xcc); + public final static Colour LIGHT_TURQUOISE2 = new Colour(0x1b, "light turquoise", 0xcc, 0xff, 0xff); + public final static Colour DARK_PURPLE = new Colour(0x1c, "dark purple", 0x66, 0x0, 0x66); + public final static Colour CORAL = new Colour(0x1d, "coral", 0xff, 0x80, 0x80); + public final static Colour OCEAN_BLUE = new Colour(0x1e, "ocean blue", 0x0, 0x66, 0xcc); + public final static Colour ICE_BLUE = new Colour(0x1f, "ice blue", 0xcc, 0xcc, 0xff); + public final static Colour DARK_BLUE2 = new Colour(0x20, "dark blue", 0, 0, 0x80); + public final static Colour PINK2 = new Colour(0x21, "pink", 0xff, 0, 0xff); + public final static Colour YELLOW2 = new Colour(0x22, "yellow", 0xff, 0xff, 0x0); + public final static Colour TURQOISE2 = new Colour(0x23, "turqoise", 0x0, 0xff, 0xff); + public final static Colour VIOLET2 = new Colour(0x24, "violet", 0x80, 0x0, 0x80); + public final static Colour DARK_RED2 = new Colour(0x25, "dark red", 0x80, 0x0, 0x0); + public final static Colour TEAL2 = new Colour(0x26, "teal", 0x0, 0x80, 0x80); + public final static Colour BLUE2 = new Colour(0x27, "blue", 0x0, 0x0, 0xff); + public final static Colour SKY_BLUE = new Colour(0x28, "sky blue", 0, 0xcc, 0xff); + public final static Colour LIGHT_TURQUOISE + = new Colour(0x29, "light turquoise", 0xcc, 0xff, 0xff); + public final static Colour LIGHT_GREEN = new Colour(0x2a, "light green", 0xcc, 0xff, 0xcc); + public final static Colour VERY_LIGHT_YELLOW + = new Colour(0x2b, "very light yellow", 0xff, 0xff, 0x99); + public final static Colour PALE_BLUE = new Colour(0x2c, "pale blue", 0x99, 0xcc, 0xff); + public final static Colour ROSE = new Colour(0x2d, "rose", 0xff, 0x99, 0xcc); + public final static Colour LAVENDER = new Colour(0x2e, "lavender", 0xcc, 0x99, 0xff); + public final static Colour TAN = new Colour(0x2f, "tan", 0xff, 0xcc, 0x99); + public final static Colour LIGHT_BLUE = new Colour(0x30, "light blue", 0x33, 0x66, 0xff); + public final static Colour AQUA = new Colour(0x31, "aqua", 0x33, 0xcc, 0xcc); + public final static Colour LIME = new Colour(0x32, "lime", 0x99, 0xcc, 0); + public final static Colour GOLD = new Colour(0x33, "gold", 0xff, 0xcc, 0); + public final static Colour LIGHT_ORANGE + = new Colour(0x34, "light orange", 0xff, 0x99, 0); + public final static Colour ORANGE = new Colour(0x35, "orange", 0xff, 0x66, 0); + public final static Colour BLUE_GREY = new Colour(0x36, "blue grey", 0x66, 0x66, 0xcc); + public final static Colour GREY_40_PERCENT = new Colour(0x37, "grey 40%", 0x96, 0x96, 0x96); + public final static Colour DARK_TEAL = new Colour(0x38, "dark teal", 0, 0x33, 0x66); + public final static Colour SEA_GREEN = new Colour(0x39, "sea green", 0x33, 0x99, 0x66); + public final static Colour DARK_GREEN = new Colour(0x3a, "dark green", 0, 0x33, 0); + public final static Colour OLIVE_GREEN = new Colour(0x3b, "olive green", 0x33, 0x33, 0); + public final static Colour BROWN = new Colour(0x3c, "brown", 0x99, 0x33, 0); + public final static Colour PLUM = new Colour(0x3d, "plum", 0x99, 0x33, 0x66); + public final static Colour INDIGO = new Colour(0x3e, "indigo", 0x33, 0x33, 0x99); + public final static Colour GREY_80_PERCENT = new Colour(0x3f, "grey 80%", 0x33, 0x33, 0x33); + public final static Colour AUTOMATIC = new Colour(0x40, "automatic", 0xff, 0xff, 0xff); + // Colours added for backwards compatibility + public final static Colour GRAY_80 = GREY_80_PERCENT; + public final static Colour GRAY_50 = GREY_50_PERCENT; + public final static Colour GRAY_25 = GREY_25_PERCENT; + /** + * The list of internal colours + */ + private static Colour[] colours = new Colour[0]; + /** + * The internal numerical representation of the colour + */ + private final int value; + /** + * The default RGB value + */ + private final RGB rgb; + /** + * The display string for the colour. Used when presenting the + * format information + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param s the display string + * @param r the default red value + * @param g the default green value + * @param b the default blue value + */ + protected Colour(int val, String s, int r, int g, int b) { + value = val; + string = s; + rgb = new RGB(r, g, b); + + Colour[] oldcols = colours; + colours = new Colour[oldcols.length + 1]; + System.arraycopy(oldcols, 0, colours, 0, oldcols.length); + colours[oldcols.length] = this; + } + + /** + * Gets the internal colour from the value + * + * @param val + * @return the colour with that value + */ + public static Colour getInternalColour(int val) { + for (int i = 0; i < colours.length; i++) { + if (colours[i].getValue() == val) { + return colours[i]; + } + } + + return UNKNOWN; + } + + /** + * Returns all available colours - used when generating the default palette + * + * @return all available colours + */ + public static Colour[] getAllColours() { + return colours; + } + + /** + * Gets the value of this colour. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } + + /** + * Gets the default red content of this colour. Used when writing the + * default colour palette + * + * @return the red content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultRed() { + return rgb.getRed(); + } + + /** + * Gets the default green content of this colour. Used when writing the + * default colour palette + * + * @return the green content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultGreen() { + return rgb.getGreen(); + } + + /** + * Gets the default blue content of this colour. Used when writing the + * default colour palette + * + * @return the blue content of this colour + * @deprecated use getDefaultRGB instead + */ + public int getDefaultBlue() { + return rgb.getBlue(); + } + + /** + * Returns the default RGB of the colour + * + * @return the default RGB + */ + public RGB getDefaultRGB() { + return rgb; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Font.java b/datastructures-xslx/src/main/java/jxl/format/Font.java new file mode 100755 index 0000000..26d5524 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Font.java @@ -0,0 +1,82 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Interface which exposes the user font display information to the user + */ +public interface Font { + /** + * Gets the name of this font + * + * @return the name of this font + */ + String getName(); + + /** + * Gets the point size for this font, if the font hasn't been initialized + * + * @return the point size + */ + int getPointSize(); + + /** + * Gets the bold weight for this font + * + * @return the bold weight for this font + */ + int getBoldWeight(); + + /** + * Returns the italic flag + * + * @return TRUE if this font is italic, FALSE otherwise + */ + boolean isItalic(); + + /** + * Returns the strike-out flag + * + * @return TRUE if this font is struck-out, FALSE otherwise + */ + boolean isStruckout(); + + /** + * Gets the underline style for this font + * + * @return the underline style + */ + UnderlineStyle getUnderlineStyle(); + + /** + * Gets the colour for this font + * + * @return the colour + */ + Colour getColour(); + + /** + * Gets the script style + * + * @return the script style + */ + ScriptStyle getScriptStyle(); +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/Format.java b/datastructures-xslx/src/main/java/jxl/format/Format.java new file mode 100755 index 0000000..842bd39 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Format.java @@ -0,0 +1,40 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Exposes the cell formatting information + */ +public interface Format { + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + String getFormatString(); +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/Orientation.java b/datastructures-xslx/src/main/java/jxl/format/Orientation.java new file mode 100755 index 0000000..b131415 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Orientation.java @@ -0,0 +1,128 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the orientation of data within a cell + */ +public final class Orientation { + /** + * Cells with this specified orientation will be horizontal + */ + public static Orientation HORIZONTAL = new Orientation(0, "horizontal"); + /** + * Cells with this specified orientation have their data + * presented vertically + */ + public static Orientation VERTICAL = new Orientation(0xff, "vertical"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees upwards + */ + public static Orientation PLUS_90 = new Orientation(90, "up 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation of 90 degrees downwards + */ + public static Orientation MINUS_90 = new Orientation(180, "down 90"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees upwards + */ + public static Orientation PLUS_45 = new Orientation(45, "up 45"); + /** + * Cells with this specified orientation will have their data + * presented with a rotation 45 degrees downwards + */ + public static Orientation MINUS_45 = new Orientation(135, "down 45"); + /** + * Cells with this specified orientation will have their text stacked + * downwards, but not rotated + */ + public static Orientation STACKED = new Orientation(255, "stacked"); + /** + * The list of alignments + */ + private static Orientation[] orientations = new Orientation[0]; + /** + * The internal binary value which gets written to the generated Excel file + */ + private final int value; + /** + * The textual description + */ + private final String string; + /** + * Constructor + * + * @param val + */ + protected Orientation(int val, String s) { + value = val; + string = s; + + Orientation[] oldorients = orientations; + orientations = new Orientation[oldorients.length + 1]; + System.arraycopy(oldorients, 0, orientations, 0, oldorients.length); + orientations[oldorients.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static Orientation getOrientation(int val) { + for (int i = 0; i < orientations.length; i++) { + if (orientations[i].getValue() == val) { + return orientations[i]; + } + } + + return HORIZONTAL; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/PageOrder.java b/datastructures-xslx/src/main/java/jxl/format/PageOrder.java new file mode 100755 index 0000000..3a1c816 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PageOrder.java @@ -0,0 +1,40 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public class PageOrder { + /** + * Top to Down then Right. + */ + public static PageOrder DOWN_THEN_RIGHT = new PageOrder(); + /** + * Left to Right then Down. + */ + public static PageOrder RIGHT_THEN_DOWN = new PageOrder(); + + /** + * Constructor + */ + private PageOrder() { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java b/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java new file mode 100755 index 0000000..c65cb38 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PageOrientation.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the page orientation + */ +public final class PageOrientation { + /** + * Portrait orientation + */ + public static PageOrientation PORTRAIT = new PageOrientation(); + /** + * Landscape orientation + */ + public static PageOrientation LANDSCAPE = new PageOrientation(); + /** + * Constructor + */ + private PageOrientation() { + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/PaperSize.java b/datastructures-xslx/src/main/java/jxl/format/PaperSize.java new file mode 100755 index 0000000..78fb4a8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/PaperSize.java @@ -0,0 +1,406 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which contains the available excel paper sizes and their + * codes + */ +public final class PaperSize { + /** + * US Letter 8.5 x 11" + */ + public static final PaperSize UNDEFINED = new PaperSize(0); + /** + * US Letter 8.5 x 11" + */ + public static final PaperSize LETTER = new PaperSize(1); + /** + * Letter small 8.5" x 11" + */ + public static final PaperSize LETTER_SMALL = new PaperSize(2); + /** + * Tabloid 11" x 17" + */ + public static final PaperSize TABLOID = new PaperSize(3); + /** + * Leger 17" x 11" + */ + public static final PaperSize LEDGER = new PaperSize(4); + /** + * US Legal 8.5" x 14" + */ + public static final PaperSize LEGAL = new PaperSize(5); + /** + * Statement 5.5" x 8.5" + */ + public static final PaperSize STATEMENT = new PaperSize(6); + /** + * Executive 7.25" x 10.5" + */ + public static final PaperSize EXECUTIVE = new PaperSize(7); + /** + * A3 297mm x 420mm + */ + public static final PaperSize A3 = new PaperSize(8); + /** + * A4 210mm x 297mm + */ + public static final PaperSize A4 = new PaperSize(9); + /** + * A4 Small 210mm x 297 mm + */ + public static final PaperSize A4_SMALL = new PaperSize(10); + /** + * A5 148mm x 210mm + */ + public static final PaperSize A5 = new PaperSize(11); + /** + * B4 (JIS) 257mm x 364mm + */ + public static final PaperSize B4 = new PaperSize(12); + /** + * B5 (JIS) 182mm x 257mm + */ + public static final PaperSize B5 = new PaperSize(13); + /** + * Folio 8.5" x 13" + */ + public static final PaperSize FOLIO = new PaperSize(14); + /** + * Quarto 215mm x 275mm + */ + public static final PaperSize QUARTO = new PaperSize(15); + /** + * 10" x 14" + */ + public static final PaperSize SIZE_10x14 = new PaperSize(16); + /** + * 11" x 17" + */ + public static final PaperSize SIZE_10x17 = new PaperSize(17); + /** + * NOTE 8.5" x 11" + */ + public static final PaperSize NOTE = new PaperSize(18); + /** + * Envelope #9 3 7/8" x 8 7/8" + */ + public static final PaperSize ENVELOPE_9 = new PaperSize(19); + /** + * Envelope #10 4 1/8" x 9.5" + */ + public static final PaperSize ENVELOPE_10 = new PaperSize(20); + /** + * Envelope #11 4.5" x 10 3/8" + */ + public static final PaperSize ENVELOPE_11 = new PaperSize(21); + /** + * Envelope #12 4.75" x 11" + */ + public static final PaperSize ENVELOPE_12 = new PaperSize(22); + /** + * Envelope #14 5" x 11.5" + */ + public static final PaperSize ENVELOPE_14 = new PaperSize(23); + /** + * C 17" x 22" + */ + public static final PaperSize C = new PaperSize(24); + /** + * D 22" x 34" + */ + public static final PaperSize D = new PaperSize(25); + /** + * E 34" x 44" + */ + public static final PaperSize E = new PaperSize(26); + /** + * Envelope DL 110mm � 220mm + */ + public static final PaperSize ENVELOPE_DL = new PaperSize(27); + /** + * Envelope C5 162mm � 229mm + */ + public static final PaperSize ENVELOPE_C5 = new PaperSize(28); + /** + * Envelope C3 324mm � 458mm + */ + public static final PaperSize ENVELOPE_C3 = new PaperSize(29); + /** + * Envelope C4 229mm � 324mm + */ + public static final PaperSize ENVELOPE_C4 = new PaperSize(30); + /** + * Envelope C6 114mm � 162mm + */ + public static final PaperSize ENVELOPE_C6 = new PaperSize(31); + /** + * Envelope C6/C5 114mm � 229mm + */ + public static final PaperSize ENVELOPE_C6_C5 = new PaperSize(32); + /** + * B4 (ISO) 250mm � 353mm + */ + public static final PaperSize B4_ISO = new PaperSize(33); + /** + * B5 (ISO) 176mm � 250mm + */ + public static final PaperSize B5_ISO = new PaperSize(34); + /** + * B6 (ISO) 125mm � 176mm + */ + public static final PaperSize B6_ISO = new PaperSize(35); + /** + * Envelope Italy 110mm � 230mm + */ + public static final PaperSize ENVELOPE_ITALY = new PaperSize(36); + /** + * Envelope Monarch 3 7/8" � 7.5" + */ + public static final PaperSize ENVELOPE_MONARCH = new PaperSize(37); + /** + * 6.75 Envelope 3 5/8" � 6.5" + */ + public static final PaperSize ENVELOPE_6_75 = new PaperSize(38); + /** + * US Standard Fanfold 14 7/8" � 11" + */ + public static final PaperSize US_FANFOLD = new PaperSize(39); + /** + * German Std. Fanfold 8.5" � 12" + */ + public static final PaperSize GERMAN_FANFOLD = new PaperSize(40); + /** + * German Legal Fanfold 8.5" � 13" + */ + public static final PaperSize GERMAN_LEGAL_FANFOLD = new PaperSize(41); + /** + * B4 (ISO) 250mm � 353mm + */ + public static final PaperSize B4_ISO_2 = new PaperSize(42); + /** + * Japanese Postcard 100mm � 148mm + */ + public static final PaperSize JAPANESE_POSTCARD = new PaperSize(43); + /** + * 9�11 9" � 11" + */ + public static final PaperSize SIZE_9x11 = new PaperSize(44); + /** + * 10�11 10" � 11" + */ + public static final PaperSize SIZE_10x11 = new PaperSize(45); + /** + * 15�11 15" � 11" + */ + public static final PaperSize SIZE_15x11 = new PaperSize(46); + /** + * Envelope Invite 220mm � 220mm + */ + public static final PaperSize ENVELOPE_INVITE = new PaperSize(47); + /** + * Letter Extra 9.5" � 12" + */ + public static final PaperSize LETTER_EXTRA = new PaperSize(50); + /** + * Legal Extra 9.5" � 15" + */ + public static final PaperSize LEGAL_EXTRA = new PaperSize(51); + /** + * Tabloid Extra 11 11/16" � 18" + */ + public static final PaperSize TABLOID_EXTRA = new PaperSize(52); + /** + * A4 Extra 235mm � 322mm + */ + public static final PaperSize A4_EXTRA = new PaperSize(53); + /** + * Letter Transverse 8.5" � 11" + */ + public static final PaperSize LETTER_TRANSVERSE = new PaperSize(54); + /** + * A4 Transverse 210mm � 297mm + */ + public static final PaperSize A4_TRANSVERSE = new PaperSize(55); + /** + * Letter Extra Transv. 9.5" � 12" + */ + public static final PaperSize LETTER_EXTRA_TRANSVERSE = new PaperSize(56); + + /* 48 & 49 Undefined */ + /** + * Super A/A4 227mm � 356mm + */ + public static final PaperSize SUPER_A_A4 = new PaperSize(57); + /** + * Super B/A3 305mm � 487mm + */ + public static final PaperSize SUPER_B_A3 = new PaperSize(58); + /** + * Letter Plus 8.5" x 12 11/16" + */ + public static final PaperSize LETTER_PLUS = new PaperSize(59); + /** + * A4 Plus 210mm � 330mm + */ + public static final PaperSize A4_PLUS = new PaperSize(60); + /** + * A5 Transverse 148mm � 210mm + */ + public static final PaperSize A5_TRANSVERSE = new PaperSize(61); + /** + * B5 (JIS) Transverse 182mm � 257mm + */ + public static final PaperSize B5_TRANSVERSE = new PaperSize(62); + /** + * A3 Extra 322mm � 445mm + */ + public static final PaperSize A3_EXTRA = new PaperSize(63); + /** + * A5 Extra 174mm � 235mm + */ + public static final PaperSize A5_EXTRA = new PaperSize(64); + /** + * B5 (ISO) Extra 201mm � 276mm + */ + public static final PaperSize B5_EXTRA = new PaperSize(65); + /** + * A2 420mm � 594mm + */ + public static final PaperSize A2 = new PaperSize(66); + /** + * A3 Transverse 297mm � 420mm + */ + public static final PaperSize A3_TRANSVERSE = new PaperSize(67); + /** + * A3 Extra Transverse 322mm � 445mm + */ + public static final PaperSize A3_EXTRA_TRANSVERSE = new PaperSize(68); + /** + * Dbl. Japanese Postcard 200mm � 148mm + */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD = new PaperSize(69); + /** + * A6 105mm � 148mm + */ + public static final PaperSize A6 = new PaperSize(70); + /** + * Letter Rotated 11" � 8.5" + */ + public static final PaperSize LETTER_ROTATED = new PaperSize(75); + /** + * A3 Rotated 420mm � 297mm + */ + public static final PaperSize A3_ROTATED = new PaperSize(76); + /** + * A4 Rotated 297mm � 210mm + */ + public static final PaperSize A4_ROTATED = new PaperSize(77); + /** + * A5 Rotated 210mm � 148mm + */ + public static final PaperSize A5_ROTATED = new PaperSize(78); + /** + * B4 (JIS) Rotated 364mm � 257mm + */ + public static final PaperSize B4_ROTATED = new PaperSize(79); + /** + * B5 (JIS) Rotated 257mm � 182mm + */ + public static final PaperSize B5_ROTATED = new PaperSize(80); + /** + * Japanese Postcard Rot. 148mm � 100mm + */ + public static final PaperSize JAPANESE_POSTCARD_ROTATED = new PaperSize(81); + + /* 71 - 74 undefined */ + /** + * Dbl. Jap. Postcard Rot. 148mm � 200mm + */ + public static final PaperSize DOUBLE_JAPANESE_POSTCARD_ROTATED = new PaperSize(82); + /** + * A6 Rotated 148mm � 105mm + */ + public static final PaperSize A6_ROTATED = new PaperSize(83); + /** + * B6 (JIS) 128mm � 182mm + */ + public static final PaperSize B6 = new PaperSize(88); + /** + * B6 (JIS) Rotated 182mm � 128mm + */ + public static final PaperSize B6_ROTATED = new PaperSize(89); + private static final int LAST_PAPER_SIZE = 89; + /** + * The paper sizes + */ + private static PaperSize[] paperSizes = new PaperSize[LAST_PAPER_SIZE + 1]; + /** + * The excel encoding + */ + private final int val; + + /** + * Constructor + */ + private PaperSize(int v, boolean growArray) { + val = v; + + if (v >= paperSizes.length && growArray) { + // Grow the array and add this to it + PaperSize[] newarray = new PaperSize[v + 1]; + System.arraycopy(paperSizes, 0, newarray, 0, paperSizes.length); + paperSizes = newarray; + } + if (v < paperSizes.length) { + paperSizes[v] = this; + } + } + + /** + * Constructor + */ + private PaperSize(int v) { + this(v, true); + } + + /* 84 - 87 undefined */ + + /** + * Gets the paper size for a specific value + * + * @param val the value + * @return the paper size + */ + public static PaperSize getPaperSize(int val) { + PaperSize p = val > paperSizes.length - 1 ? null : paperSizes[val]; + return p == null ? new PaperSize(val, false) : p; + } + + /** + * Accessor for the internal binary value association with this paper size + * + * @return the internal value + */ + public int getValue() { + return val; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/format/Pattern.java b/datastructures-xslx/src/main/java/jxl/format/Pattern.java new file mode 100755 index 0000000..30fdd90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/Pattern.java @@ -0,0 +1,120 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + */ +public /*final*/ class Pattern { + public final static Pattern NONE = new Pattern(0x0, "None"); + public final static Pattern SOLID = new Pattern(0x1, "Solid"); + public final static Pattern GRAY_50 = new Pattern(0x2, "Gray 50%"); + public final static Pattern GRAY_75 = new Pattern(0x3, "Gray 75%"); + public final static Pattern GRAY_25 = new Pattern(0x4, "Gray 25%"); + public final static Pattern PATTERN1 = new Pattern(0x5, "Pattern 1"); + public final static Pattern PATTERN2 = new Pattern(0x6, "Pattern 2"); + public final static Pattern PATTERN3 = new Pattern(0x7, "Pattern 3"); + public final static Pattern PATTERN4 = new Pattern(0x8, "Pattern 4"); + public final static Pattern PATTERN5 = new Pattern(0x9, "Pattern 5"); + public final static Pattern PATTERN6 = new Pattern(0xa, "Pattern 6"); + public final static Pattern PATTERN7 = new Pattern(0xb, "Pattern 7"); + public final static Pattern PATTERN8 = new Pattern(0xc, "Pattern 8"); + public final static Pattern PATTERN9 = new Pattern(0xd, "Pattern 9"); + public final static Pattern PATTERN10 = new Pattern(0xe, "Pattern 10"); + public final static Pattern PATTERN11 = new Pattern(0xf, "Pattern 11"); + public final static Pattern PATTERN12 = new Pattern(0x10, "Pattern 12"); + public final static Pattern PATTERN13 = new Pattern(0x11, "Pattern 13"); + public final static Pattern PATTERN14 = new Pattern(0x12, "Pattern 14"); + /** + * The list of patterns + */ + private static Pattern[] patterns = new Pattern[0]; + /** + * The internal numerical representation of the colour + */ + private final int value; + /** + * The textual description + */ + private final String string; + /** + * Private constructor + * + * @param val + * @param s + */ + protected Pattern(int val, String s) { + value = val; + string = s; + + Pattern[] oldcols = patterns; + patterns = new Pattern[oldcols.length + 1]; + System.arraycopy(oldcols, 0, patterns, 0, oldcols.length); + patterns[oldcols.length] = this; + } + + /** + * Gets the pattern from the value + * + * @param val + * @return the pattern with that value + */ + public static Pattern getPattern(int val) { + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].getValue() == val) { + return patterns[i]; + } + } + + return NONE; + } + + /** + * Gets the value of this pattern. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + * + * @return the string + */ + public String getDescription() { + return string; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/RGB.java b/datastructures-xslx/src/main/java/jxl/format/RGB.java new file mode 100755 index 0000000..9dbd994 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/RGB.java @@ -0,0 +1,82 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + + +package jxl.format; + +/** + * A structure which contains the RGB values for a particular colour + */ +public final class RGB { + /** + * The red component of this colour + */ + private final int red; + + /** + * The green component of this colour + */ + private final int green; + + /** + * The blue component of this colour + */ + private final int blue; + + /** + * Constructor + * + * @param r the red component + * @param g the green component + * @param b the blue component + */ + public RGB(int r, int g, int b) { + red = r; + green = g; + blue = b; + } + + /** + * Accessor for the red component + * + * @return the red component of the colour, between 0 and 255 + */ + public int getRed() { + return red; + } + + /** + * Accessor for the green component + * + * @return the green component of the colour, between 0 and 255 + */ + public int getGreen() { + return green; + } + + /** + * Accessor for the blue component + * + * @return the blue component of the colour, between 0 and 255 + */ + public int getBlue() { + return blue; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java b/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java new file mode 100755 index 0000000..b09b789 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/ScriptStyle.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various script styles available + * within the standard Excel ScriptStyle palette + */ +public final class ScriptStyle { + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = new ScriptStyle(0, "normal"); + public static final ScriptStyle SUPERSCRIPT = new ScriptStyle(1, "super"); + public static final ScriptStyle SUBSCRIPT = new ScriptStyle(2, "sub"); + /** + * The list of ScriptStyles + */ + private static ScriptStyle[] styles = new ScriptStyle[0]; + /** + * The internal numerical representation of the ScriptStyle + */ + private final int value; + /** + * The display string for the script style. Used when presenting the + * format information + */ + private final String string; + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected ScriptStyle(int val, String s) { + value = val; + string = s; + + ScriptStyle[] oldstyles = styles; + styles = new ScriptStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the ScriptStyle from the value + * + * @param val + * @return the ScriptStyle with that value + */ + public static ScriptStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NORMAL_SCRIPT; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } + + +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java b/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java new file mode 100755 index 0000000..0e34a39 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/UnderlineStyle.java @@ -0,0 +1,113 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration class which contains the various underline styles available + * within the standard Excel UnderlineStyle palette + */ +public final class UnderlineStyle { + // The underline styles + public static final UnderlineStyle NO_UNDERLINE = + new UnderlineStyle(0, "none"); + public static final UnderlineStyle SINGLE = + new UnderlineStyle(1, "single"); + public static final UnderlineStyle DOUBLE = + new UnderlineStyle(2, "double"); + public static final UnderlineStyle SINGLE_ACCOUNTING = + new UnderlineStyle(0x21, "single accounting"); + public static final UnderlineStyle DOUBLE_ACCOUNTING = + new UnderlineStyle(0x22, "double accounting"); + /** + * The list of UnderlineStyles + */ + private static UnderlineStyle[] styles = new UnderlineStyle[0]; + /** + * The internal numerical representation of the UnderlineStyle + */ + private final int value; + /** + * The display string for the underline style. Used when presenting the + * format information + */ + private final String string; + + /** + * Private constructor + * + * @param val + * @param s the display string + */ + protected UnderlineStyle(int val, String s) { + value = val; + string = s; + + UnderlineStyle[] oldstyles = styles; + styles = new UnderlineStyle[oldstyles.length + 1]; + System.arraycopy(oldstyles, 0, styles, 0, oldstyles.length); + styles[oldstyles.length] = this; + } + + /** + * Gets the UnderlineStyle from the value + * + * @param val + * @return the UnderlineStyle with that value + */ + public static UnderlineStyle getStyle(int val) { + for (int i = 0; i < styles.length; i++) { + if (styles[i].getValue() == val) { + return styles[i]; + } + } + + return NO_UNDERLINE; + } + + /** + * Gets the value of this style. This is the value that is written to + * the generated Excel file + * + * @return the binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the string description for display purposes + * + * @return the string description + */ + public String getDescription() { + return string; + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java b/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java new file mode 100755 index 0000000..028beb7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/format/VerticalAlignment.java @@ -0,0 +1,107 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.format; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + */ +public /*final*/ class VerticalAlignment { + /** + * Cells with this specified vertical alignment will have their data + * aligned at the top + */ + public static VerticalAlignment TOP = new VerticalAlignment(0, "top"); + /** + * Cells with this specified vertical alignment will have their data + * aligned centrally + */ + public static VerticalAlignment CENTRE = new VerticalAlignment(1, "centre"); + /** + * Cells with this specified vertical alignment will have their data + * aligned at the bottom + */ + public static VerticalAlignment BOTTOM = new VerticalAlignment(2, "bottom"); + /** + * Cells with this specified vertical alignment will have their data + * justified + */ + public static VerticalAlignment JUSTIFY = new VerticalAlignment(3, "Justify"); + /** + * The list of alignments + */ + private static VerticalAlignment[] alignments = new VerticalAlignment[0]; + /** + * The internal binary value which gets written to the generated Excel file + */ + private final int value; + /** + * The textual description + */ + private final String string; + + + /** + * Constructor + * + * @param val + */ + protected VerticalAlignment(int val, String s) { + value = val; + string = s; + + VerticalAlignment[] oldaligns = alignments; + alignments = new VerticalAlignment[oldaligns.length + 1]; + System.arraycopy(oldaligns, 0, alignments, 0, oldaligns.length); + alignments[oldaligns.length] = this; + } + + /** + * Gets the alignment from the value + * + * @param val + * @return the alignment with that value + */ + public static VerticalAlignment getAlignment(int val) { + for (int i = 0; i < alignments.length; i++) { + if (alignments[i].getValue() == val) { + return alignments[i]; + } + } + + return BOTTOM; + } + + /** + * Accessor for the binary value + * + * @return the internal binary value + */ + public int getValue() { + return value; + } + + /** + * Gets the textual description + */ + public String getDescription() { + return string; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java new file mode 100755 index 0000000..3086e37 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BOFRecord.java @@ -0,0 +1,159 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A Beginning Of File record, found at the commencement of all substreams + * within a biff8 file + */ +public class BOFRecord extends RecordData { + /** + * The code used for biff8 files + */ + private static final int Biff8 = 0x600; + /** + * The code used for biff8 files + */ + private static final int Biff7 = 0x500; + /** + * The code used for workbook globals + */ + private static final int WorkbookGlobals = 0x5; + /** + * The code used for worksheets + */ + private static final int Worksheet = 0x10; + /** + * The code used for charts + */ + private static final int Chart = 0x20; + /** + * The code used for macro sheets + */ + private static final int MacroSheet = 0x40; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BOFRecord.class); + /** + * The biff version of this substream + */ + private final int version; + /** + * The type of this substream + */ + private final int substreamType; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + BOFRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + version = IntegerHelper.getInt(data[0], data[1]); + substreamType = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Interrogates this object to see if it is a biff8 substream + * + * @return TRUE if this substream is biff8, false otherwise + */ + public boolean isBiff8() { + return version == Biff8; + } + + /** + * Interrogates this object to see if it is a biff7 substream + * + * @return TRUE if this substream is biff7, false otherwise + */ + public boolean isBiff7() { + return version == Biff7; + } + + + /** + * Interrogates this substream to see if it represents the commencement of + * the workbook globals substream + * + * @return TRUE if this is the commencement of a workbook globals substream, + * FALSE otherwise + */ + boolean isWorkbookGlobals() { + return substreamType == WorkbookGlobals; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isWorksheet() { + return substreamType == Worksheet; + } + + /** + * Interrogates the substream to see if it is the commencement of a worksheet + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isMacroSheet() { + return substreamType == MacroSheet; + } + + /** + * Interrogates the substream to see if it is a chart + * + * @return TRUE if this substream is the beginning of a worksheet, FALSE + * otherwise + */ + public boolean isChart() { + return substreamType == Chart; + } + + /** + * Gets the length of the data portion of this record + * Used to adjust when reading sheets which contain just a chart + * + * @return the length of the data portion of this record + */ + int getLength() { + return getRecord().getLength(); + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java new file mode 100755 index 0000000..9cb622b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BaseSharedFormulaRecord.java @@ -0,0 +1,164 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A base class for shared formula records + */ +public abstract class BaseSharedFormulaRecord extends CellValue + implements FormulaData { + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The position of the next record in the file. Used when looking for + * for subsequent records eg. a string value + */ + private final int filePos; + + /** + * The array of parsed tokens + */ + private byte[] tokens; + + /** + * The external sheet + */ + private final ExternalSheet externalSheet; + + /** + * The name table + */ + private final WorkbookMethods nameTable; + + /** + * Constructs this number + * + * @param t the record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + * @param pos the position of the next record in the file + */ + public BaseSharedFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + int pos) { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + filePos = pos; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Accessor for the tokens which make up this formula + * + * @return the tokens + */ + protected final byte[] getTokens() { + return tokens; + } + + /** + * Called by the shared formula record to set the tokens for + * this formula + * + * @param t the tokens + */ + void setTokens(byte[] t) { + tokens = t; + } + + /** + * Access for the external sheet + * + * @return the external sheet + */ + protected final ExternalSheet getExternalSheet() { + return externalSheet; + } + + /** + * Access for the name table + * + * @return the name table + */ + protected final WorkbookMethods getNameTable() { + return nameTable; + } + + /** + * In case the shared formula is not added for any reason, we need + * to expose the raw record data , in order to try again + * + * @return the record data from the base class + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Accessor for the position of the next record + * + * @return the position of the next record + */ + final int getFilePos() { + return filePos; + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java b/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java new file mode 100755 index 0000000..92b1cd9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BiffException.java @@ -0,0 +1,96 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.JXLException; + +/** + * Exception thrown when reading a biff file + */ +public class BiffException extends JXLException { + /** + * + */ + static final BiffMessage unrecognizedBiffVersion = + new BiffMessage("Unrecognized biff version"); + /** + * + */ + static final BiffMessage expectedGlobals = + new BiffMessage("Expected globals"); + /** + * + */ + static final BiffMessage excelFileTooBig = + new BiffMessage("Not all of the excel file could be read"); + /** + * + */ + static final BiffMessage excelFileNotFound = + new BiffMessage("The input file was not found"); + /** + * + */ + static final BiffMessage unrecognizedOLEFile = + new BiffMessage("Unable to recognize OLE stream"); + /** + * + */ + static final BiffMessage streamNotFound = + new BiffMessage("Compound file does not contain the specified stream"); + /** + * + */ + static final BiffMessage passwordProtected = + new BiffMessage("The workbook is password protected"); + /** + * + */ + static final BiffMessage corruptFileFormat = + new BiffMessage("The file format is corrupt"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public BiffException(BiffMessage m) { + super(m.message); + } + + /** + * Inner class containing the various error messages + */ + private static class BiffMessage { + /** + * The formatted message + */ + public String message; + + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + BiffMessage(String m) { + message = m; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java b/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java new file mode 100755 index 0000000..da2bca4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BiffRecordReader.java @@ -0,0 +1,74 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * Serves up Record objects from a biff file. This object is used by the + * demo programs BiffDump and ... only and has no influence whatsoever on + * the JExcelApi reading and writing of excel sheets + */ +public class BiffRecordReader { + /** + * The biff file + */ + private final File file; + + /** + * The current record retrieved + */ + private Record record; + + /** + * Constructor + * + * @param f the biff file + */ + public BiffRecordReader(File f) { + file = f; + } + + /** + * Sees if there are any more records to read + * + * @return TRUE if there are more records, FALSE otherwise + */ + public boolean hasNext() { + return file.hasNext(); + } + + /** + * Gets the next record + * + * @return the next record + */ + public Record next() { + record = file.next(); + return record; + } + + /** + * Gets the position of the current record in the biff file + * + * @return the position + */ + public int getPos() { + return file.getPos() - record.getLength() - 4; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java b/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java new file mode 100755 index 0000000..736a678 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BlankCell.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.biff.FormattingRecords; + +/** + * A blank cell. Despite the fact that this cell has no contents, it + * has formatting information applied to it + */ +public class BlankCell extends CellValue { + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + BlankCell(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + } + + /** + * Returns the contents of this cell as an empty string + * + * @return the value formatted into a string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.EMPTY; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java new file mode 100755 index 0000000..977264f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanFormulaRecord.java @@ -0,0 +1,157 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; + +/** + * A boolean formula's last calculated value + */ +class BooleanFormulaRecord extends CellValue + implements BooleanCell, FormulaData, BooleanFormulaCell { + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param es the sheet + * @param nt the name table + */ + public BooleanFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + externalSheet = es; + nameTable = nt; + value = false; + + data = getRecord().getData(); + + Assert.verify(data[6] != 2); + + value = data[8] == 1; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java new file mode 100755 index 0000000..715be90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BooleanRecord.java @@ -0,0 +1,121 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.common.Assert; + +/** + * A boolean cell last calculated value + */ +class BooleanRecord extends CellValue implements BooleanCell { + /** + * Indicates whether this cell contains an error or a boolean + */ + private boolean error; + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public BooleanRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + error = false; + value = false; + + byte[] data = getRecord().getData(); + + error = (data[7] == 1); + + if (!error) { + value = data[6] == 1; + } + } + + /** + * Interface method which queries whether this cell contains an error. + * Returns TRUE if it does, otherwise returns FALSE. + * + * @return TRUE if this cell is an error, FALSE otherwise + */ + public boolean isError() { + return error; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + Assert.verify(!isError()); + + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN; + } + + /** + * A special case which overrides the method in the subclass to get + * hold of the raw data + * + * @return the record + */ + public Record getRecord() { + return super.getRecord(); + } +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java new file mode 100755 index 0000000..1991023 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BottomMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class BottomMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + BottomMarginRecord(Record r) { + super(Type.BOTTOMMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java new file mode 100755 index 0000000..3d49c64 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/BoundsheetRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A boundsheet record, which contains the worksheet name + */ +class BoundsheetRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The offset into the sheet + */ + private final int offset; + /** + * The type of sheet this is + */ + private final byte typeFlag; + /** + * The visibility flag + */ + private final byte visibilityFlag; + /** + * The length of the worksheet name + */ + private final int length; + /** + * The worksheet name + */ + private final String name; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the workbook settings + */ + public BoundsheetRecord(Record t, WorkbookSettings s) { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + + if (data[7] == 0) { + // Standard ASCII encoding + byte[] bytes = new byte[length]; + System.arraycopy(data, 8, bytes, 0, length); + name = StringHelper.getString(bytes, length, 0, s); + } else { + // little endian Unicode encoding + byte[] bytes = new byte[length * 2]; + System.arraycopy(data, 8, bytes, 0, length * 2); + name = StringHelper.getUnicodeString(bytes, length, 0); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param biff7 a dummy value to tell the record to interpret the + * data as biff7 + */ + public BoundsheetRecord(Record t, Biff7 biff7) { + super(t); + byte[] data = getRecord().getData(); + offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); + typeFlag = data[5]; + visibilityFlag = data[4]; + length = data[6]; + byte[] bytes = new byte[length]; + System.arraycopy(data, 7, bytes, 0, length); + name = new String(bytes); + } + + /** + * Accessor for the worksheet name + * + * @return the worksheet name + */ + public String getName() { + return name; + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this is a hidden sheet, FALSE otherwise + */ + public boolean isHidden() { + return visibilityFlag != 0; + } + + /** + * Accessor to determine if this is a worksheet, or some other nefarious + * type of object + * + * @return TRUE if this is a worksheet, FALSE otherwise + */ + public boolean isSheet() { + return typeFlag == 0; + } + + /** + * Accessor to determine if this is a chart + * + * @return TRUE if this is a chart, FALSE otherwise + */ + public boolean isChart() { + return typeFlag == 2; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java new file mode 100755 index 0000000..e1606d3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ButtonPropertySetRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Data associated with a button property set + */ +public class ButtonPropertySetRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(ButtonPropertySetRecord.class); + + + /** + * Constructor + * + * @param t the record + */ + ButtonPropertySetRecord(Record t) { + super(t); + } + + /** + * Accessor for the binary data + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java new file mode 100755 index 0000000..ef1405b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CalcModeRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A calculation mode record + */ +class CalcModeRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CalcModeRecord.class); + + /** + * The calculation mode + */ + private final boolean automatic; + + /** + * Constructor + * + * @param t the record + */ + public CalcModeRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + automatic = (mode == 1); + } + + /** + * Accessor for the calculation mode + * + * @return the calculation mode + */ + public boolean isAutomatic() { + return automatic; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java b/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java new file mode 100755 index 0000000..d9a47d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CellFeaturesAccessor.java @@ -0,0 +1,41 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellFeatures; + +/** + * Allows the setting of the cell features in this package only + */ +interface CellFeaturesAccessor { + /** + * Convenience function (due to casting) to get the cell features + * + * @return the cell features + */ + CellFeatures getCellFeatures(); + + /** + * Sets the cell features + * + * @param cf the cell features + */ + void setCellFeatures(CellFeatures cf); +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java b/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java new file mode 100755 index 0000000..8391dd6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CellValue.java @@ -0,0 +1,191 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.XFRecord; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * Abstract class for all records which actually contain cell values + */ +public abstract class CellValue extends RecordData + implements Cell, CellFeaturesAccessor { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row number of this cell record + */ + private final int row; + + /** + * The column number of this cell record + */ + private final int column; + + /** + * The XF index + */ + private final int xfIndex; + + /** + * A handle to the formatting records, so that we can + * retrieve the formatting information + */ + private final FormattingRecords formattingRecords; + + /** + * A lazy initialize flag for the cell format + */ + private boolean initialized; + + /** + * The cell format + */ + private XFRecord format; + + /** + * A handle back to the sheet + */ + private final SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + /** + * Constructs this object from the raw cell data + * + * @param t the raw cell data + * @param fr the formatting records + * @param si the sheet containing this cell + */ + protected CellValue(Record t, FormattingRecords fr, SheetImpl si) { + super(t); + byte[] data = getRecord().getData(); + row = IntegerHelper.getInt(data[0], data[1]); + column = IntegerHelper.getInt(data[2], data[3]); + xfIndex = IntegerHelper.getInt(data[4], data[5]); + sheet = si; + formattingRecords = fr; + initialized = false; + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() { + return column; + } + + /** + * Gets the XFRecord corresponding to the index number. Used when + * copying a spreadsheet + * + * @return the xf index for this cell + */ + public final int getXFIndex() { + return xfIndex; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() { + if (!initialized) { + format = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return format; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && (cir.getWidth() == 0 || cir.getHidden())) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the sheet + * + * @return the sheet + */ + protected SheetImpl getSheet() { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + if (features != null) { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java new file mode 100755 index 0000000..b0fec80 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CentreRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Record which indicates the whether the horizontal center option has been set + */ +class CentreRecord extends RecordData { + /** + * The centre flag + */ + private final boolean centre; + + /** + * Constructor + * + * @param t the record to constructfrom + */ + public CentreRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + centre = IntegerHelper.getInt(data[0], data[1]) != 0; + } + + /** + * Accessor for the centre flag + * + * @return Returns the centre flag. + */ + public boolean isCentre() { + return centre; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java new file mode 100755 index 0000000..529435f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CodepageRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A codepage record + */ +class CodepageRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CodepageRecord.class); + + /** + * The character encoding + */ + private final int characterSet; + + /** + * Constructor + * + * @param t the record + */ + public CodepageRecord(Record t) { + super(t); + byte[] data = t.getData(); + characterSet = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Accessor for the encoding + * + * @return the character encoding + */ + public int getCharacterSet() { + return characterSet; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java new file mode 100755 index 0000000..a1d9d66 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ColumnInfoRecord.java @@ -0,0 +1,155 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Contains the display info data which affects the entire columns + */ +public class ColumnInfoRecord extends RecordData { + /** + * The raw data + */ + private final byte[] data; + + /** + * The start for which to apply the format information + */ + private final int startColumn; + + /** + * The end column for which to apply the format information + */ + private final int endColumn; + + /** + * The index to the XF record, which applies to each cell in this column + */ + private final int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private final int width; + + /** + * A hidden flag + */ + private final boolean hidden; + + /** + * The column's outline level + */ + private final int outlineLevel; + + /** + * The column collapsed flag + */ + private final boolean collapsed; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + ColumnInfoRecord(Record t) { + super(Type.COLINFO); + + data = t.getData(); + + startColumn = IntegerHelper.getInt(data[0], data[1]); + endColumn = IntegerHelper.getInt(data[2], data[3]); + width = IntegerHelper.getInt(data[4], data[5]); + xfIndex = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[8], data[9]); + hidden = ((options & 0x1) != 0); + outlineLevel = ((options & 0x700) >> 8); + collapsed = ((options & 0x1000) != 0); + } + + /** + * Accessor for the start column of this range + * + * @return the start column index + */ + public int getStartColumn() { + return startColumn; + } + + /** + * Accessor for the end column of this range + * + * @return the end column index + */ + public int getEndColumn() { + return endColumn; + } + + /** + * Accessor for the column format index + * + * @return the format index + */ + public int getXFIndex() { + return xfIndex; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() { + return collapsed; + } + + /** + * Accessor for the width of the column + * + * @return the width + */ + public int getWidth() { + return width; + } + + /** + * Accessor for the hidden flag. Used when copying sheets + * + * @return TRUE if the columns are hidden, FALSE otherwise + */ + public boolean getHidden() { + return hidden; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java b/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java new file mode 100755 index 0000000..da46167 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CompoundFile.java @@ -0,0 +1,561 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * Reads in and defrags an OLE compound compound file + * (Made public only for the PropertySets demo) + */ +public final class CompoundFile extends BaseCompoundFile { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The original OLE stream, organized into blocks, which can + * appear at any physical location in the file + */ + private final byte[] data; + /** + * The number of blocks it takes to store the big block depot + */ + private final int numBigBlockDepotBlocks; + /** + * The start block of the small block depot + */ + private final int sbdStartBlock; + /** + * The start block of the root entry + */ + private final int rootStartBlock; + /** + * The header extension block + */ + private int extensionBlock; + /** + * The number of header extension blocks + */ + private final int numExtensionBlocks; + /** + * The root entry + */ + private final byte[] rootEntry; + /** + * The sequence of blocks which comprise the big block chain + */ + private int[] bigBlockChain; + /** + * The sequence of blocks which comprise the small block chain + */ + private int[] smallBlockChain; + /** + * The chain of blocks which comprise the big block depot + */ + private final int[] bigBlockDepotBlocks; + /** + * The list of property sets + */ + private final ArrayList propertySets; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * The property storage root entry + */ + private PropertyStorage rootEntryPropertyStorage; + + /** + * Initializes the compound file + * + * @param d the raw data of the ole stream + * @param ws the workbook settings + * @throws BiffException + */ + public CompoundFile(byte[] d, WorkbookSettings ws) throws BiffException { + super(); + data = d; + settings = ws; + + // First verify the OLE identifier + for (int i = 0; i < IDENTIFIER.length; i++) { + if (data[i] != IDENTIFIER[i]) { + throw new BiffException(BiffException.unrecognizedOLEFile); + } + } + + propertySets = new ArrayList(); + numBigBlockDepotBlocks = IntegerHelper.getInt + (data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 1], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 2], + data[NUM_BIG_BLOCK_DEPOT_BLOCKS_POS + 3]); + sbdStartBlock = IntegerHelper.getInt + (data[SMALL_BLOCK_DEPOT_BLOCK_POS], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 1], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 2], + data[SMALL_BLOCK_DEPOT_BLOCK_POS + 3]); + rootStartBlock = IntegerHelper.getInt + (data[ROOT_START_BLOCK_POS], + data[ROOT_START_BLOCK_POS + 1], + data[ROOT_START_BLOCK_POS + 2], + data[ROOT_START_BLOCK_POS + 3]); + extensionBlock = IntegerHelper.getInt + (data[EXTENSION_BLOCK_POS], + data[EXTENSION_BLOCK_POS + 1], + data[EXTENSION_BLOCK_POS + 2], + data[EXTENSION_BLOCK_POS + 3]); + numExtensionBlocks = IntegerHelper.getInt + (data[NUM_EXTENSION_BLOCK_POS], + data[NUM_EXTENSION_BLOCK_POS + 1], + data[NUM_EXTENSION_BLOCK_POS + 2], + data[NUM_EXTENSION_BLOCK_POS + 3]); + + bigBlockDepotBlocks = new int[numBigBlockDepotBlocks]; + + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + int bbdBlocks = numBigBlockDepotBlocks; + + if (numExtensionBlocks != 0) { + bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + } + + for (int i = 0; i < bbdBlocks; i++) { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + for (int j = 0; j < numExtensionBlocks; j++) { + pos = (extensionBlock + 1) * BIG_BLOCK_SIZE; + int blocksToRead = Math.min(numBigBlockDepotBlocks - bbdBlocks, + BIG_BLOCK_SIZE / 4 - 1); + + for (int i = bbdBlocks; i < bbdBlocks + blocksToRead; i++) { + bigBlockDepotBlocks[i] = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + pos += 4; + } + + bbdBlocks += blocksToRead; + if (bbdBlocks < numBigBlockDepotBlocks) { + extensionBlock = IntegerHelper.getInt + (d[pos], d[pos + 1], d[pos + 2], d[pos + 3]); + } + } + + readBigBlockDepot(); + readSmallBlockDepot(); + + rootEntry = readData(rootStartBlock); + readPropertySets(); + } + + /** + * Reads the big block depot entries + */ + private void readBigBlockDepot() { + int pos = 0; + int index = 0; + bigBlockChain = new int[numBigBlockDepotBlocks * BIG_BLOCK_SIZE / 4]; + + for (int i = 0; i < numBigBlockDepotBlocks; i++) { + pos = (bigBlockDepotBlocks[i] + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) { + bigBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + } + } + + /** + * Reads the small block chain's depot entries + */ + private void readSmallBlockDepot() throws BiffException { + int pos = 0; + int index = 0; + int sbdBlock = sbdStartBlock; + smallBlockChain = new int[0]; + + // Some non-excel generators specify -1 for an empty small block depot + // simply warn and return + if (sbdBlock == -1) { + logger.warn("invalid small block depot number"); + return; + } + + int blockCount = 0; + for (; blockCount <= bigBlockChain.length && sbdBlock != -2; blockCount++) { + // Allocate some more space to the small block chain + int[] oldChain = smallBlockChain; + smallBlockChain = new int[smallBlockChain.length + BIG_BLOCK_SIZE / 4]; + System.arraycopy(oldChain, 0, smallBlockChain, 0, oldChain.length); + + pos = (sbdBlock + 1) * BIG_BLOCK_SIZE; + + for (int j = 0; j < BIG_BLOCK_SIZE / 4; j++) { + smallBlockChain[index] = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + index++; + } + + sbdBlock = bigBlockChain[sbdBlock]; + } + + if (blockCount > bigBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + } + + /** + * Reads all the property sets + */ + private void readPropertySets() { + int offset = 0; + byte[] d = null; + + while (offset < rootEntry.length) { + d = new byte[PROPERTY_STORAGE_BLOCK_SIZE]; + System.arraycopy(rootEntry, offset, d, 0, d.length); + PropertyStorage ps = new PropertyStorage(d); + + // sometimes the MAC Operating system leaves some property storage + // names blank. Contributed by Jacky + if (ps.name == null || ps.name.length() == 0) { + if (ps.type == ROOT_ENTRY_PS_TYPE) { + ps.name = ROOT_ENTRY_NAME; + logger.warn("Property storage name for " + ps.type + + " is empty - setting to " + ROOT_ENTRY_NAME); + } else { + if (ps.size != 0) { + logger.warn("Property storage type " + ps.type + + " is non-empty and has no associated name"); + } + } + } + propertySets.add(ps); + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + rootEntryPropertyStorage = ps; + } + offset += PROPERTY_STORAGE_BLOCK_SIZE; + } + + if (rootEntryPropertyStorage == null) { + rootEntryPropertyStorage = (PropertyStorage) propertySets.get(0); + } + } + + /** + * Gets the defragmented stream from this ole compound file + * + * @param streamName the stream name to get + * @return the defragmented ole stream + * @throws BiffException + */ + public byte[] getStream(String streamName) throws BiffException { + PropertyStorage ps = findPropertyStorage(streamName, + rootEntryPropertyStorage); + + // Property set can't be found from the direct hierarchy, so just + // search on the name + if (ps == null) { + ps = getPropertyStorage(streamName); + } + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + streamName.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + return getBigBlockStream(ps); + } else { + return getSmallBlockStream(ps); + } + } + + /** + * Gets the defragmented stream from this ole compound file. Used when + * copying workbooks with macros + * + * @param psIndex the property storage index + * @return the defragmented ole stream + * @throws BiffException + */ + public byte[] getStream(int psIndex) throws BiffException { + PropertyStorage ps = getPropertyStorage(psIndex); + + if (ps.size >= SMALL_BLOCK_THRESHOLD || + ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + return getBigBlockStream(ps); + } else { + return getSmallBlockStream(ps); + } + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. This is the public version which is + * invoked from the writable version + * when copying a sheet with addition property sets. + */ + public PropertyStorage findPropertyStorage(String name) { + return findPropertyStorage(name, rootEntryPropertyStorage); + } + + /** + * Recursively searches the property storages in hierarchy order + * for the appropriate name. + */ + private PropertyStorage findPropertyStorage(String name, + PropertyStorage base) { + if (base.child == -1) { + return null; + } + + // Get the child + PropertyStorage child = getPropertyStorage(base.child); + if (child.name.equalsIgnoreCase(name)) { + return child; + } + + // Find the previous property storages on the same level + PropertyStorage prev = child; + while (prev.previous != -1) { + prev = getPropertyStorage(prev.previous); + if (prev.name.equalsIgnoreCase(name)) { + return prev; + } + } + + // Find the next property storages on the same level + PropertyStorage next = child; + while (next.next != -1) { + next = getPropertyStorage(next.next); + if (next.name.equalsIgnoreCase(name)) { + return next; + } + } + + return findPropertyStorage(name, child); + } + + /** + * Gets the property set with the specified name + * + * @param name the property storage name + * @return the property storage record + * @throws BiffException + * @deprecated remove me + */ + private PropertyStorage getPropertyStorage(String name) + throws BiffException { + // Find the workbook property + Iterator i = propertySets.iterator(); + boolean found = false; + boolean multiple = false; + PropertyStorage ps = null; + while (i.hasNext()) { + PropertyStorage ps2 = (PropertyStorage) i.next(); + if (ps2.name.equalsIgnoreCase(name)) { + multiple = found == true; + found = true; + ps = ps2; + } + } + + if (multiple) { + logger.warn("found multiple copies of property set " + name); + } + + if (!found) { + throw new BiffException(BiffException.streamNotFound); + } + + return ps; + } + + /** + * Gets the property set with the specified name + * + * @param index the index of the property storage + * @return the property storage record + */ + private PropertyStorage getPropertyStorage(int index) { + return (PropertyStorage) propertySets.get(index); + } + + /** + * Build up the resultant stream using the big blocks + * + * @param ps the property storage + * @return the big block stream + */ + private byte[] getBigBlockStream(PropertyStorage ps) { + int numBlocks = ps.size / BIG_BLOCK_SIZE; + if (ps.size % BIG_BLOCK_SIZE != 0) { + numBlocks++; + } + + byte[] streamData = new byte[numBlocks * BIG_BLOCK_SIZE]; + + int block = ps.startBlock; + + int count = 0; + int pos = 0; + while (block != -2 && count < numBlocks) { + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, streamData, + count * BIG_BLOCK_SIZE, BIG_BLOCK_SIZE); + count++; + block = bigBlockChain[block]; + } + + if (block != -2 && count == numBlocks) { + logger.warn("Property storage size inconsistent with block chain."); + } + + return streamData; + } + + /** + * Build up the resultant stream using the small blocks + * + * @param ps the property storage + * @return the data + * @throws BiffException + */ + private byte[] getSmallBlockStream(PropertyStorage ps) + throws BiffException { + byte[] rootdata = readData(rootEntryPropertyStorage.startBlock); + byte[] sbdata = new byte[0]; + + int block = ps.startBlock; + int pos = 0; + + int blockCount = 0; + for (; blockCount <= smallBlockChain.length && block != -2; blockCount++) { + // grow the array + byte[] olddata = sbdata; + sbdata = new byte[olddata.length + SMALL_BLOCK_SIZE]; + System.arraycopy(olddata, 0, sbdata, 0, olddata.length); + + // Copy in the new data + pos = block * SMALL_BLOCK_SIZE; + System.arraycopy(rootdata, pos, sbdata, + olddata.length, SMALL_BLOCK_SIZE); + block = smallBlockChain[block]; + + if (block == -1) { + logger.warn("Incorrect terminator for small block stream " + ps.name); + block = -2; // kludge to force the loop termination + } + } + + if (blockCount > smallBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + + return sbdata; + } + + /** + * Reads the block chain from the specified block and returns the + * data as a continuous stream of bytes + * + * @param bl the block number + * @return the data + */ + private byte[] readData(int bl) throws BiffException { + int block = bl; + int pos = 0; + byte[] entry = new byte[0]; + + int blockCount = 0; + for (; blockCount <= bigBlockChain.length && block != -2; blockCount++) { + // Grow the array + byte[] oldEntry = entry; + entry = new byte[oldEntry.length + BIG_BLOCK_SIZE]; + System.arraycopy(oldEntry, 0, entry, 0, oldEntry.length); + pos = (block + 1) * BIG_BLOCK_SIZE; + System.arraycopy(data, pos, entry, + oldEntry.length, BIG_BLOCK_SIZE); + if (bigBlockChain[block] == block) { + throw new BiffException(BiffException.corruptFileFormat); + } + block = bigBlockChain[block]; + } + + if (blockCount > bigBlockChain.length) { + // Attempted to read more blocks than the block chain contains entries + // for. This indicates a loop in the chain + throw new BiffException(BiffException.corruptFileFormat); + } + + return entry; + } + + /** + * Gets the number of property sets + * + * @return the number of property sets + */ + public int getNumberOfPropertySets() { + return propertySets.size(); + } + + /** + * Gets the property set. Invoked when copying worksheets with macros. + * Simply calls the private counterpart + * + * @param ps the property set name + * @return the property set with the given name + */ + public PropertyStorage getPropertySet(int index) { + return getPropertyStorage(index); + } +} + + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java new file mode 100755 index 0000000..0dd448a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/CountryRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +public class CountryRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CountryRecord.class); + + /** + * The user interface language + */ + private final int language; + + /** + * The regional settings + */ + private final int regionalSettings; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public CountryRecord(Record t) { + super(t); + byte[] data = t.getData(); + + language = IntegerHelper.getInt(data[0], data[1]); + regionalSettings = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the language code + * + * @return the language code + */ + public int getLanguageCode() { + return language; + } + + /** + * Accessor for the regional settings code + * + * @return the regional settings code + */ + public int getRegionalSettingsCode() { + return regionalSettings; + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java new file mode 100755 index 0000000..d886966 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DateFormulaRecord.java @@ -0,0 +1,141 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A date formula's last calculated value + */ +class DateFormulaRecord extends DateRecord + implements DateCell, FormulaData, DateFormulaCell { + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the basic number formula record + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param nf flag indicating whether the 1904 date system is in use + * @param si the sheet + */ + public DateFormulaRecord(NumberFormulaRecord t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + boolean nf, SheetImpl si) throws FormulaException { + super(t, t.getXFIndex(), fr, nf, si); + + externalSheet = es; + nameTable = nt; + data = t.getFormulaData(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Data is already the formula data, so don't do any more manipulation + return data; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + // Note that the standard information was lopped off by the NumberFormula + // record when creating this formula + if (formulaString == null) { + byte[] tokens = new byte[data.length - 16]; + System.arraycopy(data, 16, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() { + return 0; + } + + /** + * Dummy implementation in order to adhere to the NumberCell interface + * + * @return NULL + */ + public NumberFormat getNumberFormat() { + return null; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java new file mode 100755 index 0000000..b954a49 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DateRecord.java @@ -0,0 +1,305 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.DateCell; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A date which is stored in the cell + */ +class DateRecord implements DateCell, CellFeaturesAccessor { + // The default formats used when returning the date as a string + private static final SimpleDateFormat dateFormat = + new SimpleDateFormat("dd MMM yyyy"); + private static final SimpleDateFormat timeFormat = + new SimpleDateFormat("HH:mm:ss"); + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 1900. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + private static final int nonLeapDay = 61; + private static final TimeZone gmtZone = TimeZone.getTimeZone("GMT"); + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + private static final int utcOffsetDays = 25569; + // The number of days between 01 Jan 1904 and 01 Jan 1970 - this gives + // the UTC offset using the 1904 date system + private static final int utcOffsetDays1904 = 24107; + // The number of milliseconds in a day + private static final long secondsInADay = 24 * 60 * 60; + private static final long msInASecond = 1000; + private static final long msInADay = secondsInADay * msInASecond; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DateRecord.class); + /** + * The date represented within this cell + */ + private final Date date; + /** + * The row number of this cell record + */ + private final int row; + /** + * The column number of this cell record + */ + private final int column; + /** + * Indicates whether this is a full date, or merely a time + */ + private final boolean time; + /** + * The format to use when displaying this cell's contents as a string + */ + private DateFormat format; + /** + * The raw cell format + */ + private CellFormat cellFormat; + /** + * The index to the XF Record + */ + private final int xfIndex; + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + /** + * The cell features + */ + private CellFeatures features; + /** + * A flag to indicate whether this objects formatting things have + * been initialized + */ + private boolean initialized; + + + /** + * Constructs this object from the raw data + * + * @param num the numerical representation of this + * @param xfi the java equivalent of the excel date format + * @param fr the formatting records + * @param nf flag indicating whether we are using the 1904 date system + * @param si the sheet + */ + public DateRecord(NumberCell num, + int xfi, FormattingRecords fr, + boolean nf, SheetImpl si) { + row = num.getRow(); + column = num.getColumn(); + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + + format = formattingRecords.getDateFormat(xfIndex); + + // This value represents the number of days since 01 Jan 1900 + double numValue = num.getValue(); + + if (Math.abs(numValue) < 1) { + if (format == null) { + format = timeFormat; + } + time = true; + } else { + if (format == null) { + format = dateFormat; + } + time = false; + } + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but in actual fact this was not a leap year. + // Therefore for values less than 61 in the 1900 date system, + // add one to the numeric value + if (!nf && !time && numValue < nonLeapDay) { + numValue += 1; + } + + // Get rid of any timezone adjustments - we are not interested + // in automatic adjustments + format.setTimeZone(gmtZone); + + // Convert this to the number of days since 01 Jan 1970 + int offsetDays = nf ? utcOffsetDays1904 : utcOffsetDays; + double utcDays = numValue - offsetDays; + + // Convert this into utc by multiplying by the number of milliseconds + // in a day. Use the round function prior to ms conversion due + // to a rounding feature of Excel (contributed by Jurgen + long utcValue = Math.round(utcDays * secondsInADay) * msInASecond; + + date = new Date(utcValue); + } + + /** + * Interface method which returns the row number of this cell + * + * @return the zero base row number + */ + public final int getRow() { + return row; + } + + /** + * Interface method which returns the column number of this cell + * + * @return the zero based column number + */ + public final int getColumn() { + return column; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() { + return date; + } + + /** + * Gets the cell contents as a string. This method will use the java + * equivalent of the excel formatting string + * + * @return the label + */ + public String getContents() { + return format.format(date); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.DATE; + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + Assert.verify(format != null); + + return format; + } + + /** + * Gets the CellFormat object for this cell. Used by the WritableWorkbook + * API + * + * @return the CellFormat used for this cell + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the sheet + * + * @return the containing sheet + */ + protected final SheetImpl getSheet() { + return sheet; + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + features = cf; + } + +} + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java new file mode 100755 index 0000000..9d56eb4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultColumnWidthRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultColumnWidthRecord extends RecordData { + /** + * The default columns width, in characters + */ + private final int width; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultColumnWidthRecord(Record t) { + super(t); + byte[] data = t.getData(); + + width = IntegerHelper.getInt(data[0], data[1]); + } + + + /** + * Accessor for the default width + * + * @return the width + */ + public int getWidth() { + return width; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java new file mode 100755 index 0000000..f9d8951 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DefaultRowHeightRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * Contains the default column width for cells in this sheet + */ +class DefaultRowHeightRecord extends RecordData { + /** + * The default row height, in 1/20ths of a point + */ + private int height; + + /** + * Constructs the def col width from the raw data + * + * @param t the raw data + */ + public DefaultRowHeightRecord(Record t) { + super(t); + byte[] data = t.getData(); + + if (data.length > 2) { + height = IntegerHelper.getInt(data[2], data[3]); + } + } + + /** + * Accessor for the default height + * + * @return the height + */ + public int getHeight() { + return height; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java new file mode 100755 index 0000000..108ce78 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/DimensionRecord.java @@ -0,0 +1,126 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class DimensionRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DimensionRecord.class); + /** + * The number of rows in this sheet + */ + private int numRows; + /** + * The number of columns in this worksheet + */ + private int numCols; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public DimensionRecord(Record t) { + super(t); + byte[] data = t.getData(); + + // Sometimes, if the spreadsheet is being generated by dodgy VB modules, + // even though the excel format is biff8, the dimension record is + // generated in the old biff 7 format. This horrible if construct + // handles that eventuality + if (data.length == 10) { + read10ByteData(data); + } else { + read14ByteData(data); + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public DimensionRecord(Record t, Biff7 biff7) { + super(t); + byte[] data = t.getData(); + read10ByteData(data); + } + + /** + * Reads in the data for data records of length 10 + * + * @param data the data to read + */ + private void read10ByteData(byte[] data) { + numRows = IntegerHelper.getInt(data[2], data[3]); + numCols = IntegerHelper.getInt(data[6], data[7]); + } + + /** + * Reads in the data for data records of length 14 + * + * @param data the data to read + */ + private void read14ByteData(byte[] data) { + numRows = IntegerHelper.getInt(data[4], data[5], data[6], data[7]); + numCols = IntegerHelper.getInt(data[10], data[11]); + } + + /** + * Accessor for the number of rows in this sheet + * + * @return the number of rows + */ + public int getNumberOfRows() { + return numRows; + } + + /** + * Accessor for the number of columns in this sheet + * + * @return the number of columns + */ + public int getNumberOfColumns() { + return numCols; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java new file mode 100755 index 0000000..8b3f51e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorFormulaRecord.java @@ -0,0 +1,164 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; + +/** + * An error resulting from the calculation of a formula + */ +class ErrorFormulaRecord extends CellValue + implements ErrorCell, FormulaData, ErrorFormulaCell { + /** + * The error code of this cell + */ + private final int errorCode; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public ErrorFormulaRecord(Record t, FormattingRecords fr, ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + Assert.verify(data[6] == 2); + + errorCode = data[8]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + if (error == null) { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java new file mode 100755 index 0000000..2b6f09a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ErrorRecord.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.biff.FormattingRecords; + +/** + * A cell containing an error code. This will usually be the result + * of some error during the calculation of a formula + */ +class ErrorRecord extends CellValue implements ErrorCell { + /** + * The error code if this cell evaluates to an error, otherwise zer0 + */ + private final int errorCode; + + /** + * Constructs this object + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public ErrorRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + errorCode = data[6]; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + return "ERROR " + errorCode; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.ERROR; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java new file mode 100755 index 0000000..409538b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Excel9FileRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A excel9file record + */ +class Excel9FileRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Excel9FileRecord.class); + + /** + * The template + */ + private final boolean excel9file; + + /** + * Constructor + * + * @param t the record + */ + public Excel9FileRecord(Record t) { + super(t); + excel9file = true; + } + + /** + * Accessor for the template mode + * + * @return the template mode + */ + public boolean getExcel9File() { + return excel9file; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java new file mode 100755 index 0000000..caa28b8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalNameRecord.java @@ -0,0 +1,98 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + + +/** + * A row record + */ +public class ExternalNameRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name + */ + private String name; + + /** + * Add in function flag + */ + private boolean addInFunction; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalNameRecord(Record t, WorkbookSettings ws) { + super(t); + + byte[] data = getRecord().getData(); + int options = IntegerHelper.getInt(data[0], data[1]); + + if (options == 0) { + addInFunction = true; + } + + if (!addInFunction) { + return; + } + + int length = data[6]; + + boolean unicode = (data[7] != 0); + + if (unicode) { + name = StringHelper.getUnicodeString(data, length, 8); + } else { + name = StringHelper.getString(data, length, 8, ws); + } + } + + /** + * Queries whether this name record refers to an external record + * + * @return TRUE if this name record is an add in function, FALSE otherwise + */ + public boolean isAddInFunction() { + return addInFunction; + } + + /** + * Gets the name + * + * @return the name + */ + public String getName() { + return name; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java new file mode 100755 index 0000000..03ebe48 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ExternalSheetRecord.java @@ -0,0 +1,181 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * An Externsheet record, containing the details of externally references + * workbooks + */ +public class ExternalSheetRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ExternalSheetRecord.class); + + /** + * The array of XTI structures + */ + private XTI[] xtiArray; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + ExternalSheetRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + int numxtis = IntegerHelper.getInt(data[0], data[1]); + + if (data.length < numxtis * 6 + 2) { + xtiArray = new XTI[0]; + logger.warn("Could not process external sheets. Formulas may " + + "be compromised."); + return; + } + + xtiArray = new XTI[numxtis]; + + int pos = 2; + for (int i = 0; i < numxtis; i++) { + int s = IntegerHelper.getInt(data[pos], data[pos + 1]); + int f = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + int l = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + xtiArray[i] = new XTI(s, f, l); + pos += 6; + } + } + + /** + * Constructs this object from the raw data in biff 7 format. + * Does nothing here + * + * @param t the raw data + * @param settings the workbook settings + * @param dummy dummy override to identify biff7 funcionality + */ + ExternalSheetRecord(Record t, WorkbookSettings settings, Biff7 dummy) { + super(t); + + logger.warn("External sheet record for Biff 7 not supported"); + } + + /** + * Accessor for the number of external sheet records + * + * @return the number of XTI records + */ + public int getNumRecords() { + return xtiArray != null ? xtiArray.length : 0; + } + + /** + * Gets the supbook index for the specified external sheet + * + * @param index the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) { + return xtiArray[index].supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) { + return xtiArray[index].firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param index the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) { + return xtiArray[index].lastTab; + } + + /** + * Used when copying a workbook to access the raw external sheet data + * + * @return the raw external sheet data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + + /** + * An XTI structure + */ + private static class XTI { + /** + * the supbook index + */ + int supbookIndex; + /** + * the first tab + */ + int firstTab; + /** + * the last tab + */ + int lastTab; + + /** + * Constructor + * + * @param s the supbook index + * @param f the first tab + * @param l the last tab + */ + XTI(int s, int f, int l) { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/File.java b/datastructures-xslx/src/main/java/jxl/read/biff/File.java new file mode 100755 index 0000000..d1a9d8d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/File.java @@ -0,0 +1,293 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import jxl.WorkbookSettings; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Logger; + +/** + * File containing the data from the binary stream + */ +public class File { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(File.class); + + /** + * The data from the excel 97 file + */ + private byte[] data; + /** + * The current position within the file + */ + private int filePos; + /** + * The saved pos + */ + private int oldPos; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * A handle to the compound file. This is only preserved when the + * copying of PropertySets is enabled + */ + private CompoundFile compoundFile; + /** + * The workbook settings + */ + private WorkbookSettings workbookSettings; + + /** + * Constructs a file from the input stream + * + * @param is the input stream + * @param ws the workbook settings + * @throws IOException + * @throws BiffException + */ + public File(InputStream is, WorkbookSettings ws) + throws IOException, BiffException { + // Initialize the file sizing parameters from the settings + workbookSettings = ws; + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + byte[] d = new byte[initialFileSize]; + int bytesRead = is.read(d); + int pos = bytesRead; + + // Handle thread interruptions, in case the user keeps pressing + // the Submit button from a browser. Thanks to Mike Smith for this + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedIOException(); + } + + while (bytesRead != -1) { + if (pos >= d.length) { + // Grow the array + byte[] newArray = new byte[d.length + arrayGrowSize]; + System.arraycopy(d, 0, newArray, 0, d.length); + d = newArray; + } + bytesRead = is.read(d, pos, d.length - pos); + pos += bytesRead; + + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedIOException(); + } + } + + bytesRead = pos + 1; + + // Perform file reading checks and throw exceptions as necessary + if (bytesRead == 0) { + throw new BiffException(BiffException.excelFileNotFound); + } + + CompoundFile cf = new CompoundFile(d, ws); + try { + data = cf.getStream("workbook"); + } catch (BiffException e) { + // this might be in excel 95 format - try again + data = cf.getStream("book"); + } + + if (!workbookSettings.getPropertySetsDisabled() && + (cf.getNumberOfPropertySets() > + BaseCompoundFile.STANDARD_PROPERTY_SETS.length)) { + compoundFile = cf; + } + + cf = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + + // Uncomment the following lines to send the pure workbook stream + // (ie. a defragged ole stream) to an output file + +// FileOutputStream fos = new FileOutputStream("defraggedxls"); +// fos.write(data); +// fos.close(); + + } + + /** + * Constructs a file from already defragged binary data. Useful for + * displaying subportions of excel streams. This is only used during + * special runs of the "BiffDump" demo program and should not be invoked + * as part of standard JExcelApi parsing + * + * @param d the already parsed data + */ + public File(byte[] d) { + data = d; + } + + /** + * Returns the next data record and increments the pointer + * + * @return the next data record + */ + Record next() { + Record r = new Record(data, filePos, this); + return r; + } + + /** + * Peek ahead to the next record, without incrementing the file position + * + * @return the next record + */ + Record peek() { + int tempPos = filePos; + Record r = new Record(data, filePos, this); + filePos = tempPos; + return r; + } + + /** + * Skips forward the specified number of bytes + * + * @param bytes the number of bytes to skip forward + */ + public void skip(int bytes) { + filePos += bytes; + } + + /** + * Copies the bytes into a new array and returns it. + * + * @param pos the position to read from + * @param length the number of bytes to read + * @return The bytes read + */ + public byte[] read(int pos, int length) { + byte[] ret = new byte[length]; + try { + System.arraycopy(data, pos, ret, 0, length); + } catch (ArrayIndexOutOfBoundsException e) { + logger.error("Array index out of bounds at position " + pos + + " record length " + length); + throw e; + } + return ret; + } + + /** + * Gets the position in the stream + * + * @return the position in the stream + */ + public int getPos() { + return filePos; + } + + /** + * Saves the current position and temporarily sets the position to be the + * new one. The original position may be restored usind the restorePos() + * method. This is used when reading in the cell values of the sheet - an + * addition in 1.6 for memory allocation reasons. + *
+ * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + * + * @param p the temporary position + */ + public void setPos(int p) { + oldPos = filePos; + filePos = p; + } + + /** + * Restores the original position + *
+ * These methods are used by the SheetImpl.readSheet() when it is reading + * in all the cell values + */ + public void restorePos() { + filePos = oldPos; + } + + /** + * Moves to the first bof in the file + */ + private void moveToFirstBof() { + boolean bofFound = false; + while (!bofFound) { + int code = IntegerHelper.getInt(data[filePos], data[filePos + 1]); + if (code == Type.BOF.value) { + bofFound = true; + } else { + skip(128); + } + } + } + + /** + * "Closes" the biff file + * + * @deprecated As of version 1.6 use workbook.close() instead + */ + public void close() { + } + + /** + * Clears the contents of the file + */ + public void clear() { + data = null; + } + + /** + * Determines if the current position exceeds the end of the file + * + * @return TRUE if there is more data left in the array, FALSE otherwise + */ + public boolean hasNext() { + // Allow four bytes for the record code and its length + return filePos < data.length - 4; + } + + /** + * Accessor for the compound file. The returned value will only be non-null + * if the property sets feature is enabled and the workbook contains + * additional property sets + * + * @return the compound file + */ + CompoundFile getCompoundFile() { + return compoundFile; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java new file mode 100755 index 0000000..57f8303 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/FooterRecord.java @@ -0,0 +1,95 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; + +/** + * A workbook page footer record + */ +public class FooterRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The footer + */ + private String footer; + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + FooterRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) { + footer = StringHelper.getUnicodeString(data, chars, 3); + } else { + footer = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + FooterRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = data[0]; + footer = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the footer string + * + * @return the footer string + */ + String getFooter() { + return footer; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java b/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java new file mode 100755 index 0000000..bdaba9a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Formula.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; + +/** + * Interface which is used for copying formulas from a read only + * to a writable spreadsheet + */ +public interface Formula extends Cell { + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + byte[] getFormulaData(); +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java new file mode 100755 index 0000000..aeaf5ab --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/FormulaRecord.java @@ -0,0 +1,243 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.WorkbookSettings; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A formula's last calculated value + */ +class FormulaRecord extends CellValue { + public static final IgnoreSharedFormula ignoreSharedFormula + = new IgnoreSharedFormula(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaRecord.class); + /** + * The "real" formula record - will be either a string a or a number + */ + private final CellValue formula; + /** + * Flag to indicate whether this is a shared formula + */ + private boolean shared; + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param si the sheet + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + + byte[] data = getRecord().getData(); + + shared = false; + + // Check to see if this forms part of a shared formula + int grbit = IntegerHelper.getInt(data[14], data[15]); + if ((grbit & 0x08) != 0) { + shared = true; + + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // It is a shared string formula + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 3 && data[12] == -1 && data[13] == -1) { + // We have a string which evaluates to null + formula = new SharedStringFormulaRecord + (t, excelFile, fr, es, nt, si, + SharedStringFormulaRecord.EMPTY_STRING); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + int errorCode = data[8]; + formula = new SharedErrorFormulaRecord(t, excelFile, errorCode, + fr, es, nt, si); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + boolean value = data[8] == 1; + formula = new SharedBooleanFormulaRecord + (t, excelFile, value, fr, es, nt, si); + } else { + // It is a numerical formula + double value = DoubleHelper.getIEEEDouble(data, 6); + SharedNumberFormulaRecord snfr = new SharedNumberFormulaRecord + (t, excelFile, value, fr, es, nt, si); + snfr.setNumberFormat(fr.getNumberFormat(getXFIndex())); + formula = snfr; + } + + return; + } + + // microsoft and their goddam magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 3 && data[12] == -1 && data[13] == -1) { + // we have a string which evaluates to null + formula = new StringFormulaRecord(t, fr, es, nt, si); + } else { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the workbook, which contains the external sheet references + * @param nt the name table + * @param i a dummy override to indicate that we don't want to do + * any shared formula processing + * @param si the sheet impl + * @param ws the workbook settings + */ + public FormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + IgnoreSharedFormula i, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + byte[] data = getRecord().getData(); + + shared = false; + + // microsoft and their magic values determine whether this + // is a string or a number value + if (data[6] == 0 && data[12] == -1 && data[13] == -1) { + // we have a string + formula = new StringFormulaRecord(t, excelFile, fr, es, nt, si, ws); + } else if (data[6] == 1 && + data[12] == -1 && + data[13] == -1) { + // We have a boolean formula + // multiple values. Thanks to Frank for spotting this + formula = new BooleanFormulaRecord(t, fr, es, nt, si); + } else if (data[6] == 2 && + data[12] == -1 && + data[13] == -1) { + // The cell is in error + formula = new ErrorFormulaRecord(t, fr, es, nt, si); + } else { + // it is most assuredly a number + formula = new NumberFormulaRecord(t, fr, es, nt, si); + } + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + Assert.verify(false); + return ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + Assert.verify(false); + return CellType.EMPTY; + } + + /** + * Gets the "real" formula + * + * @return the cell value + */ + final CellValue getFormula() { + return formula; + } + + /** + * Interrogates this formula to determine if it forms part of a shared + * formula + * + * @return TRUE if this is shared formula, FALSE otherwise + */ + final boolean isShared() { + return shared; + } + + /** + * Static class for a dummy override, indicating that the formula + * passed in is not a shared formula + */ + private static class IgnoreSharedFormula { + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java new file mode 100755 index 0000000..c27c750 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/GuttersRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * The gutters record + */ +public class GuttersRecord extends RecordData { + private final int width; + private final int height; + private final int rowOutlineLevel; + private final int columnOutlineLevel; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public GuttersRecord(Record r) { + super(r); + + byte[] data = getRecord().getData(); + width = IntegerHelper.getInt(data[0], data[1]); + height = IntegerHelper.getInt(data[2], data[3]); + rowOutlineLevel = IntegerHelper.getInt(data[4], data[5]); + columnOutlineLevel = IntegerHelper.getInt(data[6], data[7]); + } + + int getRowOutlineLevel() { + return rowOutlineLevel; + } + + int getColumnOutlineLevel() { + return columnOutlineLevel; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java new file mode 100755 index 0000000..4629806 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HeaderRecord.java @@ -0,0 +1,100 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A workbook page header record + */ +public class HeaderRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HeaderRecord.class); + /** + * The footer + */ + private String header; + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + */ + HeaderRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = IntegerHelper.getInt(data[0], data[1]); + + boolean unicode = data[2] == 1; + + if (unicode) { + header = StringHelper.getUnicodeString(data, chars, 3); + } else { + header = StringHelper.getString(data, chars, 3, ws); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the record data + * @param ws the workbook settings + * @param dummy dummy record to indicate a biff7 document + */ + HeaderRecord(Record t, WorkbookSettings ws, Biff7 dummy) { + super(t); + byte[] data = getRecord().getData(); + + if (data.length == 0) { + return; + } + + int chars = data[0]; + header = StringHelper.getString(data, chars, 1, ws); + } + + /** + * Gets the header string + * + * @return the header string + */ + String getHeader() { + return header; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java new file mode 100755 index 0000000..dae08fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HideobjRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A hideobj record + */ +class HideobjRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HideobjRecord.class); + + /** + * The hide obj mode + */ + private final int hidemode; + + /** + * Constructor + * + * @param t the record + */ + public HideobjRecord(Record t) { + super(t); + byte[] data = t.getData(); + hidemode = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Accessor for the hide mode mode + * + * @return the hide mode + */ + public int getHideMode() { + return hidemode; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java new file mode 100755 index 0000000..792d9fc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HorizontalPageBreaksRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class HorizontalPageBreaksRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (HorizontalPageBreaksRecord.class); + /** + * The row page breaks + */ + private final int[] rowBreaks; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public HorizontalPageBreaksRecord(Record t) { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public HorizontalPageBreaksRecord(Record t, Biff7 biff7) { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + rowBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) { + rowBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getRowBreaks() { + return rowBreaks; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java new file mode 100755 index 0000000..81dad89 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/HyperlinkRecord.java @@ -0,0 +1,360 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import jxl.CellReferenceHelper; +import jxl.Hyperlink; +import jxl.Range; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class HyperlinkRecord extends RecordData implements Hyperlink { + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HyperlinkRecord.class); + /** + * The first row + */ + private final int firstRow; + /** + * The last row + */ + private final int lastRow; + /** + * The first column + */ + private final int firstColumn; + /** + * The last column + */ + private final int lastColumn; + /** + * The URL referred to by this hyperlink + */ + private URL url; + /** + * The local file referred to by this hyperlink + */ + private File file; + + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + /** + * The range of cells which activate this hyperlink + */ + private final SheetRangeImpl range; + /** + * The type of this hyperlink + */ + private LinkType linkType; + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + * @param ws the workbook settings + */ + HyperlinkRecord(Record t, Sheet s, WorkbookSettings ws) { + super(t); + + linkType = unknown; + + byte[] data = getRecord().getData(); + + // Build up the range of cells occupied by this hyperlink + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstColumn = IntegerHelper.getInt(data[4], data[5]); + lastColumn = IntegerHelper.getInt(data[6], data[7]); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + int options = IntegerHelper.getInt(data[28], data[29], data[30], data[31]); + + boolean description = (options & 0x14) != 0; + int startpos = 32; + int descbytes = 0; + if (description) { + int descchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + descbytes = descchars * 2 + 4; + } + + startpos += descbytes; + + boolean targetFrame = (options & 0x80) != 0; + int targetbytes = 0; + if (targetFrame) { + int targetchars = IntegerHelper.getInt + (data[startpos], data[startpos + 1], + data[startpos + 2], data[startpos + 3]); + targetbytes = targetchars * 2 + 4; + } + + startpos += targetbytes; + + // Try and determine the type + if ((options & 0x3) == 0x03) { + linkType = urlLink; + + // check the guid monicker + if (data[startpos] == 0x03) { + linkType = fileLink; + } + } else if ((options & 0x01) != 0) { + linkType = fileLink; + // check the guid monicker + if (data[startpos] == (byte) 0xe0) { + linkType = urlLink; + } + } else if ((options & 0x08) != 0) { + linkType = workbookLink; + } + + // Try and determine the type + if (linkType == urlLink) { + String urlString = null; + try { + startpos += 16; + + // Get the url, ignoring the 0 char at the end + int bytes = IntegerHelper.getInt(data[startpos], + data[startpos + 1], + data[startpos + 2], + data[startpos + 3]); + + urlString = StringHelper.getUnicodeString(data, bytes / 2 - 1, + startpos + 4); + url = new URL(urlString); + } catch (MalformedURLException e) { + logger.warn("URL " + urlString + " is malformed. Trying a file"); + try { + linkType = fileLink; + file = new File(urlString); + } catch (Exception e3) { + logger.warn("Cannot set to file. Setting a default URL"); + + // Set a default URL + try { + linkType = urlLink; + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } catch (MalformedURLException e2) { + // fail silently + } + } + } catch (Throwable e) { + StringBuffer sb1 = new StringBuffer(); + StringBuffer sb2 = new StringBuffer(); + CellReferenceHelper.getCellReference(firstColumn, firstRow, sb1); + CellReferenceHelper.getCellReference(lastColumn, lastRow, sb2); + sb1.insert(0, "Exception when parsing URL "); + sb1.append('\"').append(sb2).append("\". Using default."); + logger.warn(sb1, e); + + // Set a default URL + try { + url = new URL("http://www.andykhan.com/jexcelapi/index.html"); + } catch (MalformedURLException e2) { + // fail silently + } + } + } else if (linkType == fileLink) { + try { + startpos += 16; + + // Get the name of the local file, ignoring the zero character at the + // end + int upLevelCount = IntegerHelper.getInt(data[startpos], + data[startpos + 1]); + int chars = IntegerHelper.getInt(data[startpos + 2], + data[startpos + 3], + data[startpos + 4], + data[startpos + 5]); + String fileName = StringHelper.getString(data, chars - 1, + startpos + 6, ws); + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < upLevelCount; i++) { + sb.append("..\\"); + } + + sb.append(fileName); + + file = new File(sb.toString()); + } catch (Throwable e) { + logger.warn("Exception when parsing file " + + e.getClass().getName() + "."); + file = new File("."); + } + } else if (linkType == workbookLink) { + int chars = IntegerHelper.getInt(data[32], data[33], data[34], data[35]); + location = StringHelper.getUnicodeString(data, chars - 1, 36); + } else { + // give up + logger.warn("Cannot determine link type"); + return; + } + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() { + return url; + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() { + return file; + } + + /** + * Exposes the base class method. This is used when copying hyperlinks + * + * @return the Record data + */ + public Record getRecord() { + return super.getRecord(); + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink + */ + public Range getRange() { + return range; + } + + /** + * Gets the location referenced by this hyperlink + * + * @return the location + */ + public String getLocation() { + return location; + } + + /** + * The excel type of hyperlink + */ + private static class LinkType { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java new file mode 100755 index 0000000..e497826 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LabelRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class LabelRecord extends CellValue implements LabelCell { + public static Biff7 biff7 = new Biff7(); + /** + * The length of the label in characters + */ + private final int length; + /** + * The label + */ + private final String string; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + */ + public LabelRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + if (data[8] == 0x0) { + string = StringHelper.getString(data, length, 9, ws); + } else { + string = StringHelper.getUnicodeString(data, length, 9); + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public LabelRecord(Record t, FormattingRecords fr, SheetImpl si, + WorkbookSettings ws, Biff7 dummy) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java new file mode 100755 index 0000000..2e8058c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LabelSSTRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; + +/** + * A label which is stored in the shared string table + */ +class LabelSSTRecord extends CellValue implements LabelCell { + /** + * The index into the shared string table + */ + private final int index; + /** + * The label + */ + private final String string; + + /** + * Constructor. Retrieves the index from the raw data and looks it up + * in the shared string table + * + * @param stringTable the shared string table + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + */ + public LabelSSTRecord(Record t, SSTRecord stringTable, FormattingRecords fr, + SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + index = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + string = stringTable.getString(index); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets this cell's contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Returns the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java new file mode 100755 index 0000000..022b2a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/LeftMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class LeftMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the raw record + */ + LeftMarginRecord(Record r) { + super(Type.LEFTMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java new file mode 100755 index 0000000..32fe710 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MarginRecord.java @@ -0,0 +1,58 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Abstract class containing the margin value for top,left,right and bottom + * margins + */ +abstract class MarginRecord extends RecordData { + /** + * The size of the margin + */ + private final double margin; + + /** + * Constructs this record from the raw data + * + * @param t the type + * @param r the record + */ + protected MarginRecord(Type t, Record r) { + super(t); + + byte[] data = r.getData(); + + margin = DoubleHelper.getIEEEDouble(data, 0); + } + + /** + * Accessor for the margin + * + * @return the margin + */ + double getMargin() { + return margin; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java new file mode 100755 index 0000000..e6d4bcc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MergedCellsRecord.java @@ -0,0 +1,86 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Range; +import jxl.Sheet; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.SheetRangeImpl; + +/** + * A merged cells record for a given sheet + */ +public class MergedCellsRecord extends RecordData { + /** + * The ranges of the cells merged on this sheet + */ + private final Range[] ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param s the sheet + */ + MergedCellsRecord(Record t, Sheet s) { + super(t); + + byte[] data = getRecord().getData(); + + int numRanges = IntegerHelper.getInt(data[0], data[1]); + + ranges = new Range[numRanges]; + + int pos = 2; + int firstRow = 0; + int lastRow = 0; + int firstCol = 0; + int lastCol = 0; + + for (int i = 0; i < numRanges; i++) { + firstRow = IntegerHelper.getInt(data[pos], data[pos + 1]); + lastRow = IntegerHelper.getInt(data[pos + 2], data[pos + 3]); + firstCol = IntegerHelper.getInt(data[pos + 4], data[pos + 5]); + lastCol = IntegerHelper.getInt(data[pos + 6], data[pos + 7]); + + ranges[i] = new SheetRangeImpl(s, firstCol, firstRow, + lastCol, lastRow); + + pos += 8; + } + } + + /** + * Gets the ranges which have been merged in this sheet + * + * @return the ranges of cells which have been merged + */ + public Range[] getRanges() { + return ranges; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java new file mode 100755 index 0000000..4245860 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankCell.java @@ -0,0 +1,188 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A blank cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class MulBlankCell implements Cell, CellFeaturesAccessor { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulBlankCell.class); + + /** + * The row containing this blank + */ + private final int row; + /** + * The column containing this blank + */ + private final int column; + /** + * The raw cell format + */ + private CellFormat cellFormat; + + /** + * The index to the XF Record + */ + private final int xfIndex; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * The cell features + */ + private CellFeatures features; + + + /** + * Constructs this cell + * + * @param r the zero based row + * @param c the zero base column + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public MulBlankCell(int r, int c, + int xfi, + FormattingRecords fr, + SheetImpl si) { + row = r; + column = c; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() { + return column; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Gets the cell format for this cell + * + * @return the cell format for these cells + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features during the reading process + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + if (features != null) { + logger.warn("current cell features not null - overwriting"); + } + + features = cf; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java new file mode 100755 index 0000000..b28c7b2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulBlankRecord.java @@ -0,0 +1,127 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains an array of Blank, formatted cells + */ +class MulBlankRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulBlankRecord.class); + + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these blank numbers occur on + */ + private final int colLast; + /** + * The number of blank numbers contained in this record + */ + private final int numblanks; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the blank records from the raw data + * + * @param t the raw data + */ + public MulBlankRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numblanks = colLast - colFirst + 1; + xfIndices = new int[numblanks]; + + readBlanks(data); + } + + /** + * Reads the blanks from the raw data + * + * @param data the raw data + */ + private void readBlanks(byte[] data) { + int pos = 4; + for (int i = 0; i < numblanks; i++) { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these blank numbers + */ + public int getRow() { + return row; + } + + /** + * The first column containing the blank numbers + * + * @return the first column + */ + public int getFirstColumn() { + return colFirst; + } + + /** + * Accessor for the number of blank values + * + * @return the number of blank values + */ + public int getNumberOfColumns() { + return numblanks; + } + + /** + * Return a specific formatting index + * + * @param index the cell index in the group + * @return the formatting index + */ + public int getXFIndex(int index) { + return xfIndices[index]; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java new file mode 100755 index 0000000..c429f62 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/MulRKRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MulRKRecord.class); + + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these rk numbers occur on + */ + private final int colLast; + /** + * The number of rk numbers contained in this record + */ + private final int numrks; + /** + * The array of rk numbers + */ + private final int[] rknumbers; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the rk numbers from the raw data + * + * @param t the raw data + */ + public MulRKRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + int length = getRecord().getLength(); + row = IntegerHelper.getInt(data[0], data[1]); + colFirst = IntegerHelper.getInt(data[2], data[3]); + colLast = IntegerHelper.getInt(data[length - 2], data[length - 1]); + numrks = colLast - colFirst + 1; + rknumbers = new int[numrks]; + xfIndices = new int[numrks]; + + readRks(data); + } + + /** + * Reads the rks from the raw data + * + * @param data the raw data + */ + private void readRks(byte[] data) { + int pos = 4; + int rk; + for (int i = 0; i < numrks; i++) { + xfIndices[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + rk = IntegerHelper.getInt + (data[pos + 2], data[pos + 3], data[pos + 4], data[pos + 5]); + rknumbers[i] = rk; + pos += 6; + } + } + + /** + * Accessor for the row + * + * @return the row of containing these rk numbers + */ + public int getRow() { + return row; + } + + /** + * The first column containing the rk numbers + * + * @return the first column + */ + public int getFirstColumn() { + return colFirst; + } + + /** + * Accessor for the number of rk values + * + * @return the number of rk values + */ + public int getNumberOfColumns() { + return numrks; + } + + /** + * Returns a specific rk number + * + * @param index the rk number to return + * @return the rk number in bits + */ + public int getRKNumber(int index) { + return rknumbers[index]; + } + + /** + * Return a specific formatting index + * + * @param index the index of the cell in this group + * @return the xf index + */ + public int getXFIndex(int index) { + return xfIndices[index]; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java new file mode 100755 index 0000000..7de4be1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NameRecord.java @@ -0,0 +1,510 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Holds an excel name record, and the details of the cells/ranges it refers + * to + */ +public class NameRecord extends RecordData { + // Constants which refer to the name type + private static final int commandMacro = 0xc; + private static final int builtIn = 0x20; + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + public static Biff7 biff7 = new Biff7(); + + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NameRecord.class); + /** + * The name + */ + private String name; + /** + * The built in name + */ + private BuiltInName builtInName; + /** + * The 0-based index in the name table + */ + private final int index; + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + /** + * Indicates whether this is a biff8 name record. Used during copying + */ + private final boolean isbiff8; + /** + * The ranges referenced by this name + */ + private ArrayList ranges; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + */ + NameRecord(Record t, WorkbookSettings ws, int ind) { + super(t); + index = ind; + isbiff8 = true; + + try { + ranges = new ArrayList(); + + byte[] data = getRecord().getData(); + int option = IntegerHelper.getInt(data[0], data[1]); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8], data[9]); + + if ((option & builtIn) != 0) { + builtInName = BuiltInName.getBuiltInName(data[15]); + } else { + name = StringHelper.getString(data, length, 15, ws); + } + + if ((option & commandMacro) != 0) { + // This is a command macro, so it has no cell references + return; + } + + int pos = length + 15; + + if (data[pos] == cellReference) { + int sheet = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + int row = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + int columnMask = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + int column = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } else if (data[pos] == areaReference) { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + } + } else if (data[pos] == subExpression) { + int sheet1 = 0; + int r1 = 0; + int columnMask = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 1], data[pos + 2]); + r1 = IntegerHelper.getInt(data[pos + 3], data[pos + 4]); + r2 = IntegerHelper.getInt(data[pos + 5], data[pos + 6]); + + columnMask = IntegerHelper.getInt(data[pos + 7], data[pos + 8]); + c1 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + columnMask = IntegerHelper.getInt(data[pos + 9], data[pos + 10]); + c2 = columnMask & 0xff; + + // Check that we are not dealing with offsets + Assert.verify((columnMask & 0xc0000) == 0); + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 11; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + } + } else { + String n = name != null ? name : builtInName.getName(); + logger.warn("Cannot read name ranges for " + n + + " - setting to empty"); + NameRange range = new NameRange(0, 0, 0, 0, 0); + ranges.add(range); + } + } catch (Throwable t1) { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name"); + name = "ERROR"; + } + } + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + * @param ind the index in the name table + * @param dummy dummy parameter to indicate a biff7 workbook + */ + NameRecord(Record t, WorkbookSettings ws, int ind, Biff7 dummy) { + super(t); + index = ind; + isbiff8 = false; + + try { + ranges = new ArrayList(); + byte[] data = getRecord().getData(); + int length = data[3]; + sheetRef = IntegerHelper.getInt(data[8], data[9]); + name = StringHelper.getString(data, length, 14, ws); + + int pos = length + 14; + + if (pos >= data.length) { + // There appears to be nothing after the name, so return + return; + } + + if (data[pos] == cellReference) { + int sheet = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + int row = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + int column = data[pos + 17]; + + NameRange r = new NameRange(sheet, column, row, column, row); + ranges.add(r); + } else if (data[pos] == areaReference) { + int sheet1 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + } + } else if (data[pos] == subExpression) { + int sheet1 = 0; + int sheet2 = 0; + int r1 = 0; + int c1 = 0; + int r2 = 0; + int c2 = 0; + NameRange range = null; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + + while (pos < data.length) { + sheet1 = IntegerHelper.getInt(data[pos + 11], data[pos + 12]); + r1 = IntegerHelper.getInt(data[pos + 15], data[pos + 16]); + r2 = IntegerHelper.getInt(data[pos + 17], data[pos + 18]); + + c1 = data[pos + 19]; + c2 = data[pos + 20]; + + range = new NameRange(sheet1, c1, r1, c2, r2); + ranges.add(range); + + pos += 21; + + // Consume unnecessary parsed tokens + if (pos < data.length && + data[pos] != cellReference && + data[pos] != areaReference) { + if (data[pos] == subExpression) { + pos += 3; + } else if (data[pos] == union) { + pos += 1; + } + } + } + } + } catch (Throwable t1) { + // Generate a warning + // Names are really a nice to have, and we don't want to halt the + // reading process for functionality that probably won't be used + logger.warn("Cannot read name."); + name = "ERROR"; + } + } + + /** + * Gets the name + * + * @return the strings + */ + public String getName() { + return name; + } + + /** + * Gets the built in name + * + * @return the built in name + */ + public BuiltInName getBuiltInName() { + return builtInName; + } + + /** + * Gets the array of ranges for this name. This method is public as it is + * used from the writable side when copying ranges + * + * @return the ranges + */ + public NameRange[] getRanges() { + NameRange[] nr = new NameRange[ranges.size()]; + return (NameRange[]) ranges.toArray(nr); + } + + /** + * Accessor for the index into the name table + * + * @return the 0-based index into the name table + */ + int getIndex() { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + */ + public void setSheetRef(int i) { + sheetRef = i; + } + + /** + * Called when copying a sheet. Just returns the raw data + * + * @return the raw data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Called when copying to determine whether this is a biff8 name + * + * @return TRUE if this is a biff8 name record, FALSE otherwise + */ + public boolean isBiff8() { + return isbiff8; + } + + /** + * Queries whether this is a global name or not + * + * @return TRUE if this is a global name, FALSE otherwise + */ + public boolean isGlobal() { + return sheetRef == 0; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } + + /** + * A nested class to hold range information + */ + public class NameRange { + /** + * The first column + */ + private final int columnFirst; + + /** + * The first row + */ + private final int rowFirst; + + /** + * The last column + */ + private final int columnLast; + + /** + * The last row + */ + private final int rowLast; + + /** + * The first sheet + */ + private final int externalSheet; + + /** + * Constructor + * + * @param s1 the sheet + * @param c1 the first column + * @param r1 the first row + * @param c2 the last column + * @param r2 the last row + */ + NameRange(int s1, int c1, int r1, int c2, int r2) { + columnFirst = c1; + rowFirst = r1; + columnLast = c2; + rowLast = r2; + externalSheet = s1; + } + + /** + * Accessor for the first column + * + * @return the index of the first column + */ + public int getFirstColumn() { + return columnFirst; + } + + /** + * Accessor for the first row + * + * @return the index of the first row + */ + public int getFirstRow() { + return rowFirst; + } + + /** + * Accessor for the last column + * + * @return the index of the last column + */ + public int getLastColumn() { + return columnLast; + } + + /** + * Accessor for the last row + * + * @return the index of the last row + */ + public int getLastRow() { + return rowLast; + } + + /** + * Accessor for the first sheet + * + * @return the index of the external sheet + */ + public int getExternalSheet() { + return externalSheet; + } + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java new file mode 100755 index 0000000..4dc7e3d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NineteenFourRecord.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Identifies the date system as the 1904 system or not + */ +class NineteenFourRecord extends RecordData { + /** + * The base year for dates + */ + private final boolean nineteenFour; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + public NineteenFourRecord(Record t) { + super(t); + + byte[] data = getRecord().getData(); + + nineteenFour = data[0] == 1; + + } + + /** + * Accessor to see whether this spreadsheets dates are based around + * 1904 + * + * @return true if this workbooks dates are based around the 1904 + * date system + */ + public boolean is1904() { + return nineteenFour; + } + + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java new file mode 100755 index 0000000..6919c86 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberFormulaRecord.java @@ -0,0 +1,179 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A formula's last calculated value + */ +class NumberFormulaRecord extends CellValue + implements NumberCell, FormulaData, NumberFormulaCell { + /** + * The string format for the double value + */ + private static final DecimalFormat defaultFormat = + new DecimalFormat("#.###"); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberFormulaRecord.class); + /** + * The last calculated value of the formula + */ + private final double value; + /** + * The number format + */ + private NumberFormat format; + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting record + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public NumberFormulaRecord(Record t, FormattingRecords fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + data = getRecord().getData(); + + format = fr.getNumberFormat(getXFIndex()); + + if (format == null) { + format = defaultFormat; + } + + value = DoubleHelper.getIEEEDouble(data, 6); + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public double getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java new file mode 100755 index 0000000..2828df6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberRecord.java @@ -0,0 +1,111 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.common.Logger; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +class NumberRecord extends CellValue implements NumberCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberRecord.class); + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value + */ + private final double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available formats + * @param si the sheet + */ + public NumberRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + + value = DoubleHelper.getIEEEDouble(data, 6); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java b/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java new file mode 100755 index 0000000..92006fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/NumberValue.java @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellFeatures; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.format.CellFormat; + +/** + * A numerical cell value, initialized indirectly from a multiple biff record + * rather than directly from the binary data + */ +class NumberValue implements NumberCell, CellFeaturesAccessor { + /** + * The format in which to return this number as a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The row containing this number + */ + private final int row; + /** + * The column containing this number + */ + private final int column; + /** + * The value of this number + */ + private final double value; + /** + * The cell format + */ + private NumberFormat format; + /** + * The raw cell format + */ + private CellFormat cellFormat; + /** + * The cell features + */ + private CellFeatures features; + /** + * The index to the XF Record + */ + private final int xfIndex; + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + /** + * A flag to indicate whether this object's formatting things have + * been initialized + */ + private boolean initialized; + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * Constructs this number + * + * @param r the zero based row + * @param c the zero base column + * @param val the value + * @param xfi the xf index + * @param fr the formatting records + * @param si the sheet + */ + public NumberValue(int r, int c, double val, + int xfi, + FormattingRecords fr, + SheetImpl si) { + row = r; + column = c; + value = val; + format = defaultFormat; + xfIndex = xfi; + formattingRecords = fr; + sheet = si; + initialized = false; + } + + /** + * Accessor for the row + * + * @return the zero based row + */ + public final int getRow() { + return row; + } + + /** + * Accessor for the column + * + * @return the zero based column + */ + public final int getColumn() { + return column; + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the cell format + * + * @return the cell format + */ + public CellFormat getCellFormat() { + if (!initialized) { + cellFormat = formattingRecords.getXFRecord(xfIndex); + initialized = true; + } + + return cellFormat; + } + + /** + * Determines whether or not this cell has been hidden + * + * @return TRUE if this cell has been hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) { + if (f != null) { + format = f; + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(CellFeatures cf) { + features = cf; + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java new file mode 100755 index 0000000..f296cac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PLSRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * The environment specific print record + */ +public class PLSRecord extends RecordData { + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public PLSRecord(Record r) { + super(r); + } + + /** + * Gets the data + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java new file mode 100755 index 0000000..fe92bca --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PaletteRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * A password record + */ +public class PaletteRecord extends RecordData { + /** + * Constructor + * + * @param t the raw bytes + */ + PaletteRecord(Record t) { + super(t); + } + + /** + * Accessor for the binary data - used when copying + * + * @return the binary data + */ + public byte[] getData() { + return getRecord().getData(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java new file mode 100755 index 0000000..75a60b5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PaneRecord.java @@ -0,0 +1,81 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class PaneRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(PaneRecord.class); + + /** + * The number of rows visible in the top left pane + */ + private final int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private final int columnsVisible; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public PaneRecord(Record t) { + super(t); + byte[] data = t.getData(); + + columnsVisible = IntegerHelper.getInt(data[0], data[1]); + rowsVisible = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the number of rows in the top left pane + * + * @return the number of rows visible in the top left pane + */ + public final int getRowsVisible() { + return rowsVisible; + } + + /** + * Accessor for the numbe rof columns visible in the top left pane + * + * @return the number of columns visible in the top left pane + */ + public final int getColumnsVisible() { + return columnsVisible; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java new file mode 100755 index 0000000..ecf1c6a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * A properly typed exception in case consumers of the API specifically + * wish to handle the case when the workbook is password protected + */ +public class PasswordException extends BiffException { + /** + * Constructor + */ + public PasswordException() { + super(passwordProtected); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java new file mode 100755 index 0000000..e13f337 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PasswordRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * A password record + */ +class PasswordRecord extends RecordData { + /** + * The password + */ + private String password; + /** + * The binary data + */ + private final int passwordHash; + + /** + * Constructor + * + * @param t the raw bytes + */ + public PasswordRecord(Record t) { + super(Type.PASSWORD); + + byte[] data = t.getData(); + passwordHash = IntegerHelper.getInt(data[0], data[1]); + } + + /** + * Gets the binary data for output to file + * + * @return the password hash + */ + public int getPasswordHash() { + return passwordHash; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java new file mode 100755 index 0000000..22c6342 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PrintGridLinesRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintGridLinesRecord extends RecordData { + /** + * print grid lines flag + */ + private final boolean printGridLines; + + /** + * Constructs the value from the raw data + * + * @param pgl the raw data + */ + public PrintGridLinesRecord(Record pgl) { + super(pgl); + byte[] data = pgl.getData(); + + printGridLines = (data[0] == 1); + } + + /** + * Accessor for the print grid lines flag + * + * @return TRUE to print grid lines, FALSE otherwise + */ + public boolean getPrintGridLines() { + return printGridLines; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java new file mode 100755 index 0000000..ca04b91 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/PrintHeadersRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; + +/** + * Contains the print grid lines option of this worksheet + */ +class PrintHeadersRecord extends RecordData { + /** + * print grid lines flag + */ + private final boolean printHeaders; + + /** + * Constructs the value from the raw data + * + * @param ph the raw data + */ + public PrintHeadersRecord(Record ph) { + super(ph); + byte[] data = ph.getData(); + + printHeaders = (data[0] == 1); + } + + /** + * Accessor for the print headers flag + * + * @return TRUE to print headers, FALSE otherwise + */ + public boolean getPrintHeaders() { + return printHeaders; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java new file mode 100755 index 0000000..8ef1c55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/ProtectRecord.java @@ -0,0 +1,67 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; + +/** + * A record detailing whether the sheet is protected + */ +class ProtectRecord extends RecordData { + /** + * Protected flag + */ + private final boolean prot; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + ProtectRecord(Record t) { + super(t); + byte[] data = getRecord().getData(); + + int protflag = IntegerHelper.getInt(data[0], data[1]); + + prot = (protflag == 1); + } + + /** + * Returns the protected flag + * + * @return TRUE if this is protected, FALSE otherwise + */ + boolean isProtected() { + return prot; + } + + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java b/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java new file mode 100755 index 0000000..d15d063 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RKHelper.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +/** + * Helper to convert an RK number into a double or an integer + */ +final class RKHelper { + /** + * Private constructor to prevent instantiation + */ + private RKHelper() { + } + + /** + * Converts excel's internal RK format into a double value + * + * @param rk the rk number in bits + * @return the double representation + */ + public static double getDouble(int rk) { + if ((rk & 0x02) != 0) { + int intval = rk >> 2; + + double value = intval; + if ((rk & 0x01) != 0) { + value /= 100; + } + + return value; + } else { + long valbits = (rk & 0xfffffffc); + valbits <<= 32; + double value = Double.longBitsToDouble(valbits); + + if ((rk & 0x01) != 0) { + value /= 100; + } + + return value; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java new file mode 100755 index 0000000..a5a10b1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RKRecord.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.common.Logger; + +/** + * An individual RK record + */ +class RKRecord extends CellValue implements NumberCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RKRecord.class); + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value + */ + private final double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the available cell formats + * @param si the sheet + */ + public RKRecord(Record t, FormattingRecords fr, SheetImpl si) { + super(t, fr, si); + byte[] data = getRecord().getData(); + int rknum = IntegerHelper.getInt(data[6], data[7], data[8], data[9]); + value = RKHelper.getDouble(rknum); + + // Now get the number format + format = fr.getNumberFormat(getXFIndex()); + if (format == null) { + format = defaultFormat; + } + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Returns the contents of this cell as a string + * + * @return the value formatted into a string + */ + public String getContents() { + return format.format(value); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java new file mode 100755 index 0000000..1b03658 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RStringRecord.java @@ -0,0 +1,93 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; + +/** + * A label which is stored in the cell + */ +class RStringRecord extends CellValue implements LabelCell { + public static Biff7 biff7 = new Biff7(); + /** + * The length of the label in characters + */ + private final int length; + /** + * The label + */ + private final String string; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param fr the formatting records + * @param si the sheet + * @param ws the workbook settings + * @param dummy dummy overload to indicate a biff 7 workbook + */ + public RStringRecord(Record t, FormattingRecords fr, + SheetImpl si, WorkbookSettings ws, Biff7 dummy) { + super(t, fr, si); + byte[] data = getRecord().getData(); + length = IntegerHelper.getInt(data[6], data[7]); + + string = StringHelper.getString(data, length, 8, ws); + } + + /** + * Gets the label + * + * @return the label + */ + public String getString() { + return string; + } + + /** + * Gets the cell contents as a string + * + * @return the label + */ + public String getContents() { + return string; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Record.java b/datastructures-xslx/src/main/java/jxl/read/biff/Record.java new file mode 100755 index 0000000..43afc94 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Record.java @@ -0,0 +1,169 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Logger; + + +/** + * A container for the raw record data within a biff file + */ +public final class Record { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Record.class); + + /** + * The excel biff code + */ + private final int code; + /** + * The data type + */ + private Type type; + /** + * The length of this record + */ + private final int length; + /** + * A pointer to the beginning of the actual data + */ + private final int dataPos; + /** + * A handle to the excel 97 file + */ + private final File file; + /** + * The raw data within this record + */ + private byte[] data; + + /** + * Any continue records + */ + private ArrayList continueRecords; + + /** + * Constructor + * + * @param offset the offset in the raw file + * @param f the excel 97 biff file + * @param d the data record + */ + Record(byte[] d, int offset, File f) { + code = IntegerHelper.getInt(d[offset], d[offset + 1]); + length = IntegerHelper.getInt(d[offset + 2], d[offset + 3]); + file = f; + file.skip(4); + dataPos = f.getPos(); + file.skip(length); + type = Type.getType(code); + } + + /** + * Gets the biff type + * + * @return the biff type + */ + public Type getType() { + return type; + } + + /** + * In the case of dodgy records, this method may be called to forcibly + * set the type in order to continue processing + * + * @param t the forcibly overridden type + */ + void setType(Type t) { + type = t; + } + + /** + * Gets the length of the record + * + * @return the length of the record + */ + public int getLength() { + return length; + } + + /** + * Gets the data portion of the record + * + * @return the data portion of the record + */ + public byte[] getData() { + if (data == null) { + data = file.read(dataPos, length); + } + + // copy in any data from the continue records + if (continueRecords != null) { + int size = 0; + byte[][] contData = new byte[continueRecords.size()][]; + for (int i = 0; i < continueRecords.size(); i++) { + Record r = (Record) continueRecords.get(i); + contData[i] = r.getData(); + byte[] d2 = contData[i]; + size += d2.length; + } + + byte[] d3 = new byte[data.length + size]; + System.arraycopy(data, 0, d3, 0, data.length); + int pos = data.length; + for (int i = 0; i < contData.length; i++) { + byte[] d2 = contData[i]; + System.arraycopy(d2, 0, d3, pos, d2.length); + pos += d2.length; + } + + data = d3; + } + + return data; + } + + /** + * The excel 97 code + * + * @return the excel code + */ + public int getCode() { + return code; + } + + /** + * Adds a continue record to this data + * + * @param d the continue record + */ + public void addContinueRecord(Record d) { + if (continueRecords == null) { + continueRecords = new ArrayList(); + } + + continueRecords.add(d); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java new file mode 100755 index 0000000..88a0de1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RefreshAllRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A Refresh all mode record + */ +class RefreshAllRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RefreshAllRecord.class); + + /** + * The refresh all mode + */ + private final boolean refreshAll; + + /** + * Constructor + * + * @param t the record + */ + public RefreshAllRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + refreshAll = (mode == 1); + } + + /** + * Accessor for the refreshAll mode + * + * @return the refreshAll mode + */ + public boolean getRefreshAll() { + return refreshAll; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java new file mode 100755 index 0000000..3bbff05 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RightMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class RightMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + RightMarginRecord(Record r) { + super(Type.RIGHTMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java new file mode 100755 index 0000000..76ec4d7 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/RowRecord.java @@ -0,0 +1,175 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A row record + */ +public class RowRecord extends RecordData { + /** + * Indicates that the row is default height + */ + private static final int defaultHeightIndicator = 0xff; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(RowRecord.class); + /** + * The number of this row + */ + private final int rowNumber; + /** + * The height of this row + */ + private final int rowHeight; + /** + * Flag to indicate whether this row is collapsed or not + */ + private final boolean collapsed; + /** + * Indicates whether this row has an explicit default format + */ + private final boolean defaultFormat; + /** + * Indicates whether the row record height matches the default font height + */ + private final boolean matchesDefFontHeight; + /** + * The (default) xf index for cells on this row + */ + private final int xfIndex; + /** + * The outline level of the row + */ + private final int outlineLevel; + /** + * Is this the icon indicator row of a group? + */ + private final boolean groupStart; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + */ + RowRecord(Record t) { + super(t); + + byte[] data = getRecord().getData(); + rowNumber = IntegerHelper.getInt(data[0], data[1]); + rowHeight = IntegerHelper.getInt(data[6], data[7]); + + int options = IntegerHelper.getInt(data[12], data[13], + data[14], data[15]); + outlineLevel = (options & 0x7); + groupStart = (options & 0x10) != 0; + collapsed = (options & 0x20) != 0; + matchesDefFontHeight = (options & 0x40) == 0; + defaultFormat = (options & 0x80) != 0; + xfIndex = (options & 0x0fff0000) >> 16; + } + + /** + * Interrogates whether this row is of default height + * + * @return TRUE if this is set to the default height, FALSE otherwise + */ + boolean isDefaultHeight() { + return rowHeight == defaultHeightIndicator; + } + + /** + * Interrogates this row to see whether it matches the default font height + * + * @return TRUE if this matches the default font height, FALSE otherwise + */ + public boolean matchesDefaultFontHeight() { + return matchesDefFontHeight; + } + + /** + * Gets the row number + * + * @return the number of this row + */ + public int getRowNumber() { + return rowNumber; + } + + /** + * Accessor for the row's outline level + * + * @return the row's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Accessor for row's groupStart value + * + * @return the row's groupStart value + */ + public boolean getGroupStart() { + return groupStart; + } + + /** + * Gets the height of the row + * + * @return the row height + */ + public int getRowHeight() { + return rowHeight; + } + + /** + * Queries whether the row is collapsed + * + * @return the collapsed indicator + */ + public boolean isCollapsed() { + return collapsed; + } + + /** + * Gets the default xf index for this row + * + * @return the xf index + */ + public int getXFIndex() { + return xfIndex; + } + + /** + * Queries whether the row has a specific default cell format applied + * + * @return the default cell format + */ + public boolean hasDefaultFormat() { + return defaultFormat; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java new file mode 100755 index 0000000..ff5d77d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SCLRecord.java @@ -0,0 +1,62 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan, Adam Caldwell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; + +/** + * Class containing the zoom factor for display + */ +class SCLRecord extends RecordData { + /** + * The numerator of the zoom + */ + private final int numerator; + + /** + * The denominator of the zoom + */ + private final int denominator; + + /** + * Constructs this record from the raw data + * + * @param r the record + */ + protected SCLRecord(Record r) { + super(Type.SCL); + + byte[] data = r.getData(); + + numerator = IntegerHelper.getInt(data[0], data[1]); + denominator = IntegerHelper.getInt(data[2], data[3]); + } + + /** + * Accessor for the zoom factor + * + * @return the zoom factor as the nearest integer percentage + */ + public int getZoomFactor() { + return numerator * 100 / denominator; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java new file mode 100755 index 0000000..c1b66bf --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SSTRecord.java @@ -0,0 +1,391 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Assert; + +/** + * Holds all the strings in the shared string table + */ +class SSTRecord extends RecordData { + /** + * The total number of strings in this table + */ + private final int totalStrings; + /** + * The number of unique strings + */ + private final int uniqueStrings; + /** + * The shared strings + */ + private final String[] strings; + /** + * The array of continuation breaks + */ + private final int[] continuationBreaks; + + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param continuations the continuations + * @param ws the workbook settings + */ + public SSTRecord(Record t, Record[] continuations, WorkbookSettings ws) { + super(t); + + // If a continue record appears in the middle of + // a string, then the encoding character is repeated + + // Concatenate everything into one big bugger of a byte array + int totalRecordLength = 0; + + for (int i = 0; i < continuations.length; i++) { + totalRecordLength += continuations[i].getLength(); + } + totalRecordLength += getRecord().getLength(); + + byte[] data = new byte[totalRecordLength]; + + // First the original data gets put in + int pos = 0; + System.arraycopy(getRecord().getData(), 0, + data, 0, getRecord().getLength()); + pos += getRecord().getLength(); + + // Now copy in everything else. + continuationBreaks = new int[continuations.length]; + Record r = null; + for (int i = 0; i < continuations.length; i++) { + r = continuations[i]; + System.arraycopy(r.getData(), 0, + data, pos, + r.getLength()); + continuationBreaks[i] = pos; + pos += r.getLength(); + } + + totalStrings = IntegerHelper.getInt(data[0], data[1], + data[2], data[3]); + uniqueStrings = IntegerHelper.getInt(data[4], data[5], + data[6], data[7]); + + strings = new String[uniqueStrings]; + readStrings(data, 8, ws); + } + + /** + * Reads in all the strings from the raw data + * + * @param data the raw data + * @param offset the offset + * @param ws the workbook settings + */ + private void readStrings(byte[] data, int offset, WorkbookSettings ws) { + int pos = offset; + int numChars; + byte optionFlags; + String s = null; + boolean asciiEncoding = false; + boolean richString = false; + boolean extendedString = false; + int formattingRuns = 0; + int extendedRunLength = 0; + + for (int i = 0; i < uniqueStrings; i++) { + // Read in the number of characters + numChars = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + optionFlags = data[pos]; + pos++; + + // See if it is an extended string + extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + richString = ((optionFlags & 0x08) != 0); + + if (richString) { + // Read in the crun + formattingRuns = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + + if (extendedString) { + // Read in cchExtRst + extendedRunLength = IntegerHelper.getInt + (data[pos], data[pos + 1], data[pos + 2], data[pos + 3]); + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + asciiEncoding = ((optionFlags & 0x01) == 0); + + ByteArrayHolder bah = new ByteArrayHolder(); + BooleanHolder bh = new BooleanHolder(); + bh.value = asciiEncoding; + pos += getChars(data, bah, pos, bh, numChars); + asciiEncoding = bh.value; + + if (asciiEncoding) { + s = StringHelper.getString(bah.bytes, numChars, 0, ws); + } else { + s = StringHelper.getUnicodeString(bah.bytes, numChars, 0); + } + + strings[i] = s; + + // For rich strings, skip over the formatting runs + if (richString) { + pos += 4 * formattingRuns; + } + + // For extended strings, skip over the extended string data + if (extendedString) { + pos += extendedRunLength; + } + + if (pos > data.length) { + Assert.verify(false, "pos exceeds record length"); + } + } + } + + /** + * Gets the chars in the ascii array, taking into account continuation + * breaks + * + * @param source the original source + * @param bah holder for the new byte array + * @param pos the current position in the source + * @param ascii holder for a return ascii flag + * @param numChars the number of chars in the string + * @return the number of bytes read from the source + */ + private int getChars(byte[] source, + ByteArrayHolder bah, + int pos, + BooleanHolder ascii, + int numChars) { + int i = 0; + boolean spansBreak = false; + + if (ascii.value) { + bah.bytes = new byte[numChars]; + } else { + bah.bytes = new byte[numChars * 2]; + } + + while (i < continuationBreaks.length && !spansBreak) { + spansBreak = pos <= continuationBreaks[i] && + (pos + bah.bytes.length > continuationBreaks[i]); + + if (!spansBreak) { + i++; + } + } + + // If it doesn't span a break simply do an array copy into the + // destination array and finish + if (!spansBreak) { + System.arraycopy(source, pos, bah.bytes, 0, bah.bytes.length); + return bah.bytes.length; + } + + // Copy the portion before the break pos into the array + int breakpos = continuationBreaks[i]; + System.arraycopy(source, pos, bah.bytes, 0, breakpos - pos); + + int bytesRead = breakpos - pos; + int charsRead; + if (ascii.value) { + charsRead = bytesRead; + } else { + charsRead = bytesRead / 2; + } + + bytesRead += getContinuedString(source, + bah, + bytesRead, + i, + ascii, + numChars - charsRead); + return bytesRead; + } + + /** + * Gets the rest of the string after a continuation break + * + * @param source the original bytes + * @param bah the holder for the new bytes + * @param destPos the des pos + * @param contBreakIndex the index of the continuation break + * @param ascii the ascii flag holder + * @param charsLeft the number of chars left in the array + * @return the number of bytes read in the continued string + */ + private int getContinuedString(byte[] source, + ByteArrayHolder bah, + int destPos, + int contBreakIndex, + BooleanHolder ascii, + int charsLeft) { + int breakpos = continuationBreaks[contBreakIndex]; + int bytesRead = 0; + + while (charsLeft > 0) { + Assert.verify(contBreakIndex < continuationBreaks.length, + "continuation break index"); + + if (ascii.value && source[breakpos] == 0) { + // The string is consistently ascii throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft : + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + destPos += length; + bytesRead += length + 1; + charsLeft -= length; + ascii.value = true; + } else if (!ascii.value && source[breakpos] != 0) { + // The string is Unicode throughout + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + // It looks like the string continues as Unicode too. That's handy + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } else if (!ascii.value && source[breakpos] == 0) { + // Bummer - the string starts off as Unicode, but after the + // continuation it is in straightforward ASCII encoding + int chars = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft : + Math.min + (charsLeft, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + for (int j = 0; j < chars; j++) { + bah.bytes[destPos] = source[breakpos + j + 1]; + destPos += 2; + } + + bytesRead += chars + 1; + charsLeft -= chars; + ascii.value = false; + } else { + // Double Bummer - the string starts off as ASCII, but after the + // continuation it is in Unicode. This impacts the allocated array + + // Reallocate what we have of the byte array so that it is all + // Unicode + byte[] oldBytes = bah.bytes; + bah.bytes = new byte[destPos * 2 + charsLeft * 2]; + for (int j = 0; j < destPos; j++) { + bah.bytes[j * 2] = oldBytes[j]; + } + + destPos = destPos * 2; + + int length = contBreakIndex == continuationBreaks.length - 1 ? + charsLeft * 2 : + Math.min + (charsLeft * 2, + continuationBreaks[contBreakIndex + 1] - breakpos - 1); + + System.arraycopy(source, + breakpos + 1, + bah.bytes, + destPos, + length); + + destPos += length; + bytesRead += length + 1; + charsLeft -= length / 2; + ascii.value = false; + } + + contBreakIndex++; + + if (contBreakIndex < continuationBreaks.length) { + breakpos = continuationBreaks[contBreakIndex]; + } + } + + return bytesRead; + } + + /** + * Gets the string at the specified position + * + * @param index the index of the string to return + * @return the strings + */ + public String getString(int index) { + Assert.verify(index < uniqueStrings); + return strings[index]; + } + + /** + * A holder for a byte array + */ + private static class ByteArrayHolder { + /** + * the byte holder + */ + public byte[] bytes; + } + + /** + * A holder for a boolean + */ + private static class BooleanHolder { + /** + * the holder holder + */ + public boolean value; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java new file mode 100755 index 0000000..6346d10 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SaveRecalcRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A calculation mode record + */ +class SaveRecalcRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SaveRecalcRecord.class); + + /** + * The calculation mode + */ + private final boolean recalculateOnSave; + + /** + * Constructor + * + * @param t the record + */ + public SaveRecalcRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + recalculateOnSave = (mode == 1); + } + + /** + * Accessor for the recalculate on save mode + * + * @return the recalculate on save mode + */ + public boolean getRecalculateOnSave() { + return recalculateOnSave; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java new file mode 100755 index 0000000..e9ae744 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SetupRecord.java @@ -0,0 +1,255 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.Type; +import jxl.common.Logger; + +/** + * Contains the page set up for a sheet + */ +public class SetupRecord extends RecordData { + // The logger + private static final Logger logger = Logger.getLogger(SetupRecord.class); + + /** + * The raw data + */ + private final byte[] data; + + /** + * The orientation flag + */ + private final boolean portraitOrientation; + + /** + * The Page Order flag + */ + private final boolean pageOrder; + + /** + * The header margin + */ + private final double headerMargin; + + /** + * The footer margin + */ + private final double footerMargin; + + /** + * The paper size + */ + private final int paperSize; + + /** + * The scale factor + */ + private final int scaleFactor; + + /** + * The page start + */ + private final int pageStart; + + /** + * The fit width + */ + private final int fitWidth; + + /** + * The fit height + */ + private final int fitHeight; + + /** + * The horizontal print resolution + */ + private final int horizontalPrintResolution; + + /** + * The vertical print resolution + */ + private final int verticalPrintResolution; + + /** + * The number of copies + */ + private final int copies; + + /** + * Indicates whether the setup data should be initiliazed in the setup + * box + */ + private final boolean initialized; + + /** + * Constructor which creates this object from the binary data + * + * @param t the record + */ + SetupRecord(Record t) { + super(Type.SETUP); + + data = t.getData(); + + paperSize = IntegerHelper.getInt(data[0], data[1]); + scaleFactor = IntegerHelper.getInt(data[2], data[3]); + pageStart = IntegerHelper.getInt(data[4], data[5]); + fitWidth = IntegerHelper.getInt(data[6], data[7]); + fitHeight = IntegerHelper.getInt(data[8], data[9]); + horizontalPrintResolution = IntegerHelper.getInt(data[12], data[13]); + verticalPrintResolution = IntegerHelper.getInt(data[14], data[15]); + copies = IntegerHelper.getInt(data[32], data[33]); + + headerMargin = DoubleHelper.getIEEEDouble(data, 16); + footerMargin = DoubleHelper.getIEEEDouble(data, 24); + + + int grbit = IntegerHelper.getInt(data[10], data[11]); + pageOrder = ((grbit & 0x01) != 0); + portraitOrientation = ((grbit & 0x02) != 0); + initialized = ((grbit & 0x04) == 0); + } + + /** + * Accessor for the orientation. Called when copying sheets + * + * @return TRUE if the orientation is portrait, FALSE if it is landscape + */ + public boolean isPortrait() { + return portraitOrientation; + } + + + /** + * Accessor for the page order. Called when copying sheets + * + * @return TRUE if the page order is Left to Right, then Down, otherwise + * FALSE + */ + public boolean isRightDown() { + return pageOrder; + } + + /** + * Accessor for the header. Called when copying sheets + * + * @return the header margin + */ + public double getHeaderMargin() { + return headerMargin; + } + + /** + * Accessor for the footer. Called when copying sheets + * + * @return the footer margin + */ + public double getFooterMargin() { + return footerMargin; + } + + /** + * Accessor for the paper size. Called when copying sheets + * + * @return the footer margin + */ + public int getPaperSize() { + return paperSize; + } + + /** + * Accessor for the scale factor. Called when copying sheets + * + * @return the scale factor + */ + public int getScaleFactor() { + return scaleFactor; + } + + /** + * Accessor for the page height. called when copying sheets + * + * @return the page to start printing at + */ + public int getPageStart() { + return pageStart; + } + + /** + * Accessor for the fit width. Called when copying sheets + * + * @return the fit width + */ + public int getFitWidth() { + return fitWidth; + } + + /** + * Accessor for the fit height. Called when copying sheets + * + * @return the fit height + */ + public int getFitHeight() { + return fitHeight; + } + + /** + * The horizontal print resolution. Called when copying sheets + * + * @return the horizontal print resolution + */ + public int getHorizontalPrintResolution() { + return horizontalPrintResolution; + } + + /** + * Accessor for the vertical print resolution. Called when copying sheets + * + * @return an vertical print resolution + */ + public int getVerticalPrintResolution() { + return verticalPrintResolution; + } + + /** + * Accessor for the number of copies + * + * @return the number of copies + */ + public int getCopies() { + return copies; + } + + /** + * Accessor for the initialized flag + * + * @return whether the print page setup should be initialized in the dialog + * box + */ + public boolean getInitialized() { + return initialized; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java new file mode 100755 index 0000000..dd4c84d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedBooleanFormulaRecord.java @@ -0,0 +1,146 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.BooleanCell; +import jxl.BooleanFormulaCell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A shared boolean formula record + */ +public class SharedBooleanFormulaRecord extends BaseSharedFormulaRecord + implements BooleanCell, FormulaData, BooleanFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedBooleanFormulaRecord.class); + + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private final boolean value; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedBooleanFormulaRecord(Record t, + File excelFile, + boolean v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + data[6] = (byte) 1; + data[8] = (byte) (value == true ? 1 : 0); + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java new file mode 100755 index 0000000..7bcc522 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedDateFormulaRecord.java @@ -0,0 +1,182 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DateFormat; +import java.util.Date; +import jxl.CellType; +import jxl.DateCell; +import jxl.DateFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedDateFormulaRecord extends BaseSharedFormulaRecord + implements DateCell, FormulaData, DateFormulaCell { + /** + * Re-use the date record to handle all the formatting information and + * date calculations + */ + private final DateRecord dateRecord; + + /** + * The double value + */ + private final double value; + + /** + * Constructs this number formula + * + * @param nfr the number formula records + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @param si the sheet + * @param pos the position + */ + public SharedDateFormulaRecord(SharedNumberFormulaRecord nfr, + FormattingRecords fr, + boolean nf, + SheetImpl si, + int pos) { + super(nfr.getRecord(), + fr, + nfr.getExternalSheet(), + nfr.getNameTable(), + si, + pos); + dateRecord = new DateRecord(nfr, nfr.getXFIndex(), fr, nf, si); + value = nfr.getValue(); + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return dateRecord.getContents(); + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.DATE_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the date + * + * @return the date + */ + public Date getDate() { + return dateRecord.getDate(); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time + * + * @return TRUE if the value refers to a time + */ + public boolean isTime() { + return dateRecord.isTime(); + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + return dateRecord.getDateFormat(); + } + +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java new file mode 100755 index 0000000..4429656 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedErrorFormulaRecord.java @@ -0,0 +1,168 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.ErrorCell; +import jxl.ErrorFormulaCell; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaErrorCode; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedErrorFormulaRecord extends BaseSharedFormulaRecord + implements ErrorCell, FormulaData, ErrorFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedErrorFormulaRecord.class); + + /** + * The error code of this cell + */ + private final int errorCode; + + /** + * The raw data + */ + private byte[] data; + + /** + * The error code + */ + private FormulaErrorCode error; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the errorCode + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedErrorFormulaRecord(Record t, + File excelFile, + int ec, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + errorCode = ec; + } + + /** + * Interface method which gets the error code for this cell. If this cell + * does not represent an error, then it returns 0. Always use the + * method isError() to determine this prior to calling this method + * + * @return the error code if this cell contains an error, 0 otherwise + */ + public int getErrorCode() { + return errorCode; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + if (error == null) { + error = FormulaErrorCode.getErrorCode(errorCode); + } + + return error != FormulaErrorCode.UNKNOWN ? + error.getDescription() : "ERROR " + errorCode; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.FORMULA_ERROR; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + data[6] = (byte) 0x02; // indicates this cell is an error value + data[8] = (byte) errorCode; + data[12] = (byte) 0xff; + data[13] = (byte) 0xff; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java new file mode 100755 index 0000000..03cdfe9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedFormulaRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.NumberFormat; +import java.util.ArrayList; +import jxl.Cell; +import jxl.CellType; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Logger; + +/** + * A shared formula + */ +class SharedFormulaRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SharedFormulaRecord.class); + + /** + * The first row to which this shared formula applies + */ + private final int firstRow; + + /** + * The last row to which this shared formula applies + */ + private final int lastRow; + + /** + * The first column to which this shared formula applies + */ + private final int firstCol; + + /** + * The last column to which this shared formula applies + */ + private final int lastCol; + + /** + * The first (template) formula comprising this group + */ + private BaseSharedFormulaRecord templateFormula; + + /** + * The rest of the cells comprising this shared formula + */ + private final ArrayList formulas; + + /** + * The token data + */ + private final byte[] tokens; + + /** + * A handle to the external sheet + */ + private ExternalSheet externalSheet; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + + /** + * Constructs this object from the raw data. Creates either a + * NumberFormulaRecord or a StringFormulaRecord depending on whether + * this formula represents a numerical calculation or not + * + * @param t the raw data + * @param fr the base shared formula + * @param es the workbook, which contains the external sheet references + * @param nt the workbook + * @param si the sheet + */ + public SharedFormulaRecord(Record t, BaseSharedFormulaRecord fr, + ExternalSheet es, WorkbookMethods nt, + SheetImpl si) { + sheet = si; + byte[] data = t.getData(); + + firstRow = IntegerHelper.getInt(data[0], data[1]); + lastRow = IntegerHelper.getInt(data[2], data[3]); + firstCol = data[4] & 0xff; + lastCol = data[5] & 0xff; + + formulas = new ArrayList(); + + templateFormula = fr; + + tokens = new byte[data.length - 10]; + System.arraycopy(data, 10, tokens, 0, tokens.length); + } + + /** + * Adds this formula to the list of formulas, if it falls within + * the bounds + * + * @param fr the formula record to test for membership of this group + * @return TRUE if the formulas was added, FALSE otherwise + */ + public boolean add(BaseSharedFormulaRecord fr) { + boolean added = false; + int r = fr.getRow(); + if (r >= firstRow && r <= lastRow) { + int c = fr.getColumn(); + if (c >= firstCol && c <= lastCol) { + formulas.add(fr); + added = true; + } + } + + + return added; + } + + /** + * Manufactures individual cell formulas out the whole shared formula + * debacle + * + * @param fr the formatting records + * @param nf flag indicating whether this uses the 1904 date system + * @return an array of formulas to be added to the sheet + */ + Cell[] getFormulas(FormattingRecords fr, boolean nf) { + Cell[] sfs = new Cell[formulas.size() + 1]; + + // This can happen if there are many identical formulas in the + // sheet and excel has not sliced and diced them exclusively + if (templateFormula == null) { + logger.warn("Shared formula template formula is null"); + return new Cell[0]; + } + + templateFormula.setTokens(tokens); + NumberFormat templateNumberFormat = null; + + // See if the template formula evaluates to date + if (templateFormula.getType() == CellType.NUMBER_FORMULA) { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) + templateFormula; + templateNumberFormat = snfr.getNumberFormat(); + + if (fr.isDate(templateFormula.getXFIndex())) { + templateFormula = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + templateFormula.setTokens(snfr.getTokens()); + } + } + + sfs[0] = templateFormula; + + BaseSharedFormulaRecord f = null; + + for (int i = 0; i < formulas.size(); i++) { + f = (BaseSharedFormulaRecord) formulas.get(i); + + // See if the formula evaluates to date + if (f.getType() == CellType.NUMBER_FORMULA) { + SharedNumberFormulaRecord snfr = (SharedNumberFormulaRecord) f; + + if (fr.isDate(f.getXFIndex())) { + f = new SharedDateFormulaRecord(snfr, fr, nf, sheet, + snfr.getFilePos()); + } else { + // snfr.setNumberFormat(templateNumberFormat); + } + } + + f.setTokens(tokens); + sfs[i + 1] = f; + } + + return sfs; + } + + /** + * Accessor for the template formula. Called when a shared formula has, + * for some reason, specified an inappropriate range and it is necessary + * to retrieve the template from a previously available shared formula + */ + BaseSharedFormulaRecord getTemplateFormula() { + return templateFormula; + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java new file mode 100755 index 0000000..14a8499 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedNumberFormulaRecord.java @@ -0,0 +1,187 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.NumberFormulaCell; +import jxl.biff.DoubleHelper; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Logger; + +/** + * A number formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedNumberFormulaRecord extends BaseSharedFormulaRecord + implements NumberCell, FormulaData, NumberFormulaCell { + /** + * The logger + */ + private static final Logger logger = + Logger.getLogger(SharedNumberFormulaRecord.class); + /** + * The string format for the double value + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The value of this number + */ + private final double value; + /** + * The cell format + */ + private NumberFormat format; + /** + * A handle to the formatting records + */ + private FormattingRecords formattingRecords; + + /** + * Constructs this number + * + * @param t the data + * @param excelFile the excel biff data + * @param v the value + * @param fr the formatting records + * @param es the external sheet + * @param nt the name table + * @param si the sheet + */ + public SharedNumberFormulaRecord(Record t, + File excelFile, + double v, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, es, nt, si, excelFile.getPos()); + value = v; + format = defaultFormat; // format is set up later from the + // SharedFormulaRecord + } + + /** + * Accessor for the value + * + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return !Double.isNaN(value) ? format.format(value) : ""; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.NUMBER_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + DoubleHelper.getIEEEBytes(value, data, 6); + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return format; + } + + /** + * Sets the format for the number based on the Excel spreadsheets' format. + * This is called from SheetImpl when it has been definitely established + * that this cell is a number and not a date + * + * @param f the format + */ + final void setNumberFormat(NumberFormat f) { + if (f != null) { + format = f; + } + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java new file mode 100755 index 0000000..2b13945 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SharedStringFormulaRecord.java @@ -0,0 +1,241 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A string formula record, manufactured out of the Shared Formula + * "optimization" + */ +public class SharedStringFormulaRecord extends BaseSharedFormulaRecord + implements LabelCell, FormulaData, StringFormulaCell { + protected static final EmptyString EMPTY_STRING = new EmptyString(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger + (SharedStringFormulaRecord.class); + /** + * The value of this string formula + */ + private final String value; + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param ws the workbook settings + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, es, nt, si, excelFile.getPos()); + int pos = excelFile.getPos(); + + // Save the position in the excel file + int filepos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent ednas + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + + int chars = IntegerHelper.getInt(stringData[0], stringData[1]); + + boolean unicode = false; + int startpos = 3; + if (stringData.length == chars + 2) { + // String might only consist of a one byte length indicator, instead + // of the more normal 2 + startpos = 2; + unicode = false; + } else if (stringData[2] == 0x1) { + // unicode string, two byte length indicator + startpos = 3; + unicode = true; + } else { + // ascii string, two byte length indicator + startpos = 3; + unicode = false; + } + + if (!unicode) { + value = StringHelper.getString(stringData, chars, startpos, ws); + } else { + value = StringHelper.getUnicodeString(stringData, chars, startpos); + } + + // Restore the position in the excel file, to enable the SHRFMLA + // record to be picked up + excelFile.setPos(filepos); + } + + /** + * Constructs this string formula + * + * @param t the record + * @param excelFile the excel file + * @param fr the formatting record + * @param es the external sheet + * @param nt the workbook + * @param si the sheet + * @param dummy the overload indicator + */ + public SharedStringFormulaRecord(Record t, + File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + EmptyString dummy) { + super(t, fr, es, nt, si, excelFile.getPos()); + + value = ""; + } + + /** + * Accessor for the value + * + * @return the value + */ + public String getString() { + return value; + } + + /** + * Accessor for the contents as a string + * + * @return the value as a string + */ + public String getContents() { + return value; + } + + /** + * Accessor for the cell type + * + * @return the cell type + */ + public CellType getType() { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array. Used when copying spreadsheets + * + * @return the raw record data + * @throws FormulaException + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Get the tokens, taking into account the mapping from shared + // formula specific values into normal values + FormulaParser fp = new FormulaParser + (getTokens(), this, + getExternalSheet(), getNameTable(), + getSheet().getWorkbook().getSettings()); + fp.parse(); + byte[] rpnTokens = fp.getBytes(); + + byte[] data = new byte[rpnTokens.length + 22]; + + // Set the standard info for this cell + IntegerHelper.getTwoBytes(getRow(), data, 0); + IntegerHelper.getTwoBytes(getColumn(), data, 2); + IntegerHelper.getTwoBytes(getXFIndex(), data, 4); + + // Set the two most significant bytes of the value to be 0xff in + // order to identify this as a string + data[6] = 0; + data[12] = -1; + data[13] = -1; + + // Now copy in the parsed tokens + System.arraycopy(rpnTokens, 0, data, 22, rpnTokens.length); + IntegerHelper.getTwoBytes(rpnTokens.length, data, 20); + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + // Dummy value for overloading the constructor when the string evaluates + // to null + private static final class EmptyString { + } +} + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java b/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java new file mode 100755 index 0000000..9ba897d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SheetImpl.java @@ -0,0 +1,1137 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Pattern; +import jxl.Cell; +import jxl.CellView; +import jxl.Hyperlink; +import jxl.Image; +import jxl.LabelCell; +import jxl.Range; +import jxl.Sheet; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.BuiltInName; +import jxl.biff.CellFinder; +import jxl.biff.CellReferenceHelper; +import jxl.biff.ConditionalFormat; +import jxl.biff.DataValidation; +import jxl.biff.EmptyCell; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingGroupObject; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * Represents a sheet within a workbook. Provides a handle to the individual + * cells, or lines of cells (grouped by Row or Column) + * In order to simplify this class due to code bloat, the actual reading + * logic has been delegated to the SheetReaderClass. This class' main + * responsibility is now to implement the API methods declared in the + * Sheet interface + */ +public class SheetImpl implements Sheet { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetImpl.class); + + /** + * The excel file + */ + private final File excelFile; + /** + * A handle to the shared string table + */ + private final SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private final BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private final BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * The name of this sheet + */ + private String name; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * The start position in the stream of this sheet + */ + private int startPosition; + + /** + * The list of specified (ie. non default) column widths + */ + private ColumnInfoRecord[] columnInfos; + + /** + * The array of row records + */ + private RowRecord[] rowRecords; + + /** + * The list of non-default row properties + */ + private ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private final ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private ArrayList hyperlinks; + + /** + * A list of charts on this page + */ + private ArrayList charts; + + /** + * A list of drawings on this page + */ + private ArrayList drawings; + + /** + * A list of drawings (as opposed to comments/validation/charts) on this + * page + */ + private ArrayList images; + + /** + * A list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * Indicates whether the columnInfos array has been initialized + */ + private boolean columnInfosInitialized; + + /** + * Indicates whether the rowRecords array has been initialized + */ + private boolean rowRecordsInitialized; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private final boolean nineteenFour; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The hidden flag + */ + private boolean hidden; + + /** + * The environment specific print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The sheet settings + */ + private SheetSettings settings; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The list of local names for this sheet + */ + private ArrayList localNames; + + /** + * The list of conditional formats for this sheet + */ + private ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private final WorkbookParser workbook; + + /** + * A handle to the workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param f the excel file + * @param sst the shared string table + * @param fr formatting records + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param nf the 1904 flag + * @param wp the workbook which this sheet belongs to + * @throws BiffException + */ + SheetImpl(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp) + throws BiffException { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + rowProperties = new ArrayList(10); + columnInfosInitialized = false; + rowRecordsInitialized = false; + nineteenFour = nf; + workbook = wp; + workbookSettings = workbook.getSettings(); + + // Mark the position in the stream, and then skip on until the end + startPosition = f.getPos(); + + if (sheetBof.isChart()) { + // Set the start pos to include the bof so the sheet reader can handle it + startPosition -= (sheetBof.getLength() + 4); + } + + Record r = null; + int bofs = 1; + + while (bofs >= 1) { + r = f.next(); + + // use this form for quick performance + if (r.getCode() == Type.EOF.value) { + bofs--; + } + + if (r.getCode() == Type.BOF.value) { + bofs++; + } + } + } + + /** + * Returns the cell for the specified location eg. "A4", using the + * CellReferenceHelper + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + public Cell getCell(String loc) { + return getCell(CellReferenceHelper.getColumn(loc), + CellReferenceHelper.getRow(loc)); + } + + /** + * Returns the cell specified at this row and at this column + * + * @param row the row number + * @param column the column number + * @return the cell at the specified co-ordinates + */ + public Cell getCell(int column, int row) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + Cell c = cells[row][column]; + + if (c == null) { + c = new EmptyCell(column, row); + cells[row][column] = c; + } + + return c; + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public Cell findCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param contents the string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastCol the last column within the range + * @param lastRow the last row within the range + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(String contents, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(contents, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the regular expressionstring passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform + * + * @param pattern the regular expression string to match + * @param firstCol the first column within the range + * @param firstRow the first row of the range + * @param lastRow the last row within the range + * @param lastCol the last column within the ranage + * @param reverse indicates whether to perform a reverse search or not + * @return the Cell whose contents match the parameter, null if not found + */ + public Cell findCell(Pattern pattern, + int firstCol, + int firstRow, + int lastCol, + int lastRow, + boolean reverse) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findCell(pattern, + firstCol, + firstRow, + lastCol, + lastRow, + reverse); + } + + /** + * Gets the cell whose contents match the string passed in. + * If no match is found, then null is returned. The search is performed + * on a row by row basis, so the lower the row number, the more + * efficiently the algorithm will perform. This method differs + * from the findCell methods in that only cells with labels are + * queried - all numerical cells are ignored. This should therefore + * improve performance. + * + * @param contents the string to match + * @return the Cell whose contents match the paramter, null if not found + */ + public LabelCell findLabelCell(String contents) { + CellFinder cellFinder = new CellFinder(this); + return cellFinder.findLabelCell(contents); + } + + /** + * Returns the number of rows in this sheet + * + * @return the number of rows in this sheet + */ + public int getRows() { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + return numRows; + } + + /** + * Returns the number of columns in this sheet + * + * @return the number of columns in this sheet + */ + public int getColumns() { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + return numCols; + } + + /** + * Gets all the cells on the specified row. The returned array will + * be stripped of all trailing empty cells + * + * @param row the rows whose cells are to be returned + * @return the cells on the given row + */ + public Cell[] getRow(int row) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int col = numCols - 1; + while (col >= 0 && !found) { + if (cells[row][col] != null) { + found = true; + } else { + col--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[col + 1]; + + for (int i = 0; i <= col; i++) { + c[i] = getCell(i, row); + } + return c; + } + + /** + * Gets all the cells on the specified column. The returned array + * will be stripped of all trailing empty cells + * + * @param col the column whose cells are to be returned + * @return the cells on the specified column + */ + public Cell[] getColumn(int col) { + // just in case this has been cleared, but something else holds + // a reference to it + if (cells == null) { + readSheet(); + } + + // Find the last non-null cell + boolean found = false; + int row = numRows - 1; + while (row >= 0 && !found) { + if (cells[row][col] != null) { + found = true; + } else { + row--; + } + } + + // Only create entries for non-null cells + Cell[] c = new Cell[row + 1]; + + for (int i = 0; i <= row; i++) { + c[i] = getCell(col, i); + } + return c; + } + + /** + * Gets the name of this sheet + * + * @return the name of the sheet + */ + public String getName() { + return name; + } + + /** + * Sets the name of this sheet + * + * @param s the sheet name + */ + final void setName(String s) { + name = s; + } + + /** + * Determines whether the sheet is hidden + * + * @return whether or not the sheet is hidden + * @deprecated in favour of the getSettings function + */ + public boolean isHidden() { + return hidden; + } + + /** + * Sets the visibility of this sheet + * + * @param h hidden flag + */ + final void setHidden(boolean h) { + hidden = h; + } + + /** + * Gets the column info record for the specified column. If no + * column is specified, null is returned + * + * @param col the column + * @return the ColumnInfoRecord if specified, NULL otherwise + */ + public ColumnInfoRecord getColumnInfo(int col) { + if (!columnInfosInitialized) { + // Initialize the array + Iterator i = columnInfosArray.iterator(); + ColumnInfoRecord cir = null; + while (i.hasNext()) { + cir = (ColumnInfoRecord) i.next(); + + int startcol = Math.max(0, cir.getStartColumn()); + int endcol = Math.min(columnInfos.length - 1, cir.getEndColumn()); + + for (int c = startcol; c <= endcol; c++) { + columnInfos[c] = cir; + } + + if (endcol < startcol) { + columnInfos[startcol] = cir; + } + } + + columnInfosInitialized = true; + } + + return col < columnInfos.length ? columnInfos[col] : null; + } + + /** + * Gets all the column info records + * + * @return the ColumnInfoRecordArray + */ + public ColumnInfoRecord[] getColumnInfos() { + // Just chuck all the column infos we have into an array + ColumnInfoRecord[] infos = new ColumnInfoRecord[columnInfosArray.size()]; + for (int i = 0; i < columnInfosArray.size(); i++) { + infos[i] = (ColumnInfoRecord) columnInfosArray.get(i); + } + + return infos; + } + + /** + * Clears out the array of cells. This is done for memory allocation + * reasons when reading very large sheets + */ + final void clear() { + cells = null; + mergedCells = null; + columnInfosArray.clear(); + sharedFormulas.clear(); + hyperlinks.clear(); + columnInfosInitialized = false; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Reads in the contents of this sheet + */ + final void readSheet() { + // If this sheet contains only a chart, then set everything to + // empty and do not bother parsing the sheet + // Thanks to steve.brophy for spotting this + if (!sheetBof.isWorksheet()) { + numRows = 0; + numCols = 0; + cells = new Cell[0][0]; + // return; + } + + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + + // Take stuff that was read in + numRows = reader.getNumRows(); + numCols = reader.getNumCols(); + cells = reader.getCells(); + rowProperties = reader.getRowProperties(); + columnInfosArray = reader.getColumnInfosArray(); + hyperlinks = reader.getHyperlinks(); + conditionalFormats = reader.getConditionalFormats(); + autoFilter = reader.getAutoFilter(); + charts = reader.getCharts(); + drawings = reader.getDrawings(); + dataValidation = reader.getDataValidation(); + mergedCells = reader.getMergedCells(); + settings = reader.getSettings(); + settings.setHidden(hidden); + rowBreaks = reader.getRowBreaks(); + columnBreaks = reader.getColumnBreaks(); + workspaceOptions = reader.getWorkspaceOptions(); + plsRecord = reader.getPLS(); + buttonPropertySet = reader.getButtonPropertySet(); + maxRowOutlineLevel = reader.getMaxRowOutlineLevel(); + maxColumnOutlineLevel = reader.getMaxColumnOutlineLevel(); + + reader = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + + if (columnInfosArray.size() > 0) { + ColumnInfoRecord cir = (ColumnInfoRecord) + columnInfosArray.get(columnInfosArray.size() - 1); + columnInfos = new ColumnInfoRecord[cir.getEndColumn() + 1]; + } else { + columnInfos = new ColumnInfoRecord[0]; + } + + // Add any local names + if (localNames != null) { + for (Iterator it = localNames.iterator(); it.hasNext(); ) { + NameRecord nr = (NameRecord) it.next(); + if (nr.getBuiltInName() == BuiltInName.PRINT_AREA) { + if (nr.getRanges().length > 0) { + NameRecord.NameRange rng = nr.getRanges()[0]; + settings.setPrintArea(rng.getFirstColumn(), + rng.getFirstRow(), + rng.getLastColumn(), + rng.getLastRow()); + } + } else if (nr.getBuiltInName() == BuiltInName.PRINT_TITLES) { + // There can be 1 or 2 entries. + // Row entries have hardwired column entries (first and last + // possible column) + // Column entries have hardwired row entries (first and last + // possible row) + for (int i = 0; i < nr.getRanges().length; i++) { + NameRecord.NameRange rng = nr.getRanges()[i]; + if (rng.getFirstColumn() == 0 && rng.getLastColumn() == 255) { + settings.setPrintTitlesRow(rng.getFirstRow(), + rng.getLastRow()); + } else { + settings.setPrintTitlesCol(rng.getFirstColumn(), + rng.getLastColumn()); + } + } + } + } + } + } + + /** + * Gets the hyperlinks on this sheet + * + * @return an array of hyperlinks + */ + public Hyperlink[] getHyperlinks() { + Hyperlink[] hl = new Hyperlink[hyperlinks.size()]; + + for (int i = 0; i < hyperlinks.size(); i++) { + hl[i] = (Hyperlink) hyperlinks.get(i); + } + + return hl; + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + public Range[] getMergedCells() { + if (mergedCells == null) { + return new Range[0]; + } + + return mergedCells; + } + + /** + * Gets the non-default rows. Used when copying spreadsheets + * + * @return an array of row properties + */ + public RowRecord[] getRowProperties() { + RowRecord[] rp = new RowRecord[rowProperties.size()]; + for (int i = 0; i < rp.length; i++) { + rp[i] = (RowRecord) rowProperties.get(i); + } + + return rp; + } + + /** + * Gets the data validations. Used when copying sheets + * + * @return the data validations + */ + public DataValidation getDataValidation() { + return dataValidation; + } + + /** + * Gets the row record. Usually called by the cell in the specified + * row in order to determine its size + * + * @param r the row + * @return the RowRecord for the specified row + */ + RowRecord getRowInfo(int r) { + if (!rowRecordsInitialized) { + rowRecords = new RowRecord[getRows()]; + Iterator i = rowProperties.iterator(); + + int rownum = 0; + RowRecord rr = null; + while (i.hasNext()) { + rr = (RowRecord) i.next(); + rownum = rr.getRowNumber(); + if (rownum < rowRecords.length) { + rowRecords[rownum] = rr; + } + } + + rowRecordsInitialized = true; + } + + return r < rowRecords.length ? rowRecords[r] : null; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getRowPageBreaks() { + return rowBreaks; + } + + /** + * Gets the row breaks. Called when copying sheets + * + * @return the explicit row breaks + */ + public final int[] getColumnPageBreaks() { + return columnBreaks; + } + + /** + * Gets the charts. Called when copying sheets + * + * @return the charts on this page + */ + public final Chart[] getCharts() { + Chart[] ch = new Chart[charts.size()]; + + for (int i = 0; i < ch.length; i++) { + ch[i] = (Chart) charts.get(i); + } + return ch; + } + + /** + * Gets the drawings. Called when copying sheets + * + * @return the drawings on this page + */ + public final DrawingGroupObject[] getDrawings() { + DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()]; + dr = (DrawingGroupObject[]) drawings.toArray(dr); + return dr; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + * @deprecated in favour of the getSettings() api + */ + public boolean isProtected() { + return settings.isProtected(); + } + + /** + * Gets the workspace options for this sheet. Called during the copy + * process + * + * @return the workspace options + */ + public WorkspaceInformationRecord getWorkspaceOptions() { + return workspaceOptions; + } + + /** + * Accessor for the sheet settings + * + * @return the settings for this sheet + */ + public SheetSettings getSettings() { + return settings; + } + + /** + * Accessor for the workbook. In addition to be being used by this package, + * it is also used during the importSheet process + * + * @return the workbook + */ + public WorkbookParser getWorkbook() { + return workbook; + } + + /** + * Gets the column format for the specified column + * + * @param col the column number + * @return the column format, or NULL if the column has no specific format + * @deprecated use getColumnView instead + */ + public CellFormat getColumnFormat(int col) { + CellView cv = getColumnView(col); + return cv.getFormat(); + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column width, or the default width if the column has no + * specified format + */ + public int getColumnWidth(int col) { + return getColumnView(col).getSize() / 256; + } + + /** + * Gets the column width for the specified column + * + * @param col the column number + * @return the column format, or the default format if no override is + * specified + */ + public CellView getColumnView(int col) { + ColumnInfoRecord cir = getColumnInfo(col); + CellView cv = new CellView(); + + if (cir != null) { + cv.setDimension(cir.getWidth() / 256); //deprecated + cv.setSize(cir.getWidth()); + cv.setHidden(cir.getHidden()); + cv.setFormat(formattingRecords.getXFRecord(cir.getXFIndex())); + } else { + cv.setDimension(settings.getDefaultColumnWidth()); //deprecated + cv.setSize(settings.getDefaultColumnWidth() * 256); + } + + return cv; + } + + /** + * Gets the row height for the specified column + * + * @param row the row number + * @return the row height, or the default height if the row has no + * specified format + * @deprecated use getRowView instead + */ + public int getRowHeight(int row) { + return getRowView(row).getDimension(); + } + + /** + * Gets the row view for the specified row + * + * @param row the row number + * @return the row format, or the default format if no override is + * specified + */ + public CellView getRowView(int row) { + RowRecord rr = getRowInfo(row); + + CellView cv = new CellView(); + + if (rr != null) { + cv.setDimension(rr.getRowHeight()); //deprecated + cv.setSize(rr.getRowHeight()); + cv.setHidden(rr.isCollapsed()); + if (rr.hasDefaultFormat()) { + cv.setFormat(formattingRecords.getXFRecord(rr.getXFIndex())); + } + } else { + cv.setDimension(settings.getDefaultRowHeight()); + cv.setSize(settings.getDefaultRowHeight()); //deprecated + } + + return cv; + } + + + /** + * Used when copying sheets in order to determine the type of this sheet + * + * @return the BOF Record + */ + public BOFRecord getSheetBof() { + return sheetBof; + } + + /** + * Used when copying sheets in order to determine the type of the containing + * workboook + * + * @return the workbook BOF Record + */ + public BOFRecord getWorkbookBof() { + return workbookBof; + } + + /** + * Accessor for the environment specific print record, invoked when + * copying sheets + * + * @return the environment specific print record + */ + public PLSRecord getPLS() { + return plsRecord; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + public int getNumberOfImages() { + if (images == null) { + initializeImages(); + } + + return images.size(); + } + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + public Image getDrawing(int i) { + if (images == null) { + initializeImages(); + } + + return (Image) images.get(i); + } + + /** + * Initializes the images + */ + private void initializeImages() { + if (images != null) { + return; + } + + images = new ArrayList(); + DrawingGroupObject[] dgos = getDrawings(); + + for (int i = 0; i < dgos.length; i++) { + if (dgos[i] instanceof Drawing) { + images.add(dgos[i]); + } + } + } + + /** + * Used by one of the demo programs for debugging purposes only + */ + public DrawingData getDrawingData() { + SheetReader reader = new SheetReader(excelFile, + sharedStrings, + formattingRecords, + sheetBof, + workbookBof, + nineteenFour, + workbook, + startPosition, + this); + reader.read(); + return reader.getDrawingData(); + } + + /** + * Adds a local name to this shate + * + * @param nr the local name to add + */ + void addLocalName(NameRecord nr) { + if (localNames == null) { + localNames = new ArrayList(); + } + + localNames.add(nr); + } + + /** + * Gets the conditional formats + * + * @return the conditional formats + */ + public ConditionalFormat[] getConditionalFormats() { + ConditionalFormat[] formats = + new ConditionalFormat[conditionalFormats.size()]; + formats = (ConditionalFormat[]) conditionalFormats.toArray(formats); + return formats; + } + + /** + * Returns the autofilter + * + * @return the autofilter + */ + public AutoFilter getAutoFilter() { + return autoFilter; + } + + /** + * Accessor for the maximum column outline level. Used during a copy + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level. Used during a copy + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java b/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java new file mode 100755 index 0000000..fc1e353 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SheetReader.java @@ -0,0 +1,1628 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.DateCell; +import jxl.HeaderFooter; +import jxl.Range; +import jxl.SheetSettings; +import jxl.WorkbookSettings; +import jxl.biff.AutoFilter; +import jxl.biff.AutoFilterInfoRecord; +import jxl.biff.AutoFilterRecord; +import jxl.biff.ConditionalFormat; +import jxl.biff.ConditionalFormatRangeRecord; +import jxl.biff.ConditionalFormatRecord; +import jxl.biff.ContinueRecord; +import jxl.biff.DataValidation; +import jxl.biff.DataValidityListRecord; +import jxl.biff.DataValiditySettingsRecord; +import jxl.biff.FilterModeRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.Type; +import jxl.biff.WorkspaceInformationRecord; +import jxl.biff.drawing.Button; +import jxl.biff.drawing.Chart; +import jxl.biff.drawing.CheckBox; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.Drawing2; +import jxl.biff.drawing.DrawingData; +import jxl.biff.drawing.DrawingDataException; +import jxl.biff.drawing.MsoDrawingRecord; +import jxl.biff.drawing.NoteRecord; +import jxl.biff.drawing.ObjRecord; +import jxl.biff.drawing.TextObjectRecord; +import jxl.biff.formula.FormulaException; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.PageOrder; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; + +/** + * Reads the sheet. This functionality was originally part of the + * SheetImpl class, but was separated out in order to simplify the former + * class + */ +final class SheetReader { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SheetReader.class); + + /** + * The excel file + */ + private final File excelFile; + + /** + * A handle to the shared string table + */ + private final SSTRecord sharedStrings; + + /** + * A handle to the sheet BOF record, which indicates the stream type + */ + private final BOFRecord sheetBof; + + /** + * A handle to the workbook BOF record, which indicates the stream type + */ + private final BOFRecord workbookBof; + + /** + * A handle to the formatting records + */ + private final FormattingRecords formattingRecords; + + /** + * The number of rows + */ + private int numRows; + + /** + * The number of columns + */ + private int numCols; + + /** + * The cells + */ + private Cell[][] cells; + + /** + * Any cells which are out of the defined bounds + */ + private final ArrayList outOfBoundsCells; + + /** + * The start position in the stream of this sheet + */ + private final int startPosition; + + /** + * The list of non-default row properties + */ + private final ArrayList rowProperties; + + /** + * An array of column info records. They are held this way before + * they are transferred to the more convenient array + */ + private final ArrayList columnInfosArray; + + /** + * A list of shared formula groups + */ + private final ArrayList sharedFormulas; + + /** + * A list of hyperlinks on this page + */ + private final ArrayList hyperlinks; + + /** + * The list of conditional formats on this page + */ + private final ArrayList conditionalFormats; + + /** + * The autofilter information + */ + private AutoFilter autoFilter; + + /** + * A list of merged cells on this page + */ + private Range[] mergedCells; + + /** + * The list of data validations on this page + */ + private DataValidation dataValidation; + + /** + * The list of charts on this page + */ + private final ArrayList charts; + + /** + * The list of drawings on this page + */ + private final ArrayList drawings; + + /** + * The drawing data for the drawings + */ + private DrawingData drawingData; + + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private final boolean nineteenFour; + + /** + * The PLS print record + */ + private PLSRecord plsRecord; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * The workspace options + */ + private WorkspaceInformationRecord workspaceOptions; + + /** + * The horizontal page breaks contained on this sheet + */ + private int[] rowBreaks; + + /** + * The vertical page breaks contained on this sheet + */ + private int[] columnBreaks; + + /** + * The maximum row outline level + */ + private int maxRowOutlineLevel; + + /** + * The maximum column outline level + */ + private int maxColumnOutlineLevel; + + /** + * The sheet settings + */ + private final SheetSettings settings; + + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * A handle to the workbook which contains this sheet. Some of the records + * need this in order to reference external sheets + */ + private final WorkbookParser workbook; + + /** + * A handle to the sheet + */ + private final SheetImpl sheet; + + /** + * Constructor + * + * @param fr the formatting records + * @param sst the shared string table + * @param f the excel file + * @param sb the bof record which indicates the start of the sheet + * @param wb the bof record which indicates the start of the sheet + * @param wp the workbook which this sheet belongs to + * @param sp the start position of the sheet bof in the excel file + * @param sh the sheet + * @param nf 1904 date record flag + * @throws BiffException + */ + SheetReader(File f, + SSTRecord sst, + FormattingRecords fr, + BOFRecord sb, + BOFRecord wb, + boolean nf, + WorkbookParser wp, + int sp, + SheetImpl sh) { + excelFile = f; + sharedStrings = sst; + formattingRecords = fr; + sheetBof = sb; + workbookBof = wb; + columnInfosArray = new ArrayList(); + sharedFormulas = new ArrayList(); + hyperlinks = new ArrayList(); + conditionalFormats = new ArrayList(); + rowProperties = new ArrayList(10); + charts = new ArrayList(); + drawings = new ArrayList(); + outOfBoundsCells = new ArrayList(); + nineteenFour = nf; + workbook = wp; + startPosition = sp; + sheet = sh; + settings = new SheetSettings(sh); + workbookSettings = workbook.getSettings(); + } + + /** + * Adds the cell to the array + * + * @param cell the cell to add + */ + private void addCell(Cell cell) { + // Sometimes multiple cells (eg. MULBLANK) can exceed the + // column/row boundaries. Ignore these + if (cell.getRow() < numRows && cell.getColumn() < numCols) { + if (cells[cell.getRow()][cell.getColumn()] != null) { + StringBuffer sb = new StringBuffer(); + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow(), sb); + logger.warn("Cell " + sb + + " already contains data"); + } + cells[cell.getRow()][cell.getColumn()] = cell; + } else { + outOfBoundsCells.add(cell); + /* + logger.warn("Cell " + + CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + + " exceeds defined cell boundaries in Dimension record " + + "(" + numCols + "x" + numRows + ")"); + */ + } + } + + /** + * Reads in the contents of this sheet + */ + final void read() { + Record r = null; + BaseSharedFormulaRecord sharedFormula = null; + boolean sharedFormulaAdded = false; + + boolean cont = true; + + // Set the position within the file + excelFile.setPos(startPosition); + + // Handles to the last drawing and obj records + MsoDrawingRecord msoRecord = null; + ObjRecord objRecord = null; + boolean firstMsoRecord = true; + + // Handle to the last conditional format record + ConditionalFormat condFormat = null; + + // Handle to the autofilter records + FilterModeRecord filterMode = null; + AutoFilterInfoRecord autoFilterInfo = null; + + // A handle to window2 record + Window2Record window2Record = null; + + // A handle to printgridlines record + PrintGridLinesRecord printGridLinesRecord = null; + + // A handle to printheaders record + PrintHeadersRecord printHeadersRecord = null; + + // Hash map of comments, indexed on objectId. As each corresponding + // note record is encountered, these are removed from the array + HashMap comments = new HashMap(); + + // A list of object ids - used for cross referencing + ArrayList objectIds = new ArrayList(); + + // A handle to a continue record read in + ContinueRecord continueRecord = null; + + while (cont) { + r = excelFile.next(); + Type type = r.getType(); + + if (type == Type.UNKNOWN && r.getCode() == 0) { + logger.warn("Biff code zero found"); + + // Try a dimension record + if (r.getLength() == 0xa) { + logger.warn("Biff code zero found - trying a dimension record."); + r.setType(Type.DIMENSION); + } else { + logger.warn("Biff code zero found - Ignoring."); + } + } + + if (type == Type.DIMENSION) { + DimensionRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new DimensionRecord(r); + } else { + dr = new DimensionRecord(r, DimensionRecord.biff7); + } + numRows = dr.getNumberOfRows(); + numCols = dr.getNumberOfColumns(); + cells = new Cell[numRows][numCols]; + } else if (type == Type.LABELSST) { + LabelSSTRecord label = new LabelSSTRecord(r, + sharedStrings, + formattingRecords, + sheet); + addCell(label); + } else if (type == Type.RK || type == Type.RK2) { + RKRecord rkr = new RKRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(rkr.getXFIndex())) { + DateCell dc = new DateRecord + (rkr, rkr.getXFIndex(), formattingRecords, nineteenFour, sheet); + addCell(dc); + } else { + addCell(rkr); + } + } else if (type == Type.HLINK) { + HyperlinkRecord hr = new HyperlinkRecord(r, sheet, workbookSettings); + hyperlinks.add(hr); + } else if (type == Type.MERGEDCELLS) { + MergedCellsRecord mc = new MergedCellsRecord(r, sheet); + if (mergedCells == null) { + mergedCells = mc.getRanges(); + } else { + Range[] newMergedCells = + new Range[mergedCells.length + mc.getRanges().length]; + System.arraycopy(mergedCells, 0, newMergedCells, 0, + mergedCells.length); + System.arraycopy(mc.getRanges(), + 0, + newMergedCells, mergedCells.length, + mc.getRanges().length); + mergedCells = newMergedCells; + } + } else if (type == Type.MULRK) { + MulRKRecord mulrk = new MulRKRecord(r); + + // Get the individual cell records from the multiple record + int num = mulrk.getNumberOfColumns(); + int ixf = 0; + for (int i = 0; i < num; i++) { + ixf = mulrk.getXFIndex(i); + + NumberValue nv = new NumberValue + (mulrk.getRow(), + mulrk.getFirstColumn() + i, + RKHelper.getDouble(mulrk.getRKNumber(i)), + ixf, + formattingRecords, + sheet); + + + if (formattingRecords.isDate(ixf)) { + DateCell dc = new DateRecord(nv, + ixf, + formattingRecords, + nineteenFour, + sheet); + addCell(dc); + } else { + nv.setNumberFormat(formattingRecords.getNumberFormat(ixf)); + addCell(nv); + } + } + } else if (type == Type.NUMBER) { + NumberRecord nr = new NumberRecord(r, formattingRecords, sheet); + + if (formattingRecords.isDate(nr.getXFIndex())) { + DateCell dc = new DateRecord(nr, + nr.getXFIndex(), + formattingRecords, + nineteenFour, sheet); + addCell(dc); + } else { + addCell(nr); + } + } else if (type == Type.BOOLERR) { + BooleanRecord br = new BooleanRecord(r, formattingRecords, sheet); + + if (br.isError()) { + ErrorRecord er = new ErrorRecord(br.getRecord(), formattingRecords, + sheet); + addCell(er); + } else { + addCell(br); + } + } else if (type == Type.PRINTGRIDLINES) { + printGridLinesRecord = new PrintGridLinesRecord(r); + settings.setPrintGridLines(printGridLinesRecord.getPrintGridLines()); + } else if (type == Type.PRINTHEADERS) { + printHeadersRecord = new PrintHeadersRecord(r); + settings.setPrintHeaders(printHeadersRecord.getPrintHeaders()); + } else if (type == Type.WINDOW2) { + window2Record = null; + + if (workbookBof.isBiff8()) { + window2Record = new Window2Record(r); + } else { + window2Record = new Window2Record(r, Window2Record.biff7); + } + + settings.setShowGridLines(window2Record.getShowGridLines()); + settings.setDisplayZeroValues(window2Record.getDisplayZeroValues()); + settings.setSelected(true); + settings.setPageBreakPreviewMode(window2Record.isPageBreakPreview()); + } else if (type == Type.PANE) { + PaneRecord pr = new PaneRecord(r); + + if (window2Record != null && + window2Record.getFrozen()) { + settings.setVerticalFreeze(pr.getRowsVisible()); + settings.setHorizontalFreeze(pr.getColumnsVisible()); + } + } else if (type == Type.CONTINUE) { + // don't know what this is for, but keep hold of it anyway + continueRecord = new ContinueRecord(r); + } else if (type == Type.NOTE) { + if (!workbookSettings.getDrawingsDisabled()) { + NoteRecord nr = new NoteRecord(r); + + // Get the comment for the object id + Comment comment = (Comment) comments.remove + (new Integer(nr.getObjectId())); + + if (comment == null) { + logger.warn(" cannot find comment for note id " + + nr.getObjectId() + "...ignoring"); + } else { + comment.setNote(nr); + + drawings.add(comment); + + addCellComment(comment.getColumn(), + comment.getRow(), + comment.getText(), + comment.getWidth(), + comment.getHeight()); + } + } + } else if (type == Type.ARRAY) { + } else if (type == Type.PROTECT) { + ProtectRecord pr = new ProtectRecord(r); + settings.setProtected(pr.isProtected()); + } else if (type == Type.SHAREDFORMULA) { + if (sharedFormula == null) { + logger.warn("Shared template formula is null - " + + "trying most recent formula template"); + SharedFormulaRecord lastSharedFormula = + (SharedFormulaRecord) sharedFormulas.get(sharedFormulas.size() - 1); + + if (lastSharedFormula != null) { + sharedFormula = lastSharedFormula.getTemplateFormula(); + } + } + + SharedFormulaRecord sfr = new SharedFormulaRecord + (r, sharedFormula, workbook, workbook, sheet); + sharedFormulas.add(sfr); + sharedFormula = null; + } else if (type == Type.FORMULA || type == Type.FORMULA2) { + FormulaRecord fr = new FormulaRecord(r, + excelFile, + formattingRecords, + workbook, + workbook, + sheet, + workbookSettings); + + if (fr.isShared()) { + BaseSharedFormulaRecord prevSharedFormula = sharedFormula; + sharedFormula = (BaseSharedFormulaRecord) fr.getFormula(); + + // See if it fits in any of the shared formulas + sharedFormulaAdded = addToSharedFormulas(sharedFormula); + + if (sharedFormulaAdded) { + sharedFormula = prevSharedFormula; + } + + // If we still haven't added the previous base shared formula, + // revert it to an ordinary formula and add it to the cell + if (!sharedFormulaAdded && prevSharedFormula != null) { + // Do nothing. It's possible for the biff file to contain the + // record sequence + // FORMULA-SHRFMLA-FORMULA-SHRFMLA-FORMULA-FORMULA-FORMULA + // ie. it first lists all the formula templates, then it + // lists all the individual formulas + addCell(revertSharedFormula(prevSharedFormula)); + } + } else { + Cell cell = fr.getFormula(); + try { + // See if the formula evaluates to date + if (fr.getFormula().getType() == CellType.NUMBER_FORMULA) { + NumberFormulaRecord nfr = (NumberFormulaRecord) fr.getFormula(); + if (formattingRecords.isDate(nfr.getXFIndex())) { + cell = new DateFormulaRecord(nfr, + formattingRecords, + workbook, + workbook, + nineteenFour, + sheet); + } + } + + addCell(cell); + } catch (FormulaException e) { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference + (cell.getColumn(), cell.getRow()) + " " + e.getMessage()); + } + } + } else if (type == Type.LABEL) { + LabelRecord lr = null; + + if (workbookBof.isBiff8()) { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings); + } else { + lr = new LabelRecord(r, formattingRecords, sheet, workbookSettings, + LabelRecord.biff7); + } + addCell(lr); + } else if (type == Type.RSTRING) { + RStringRecord lr = null; + + // RString records are obsolete in biff 8 + Assert.verify(!workbookBof.isBiff8()); + lr = new RStringRecord(r, formattingRecords, + sheet, workbookSettings, + RStringRecord.biff7); + addCell(lr); + } else if (type == Type.NAME) { + } else if (type == Type.PASSWORD) { + PasswordRecord pr = new PasswordRecord(r); + settings.setPasswordHash(pr.getPasswordHash()); + } else if (type == Type.ROW) { + RowRecord rr = new RowRecord(r); + + // See if the row has anything funny about it + if (!rr.isDefaultHeight() || + !rr.matchesDefaultFontHeight() || + rr.isCollapsed() || + rr.hasDefaultFormat() || + rr.getOutlineLevel() != 0) { + rowProperties.add(rr); + } + } else if (type == Type.BLANK) { + if (!workbookSettings.getIgnoreBlanks()) { + BlankCell bc = new BlankCell(r, formattingRecords, sheet); + addCell(bc); + } + } else if (type == Type.MULBLANK) { + if (!workbookSettings.getIgnoreBlanks()) { + MulBlankRecord mulblank = new MulBlankRecord(r); + + // Get the individual cell records from the multiple record + int num = mulblank.getNumberOfColumns(); + + for (int i = 0; i < num; i++) { + int ixf = mulblank.getXFIndex(i); + + MulBlankCell mbc = new MulBlankCell + (mulblank.getRow(), + mulblank.getFirstColumn() + i, + ixf, + formattingRecords, + sheet); + + addCell(mbc); + } + } + } else if (type == Type.SCL) { + SCLRecord scl = new SCLRecord(r); + settings.setZoomFactor(scl.getZoomFactor()); + } else if (type == Type.COLINFO) { + ColumnInfoRecord cir = new ColumnInfoRecord(r); + columnInfosArray.add(cir); + } else if (type == Type.HEADER) { + HeaderRecord hr = null; + if (workbookBof.isBiff8()) { + hr = new HeaderRecord(r, workbookSettings); + } else { + hr = new HeaderRecord(r, workbookSettings, HeaderRecord.biff7); + } + + HeaderFooter header = new HeaderFooter(hr.getHeader()); + settings.setHeader(header); + } else if (type == Type.FOOTER) { + FooterRecord fr = null; + if (workbookBof.isBiff8()) { + fr = new FooterRecord(r, workbookSettings); + } else { + fr = new FooterRecord(r, workbookSettings, FooterRecord.biff7); + } + + HeaderFooter footer = new HeaderFooter(fr.getFooter()); + settings.setFooter(footer); + } else if (type == Type.SETUP) { + SetupRecord sr = new SetupRecord(r); + + // If the setup record has its not initialized bit set, then + // use the sheet settings default values + if (sr.getInitialized()) { + if (sr.isPortrait()) { + settings.setOrientation(PageOrientation.PORTRAIT); + } else { + settings.setOrientation(PageOrientation.LANDSCAPE); + } + if (sr.isRightDown()) { + settings.setPageOrder(PageOrder.RIGHT_THEN_DOWN); + } else { + settings.setPageOrder(PageOrder.DOWN_THEN_RIGHT); + } + settings.setPaperSize(PaperSize.getPaperSize(sr.getPaperSize())); + settings.setHeaderMargin(sr.getHeaderMargin()); + settings.setFooterMargin(sr.getFooterMargin()); + settings.setScaleFactor(sr.getScaleFactor()); + settings.setPageStart(sr.getPageStart()); + settings.setFitWidth(sr.getFitWidth()); + settings.setFitHeight(sr.getFitHeight()); + settings.setHorizontalPrintResolution + (sr.getHorizontalPrintResolution()); + settings.setVerticalPrintResolution(sr.getVerticalPrintResolution()); + settings.setCopies(sr.getCopies()); + + if (workspaceOptions != null) { + settings.setFitToPages(workspaceOptions.getFitToPages()); + } + } + } else if (type == Type.WSBOOL) { + workspaceOptions = new WorkspaceInformationRecord(r); + } else if (type == Type.DEFCOLWIDTH) { + DefaultColumnWidthRecord dcwr = new DefaultColumnWidthRecord(r); + settings.setDefaultColumnWidth(dcwr.getWidth()); + } else if (type == Type.DEFAULTROWHEIGHT) { + DefaultRowHeightRecord drhr = new DefaultRowHeightRecord(r); + if (drhr.getHeight() != 0) { + settings.setDefaultRowHeight(drhr.getHeight()); + } + } else if (type == Type.CONDFMT) { + ConditionalFormatRangeRecord cfrr = + new ConditionalFormatRangeRecord(r); + condFormat = new ConditionalFormat(cfrr); + conditionalFormats.add(condFormat); + } else if (type == Type.CF) { + ConditionalFormatRecord cfr = new ConditionalFormatRecord(r); + condFormat.addCondition(cfr); + } else if (type == Type.FILTERMODE) { + filterMode = new FilterModeRecord(r); + } else if (type == Type.AUTOFILTERINFO) { + autoFilterInfo = new AutoFilterInfoRecord(r); + } else if (type == Type.AUTOFILTER) { + if (!workbookSettings.getAutoFilterDisabled()) { + AutoFilterRecord af = new AutoFilterRecord(r); + + if (autoFilter == null) { + autoFilter = new AutoFilter(filterMode, autoFilterInfo); + filterMode = null; + autoFilterInfo = null; + } + + autoFilter.add(af); + } + } else if (type == Type.LEFTMARGIN) { + MarginRecord m = new LeftMarginRecord(r); + settings.setLeftMargin(m.getMargin()); + } else if (type == Type.RIGHTMARGIN) { + MarginRecord m = new RightMarginRecord(r); + settings.setRightMargin(m.getMargin()); + } else if (type == Type.TOPMARGIN) { + MarginRecord m = new TopMarginRecord(r); + settings.setTopMargin(m.getMargin()); + } else if (type == Type.BOTTOMMARGIN) { + MarginRecord m = new BottomMarginRecord(r); + settings.setBottomMargin(m.getMargin()); + } else if (type == Type.HORIZONTALPAGEBREAKS) { + HorizontalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new HorizontalPageBreaksRecord(r); + } else { + dr = new HorizontalPageBreaksRecord + (r, HorizontalPageBreaksRecord.biff7); + } + rowBreaks = dr.getRowBreaks(); + } else if (type == Type.VERTICALPAGEBREAKS) { + VerticalPageBreaksRecord dr = null; + + if (workbookBof.isBiff8()) { + dr = new VerticalPageBreaksRecord(r); + } else { + dr = new VerticalPageBreaksRecord + (r, VerticalPageBreaksRecord.biff7); + } + columnBreaks = dr.getColumnBreaks(); + } else if (type == Type.PLS) { + plsRecord = new PLSRecord(r); + + // Check for Continue records + while (excelFile.peek().getType() == Type.CONTINUE) { + r.addContinueRecord(excelFile.next()); + } + } else if (type == Type.DVAL) { + if (!workbookSettings.getCellValidationDisabled()) { + DataValidityListRecord dvlr = new DataValidityListRecord(r); + if (dvlr.getObjectId() == -1) { + if (msoRecord != null && objRecord == null) { + // there is a drop down associated with this data validation + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Drawing2 d2 = new Drawing2(msoRecord, drawingData, + workbook.getDrawingGroup()); + drawings.add(d2); + msoRecord = null; + + dataValidation = new DataValidation(dvlr); + } else { + // no drop down + dataValidation = new DataValidation(dvlr); + } + } else if (objectIds.contains(new Integer(dvlr.getObjectId()))) { + dataValidation = new DataValidation(dvlr); + } else { + logger.warn("object id " + dvlr.getObjectId() + " referenced " + + " by data validity list record not found - ignoring"); + } + } + } else if (type == Type.HCENTER) { + CentreRecord hr = new CentreRecord(r); + settings.setHorizontalCentre(hr.isCentre()); + } else if (type == Type.VCENTER) { + CentreRecord vc = new CentreRecord(r); + settings.setVerticalCentre(vc.isCentre()); + } else if (type == Type.DV) { + if (!workbookSettings.getCellValidationDisabled()) { + DataValiditySettingsRecord dvsr = + new DataValiditySettingsRecord(r, + workbook, + workbook, + workbook.getSettings()); + if (dataValidation != null) { + dataValidation.add(dvsr); + addCellValidation(dvsr.getFirstColumn(), + dvsr.getFirstRow(), + dvsr.getLastColumn(), + dvsr.getLastRow(), + dvsr); + } else { + logger.warn("cannot add data validity settings"); + } + } + } else if (type == Type.OBJ) { + objRecord = new ObjRecord(r); + + if (!workbookSettings.getDrawingsDisabled()) { + // sometimes excel writes out continue records instead of drawing + // records, so forcibly hack the stashed continue record into + // a drawing record + if (msoRecord == null && continueRecord != null) { + logger.warn("Cannot find drawing record - using continue record"); + msoRecord = new MsoDrawingRecord(continueRecord.getRecord()); + continueRecord = null; + } + handleObjectRecord(objRecord, msoRecord, comments); + objectIds.add(new Integer(objRecord.getObjectId())); + } + + // Save chart handling until the chart BOF record appears + if (objRecord.getType() != ObjRecord.CHART) { + objRecord = null; + msoRecord = null; + } + } else if (type == Type.MSODRAWING) { + if (!workbookSettings.getDrawingsDisabled()) { + if (msoRecord != null) { + // For form controls, a rogue MSODRAWING record can crop up + // after the main one. Add these into the drawing data + drawingData.addRawData(msoRecord.getData()); + } + msoRecord = new MsoDrawingRecord(r); + + if (firstMsoRecord) { + msoRecord.setFirst(); + firstMsoRecord = false; + } + } + } else if (type == Type.BUTTONPROPERTYSET) { + buttonPropertySet = new ButtonPropertySetRecord(r); + } else if (type == Type.CALCMODE) { + CalcModeRecord cmr = new CalcModeRecord(r); + settings.setAutomaticFormulaCalculation(cmr.isAutomatic()); + } else if (type == Type.SAVERECALC) { + SaveRecalcRecord cmr = new SaveRecalcRecord(r); + settings.setRecalculateFormulasBeforeSave(cmr.getRecalculateOnSave()); + } else if (type == Type.GUTS) { + GuttersRecord gr = new GuttersRecord(r); + maxRowOutlineLevel = + gr.getRowOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + maxColumnOutlineLevel = + gr.getColumnOutlineLevel() > 0 ? gr.getRowOutlineLevel() - 1 : 0; + } else if (type == Type.BOF) { + BOFRecord br = new BOFRecord(r); + Assert.verify(!br.isWorksheet()); + + int startpos = excelFile.getPos() - r.getLength() - 4; + + // Skip to the end of the nested bof + // Thanks to Rohit for spotting this + Record r2 = excelFile.next(); + while (r2.getCode() != Type.EOF.value) { + r2 = excelFile.next(); + } + + if (br.isChart()) { + if (!workbook.getWorkbookBof().isBiff8()) { + logger.warn("only biff8 charts are supported"); + } else { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + if (!workbookSettings.getDrawingsDisabled()) { + Chart chart = new Chart(msoRecord, objRecord, drawingData, + startpos, excelFile.getPos(), + excelFile, workbookSettings); + charts.add(chart); + + if (workbook.getDrawingGroup() != null) { + workbook.getDrawingGroup().add(chart); + } + } + } + + // Reset the drawing records + msoRecord = null; + objRecord = null; + } + + // If this worksheet is just a chart, then the EOF reached + // represents the end of the sheet as well as the end of the chart + if (sheetBof.isChart()) { + cont = false; + } + } else if (type == Type.EOF) { + cont = false; + } + } + + // Restore the file to its accurate position + excelFile.restorePos(); + + // Add any out of bounds cells + if (outOfBoundsCells.size() > 0) { + handleOutOfBoundsCells(); + } + + // Add all the shared formulas to the sheet as individual formulas + Iterator i = sharedFormulas.iterator(); + + while (i.hasNext()) { + SharedFormulaRecord sfr = (SharedFormulaRecord) i.next(); + + Cell[] sfnr = sfr.getFormulas(formattingRecords, nineteenFour); + + for (int sf = 0; sf < sfnr.length; sf++) { + addCell(sfnr[sf]); + } + } + + // If the last base shared formula wasn't added to the sheet, then + // revert it to an ordinary formula and add it + if (!sharedFormulaAdded && sharedFormula != null) { + addCell(revertSharedFormula(sharedFormula)); + } + + // If there is a stray msoDrawing record, then flag to the drawing group + // that one has been omitted + if (msoRecord != null && workbook.getDrawingGroup() != null) { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, objRecord); + } + + // Check that the comments hash is empty + if (!comments.isEmpty()) { + logger.warn("Not all comments have a corresponding Note record"); + } + } + + /** + * Sees if the shared formula belongs to any of the shared formula + * groups + * + * @param fr the candidate shared formula + * @return TRUE if the formula was added, FALSE otherwise + */ + private boolean addToSharedFormulas(BaseSharedFormulaRecord fr) { + boolean added = false; + SharedFormulaRecord sfr = null; + + for (int i = 0, size = sharedFormulas.size(); i < size && !added; ++i) { + sfr = (SharedFormulaRecord) sharedFormulas.get(i); + added = sfr.add(fr); + } + + return added; + } + + /** + * Reverts the shared formula passed in to an ordinary formula and adds + * it to the list + * + * @param f the formula + * @return the new formula + * @throws FormulaException + */ + private Cell revertSharedFormula(BaseSharedFormulaRecord f) { + // String formulas look for a STRING record soon after the formula + // occurred. Temporarily the position in the excel file back + // to the point immediately after the formula record + int pos = excelFile.getPos(); + excelFile.setPos(f.getFilePos()); + + FormulaRecord fr = new FormulaRecord(f.getRecord(), + excelFile, + formattingRecords, + workbook, + workbook, + FormulaRecord.ignoreSharedFormula, + sheet, + workbookSettings); + + try { + Cell cell = fr.getFormula(); + + // See if the formula evaluates to date + if (fr.getFormula().getType() == CellType.NUMBER_FORMULA) { + NumberFormulaRecord nfr = (NumberFormulaRecord) fr.getFormula(); + if (formattingRecords.isDate(fr.getXFIndex())) { + cell = new DateFormulaRecord(nfr, + formattingRecords, + workbook, + workbook, + nineteenFour, + sheet); + } + } + + excelFile.setPos(pos); + return cell; + } catch (FormulaException e) { + // Something has gone wrong trying to read the formula data eg. it + // might be unsupported biff7 data + logger.warn + (CellReferenceHelper.getCellReference(fr.getColumn(), fr.getRow()) + + " " + e.getMessage()); + + return null; + } + } + + + /** + * Accessor + * + * @return the number of rows + */ + final int getNumRows() { + return numRows; + } + + /** + * Accessor + * + * @return the number of columns + */ + final int getNumCols() { + return numCols; + } + + /** + * Accessor + * + * @return the cells + */ + final Cell[][] getCells() { + return cells; + } + + /** + * Accessor + * + * @return the row properties + */ + final ArrayList getRowProperties() { + return rowProperties; + } + + /** + * Accessor + * + * @return the column information + */ + final ArrayList getColumnInfosArray() { + return columnInfosArray; + } + + /** + * Accessor + * + * @return the hyperlinks + */ + final ArrayList getHyperlinks() { + return hyperlinks; + } + + /** + * Accessor + * + * @return the conditional formatting + */ + final ArrayList getConditionalFormats() { + return conditionalFormats; + } + + /** + * Accessor + * + * @return the autofilter + */ + final AutoFilter getAutoFilter() { + return autoFilter; + } + + /** + * Accessor + * + * @return the charts + */ + final ArrayList getCharts() { + return charts; + } + + /** + * Accessor + * + * @return the drawings + */ + final ArrayList getDrawings() { + return drawings; + } + + /** + * Accessor + * + * @return the data validations + */ + final DataValidation getDataValidation() { + return dataValidation; + } + + /** + * Accessor + * + * @return the ranges + */ + final Range[] getMergedCells() { + return mergedCells; + } + + /** + * Accessor + * + * @return the sheet settings + */ + final SheetSettings getSettings() { + return settings; + } + + /** + * Accessor + * + * @return the row breaks + */ + final int[] getRowBreaks() { + return rowBreaks; + } + + /** + * Accessor + * + * @return the column breaks + */ + final int[] getColumnBreaks() { + return columnBreaks; + } + + /** + * Accessor + * + * @return the workspace options + */ + final WorkspaceInformationRecord getWorkspaceOptions() { + return workspaceOptions; + } + + /** + * Accessor + * + * @return the environment specific print record + */ + final PLSRecord getPLS() { + return plsRecord; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + final ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Adds a cell comment to a cell just read in + * + * @param col the column for the comment + * @param row the row for the comment + * @param text the comment text + * @param width the width of the comment text box + * @param height the height of the comment text box + */ + private void addCellComment(int col, + int row, + String text, + double width, + double height) { + Cell c = cells[row][col]; + if (c == null) { + logger.warn("Cell at " + CellReferenceHelper.getCellReference(col, row) + + " not present - adding a blank"); + MulBlankCell mbc = new MulBlankCell(row, + col, + 0, + formattingRecords, + sheet); + CellFeatures cf = new CellFeatures(); + cf.setReadComment(text, width, height); + mbc.setCellFeatures(cf); + addCell(mbc); + + return; + } + + if (c instanceof CellFeaturesAccessor) { + CellFeaturesAccessor cv = (CellFeaturesAccessor) c; + CellFeatures cf = cv.getCellFeatures(); + + if (cf == null) { + cf = new CellFeatures(); + cv.setCellFeatures(cf); + } + + cf.setReadComment(text, width, height); + } else { + logger.warn("Not able to add comment to cell type " + + c.getClass().getName() + + " at " + CellReferenceHelper.getCellReference(col, row)); + } + } + + /** + * Adds a cell comment to a cell just read in + * + * @param col1 the column for the comment + * @param row1 the row for the comment + * @param col2 the row for the comment + * @param row2 the row for the comment + * @param dvsr the validation settings + */ + private void addCellValidation(int col1, + int row1, + int col2, + int row2, + DataValiditySettingsRecord dvsr) { + for (int row = row1; row <= row2; row++) { + for (int col = col1; col <= col2; col++) { + Cell c = null; + + if (cells.length > row && cells[row].length > col) { + c = cells[row][col]; + } + + if (c == null) { + MulBlankCell mbc = new MulBlankCell(row, + col, + 0, + formattingRecords, + sheet); + CellFeatures cf = new CellFeatures(); + cf.setValidationSettings(dvsr); + mbc.setCellFeatures(cf); + addCell(mbc); + } else if (c instanceof CellFeaturesAccessor) { // Check to see if the cell already contains a comment + CellFeaturesAccessor cv = (CellFeaturesAccessor) c; + CellFeatures cf = cv.getCellFeatures(); + + if (cf == null) { + cf = new CellFeatures(); + cv.setCellFeatures(cf); + } + + cf.setValidationSettings(dvsr); + } else { + logger.warn("Not able to add comment to cell type " + + c.getClass().getName() + + " at " + CellReferenceHelper.getCellReference(col, row)); + } + } + } + } + + /** + * Reads in the object record + * + * @param objRecord the obj record + * @param msoRecord the mso drawing record read in earlier + * @param comments the hash map of comments + */ + private void handleObjectRecord(ObjRecord objRecord, + MsoDrawingRecord msoRecord, + HashMap comments) { + if (msoRecord == null) { + logger.warn("Object record is not associated with a drawing " + + " record - ignoring"); + return; + } + + try { + // Handle images + if (objRecord.getType() == ObjRecord.PICTURE) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Drawing drawing = new Drawing(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + sheet); + drawings.add(drawing); + return; + } + + // Handle comments + if (objRecord.getType() == ObjRecord.EXCELNOTE) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Comment comment = new Comment(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + // Sometimes Excel writes out Continue records instead of drawing + // records, so forcibly hack all of these into a drawing record + Record r2 = excelFile.next(); + if (r2.getType() == Type.MSODRAWING || r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + comment.addMso(mso); + r2 = excelFile.next(); + } + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + comment.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + comment.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + comment.setFormatting(formatting); + } + + comments.put(new Integer(comment.getObjectId()), comment); + return; + } + + // Handle combo boxes + if (objRecord.getType() == ObjRecord.COMBOBOX) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + ComboBox comboBox = new ComboBox(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + drawings.add(comboBox); + return; + } + + // Handle check boxes + if (objRecord.getType() == ObjRecord.CHECKBOX) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + CheckBox checkBox = new CheckBox(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + checkBox.addMso(mso); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + checkBox.setTextObject(txo); + + if (txo.getTextLength() == 0) { + return; + } + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + checkBox.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + checkBox.setFormatting(formatting); + } + + drawings.add(checkBox); + + return; + } + + // Handle form buttons + if (objRecord.getType() == ObjRecord.BUTTON) { + if (drawingData == null) { + drawingData = new DrawingData(); + } + + Button button = new Button(msoRecord, + objRecord, + drawingData, + workbook.getDrawingGroup(), + workbookSettings); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + button.addMso(mso); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + TextObjectRecord txo = new TextObjectRecord(r2); + button.setTextObject(txo); + + r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.CONTINUE); + ContinueRecord text = new ContinueRecord(r2); + button.setText(text); + + r2 = excelFile.next(); + if (r2.getType() == Type.CONTINUE) { + ContinueRecord formatting = new ContinueRecord(r2); + button.setFormatting(formatting); + } + + drawings.add(button); + + return; + } + + // Non-supported types which have multiple record types + if (objRecord.getType() == ObjRecord.TEXT) { + logger.warn(objRecord.getType() + " Object on sheet \"" + + sheet.getName() + + "\" not supported - omitting"); + + // Still need to add the drawing data to preserve the hierarchy + if (drawingData == null) { + drawingData = new DrawingData(); + + } + drawingData.addData(msoRecord.getData()); + + Record r2 = excelFile.next(); + Assert.verify(r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE); + if (r2.getType() == Type.MSODRAWING || + r2.getType() == Type.CONTINUE) { + MsoDrawingRecord mso = new MsoDrawingRecord(r2); + drawingData.addRawData(mso.getData()); + r2 = excelFile.next(); + } + + Assert.verify(r2.getType() == Type.TXO); + + if (workbook.getDrawingGroup() != null) // can be null for Excel 95 + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, + objRecord); + } + + return; + } + + // Handle other types + if (objRecord.getType() != ObjRecord.CHART) { + logger.warn(objRecord.getType() + " Object on sheet \"" + + sheet.getName() + + "\" not supported - omitting"); + + // Still need to add the drawing data to preserve the hierarchy + if (drawingData == null) { + drawingData = new DrawingData(); + } + + drawingData.addData(msoRecord.getData()); + + if (workbook.getDrawingGroup() != null) // can be null for Excel 95 + { + workbook.getDrawingGroup().setDrawingsOmitted(msoRecord, + objRecord); + } + + return; + } + } catch (DrawingDataException e) { + logger.warn(e.getMessage() + + "...disabling drawings for the remainder of the workbook"); + workbookSettings.setDrawingsDisabled(true); + } + } + + /** + * Gets the drawing data - for use as part of the Escher debugging tool + */ + DrawingData getDrawingData() { + return drawingData; + } + + /** + * Handle any cells which fall outside of the bounds specified within + * the dimension record + */ + private void handleOutOfBoundsCells() { + int resizedRows = numRows; + int resizedCols = numCols; + + // First, determine the new bounds + for (Iterator i = outOfBoundsCells.iterator(); i.hasNext(); ) { + Cell cell = (Cell) i.next(); + resizedRows = Math.max(resizedRows, cell.getRow() + 1); + resizedCols = Math.max(resizedCols, cell.getColumn() + 1); + } + + // There used to be a warning here about exceeding the sheet dimensions, + // but removed it when I added the ability to perform data validation + // on entire rows or columns - in which case it would blow out any + // existing dimensions + + // Resize the columns, if necessary + if (resizedCols > numCols) { + for (int r = 0; r < numRows; r++) { + Cell[] newRow = new Cell[resizedCols]; + Cell[] oldRow = cells[r]; + System.arraycopy(oldRow, 0, newRow, 0, oldRow.length); + cells[r] = newRow; + } + } + + // Resize the rows, if necessary + if (resizedRows > numRows) { + Cell[][] newCells = new Cell[resizedRows][]; + System.arraycopy(cells, 0, newCells, 0, cells.length); + cells = newCells; + + // Create the new rows + for (int i = numRows; i < resizedRows; i++) { + newCells[i] = new Cell[resizedCols]; + } + } + + numRows = resizedRows; + numCols = resizedCols; + + // Now add all the out of bounds cells into the new cells + for (Iterator i = outOfBoundsCells.iterator(); i.hasNext(); ) { + Cell cell = (Cell) i.next(); + addCell(cell); + } + + outOfBoundsCells.clear(); + } + + /** + * Accessor for the maximum column outline level + * + * @return the maximum column outline level, or 0 if no outlines/groups + */ + public int getMaxColumnOutlineLevel() { + return maxColumnOutlineLevel; + } + + /** + * Accessor for the maximum row outline level + * + * @return the maximum row outline level, or 0 if no outlines/groups + */ + public int getMaxRowOutlineLevel() { + return maxRowOutlineLevel; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java new file mode 100755 index 0000000..5bded55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SortRecord.java @@ -0,0 +1,170 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan, Al Mantei + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.biff.Type; + +/** + * A storage area for the last Sort dialog box area + */ +public class SortRecord extends RecordData { + private final int col1Size; + private final int col2Size; + private final int col3Size; + private final String col1Name; + private final String col2Name; + private final String col3Name; + private final byte optionFlags; + private boolean sortColumns = false; + private boolean sortKey1Desc = false; + private boolean sortKey2Desc = false; + private boolean sortKey3Desc = false; + private boolean sortCaseSensitive = false; + + /** + * Constructs this object from the raw data + * + * @param r the raw data + */ + public SortRecord(Record r) { + super(Type.SORT); + + byte[] data = r.getData(); + + optionFlags = data[0]; + + sortColumns = ((optionFlags & 0x01) != 0); + sortKey1Desc = ((optionFlags & 0x02) != 0); + sortKey2Desc = ((optionFlags & 0x04) != 0); + sortKey3Desc = ((optionFlags & 0x08) != 0); + sortCaseSensitive = ((optionFlags & 0x10) != 0); + + // data[1] contains sort list index - not implemented... + + col1Size = data[2]; + col2Size = data[3]; + col3Size = data[4]; + int curPos = 5; + if (data[curPos++] == 0x00) { + col1Name = new String(data, curPos, col1Size); + curPos += col1Size; + } else { + col1Name = StringHelper.getUnicodeString(data, col1Size, curPos); + curPos += col1Size * 2; + } + + if (col2Size > 0) { + if (data[curPos++] == 0x00) { + col2Name = new String(data, curPos, col2Size); + curPos += col2Size; + } else { + col2Name = StringHelper.getUnicodeString(data, col2Size, curPos); + curPos += col2Size * 2; + } + } else { + col2Name = ""; + } + if (col3Size > 0) { + if (data[curPos++] == 0x00) { + col3Name = new String(data, curPos, col3Size); + curPos += col3Size; + } else { + col3Name = StringHelper.getUnicodeString(data, col3Size, curPos); + curPos += col3Size * 2; + } + } else { + col3Name = ""; + } + } + + /** + * Accessor for the 1st Sort Column Name + * + * @return the 1st Sort Column Name + */ + public String getSortCol1Name() { + return col1Name; + } + + /** + * Accessor for the 2nd Sort Column Name + * + * @return the 2nd Sort Column Name + */ + public String getSortCol2Name() { + return col2Name; + } + + /** + * Accessor for the 3rd Sort Column Name + * + * @return the 3rd Sort Column Name + */ + public String getSortCol3Name() { + return col3Name; + } + + /** + * Accessor for the Sort by Columns flag + * + * @return the Sort by Columns flag + */ + public boolean getSortColumns() { + return sortColumns; + } + + /** + * Accessor for the Sort Column 1 Descending flag + * + * @return the Sort Column 1 Descending flag + */ + public boolean getSortKey1Desc() { + return sortKey1Desc; + } + + /** + * Accessor for the Sort Column 2 Descending flag + * + * @return the Sort Column 2 Descending flag + */ + public boolean getSortKey2Desc() { + return sortKey2Desc; + } + + /** + * Accessor for the Sort Column 3 Descending flag + * + * @return the Sort Column 3 Descending flag + */ + public boolean getSortKey3Desc() { + return sortKey3Desc; + } + + /** + * Accessor for the Sort Case Sensitivity flag + * + * @return the Sort Case Secsitivity flag + */ + public boolean getSortCaseSensitive() { + return sortCaseSensitive; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java new file mode 100755 index 0000000..6158a3e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/StringFormulaRecord.java @@ -0,0 +1,268 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.StringFormulaCell; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * A string formula's last calculated value + */ +class StringFormulaRecord extends CellValue + implements LabelCell, FormulaData, StringFormulaCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(StringFormulaRecord.class); + + /** + * The last calculated value of the formula + */ + private String value; + + /** + * A handle to the class needed to access external sheets + */ + private final ExternalSheet externalSheet; + + /** + * A handle to the name table + */ + private final WorkbookMethods nameTable; + + /** + * The formula as an excel string + */ + private String formulaString; + + /** + * The raw data + */ + private final byte[] data; + + /** + * Constructs this object from the raw data. We need to use the excelFile + * to retrieve the String record which follows this formula record + * + * @param t the raw data + * @param excelFile the excel file + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, File excelFile, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si, + WorkbookSettings ws) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + + int pos = excelFile.getPos(); + + // Look for the string record in one of the records after the + // formula. Put a cap on it to prevent looping + + Record nextRecord = excelFile.next(); + int count = 0; + while (nextRecord.getType() != Type.STRING && count < 4) { + nextRecord = excelFile.next(); + count++; + } + Assert.verify(count < 4, " @ " + pos); + byte[] stringData = nextRecord.getData(); + + // Read in any continuation records + nextRecord = excelFile.peek(); + while (nextRecord.getType() == Type.CONTINUE) { + nextRecord = excelFile.next(); // move the pointer within the data + byte[] d = new byte[stringData.length + nextRecord.getLength() - 1]; + System.arraycopy(stringData, 0, d, 0, stringData.length); + System.arraycopy(nextRecord.getData(), 1, d, + stringData.length, nextRecord.getLength() - 1); + stringData = d; + nextRecord = excelFile.peek(); + } + readString(stringData, ws); + } + + /** + * Constructs this object from the raw data. Used when reading in formula + * strings which evaluate to null (in the case of some IF statements) + * + * @param t the raw data + * @param fr the formatting records + * @param es the external sheet records + * @param nt the workbook + * @param si the sheet impl + * @param ws the workbook settings + */ + public StringFormulaRecord(Record t, + FormattingRecords fr, + ExternalSheet es, + WorkbookMethods nt, + SheetImpl si) { + super(t, fr, si); + + externalSheet = es; + nameTable = nt; + + data = getRecord().getData(); + value = ""; + } + + + /** + * Reads in the string + * + * @param d the data + * @param ws the workbook settings + */ + private void readString(byte[] d, WorkbookSettings ws) { + int pos = 0; + int chars = IntegerHelper.getInt(d[0], d[1]); + + if (chars == 0) { + value = ""; + return; + } + pos += 2; + int optionFlags = d[pos]; + pos++; + + if ((optionFlags & 0xf) != optionFlags) { + // Uh oh - looks like a plain old string, not unicode + // Recalculate all the positions + pos = 0; + chars = IntegerHelper.getInt(d[0], (byte) 0); + optionFlags = d[1]; + pos = 2; + } + + // See if it is an extended string + boolean extendedString = ((optionFlags & 0x04) != 0); + + // See if string contains formatting information + boolean richString = ((optionFlags & 0x08) != 0); + + if (richString) { + pos += 2; + } + + if (extendedString) { + pos += 4; + } + + // See if string is ASCII (compressed) or unicode + boolean asciiEncoding = ((optionFlags & 0x01) == 0); + + if (asciiEncoding) { + value = StringHelper.getString(d, chars, pos, ws); + } else { + value = StringHelper.getUnicodeString(d, chars, pos); + } + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getContents() { + return value; + } + + /** + * Interface method which returns the value + * + * @return the last calculated value of the formula + */ + public String getString() { + return value; + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.STRING_FORMULA; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() throws FormulaException { + if (!getSheet().getWorkbook().getWorkbookBof().isBiff8()) { + throw new FormulaException(FormulaException.BIFF8_SUPPORTED); + } + + // Lop off the standard information + byte[] d = new byte[data.length - 6]; + System.arraycopy(data, 6, d, 0, data.length - 6); + + return d; + } + + /** + * Gets the formula as an excel string + * + * @return the formula as an excel string + * @throws FormulaException + */ + public String getFormula() throws FormulaException { + if (formulaString == null) { + byte[] tokens = new byte[data.length - 22]; + System.arraycopy(data, 22, tokens, 0, tokens.length); + FormulaParser fp = new FormulaParser + (tokens, this, externalSheet, nameTable, + getSheet().getWorkbook().getSettings()); + fp.parse(); + formulaString = fp.getFormula(); + } + + return formulaString; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java new file mode 100755 index 0000000..7136902 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/SupbookRecord.java @@ -0,0 +1,277 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.common.Logger; + +/** + * A record containing the references to the various sheets (internal and + * external) referenced by formulas in this workbook + */ +public class SupbookRecord extends RecordData { + public static final Type INTERNAL = new Type(); + public static final Type EXTERNAL = new Type(); + public static final Type ADDIN = new Type(); + public static final Type LINK = new Type(); + public static final Type UNKNOWN = new Type(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(SupbookRecord.class); + + /** + * The type of this supbook record + */ + private final Type type; + /** + * The number of sheets - internal & external supbooks only + */ + private int numSheets; + /** + * The name of the external file + */ + private String fileName; + /** + * The names of the external sheets + */ + private String[] sheetNames; + /** + * Constructs this object from the raw data + * + * @param t the raw data + * @param ws the workbook settings + */ + SupbookRecord(Record t, WorkbookSettings ws) { + super(t); + byte[] data = getRecord().getData(); + + // First deduce the type + if (data.length == 4) { + if (data[2] == 0x01 && data[3] == 0x04) { + type = INTERNAL; + } else if (data[2] == 0x01 && data[3] == 0x3a) { + type = ADDIN; + } else { + type = UNKNOWN; + } + } else if (data[0] == 0 && data[1] == 0) { + type = LINK; + } else { + type = EXTERNAL; + } + + if (type == INTERNAL) { + numSheets = IntegerHelper.getInt(data[0], data[1]); + } + + if (type == EXTERNAL) { + readExternal(data, ws); + } + } + + /** + * Reads the external data records + * + * @param data the data + * @param ws the workbook settings + */ + private void readExternal(byte[] data, WorkbookSettings ws) { + numSheets = IntegerHelper.getInt(data[0], data[1]); + + // subtract file name encoding from the length + int ln = IntegerHelper.getInt(data[2], data[3]) - 1; + int pos = 0; + + if (data[4] == 0) { + // non-unicode string + int encoding = data[5]; + pos = 6; + if (encoding == 0) { + fileName = StringHelper.getString(data, ln, pos, ws); + pos += ln; + } else { + fileName = getEncodedFilename(data, ln, pos); + pos += ln; + } + } else { + // unicode string + int encoding = IntegerHelper.getInt(data[5], data[6]); + pos = 7; + if (encoding == 0) { + fileName = StringHelper.getUnicodeString(data, ln, pos); + pos += ln * 2; + } else { + fileName = getUnicodeEncodedFilename(data, ln, pos); + pos += ln * 2; + } + } + + sheetNames = new String[numSheets]; + + for (int i = 0; i < sheetNames.length; i++) { + ln = IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (data[pos + 2] == 0x0) { + sheetNames[i] = StringHelper.getString(data, ln, pos + 3, ws); + pos += ln + 3; + } else if (data[pos + 2] == 0x1) { + sheetNames[i] = StringHelper.getUnicodeString(data, ln, pos + 3); + pos += ln * 2 + 3; + } + } + } + + /** + * Gets the type of this supbook record + * + * @return the type of this supbook + */ + public Type getType() { + return type; + } + + /** + * Gets the number of sheets. This will only be non-zero for internal + * and external supbooks + * + * @return the number of sheets + */ + public int getNumberOfSheets() { + return numSheets; + } + + /** + * Gets the name of the external file + * + * @return the name of the external file + */ + public String getFileName() { + return fileName; + } + + /** + * Gets the name of the external sheet + * + * @param i the index of the external sheet + * @return the name of the sheet + */ + public String getSheetName(int i) { + return sheetNames[i]; + } + + /** + * Gets the data - used when copying a spreadsheet + * + * @return the raw external sheet data + */ + public byte[] getData() { + return getRecord().getData(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getEncodedFilename(byte[] data, int ln, int pos) { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln; + while (pos < endpos) { + char c = (char) data[pos]; + + if (c == '\u0001') { + // next character is a volume letter + pos++; + c = (char) data[pos]; + buf.append(c); + buf.append(":\\\\"); + } else if (c == '\u0002') { + // file is on the same volume + buf.append('\\'); + } else if (c == '\u0003') { + // down directory + buf.append('\\'); + } else if (c == '\u0004') { + // up directory + buf.append("..\\"); + } else { + // just add on the character + buf.append(c); + } + + pos++; + } + + return buf.toString(); + } + + /** + * Gets the encoded string from the data array + * + * @param data the data + * @param ln length of the string + * @param pos the position in the array + * @return the string + */ + private String getUnicodeEncodedFilename(byte[] data, int ln, int pos) { + StringBuffer buf = new StringBuffer(); + int endpos = pos + ln * 2; + while (pos < endpos) { + char c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + + if (c == '\u0001') { + // next character is a volume letter + pos += 2; + c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]); + buf.append(c); + buf.append(":\\\\"); + } else if (c == '\u0002') { + // file is on the same volume + buf.append('\\'); + } else if (c == '\u0003') { + // down directory + buf.append('\\'); + } else if (c == '\u0004') { + // up directory + buf.append("..\\"); + } else { + // just add on the character + buf.append(c); + } + + pos += 2; + } + + return buf.toString(); + } + + /** + * The type of supbook this refers to + */ + private static class Type { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java new file mode 100755 index 0000000..ec5d4cd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/TemplateRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A template record + */ +class TemplateRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(TemplateRecord.class); + + /** + * The template + */ + private final boolean template; + + /** + * Constructor + * + * @param t the record + */ + public TemplateRecord(Record t) { + super(t); + template = true; + } + + /** + * Accessor for the template mode + * + * @return the template mode + */ + public boolean getTemplate() { + return template; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java new file mode 100755 index 0000000..efaae34 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/TopMarginRecord.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.Type; + +/** + * Record for the left margin settings + */ +class TopMarginRecord extends MarginRecord { + /** + * Constructor + * + * @param r the record + */ + TopMarginRecord(Record r) { + super(Type.TOPMARGIN, r); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java new file mode 100755 index 0000000..895dbfd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/VerticalPageBreaksRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class VerticalPageBreaksRecord extends RecordData { + public static Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private final Logger logger = Logger.getLogger + (VerticalPageBreaksRecord.class); + /** + * The row page breaks + */ + private final int[] columnBreaks; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public VerticalPageBreaksRecord(Record t) { + super(t); + + byte[] data = t.getData(); + + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + + for (int i = 0; i < numbreaks; i++) { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 6; + } + } + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + * @param biff7 an indicator to initialise this record for biff 7 format + */ + public VerticalPageBreaksRecord(Record t, Biff7 biff7) { + super(t); + + byte[] data = t.getData(); + int numbreaks = IntegerHelper.getInt(data[0], data[1]); + int pos = 2; + columnBreaks = new int[numbreaks]; + for (int i = 0; i < numbreaks; i++) { + columnBreaks[i] = IntegerHelper.getInt(data[pos], data[pos + 1]); + pos += 2; + } + } + + /** + * Gets the row breaks + * + * @return the row breaks on the current sheet + */ + public int[] getColumnBreaks() { + return columnBreaks; + } + + /** + * Dummy indicators for overloading the constructor + */ + private static class Biff7 { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java b/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java new file mode 100755 index 0000000..a21ab22 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/Window2Record.java @@ -0,0 +1,194 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * Contains the cell dimensions of this worksheet + */ +class Window2Record extends RecordData { + public static final Biff7 biff7 = new Biff7(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(Window2Record.class); + /** + * Selected flag + */ + private final boolean selected; + /** + * Show grid lines flag + */ + private final boolean showGridLines; + /** + * Display zero values flag + */ + private final boolean displayZeroValues; + /** + * The window contains frozen panes + */ + private final boolean frozenPanes; + /** + * The window contains panes that are frozen but not split + */ + private final boolean frozenNotSplit; + /** + * The view mode: normal or pagebreakpreview + */ + private final boolean pageBreakPreviewMode; + /** + * The page break preview magnification + */ + private int pageBreakPreviewMagnification; + /** + * The normal view magnification + */ + private int normalMagnification; + + /** + * Constructs the dimensions from the raw data + * + * @param t the raw data + */ + public Window2Record(Record t) { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + + pageBreakPreviewMagnification = IntegerHelper.getInt(data[10], data[11]); + normalMagnification = IntegerHelper.getInt(data[12], data[13]); + } + + /** + * Constructs the dimensions from the raw data. Dummy overload for + * biff7 workbooks + * + * @param t the raw data + * @param dummy the overload + */ + public Window2Record(Record t, Biff7 biff7) { + super(t); + byte[] data = t.getData(); + + int options = IntegerHelper.getInt(data[0], data[1]); + + selected = ((options & 0x200) != 0); + showGridLines = ((options & 0x02) != 0); + frozenPanes = ((options & 0x08) != 0); + displayZeroValues = ((options & 0x10) != 0); + frozenNotSplit = ((options & 0x100) != 0); + pageBreakPreviewMode = ((options & 0x800) != 0); + } + + /** + * Accessor for the selected flag + * + * @return TRUE if this sheet is selected, FALSE otherwise + */ + public boolean isSelected() { + return selected; + } + + /** + * Accessor for the show grid lines flag + * + * @return TRUE to show grid lines, FALSE otherwise + */ + public boolean getShowGridLines() { + return showGridLines; + } + + /** + * Accessor for the zero values flag + * + * @return TRUE if this sheet displays zero values, FALSE otherwise + */ + public boolean getDisplayZeroValues() { + return displayZeroValues; + } + + /** + * Accessor for the frozen panes flag + * + * @return TRUE if this contains frozen panes, FALSE otherwise + */ + public boolean getFrozen() { + return frozenPanes; + } + + /** + * Accessor for the frozen not split flag + * + * @return TRUE if this contains frozen, FALSE otherwise + */ + public boolean getFrozenNotSplit() { + return frozenNotSplit; + } + + /** + * Accessor for the page break preview mode + * + * @return TRUE if this sheet is in page break preview, FALSE otherwise + */ + public boolean isPageBreakPreview() { + return pageBreakPreviewMode; + } + + /** + * Accessor for the page break preview magnification + * + * @return the cached paged break preview magnfication factor in percent + */ + public int getPageBreakPreviewMagnificaiton() { + return pageBreakPreviewMagnification; + } + + /** + * Accessor for the normal view magnification + * + * @return the cached normal view magnfication factor in percent + */ + public int getNormalMagnificaiton() { + return normalMagnification; + } + + // Dummy overload + private static class Biff7 { + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java new file mode 100755 index 0000000..a2ed5d9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WindowProtectedRecord.java @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.RecordData; +import jxl.common.Logger; + +/** + * A windowProtected mode record + */ +class WindowProtectedRecord extends RecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WindowProtectedRecord.class); + + /** + * The windowProtected mode + */ + private final boolean windowProtected; + + /** + * Constructor + * + * @param t the record + */ + public WindowProtectedRecord(Record t) { + super(t); + byte[] data = t.getData(); + int mode = IntegerHelper.getInt(data[0], data[1]); + windowProtected = (mode == 1); + } + + /** + * Accessor for the windowProtected + * + * @return the windowProtected mode + */ + public boolean getWindowProtected() { + return windowProtected; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java b/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java new file mode 100755 index 0000000..924dc94 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WorkbookParser.java @@ -0,0 +1,1108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.Cell; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.WorkbookSettings; +import jxl.biff.BuiltInName; +import jxl.biff.CellReferenceHelper; +import jxl.biff.EmptyCell; +import jxl.biff.FontRecord; +import jxl.biff.Fonts; +import jxl.biff.FormatRecord; +import jxl.biff.FormattingRecords; +import jxl.biff.NameRangeException; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.PaletteRecord; +import jxl.biff.RangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.XCTRecord; +import jxl.biff.XFRecord; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.MsoDrawingGroupRecord; +import jxl.biff.drawing.Origin; +import jxl.biff.formula.ExternalSheet; +import jxl.common.Assert; +import jxl.common.Logger; + +/** + * Parses the biff file passed in, and builds up an internal representation of + * the spreadsheet + */ +public class WorkbookParser extends Workbook + implements ExternalSheet, WorkbookMethods { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(WorkbookParser.class); + + /** + * The excel file + */ + private final File excelFile; + /** + * The number of open bofs + */ + private int bofs; + /** + * Indicates whether or not the dates are based around the 1904 date system + */ + private boolean nineteenFour; + /** + * The shared string table + */ + private SSTRecord sharedStrings; + /** + * The names of all the worksheets + */ + private final ArrayList boundsheets; + /** + * The xf records + */ + private final FormattingRecords formattingRecords; + /** + * The fonts used by this workbook + */ + private final Fonts fonts; + + /** + * The sheets contained in this workbook + */ + private final ArrayList sheets; + + /** + * The last sheet accessed + */ + private SheetImpl lastSheet; + + /** + * The index of the last sheet retrieved + */ + private int lastSheetIndex; + + /** + * The named records found in this workbook + */ + private final HashMap namedRecords; + + /** + * The list of named records + */ + private ArrayList nameTable; + + /** + * The list of add in functions + */ + private ArrayList addInFunctions; + + /** + * The external sheet record. Used by formulas, and names + */ + private ExternalSheetRecord externSheet; + + /** + * The list of supporting workbooks - used by formulas + */ + private final ArrayList supbooks; + + /** + * The bof record for this workbook + */ + private BOFRecord workbookBof; + + /** + * The Mso Drawing Group record for this workbook + */ + private MsoDrawingGroupRecord msoDrawingGroup; + + /** + * The property set record associated with this workbook + */ + private ButtonPropertySetRecord buttonPropertySet; + + /** + * Workbook protected flag + */ + private boolean wbProtected; + + /** + * Contains macros flag + */ + private boolean containsMacros; + + /** + * The workbook settings + */ + private final WorkbookSettings settings; + + /** + * The drawings contained in this workbook + */ + private DrawingGroup drawingGroup; + + /** + * The country record (containing the language and regional settings) + * for this workbook + */ + private CountryRecord countryRecord; + + private final ArrayList xctRecords; + + /** + * Constructs this object from the raw excel data + * + * @param f the excel 97 biff file + * @param s the workbook settings + */ + public WorkbookParser(File f, WorkbookSettings s) { + super(); + excelFile = f; + boundsheets = new ArrayList(10); + fonts = new Fonts(); + formattingRecords = new FormattingRecords(fonts); + sheets = new ArrayList(10); + supbooks = new ArrayList(10); + namedRecords = new HashMap(); + lastSheetIndex = -1; + wbProtected = false; + containsMacros = false; + settings = s; + xctRecords = new ArrayList(10); + } + + /** + * Gets the sheets within this workbook. + * NOTE: Use of this method for + * very large worksheets can cause performance and out of memory problems. + * Use the alternative method getSheet() to retrieve each sheet individually + * + * @return an array of the individual sheets + */ + public Sheet[] getSheets() { + Sheet[] sheetArray = new Sheet[getNumberOfSheets()]; + return (Sheet[]) sheets.toArray(sheetArray); + } + + /** + * Interface method from WorkbookMethods - gets the specified + * sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getReadSheet(int index) { + return getSheet(index); + } + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the required sheet + * @return The sheet specified by the index + */ + public Sheet getSheet(int index) { + // First see if the last sheet index is the same as this sheet index. + // If so, then the same sheet is being re-requested, so simply + // return it instead of rereading it + if ((lastSheet != null) && lastSheetIndex == index) { + return lastSheet; + } + + // Flush out all of the cached data in the last sheet + if (lastSheet != null) { + lastSheet.clear(); + + if (!settings.getGCDisabled()) { + System.gc(); + } + } + + lastSheet = (SheetImpl) sheets.get(index); + lastSheetIndex = index; + lastSheet.readSheet(); + + return lastSheet; + } + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public Sheet getSheet(String name) { + // Iterate through the boundsheet records + int pos = 0; + boolean found = false; + Iterator i = boundsheets.iterator(); + BoundsheetRecord br = null; + + while (i.hasNext() && !found) { + br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) { + found = true; + } else { + pos++; + } + } + + return found ? getSheet(pos) : null; + } + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public String[] getSheetNames() { + String[] names = new String[boundsheets.size()]; + + BoundsheetRecord br = null; + for (int i = 0; i < names.length; i++) { + br = (BoundsheetRecord) boundsheets.get(i); + names[i] = br.getName(); + } + + return names; + } + + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getExternalSheetIndex(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + return index; + } + + Assert.verify(externSheet != null); + + int firstTab = externSheet.getFirstTabIndex(index); + + return firstTab; + } + + /** + * Package protected function which gets the real internal sheet index + * based upon the external sheet reference. This is used for extern sheet + * references which are specified in formulas + * + * @param index the external sheet reference + * @return the actual sheet index + */ + public int getLastExternalSheetIndex(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + return index; + } + + Assert.verify(externSheet != null); + + int lastTab = externSheet.getLastTabIndex(index); + + return lastTab; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getExternalSheetName(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int firstTab = externSheet.getFirstTabIndex(index); + int lastTab = externSheet.getLastTabIndex(index); + String firstTabName = ""; + String lastTabName = ""; + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the boundsheets list + if (firstTab == 65535) { + firstTabName = "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(firstTab); + firstTabName = br.getName(); + } + + if (lastTab == 65535) { + lastTabName = "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + lastTabName = br.getName(); + } + + String sheetName = (firstTab == lastTab) ? firstTabName : + firstTabName + ':' + lastTabName; + + // if the sheet name contains apostrophes then escape them + sheetName = sheetName.indexOf('\'') == -1 ? sheetName : + StringHelper.replace(sheetName, "'", "''"); + + + // if the sheet name contains spaces, then enclose in quotes + return sheetName.indexOf(' ') == -1 ? sheetName : + '\'' + sheetName + '\''; + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((firstTab == 65535) ? "#REF" : sr.getSheetName(firstTab)); + if (lastTab != firstTab) { + sb.append(sr.getSheetName(lastTab)); + } + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 3"); + return "[UNKNOWN]"; + } + + /** + * Gets the name of the external sheet specified by the index + * + * @param index the external sheet index + * @return the name of the external sheet + */ + public String getLastExternalSheetName(int index) { + // For biff7, the whole external reference thing works differently + // Hopefully for our purposes sheet references will all be local + if (workbookBof.isBiff7()) { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(index); + + return br.getName(); + } + + int supbookIndex = externSheet.getSupbookIndex(index); + SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex); + + int lastTab = externSheet.getLastTabIndex(index); + + if (sr.getType() == SupbookRecord.INTERNAL) { + // It's an internal reference - get the name from the boundsheets list + if (lastTab == 65535) { + return "#REF"; + } else { + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get(lastTab); + return br.getName(); + } + } else if (sr.getType() == SupbookRecord.EXTERNAL) { + // External reference - get the sheet name from the supbook record + StringBuffer sb = new StringBuffer(); + java.io.File fl = new java.io.File(sr.getFileName()); + sb.append("'"); + sb.append(fl.getAbsolutePath()); + sb.append("["); + sb.append(fl.getName()); + sb.append("]"); + sb.append((lastTab == 65535) ? "#REF" : sr.getSheetName(lastTab)); + sb.append("'"); + return sb.toString(); + } + + // An unknown supbook - return unkown + logger.warn("Unknown Supbook 4"); + return "[UNKNOWN]"; + } + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public int getNumberOfSheets() { + return sheets.size(); + } + + /** + * Closes this workbook, and frees makes any memory allocated available + * for garbage collection + */ + public void close() { + if (lastSheet != null) { + lastSheet.clear(); + } + excelFile.clear(); + + if (!settings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Adds the sheet to the end of the array + * + * @param s the sheet to add + */ + final void addSheet(Sheet s) { + sheets.add(s); + } + + /** + * Does the hard work of building up the object graph from the excel bytes + * + * @throws BiffException + * @throws PasswordException if the workbook is password protected + */ + protected void parse() throws BiffException, PasswordException { + Record r = null; + + BOFRecord bof = new BOFRecord(excelFile.next()); + workbookBof = bof; + bofs++; + + if (!bof.isBiff8() && !bof.isBiff7()) { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (!bof.isWorkbookGlobals()) { + throw new BiffException(BiffException.expectedGlobals); + } + ArrayList continueRecords = new ArrayList(); + ArrayList localNames = new ArrayList(); + nameTable = new ArrayList(); + addInFunctions = new ArrayList(); + + // Skip to the first worksheet + while (bofs == 1) { + r = excelFile.next(); + + if (r.getType() == Type.SST) { + continueRecords.clear(); + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + continueRecords.add(excelFile.next()); + nextrec = excelFile.peek(); + } + + // cast the array + Record[] records = new Record[continueRecords.size()]; + records = (Record[]) continueRecords.toArray(records); + + sharedStrings = new SSTRecord(r, records, settings); + } else if (r.getType() == Type.FILEPASS) { + throw new PasswordException(); + } else if (r.getType() == Type.NAME) { + NameRecord nr = null; + + if (bof.isBiff8()) { + nr = new NameRecord(r, settings, nameTable.size()); + + } else { + nr = new NameRecord(r, settings, nameTable.size(), + NameRecord.biff7); + } + + // Add all local and global names to the name table in order to + // preserve the indexing + nameTable.add(nr); + + if (nr.isGlobal()) { + namedRecords.put(nr.getName(), nr); + } else { + localNames.add(nr); + } + } else if (r.getType() == Type.FONT) { + FontRecord fr = null; + + if (bof.isBiff8()) { + fr = new FontRecord(r, settings); + } else { + fr = new FontRecord(r, settings, FontRecord.biff7); + } + fonts.addFont(fr); + } else if (r.getType() == Type.PALETTE) { + PaletteRecord palette = new PaletteRecord(r); + formattingRecords.setPalette(palette); + } else if (r.getType() == Type.NINETEENFOUR) { + NineteenFourRecord nr = new NineteenFourRecord(r); + nineteenFour = nr.is1904(); + } else if (r.getType() == Type.FORMAT) { + FormatRecord fr = null; + if (bof.isBiff8()) { + fr = new FormatRecord(r, settings, FormatRecord.biff8); + } else { + fr = new FormatRecord(r, settings, FormatRecord.biff7); + } + try { + formattingRecords.addFormat(fr); + } catch (NumFormatRecordsException e) { + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } else if (r.getType() == Type.XF) { + XFRecord xfr = null; + if (bof.isBiff8()) { + xfr = new XFRecord(r, settings, XFRecord.biff8); + } else { + xfr = new XFRecord(r, settings, XFRecord.biff7); + } + + try { + formattingRecords.addStyle(xfr); + } catch (NumFormatRecordsException e) { + // This should not happen. Bomb out + Assert.verify(false, e.getMessage()); + } + } else if (r.getType() == Type.BOUNDSHEET) { + BoundsheetRecord br = null; + + if (bof.isBiff8()) { + br = new BoundsheetRecord(r, settings); + } else { + br = new BoundsheetRecord(r, BoundsheetRecord.biff7); + } + + if (br.isSheet()) { + boundsheets.add(br); + } else if (br.isChart() && !settings.getDrawingsDisabled()) { + boundsheets.add(br); + } + } else if (r.getType() == Type.EXTERNSHEET) { + if (bof.isBiff8()) { + externSheet = new ExternalSheetRecord(r, settings); + } else { + externSheet = new ExternalSheetRecord(r, settings, + ExternalSheetRecord.biff7); + } + } else if (r.getType() == Type.XCT) { + XCTRecord xctr = new XCTRecord(r); + xctRecords.add(xctr); + } else if (r.getType() == Type.CODEPAGE) { + CodepageRecord cr = new CodepageRecord(r); + settings.setCharacterSet(cr.getCharacterSet()); + } else if (r.getType() == Type.SUPBOOK) { + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + r.addContinueRecord(excelFile.next()); + nextrec = excelFile.peek(); + } + + SupbookRecord sr = new SupbookRecord(r, settings); + supbooks.add(sr); + } else if (r.getType() == Type.EXTERNNAME) { + ExternalNameRecord enr = new ExternalNameRecord(r, settings); + + if (enr.isAddInFunction()) { + addInFunctions.add(enr.getName()); + } + } else if (r.getType() == Type.PROTECT) { + ProtectRecord pr = new ProtectRecord(r); + wbProtected = pr.isProtected(); + } else if (r.getType() == Type.OBJPROJ) { + containsMacros = true; + } else if (r.getType() == Type.COUNTRY) { + countryRecord = new CountryRecord(r); + } else if (r.getType() == Type.MSODRAWINGGROUP) { + if (!settings.getDrawingsDisabled()) { + msoDrawingGroup = new MsoDrawingGroupRecord(r); + + if (drawingGroup == null) { + drawingGroup = new DrawingGroup(Origin.READ); + } + + drawingGroup.add(msoDrawingGroup); + + Record nextrec = excelFile.peek(); + while (nextrec.getType() == Type.CONTINUE) { + drawingGroup.add(excelFile.next()); + nextrec = excelFile.peek(); + } + } + } else if (r.getType() == Type.BUTTONPROPERTYSET) { + buttonPropertySet = new ButtonPropertySetRecord(r); + } else if (r.getType() == Type.EOF) { + bofs--; + } else if (r.getType() == Type.REFRESHALL) { + RefreshAllRecord rfm = new RefreshAllRecord(r); + settings.setRefreshAll(rfm.getRefreshAll()); + } else if (r.getType() == Type.TEMPLATE) { + TemplateRecord rfm = new TemplateRecord(r); + settings.setTemplate(rfm.getTemplate()); + } else if (r.getType() == Type.EXCEL9FILE) { + Excel9FileRecord e9f = new Excel9FileRecord(r); + settings.setExcel9File(e9f.getExcel9File()); + } else if (r.getType() == Type.WINDOWPROTECT) { + WindowProtectedRecord winp = new WindowProtectedRecord(r); + settings.setWindowProtected(winp.getWindowProtected()); + } else if (r.getType() == Type.HIDEOBJ) { + HideobjRecord hobj = new HideobjRecord(r); + settings.setHideobj(hobj.getHideMode()); + } else if (r.getType() == Type.WRITEACCESS) { + WriteAccessRecord war = new WriteAccessRecord(r, bof.isBiff8(), + settings); + settings.setWriteAccess(war.getWriteAccess()); + } else { + // logger.info("Unsupported record type: " + + // Integer.toHexString(r.getCode())+"h"); + } + } + + bof = null; + if (excelFile.hasNext()) { + r = excelFile.next(); + + if (r.getType() == Type.BOF) { + bof = new BOFRecord(r); + } + } + + // Only get sheets for which there is a corresponding Boundsheet record + while (bof != null && getNumberOfSheets() < boundsheets.size()) { + if (!bof.isBiff8() && !bof.isBiff7()) { + throw new BiffException(BiffException.unrecognizedBiffVersion); + } + + if (bof.isWorksheet()) { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } else if (bof.isChart()) { + // Read the sheet in + SheetImpl s = new SheetImpl(excelFile, + sharedStrings, + formattingRecords, + bof, + workbookBof, + nineteenFour, + this); + + BoundsheetRecord br = (BoundsheetRecord) boundsheets.get + (getNumberOfSheets()); + s.setName(br.getName()); + s.setHidden(br.isHidden()); + addSheet(s); + } else { + logger.warn("BOF is unrecognized"); + + + while (excelFile.hasNext() && r.getType() != Type.EOF) { + r = excelFile.next(); + } + } + + // The next record will normally be a BOF or empty padding until + // the end of the block is reached. In exceptionally unlucky cases, + // the last EOF will coincide with a block division, so we have to + // check there is more data to retrieve. + // Thanks to liamg for spotting this + bof = null; + if (excelFile.hasNext()) { + r = excelFile.next(); + + if (r.getType() == Type.BOF) { + bof = new BOFRecord(r); + } + } + } + + // Add all the local names to the specific sheets + for (Iterator it = localNames.iterator(); it.hasNext(); ) { + NameRecord nr = (NameRecord) it.next(); + + if (nr.getBuiltInName() == null) { + logger.warn("Usage of a local non-builtin name"); + } else if (nr.getBuiltInName() == BuiltInName.PRINT_AREA || + nr.getBuiltInName() == BuiltInName.PRINT_TITLES) { + // appears to use the internal tab number rather than the + // external sheet index + SheetImpl s = (SheetImpl) sheets.get(nr.getSheetRef() - 1); + s.addLocalName(nr); + } + } + } + + /** + * Accessor for the formattingRecords, used by the WritableWorkbook + * when creating a copy of this + * + * @return the formatting records + */ + public FormattingRecords getFormattingRecords() { + return formattingRecords; + } + + /** + * Accessor for the externSheet, used by the WritableWorkbook + * when creating a copy of this + * + * @return the external sheet record + */ + public ExternalSheetRecord getExternalSheetRecord() { + return externSheet; + } + + /** + * Accessor for the MsoDrawingGroup, used by the WritableWorkbook + * when creating a copy of this + * + * @return the Mso Drawing Group record + */ + public MsoDrawingGroupRecord getMsoDrawingGroupRecord() { + return msoDrawingGroup; + } + + /** + * Accessor for the supbook records, used by the WritableWorkbook + * when creating a copy of this + * + * @return the supbook records + */ + public SupbookRecord[] getSupbookRecords() { + SupbookRecord[] sr = new SupbookRecord[supbooks.size()]; + return (SupbookRecord[]) supbooks.toArray(sr); + } + + /** + * Accessor for the name records. Used by the WritableWorkbook when + * creating a copy of this + * + * @return the array of names + */ + public NameRecord[] getNameRecords() { + NameRecord[] na = new NameRecord[nameTable.size()]; + return (NameRecord[]) nameTable.toArray(na); + } + + /** + * Accessor for the fonts, used by the WritableWorkbook + * when creating a copy of this + * + * @return the fonts used in this workbook + */ + public Fonts getFonts() { + return fonts; + } + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public Cell getCell(String loc) { + Sheet s = getSheet(CellReferenceHelper.getSheet(loc)); + return s.getCell(loc); + } + + /** + * Gets the named cell from this workbook. If the name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be found, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public Cell findCellByName(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + // Go and retrieve the first cell in the first range + Sheet s = getSheet(getExternalSheetIndex(ranges[0].getExternalSheet())); + int col = ranges[0].getFirstColumn(); + int row = ranges[0].getFirstRow(); + + // If the sheet boundaries fall short of the named cell, then return + // an empty cell to stop an exception being thrown + if (col > s.getColumns() || row > s.getRows()) { + return new EmptyCell(col, row); + } + + Cell cell = s.getCell(col, row); + + return cell; + } + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name to find + * @return the range of cells + */ + public Range[] findByName(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + if (nr == null) { + return null; + } + + NameRecord.NameRange[] ranges = nr.getRanges(); + + Range[] cellRanges = new Range[ranges.length]; + + for (int i = 0; i < ranges.length; i++) { + cellRanges[i] = new RangeImpl + (this, + getExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getFirstColumn(), + ranges[i].getFirstRow(), + getLastExternalSheetIndex(ranges[i].getExternalSheet()), + ranges[i].getLastColumn(), + ranges[i].getLastRow()); + } + + return cellRanges; + } + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public String[] getRangeNames() { + Object[] keys = namedRecords.keySet().toArray(); + String[] names = new String[keys.length]; + System.arraycopy(keys, 0, names, 0, keys.length); + + return names; + } + + /** + * Method used when parsing formulas to make sure we are trying + * to parse a supported biff version + * + * @return the BOF record + */ + public BOFRecord getWorkbookBof() { + return workbookBof; + } + + /** + * Determines whether the sheet is protected + * + * @return whether or not the sheet is protected + */ + public boolean isProtected() { + return wbProtected; + } + + /** + * Accessor for the settings + * + * @return the workbook settings + */ + public WorkbookSettings getSettings() { + return settings; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getExternalSheetIndex(String sheetName) { + return 0; + } + + /** + * Accessor/implementation method for the external sheet reference + * + * @param sheetName the sheet name to look for + * @return the external sheet index + */ + public int getLastExternalSheetIndex(String sheetName) { + return 0; + } + + /** + * Gets the name at the specified index + * + * @param index the index into the name table + * @return the name of the cell + * @throws NameRangeException + */ + public String getName(int index) throws NameRangeException { + // Assert.verify(index >= 0 && index < nameTable.size()); + if (index < 0 || index >= nameTable.size()) { + throw new NameRangeException(); + } + return ((NameRecord) nameTable.get(index)).getName(); + } + + /** + * Gets the index of the name record for the name + * + * @param name the name to search for + * @return the index in the name table + */ + public int getNameIndex(String name) { + NameRecord nr = (NameRecord) namedRecords.get(name); + + return nr != null ? nr.getIndex() : 0; + } + + /** + * Accessor for the drawing group + * + * @return the drawing group + */ + public DrawingGroup getDrawingGroup() { + return drawingGroup; + } + + /** + * Accessor for the CompoundFile. For this feature to return non-null + * value, the propertySets feature in WorkbookSettings must be enabled + * and the workbook must contain additional property sets. This + * method is used during the workbook copy + * + * @return the base compound file if it contains additional data items + * and property sets are enabled + */ + public CompoundFile getCompoundFile() { + return excelFile.getCompoundFile(); + } + + /** + * Accessor for the containsMacros + * + * @return TRUE if this workbook contains macros, FALSE otherwise + */ + public boolean containsMacros() { + return containsMacros; + } + + /** + * Accessor for the button property set, used during copying + * + * @return the button property set + */ + public ButtonPropertySetRecord getButtonPropertySet() { + return buttonPropertySet; + } + + /** + * Accessor for the country record, using during copying + * + * @return the country record read in + */ + public CountryRecord getCountryRecord() { + return countryRecord; + } + + /** + * Accessor for addin function names + * + * @return list of add in function names + */ + public String[] getAddInFunctionNames() { + String[] addins = new String[0]; + return (String[]) addInFunctions.toArray(addins); + } + + /** + * Gets the sheet index in this workbook. Used when importing a sheet + * + * @param sheet the sheet + * @return the 0-based sheet index, or -1 if it is not found + */ + public int getIndex(Sheet sheet) { + String name = sheet.getName(); + int index = -1; + int pos = 0; + + for (Iterator i = boundsheets.iterator(); i.hasNext() && index == -1; ) { + BoundsheetRecord br = (BoundsheetRecord) i.next(); + + if (br.getName().equals(name)) { + index = pos; + } else { + pos++; + } + } + + return index; + } + + public XCTRecord[] getXCTRecords() { + XCTRecord[] xctr = new XCTRecord[0]; + return (XCTRecord[]) xctRecords.toArray(xctr); + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java b/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java new file mode 100755 index 0000000..16e3a55 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/read/biff/WriteAccessRecord.java @@ -0,0 +1,63 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.read.biff; + +import jxl.WorkbookSettings; +import jxl.biff.RecordData; +import jxl.biff.StringHelper; +import jxl.biff.Type; + +/** + * A write access record + */ +class WriteAccessRecord extends RecordData { + /** + * The write access user name + */ + private final String wauser; + + /** + * Constructor + * + * @param t the raw bytes + * @param isBiff8 Is record BIFF8 (else BIFF7) + */ + public WriteAccessRecord(Record t, boolean isBiff8, WorkbookSettings ws) { + super(Type.WRITEACCESS); + + byte[] data = t.getData(); + if (isBiff8) { + wauser = StringHelper.getUnicodeString(data, 112 / 2, 0); + } else { + // BIFF7 does not use unicode encoding in string + int length = data[1]; + wauser = StringHelper.getString(data, length, 1, ws); + } + } + + /** + * Gets the binary data for output to file + * + * @return write access user name + */ + public String getWriteAccess() { + return wauser; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Alignment.java b/datastructures-xslx/src/main/java/jxl/write/Alignment.java new file mode 100755 index 0000000..84339fc --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Alignment.java @@ -0,0 +1,37 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various alignments for data within a + * cell + * + * @deprecated Repackaged as jxl.Alignment. This version is retained + * for backwards compatibility + */ +public final class Alignment extends jxl.format.Alignment { + /** + * Private constructor + */ + private Alignment() { + super(0, null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Blank.java b/datastructures-xslx/src/main/java/jxl/write/Blank.java new file mode 100755 index 0000000..928a1ca --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Blank.java @@ -0,0 +1,89 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; +import jxl.write.biff.BlankRecord; + +/** + * A blank cell. Despite not having any contents, it may contain + * formatting information. Such cells are typically used when creating + * templates + */ +public class Blank extends BlankRecord implements WritableCell { + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * + * @param c the column + * @param r the row + */ + public Blank(int c, int r) { + super(c, r); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param r the row + * @param st the cell format + */ + public Blank(int c, int r, CellFormat st) { + super(c, r, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the cell to copy + */ + public Blank(Cell lc) { + super(lc); + } + + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the balnk cell to copy + */ + protected Blank(int col, int row, Blank b) { + super(col, row, b); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Blank(col, row, this); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java b/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java new file mode 100755 index 0000000..a4f8a2d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/BoldStyle.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class containing the various bold styles for data + */ +public final class BoldStyle extends jxl.format.BoldStyle { + /** + * Constructor + * + * @param val a dummy value + */ + private BoldStyle(int val) { + super(0, ""); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Boolean.java b/datastructures-xslx/src/main/java/jxl/write/Boolean.java new file mode 100755 index 0000000..c390f43 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Boolean.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.BooleanCell; +import jxl.format.CellFormat; +import jxl.write.biff.BooleanRecord; + +/** + * A cell, created by user applications, which contains a boolean (or + * in some cases an error) value + */ +public class Boolean extends BooleanRecord implements WritableCell, BooleanCell { + /** + * Constructs a boolean value, which, when added to a spreadsheet, will + * display the specified value at the column/row position indicated. + * + * @param c the column + * @param r the row + * @param val the value + */ + public Boolean(int c, int r, boolean val) { + super(c, r, val); + } + + /** + * Constructs a boolean, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Boolean(int c, int r, boolean val, CellFormat st) { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Boolean(BooleanCell nc) { + super(nc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param b the cell to copy + */ + protected Boolean(int col, int row, Boolean b) { + super(col, row, b); + } + + /** + * Sets the boolean value for this cell + * + * @param val the value + */ + public void setValue(boolean val) { + super.setValue(val); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Boolean(col, row, this); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Border.java b/datastructures-xslx/src/main/java/jxl/write/Border.java new file mode 100755 index 0000000..b2f8481 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Border.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * @deprecated repackaged as jxl.format.Border + */ +public final class Border extends jxl.format.Border { + /** + * Constructor + */ + private Border() { + super(null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java b/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java new file mode 100755 index 0000000..c282d7e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/BorderLineStyle.java @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * @deprecated Repackaged as jxl.format.BorderLineStyle + */ +public final class BorderLineStyle extends jxl.format.BorderLineStyle { + /** + * Constructor + */ + private BorderLineStyle() { + super(0, null); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Colour.java b/datastructures-xslx/src/main/java/jxl/write/Colour.java new file mode 100755 index 0000000..95dd57f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Colour.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various colours available within + * the standard Excel colour palette + * + * @deprecated This has been repackaged as jxl.format.Colour + */ +public final class Colour extends jxl.format.Colour { + /** + * Constructor + * This is currently just a placeholder for backwards compatibility + */ + private Colour() { + super(0, null, 0, 0, 0); + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/DateFormat.java b/datastructures-xslx/src/main/java/jxl/write/DateFormat.java new file mode 100755 index 0000000..7cb7c43 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateFormat.java @@ -0,0 +1,49 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.text.SimpleDateFormat; +import jxl.biff.DisplayFormat; +import jxl.write.biff.DateFormatRecord; + +/** + * A custom user defined number format which may be instantiated within user + * applications in order to present date and time values in the appropriate + * format. + * The string format used to create a DateFormat adheres to the standard + * java specification, and JExcelApi makes the necessary modifications so + * that it is rendered as its nearest equivalent in Excel. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the DateTime cell + */ +public class DateFormat extends DateFormatRecord implements DisplayFormat { + /** + * Constructor. The date format that is passed should comply to the standard + * Java date formatting conventions + * + * @param format the date format + */ + public DateFormat(String format) { + super(format); + + // Verify that the format is valid + SimpleDateFormat df = new SimpleDateFormat(format); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/DateFormats.java b/datastructures-xslx/src/main/java/jxl/write/DateFormats.java new file mode 100755 index 0000000..4a029b6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateFormats.java @@ -0,0 +1,200 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; + +/** + * Static class which contains Excels predefined Date formats + */ +public final class DateFormats { + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x0e, "M/d/yy"); + + // The available built in date formats + /** + * The default format. This is equivalent to a date format of "M/d/yy" + */ + public static final DisplayFormat DEFAULT = FORMAT1; + /** + * Equivalent to a date format of "d-MMM-yy" + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0xf, "d-MMM-yy"); + /** + * Equivalent to a date format of "d-MMM" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x10, "d-MMM"); + /** + * Equivalent to a date format of "MMM-yy" + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x11, "MMM-yy"); + /** + * Equivalent to a date format of "h:mm a" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x12, "h:mm a"); + /** + * Equivalent to a date format of "h:mm:ss a" + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x13, "h:mm:ss a"); + /** + * Equivalent to a date format of "H:mm" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x14, "H:mm"); + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x15, "H:mm:ss"); + /** + * Equivalent to a date format of "M/d/yy H:mm" + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x16, "M/d/yy H:mm"); + /** + * Equivalent to a date format of "mm:ss" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x2d, "mm:ss"); + /** + * Equivalent to a date format of "H:mm:ss" + */ + public static final DisplayFormat FORMAT11 = + new BuiltInFormat(0x2e, "H:mm:ss"); + /** + * Equivalent to a date format of "mm:ss.S" + */ + public static final DisplayFormat FORMAT12 = + new BuiltInFormat(0x2f, "H:mm:ss"); + + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat { + /** + * The index of this date format + */ + private final int index; + /** + * The excel format + */ + private final String formatString; + + /** + * Constructor + * + * @param i the index + * @param s the format string + */ + public BuiltInFormat(int i, String s) { + index = i; + formatString = s; + } + + /** + * Gets the format index + * + * @return the format index + */ + public int getFormatIndex() { + return index; + } + + /** + * Interface method which determines whether the index has been set. For + * built ins this is always true + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() { + return true; + } + + /** + * Initialize this format with the specified position. Since this is a + * built in format, this is always initialized, so this method body for + * this is empty + * + * @param pos the position in the array + */ + public void initialize(int pos) { + } + + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in format + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Hash code implementation + * + * @return the hash code + */ + public int hashCode() { + return index; + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/DateTime.java b/datastructures-xslx/src/main/java/jxl/write/DateTime.java new file mode 100755 index 0000000..89c6220 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/DateTime.java @@ -0,0 +1,167 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.util.Date; +import jxl.DateCell; +import jxl.format.CellFormat; +import jxl.write.biff.DateRecord; + +/** + * A Date which may be created on the fly by a user application and added to a + * spreadsheet + *
+ * NOTE: By default, all dates will have local timezone information added to + * their UTC value. If this is not desired (eg. if the date entered + * represents an interval eg. 9.83s for the 100m world record, then use + * the overloaded constructor which indicate that the date passed in was + * created under the GMT timezone. It is important that when the date + * was created, an instruction like + * Calendar.setTimeZone(TimeZone.getTimeZone("GMT")) + * was made prior to that + */ +public class DateTime extends DateRecord implements WritableCell, DateCell { + /** + * Instance variable for dummy variable overload + */ + public static final GMTDate GMT = new GMTDate(); + + /** + * Constructor. The date will be displayed with date and time components + * using the default date format + * + * @param c the column + * @param r the row + * @param d the date + */ + public DateTime(int c, int r, Date d) { + super(c, r, d); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account. The date passed in will be displayed with + * date and time components using the default date format + * + * @param c the column + * @param r the row + * @param d the date + * @param a dummy overload + */ + public DateTime(int c, int r, Date d, GMTDate a) { + super(c, r, d, a); + } + + /** + * Constructor which takes the format for this cell + * + * @param c the column + * @param r the row + * @param st the format + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st) { + super(c, r, d, st); + } + + /** + * Constructor, which adjusts the specified date to take timezone + * considerations into account + * + * @param c the column + * @param r the row + * @param d the date + * @param st the cell format + * @param a the cummy overload + */ + public DateTime(int c, int r, Date d, CellFormat st, GMTDate a) { + super(c, r, d, st, a); + } + + /** + * Constructor which takes the format for the cell and an indicator + * as to whether this cell is a full date time or purely just a time + * eg. if the spreadsheet is to contain the world record for 100m, then the + * value would be 9.83s, which would be indicated as just a time + * + * @param c the column + * @param r the row + * @param st the style + * @param tim flag indicating that this represents a time + * @param d the date + */ + public DateTime(int c, int r, Date d, CellFormat st, boolean tim) { + super(c, r, d, st, tim); + } + + /** + * A constructor called by the worksheet when creating a writable version + * of a spreadsheet that has been read in + * + * @param dc the date to copy + */ + public DateTime(DateCell dc) { + super(dc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param dt the date to copy + */ + protected DateTime(int col, int row, DateTime dt) { + super(col, row, dt); + } + + + /** + * Sets the date for this cell + * + * @param d the date + */ + public void setDate(Date d) { + super.setDate(d); + } + + /** + * Sets the date for this cell, performing the necessary timezone adjustments + * + * @param d the date + * @param a the dummy overload + */ + public void setDate(Date d, GMTDate a) { + super.setDate(d, a); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new DateTime(col, row, this); + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/Font.java b/datastructures-xslx/src/main/java/jxl/write/Font.java new file mode 100755 index 0000000..5fdccba --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Font.java @@ -0,0 +1,212 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + * + * @deprecated Renamed to writable font + */ +public class Font extends WritableFont { + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + * + * @deprecated + */ + public static final FontName ARIAL = WritableFont.ARIAL; + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + * + * @deprecated + */ + public static final FontName TIMES = WritableFont.TIMES; + + // The bold styles + + /** + * Indicates that this font should not be presented as bold + * + * @deprecated + */ + public static final BoldStyle NO_BOLD = WritableFont.NO_BOLD; + /** + * Indicates that this font should be presented in a BOLD style + * + * @deprecated + */ + public static final BoldStyle BOLD = WritableFont.BOLD; + + // The underline styles + /** + * @deprecated + */ + public static final UnderlineStyle NO_UNDERLINE = + UnderlineStyle.NO_UNDERLINE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE = UnderlineStyle.SINGLE; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE = UnderlineStyle.DOUBLE; + + /** + * @deprecated + */ + public static final UnderlineStyle SINGLE_ACCOUNTING = + UnderlineStyle.SINGLE_ACCOUNTING; + + /** + * @deprecated + */ + public static final UnderlineStyle DOUBLE_ACCOUNTING = + UnderlineStyle.DOUBLE_ACCOUNTING; + + // The script styles + public static final ScriptStyle NORMAL_SCRIPT = ScriptStyle.NORMAL_SCRIPT; + public static final ScriptStyle SUPERSCRIPT = ScriptStyle.SUPERSCRIPT; + public static final ScriptStyle SUBSCRIPT = ScriptStyle.SUBSCRIPT; + + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + * @deprecated Use jxl.write.WritableFont + */ + public Font(FontName fn) { + super(fn); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps) { + super(fn, ps); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs) { + super(fn, ps, bs); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, int ps, BoldStyle bs, boolean italic) { + super(fn, ps, bs, italic); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us underscore flag + * @param fn font name + * @param it italic flag + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) { + super(fn, ps, bs, it, us); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) { + super(fn, ps, bs, it, us, c); + } + + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + * @deprecated use jxl.write.WritableFont + */ + public Font(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) { + super(fn, ps, bs, it, us, c, ss); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Formula.java b/datastructures-xslx/src/main/java/jxl/write/Formula.java new file mode 100755 index 0000000..3456356 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Formula.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.CellFormat; +import jxl.write.biff.FormulaRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Formula extends FormulaRecord implements WritableCell { + /** + * Constructs the formula + * + * @param c the column + * @param r the row + * @param form the formula + */ + public Formula(int c, int r, String form) { + super(c, r, form); + } + + /** + * Constructs a formula + * + * @param c the column + * @param r the row + * @param form the formula + * @param st the cell style + */ + public Formula(int c, int r, String form, CellFormat st) { + super(c, r, form, st); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param f the record to copy + */ + protected Formula(int c, int r, Formula f) { + super(c, r, f); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Formula(col, row, this); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/Label.java b/datastructures-xslx/src/main/java/jxl/write/Label.java new file mode 100755 index 0000000..b1eaffa --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Label.java @@ -0,0 +1,97 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.LabelCell; +import jxl.format.CellFormat; +import jxl.write.biff.LabelRecord; + +/** + * A cell containing text which may be created by user applications + */ +public class Label extends LabelRecord implements WritableCell, LabelCell { + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will contain the specified text + * + * @param c the column + * @param cont the text + * @param r the row + */ + public Label(int c, int r, String cont) { + super(c, r, cont); + } + + /** + * Creates a cell which, when added to the sheet, will be presented at the + * specified column and row co-ordinates and will present the specified text + * in the manner specified by the CellFormat parameter + * + * @param c the column + * @param cont the data + * @param r the row + * @param st the cell format + */ + public Label(int c, int r, String cont, CellFormat st) { + super(c, r, cont, st); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param l the label to copy + */ + protected Label(int col, int row, Label l) { + super(col, row, l); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet being read in + * + * @param lc the label to copy + */ + public Label(LabelCell lc) { + super(lc); + } + + /** + * Sets the string contents of this cell + * + * @param s the new data + */ + public void setString(String s) { + super.setString(s); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Label(col, row, this); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/Number.java b/datastructures-xslx/src/main/java/jxl/write/Number.java new file mode 100755 index 0000000..5ee2300 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Number.java @@ -0,0 +1,99 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.NumberCell; +import jxl.format.CellFormat; +import jxl.write.biff.NumberRecord; + +/** + * A cell, created by user applications, which contains a numerical value + */ +public class Number extends NumberRecord implements WritableCell, NumberCell { + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position indicated. By default, the + * cell will display with an accuracy of 3 decimal places + * + * @param c the column + * @param r the row + * @param val the value + */ + public Number(int c, int r, double val) { + super(c, r, val); + } + + /** + * Constructs a number, which, when added to a spreadsheet, will display the + * specified value at the column/row position with the specified CellFormat. + * The CellFormat may specify font information and number format information + * such as the number of decimal places + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + public Number(int c, int r, double val, CellFormat st) { + super(c, r, val, st); + } + + /** + * Constructor used internally by the application when making a writable + * copy of a spreadsheet that has been read in + * + * @param nc the cell to copy + */ + public Number(NumberCell nc) { + super(nc); + } + + /** + * Copy constructor used for deep copying + * + * @param col the column + * @param row the row + * @param n the number to copy + */ + protected Number(int col, int row, Number n) { + super(col, row, n); + } + + /** + * Sets the numerical value for this cell + * + * @param val the value + */ + public void setValue(double val) { + super.setValue(val); + } + + /** + * Implementation of the deep copy function + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + public WritableCell copyTo(int col, int row) { + return new Number(col, row, this); + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java b/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java new file mode 100755 index 0000000..c8eaf67 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/NumberFormat.java @@ -0,0 +1,135 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.text.DecimalFormat; +import jxl.biff.DisplayFormat; +import jxl.write.biff.NumberFormatRecord; + +/** + * A custom user defined number format, which may be instantiated within user + * applications in order to present numerical values to the appropriate level + * of accuracy. + * The string format used to create a number format adheres to the standard + * java specification, and JExcelAPI makes the necessary modifications so + * that it is rendered in Excel as the nearest possible equivalent. + * Once created, this may be used within a CellFormat object, which in turn + * is a parameter passed to the constructor of the Number cell + */ +public class NumberFormat extends NumberFormatRecord implements DisplayFormat { + /** + * Pass in to the constructor to bypass the format validation + */ + public static final NonValidatingFormat COMPLEX_FORMAT = + new NonValidatingFormat(); + + // Some format strings + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_PREFIX = "[$�-2]"; + + /** + * Constant format string for the Euro currency symbol where it precedes + * the format + */ + public static final String CURRENCY_EURO_SUFFIX = "[$�-1]"; + + /** + * Constant format string for the UK pound sign + */ + public static final String CURRENCY_POUND = "�"; + + /** + * Constant format string for the Japanese Yen sign + */ + public static final String CURRENCY_JAPANESE_YEN = "[$�-411]"; + + /** + * Constant format string for the US Dollar sign + */ + public static final String CURRENCY_DOLLAR = "[$$-409]"; + + /** + * Constant format string for three digit fractions + */ + public static final String FRACTION_THREE_DIGITS = "???/???"; + + /** + * Constant format string for fractions as halves + */ + public static final String FRACTION_HALVES = "?/2"; + + /** + * Constant format string for fractions as quarter + */ + public static final String FRACTION_QUARTERS = "?/4"; + + /** + * Constant format string for fractions as eighths + */ + public static final String FRACTIONS_EIGHTHS = "?/8"; + + /** + * Constant format string for fractions as sixteenths + */ + public static final String FRACTION_SIXTEENTHS = "?/16"; + + /** + * Constant format string for fractions as tenths + */ + public static final String FRACTION_TENTHS = "?/10"; + + /** + * Constant format string for fractions as hundredths + */ + public static final String FRACTION_HUNDREDTHS = "?/100"; + + /** + * Constructor, taking in the Java compliant number format + * + * @param format the format string + */ + public NumberFormat(String format) { + super(format); + + // Verify that the format is valid + DecimalFormat df = new DecimalFormat(format); + } + + /** + * Constructor, taking in the non-Java compliant number format. This + * may be used for currencies and more complex custom formats, which + * will not be subject to the standard validation rules. + * As there is no validation, there is a resultant risk that the + * generated Excel file will be corrupt + *
+ * USE THIS CONSTRUCTOR ONLY IF YOU ARE CERTAIN THAT THE NUMBER FORMAT + * YOU ARE USING IS EXCEL COMPLIANT + * + * @param format the format string + * @param dummy dummy parameter + */ + public NumberFormat(String format, NonValidatingFormat dummy) { + super(format, dummy); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java b/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java new file mode 100755 index 0000000..43469ae --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/NumberFormats.java @@ -0,0 +1,273 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Format; + +/** + * Static class which contains the available list of built in Number formats + */ +public final class NumberFormats { + /** + * The default format. This is equivalent to a number format of '#' + */ + public static final DisplayFormat DEFAULT = new BuiltInFormat(0x0, "#"); + + + // The available built in number formats + // First describe the fairly bog standard formats + /** + * Formatting for an integer number. This is equivalent to a DecimalFormat + * of "0" + */ + public static final DisplayFormat INTEGER = new BuiltInFormat(0x1, "0"); + /** + * Formatting for a float. This formats number to two decimal places. It + * is equivalent to a DecimalFormat of "0.00" + */ + public static final DisplayFormat FLOAT = new BuiltInFormat(0x2, "0.00"); + /** + * Formatting for an integer that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0" + */ + public static final DisplayFormat THOUSANDS_INTEGER = + new BuiltInFormat(0x3, "#,##0"); + /** + * Formatting for a float that has a thousands separator. + * Equivalent to a DecimalFormat of "#,##0.00" + */ + public static final DisplayFormat THOUSANDS_FLOAT = + new BuiltInFormat(0x4, "#,##0.00"); + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_INTEGER = + new BuiltInFormat(0x5, "$#,##0;($#,##0)"); + /** + * As ACCOUNTING_INTEGER except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_INTEGER = + new BuiltInFormat(0x6, "$#,##0;($#,##0)"); + /** + * Formatting for an integer which is presented in accounting format + * (ie. deficits appear in parentheses) + * Equivalent to a DecimalFormat of "$#,##0;($#,##0)" + */ + public static final DisplayFormat ACCOUNTING_FLOAT = + new BuiltInFormat(0x7, "$#,##0;($#,##0)"); + /** + * As ACCOUNTING_FLOAT except that deficits appear coloured red + */ + public static final DisplayFormat ACCOUNTING_RED_FLOAT = + new BuiltInFormat(0x8, "$#,##0;($#,##0)"); + /** + * Formatting for an integer presented as a percentage + * Equivalent to a DecimalFormat of "0%" + */ + public static final DisplayFormat PERCENT_INTEGER = + new BuiltInFormat(0x9, "0%"); + /** + * Formatting for a float percentage + * Equivalent to a DecimalFormat "0.00%" + */ + public static final DisplayFormat PERCENT_FLOAT = + new BuiltInFormat(0xa, "0.00%"); + /** + * Formatting for exponential or scientific notation + * Equivalent to a DecimalFormat "0.00E00" + */ + public static final DisplayFormat EXPONENTIAL = + new BuiltInFormat(0xb, "0.00E00"); + /** + * Formatting for one digit fractions + */ + public static final DisplayFormat FRACTION_ONE_DIGIT = + new BuiltInFormat(0xc, "?/?"); + /** + * Formatting for two digit fractions + */ + public static final DisplayFormat FRACTION_TWO_DIGITS = + new BuiltInFormat(0xd, "??/??"); + /** + * Equivalent to a DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT1 = + new BuiltInFormat(0x25, "#,##0;(#,##0)"); + + // Now describe the more obscure formats + /** + * Equivalent to FORMAT1 except deficits are coloured red + */ + public static final DisplayFormat FORMAT2 = + new BuiltInFormat(0x26, "#,##0;(#,##0)"); + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT3 = + new BuiltInFormat(0x27, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT3 except deficits are coloured red + */ + public static final DisplayFormat FORMAT4 = + new BuiltInFormat(0x28, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to DecimalFormat "#,##0;(#,##0)" + */ + public static final DisplayFormat FORMAT5 = + new BuiltInFormat(0x29, "#,##0;(#,##0)"); + /** + * Equivalent to FORMAT5 except deficits are coloured red + */ + public static final DisplayFormat FORMAT6 = + new BuiltInFormat(0x2a, "#,##0;(#,##0)"); + /** + * Equivalent to DecimalFormat "#,##0.00;(#,##0.00)" + */ + public static final DisplayFormat FORMAT7 = + new BuiltInFormat(0x2b, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT7 except deficits are coloured red + */ + public static final DisplayFormat FORMAT8 = + new BuiltInFormat(0x2c, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to FORMAT7 + */ + public static final DisplayFormat FORMAT9 = + new BuiltInFormat(0x2e, "#,##0.00;(#,##0.00)"); + /** + * Equivalent to DecimalFormat "##0.0E0" + */ + public static final DisplayFormat FORMAT10 = + new BuiltInFormat(0x30, "##0.0E0"); + /** + * Forces numbers to be interpreted as text + */ + public static final DisplayFormat TEXT = new BuiltInFormat(0x31, "@"); + + /** + * Inner class which holds the format index + */ + private static class BuiltInFormat implements DisplayFormat, Format { + /** + * The built in number format index + */ + private final int index; + + /** + * The format string + */ + private final String formatString; + + /** + * Constructor, using the predetermined index + * + * @param i the index + * @param s the string + */ + public BuiltInFormat(int i, String s) { + index = i; + formatString = s; + } + + /** + * Accessor for the format index + * + * @return the index + */ + public int getFormatIndex() { + return index; + } + + /** + * Accessor to determine if this format has been initialized. Since it is + * built in, this will always return TRUE + * + * @return TRUE, since this is a built in format + */ + public boolean isInitialized() { + return true; + } + + /** + * Determines whether this format is a built in format + * + * @return TRUE, since this is a built in numerical format + */ + public boolean isBuiltIn() { + return true; + } + + /** + * Initializes this format with a dynamically determined index value. + * Since this is a built in, and hence the index value is predetermined, + * this method has an empty body + * + * @param pos the pos in the number formats list + */ + public void initialize(int pos) { + } + + /** + * Accesses the excel format string which is applied to the cell + * Note that this is the string that excel uses, and not the java + * equivalent + * + * @return the cell format string + */ + public String getFormatString() { + return formatString; + } + + /** + * Standard equals method + * + * @param o the object to compare + * @return TRUE if the two objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof BuiltInFormat)) { + return false; + } + + BuiltInFormat bif = (BuiltInFormat) o; + + return index == bif.index; + } + + /** + * Standard hash code method + * + * @return the hash code + */ + public int hashCode() { + return index; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/Pattern.java b/datastructures-xslx/src/main/java/jxl/write/Pattern.java new file mode 100755 index 0000000..ea60896 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/Pattern.java @@ -0,0 +1,48 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration class which contains the various patterns available within + * the standard Excel pattern palette + * + * @deprecated Repackaged as jxl.format.Pattern + */ +public final class Pattern extends jxl.format.Pattern { + /** + * Private constructor + * + * @param val + */ + private Pattern() { + super(0, null); + } +} + + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java b/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java new file mode 100755 index 0000000..21a0cec --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/VerticalAlignment.java @@ -0,0 +1,35 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +/** + * Enumeration type which describes the vertical alignment of data within a cell + * + * @deprecated Repackaged as jxl.format.VerticalAlignment + */ +public final class VerticalAlignment extends jxl.format.VerticalAlignment { + /** + * Constructor + */ + private VerticalAlignment() { + super(0, null); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCell.java b/datastructures-xslx/src/main/java/jxl/write/WritableCell.java new file mode 100755 index 0000000..e37eff9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCell.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.Cell; +import jxl.format.CellFormat; + +/** + * The interface for all writable cells + */ +public interface WritableCell extends Cell { + /** + * Sets the cell format for this cell + * + * @param cf the cell format + */ + void setCellFormat(CellFormat cf); + + /** + * A deep copy. The returned cell still needs to be added to the sheet. + * By not automatically adding the cell to the sheet, the client program + * may change certain attributes, such as the value or the format + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return a copy of this cell, which can then be added to the sheet + */ + WritableCell copyTo(int col, int row); + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + WritableCellFeatures getWritableCellFeatures(); + + /** + * Sets the cell features + * + * @param cf the cell features + */ + void setCellFeatures(WritableCellFeatures cf); +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java b/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java new file mode 100755 index 0000000..238cd88 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCellFeatures.java @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Copyright (C) 2004 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.util.Collection; +import jxl.CellFeatures; +import jxl.biff.BaseCellFeatures; + +/** + * Container for any additional cell features + */ +public class WritableCellFeatures extends CellFeatures { + // shadow the conditions in the base class so that they appear on + // the public generated javadoc + public static final ValidationCondition BETWEEN = BaseCellFeatures.BETWEEN; + public static final ValidationCondition NOT_BETWEEN = + BaseCellFeatures.NOT_BETWEEN; + public static final ValidationCondition EQUAL = BaseCellFeatures.EQUAL; + public static final ValidationCondition NOT_EQUAL = + BaseCellFeatures.NOT_EQUAL; + public static final ValidationCondition GREATER_THAN = + BaseCellFeatures.GREATER_THAN; + public static final ValidationCondition LESS_THAN = + BaseCellFeatures.LESS_THAN; + public static final ValidationCondition GREATER_EQUAL = + BaseCellFeatures.GREATER_EQUAL; + public static final ValidationCondition LESS_EQUAL = + BaseCellFeatures.LESS_EQUAL; + + /** + * Constructor + */ + public WritableCellFeatures() { + super(); + } + + /** + * Copy constructor + * + * @param cf the cell to copy + */ + public WritableCellFeatures(CellFeatures cf) { + super(cf); + } + + /** + * Sets the cell comment + * + * @param s the comment + */ + public void setComment(String s) { + super.setComment(s); + } + + /** + * Sets the cell comment and sets the size of the text box (in cells) + * in which the comment is displayed + * + * @param s the comment + * @param width the width of the comment box in cells + * @param height the height of the comment box in cells + */ + public void setComment(String s, double width, double height) { + super.setComment(s, width, height); + } + + /** + * Removes the cell comment, if present + */ + public void removeComment() { + super.removeComment(); + } + + + /** + * Removes any data validation, if present + */ + public void removeDataValidation() { + super.removeDataValidation(); + } + + /** + * The list of items to validate for this cell. For each object in the + * collection, the toString() method will be called and the data entered + * will be validated against that string + * + * @param c the list of valid values + */ + public void setDataValidationList(Collection c) { + super.setDataValidationList(c); + } + + /** + * The list of items to validate for this cell in the form of a cell range. + * + * @param col1 the first column containing the data to validate against + * @param row1 the first row containing the data to validate against + * @param col2 the second column containing the data to validate against + * @param row2 the second row containing the data to validate against + */ + public void setDataValidationRange(int col1, int row1, int col2, int row2) { + super.setDataValidationRange(col1, row1, col2, row2); + } + + /** + * Sets the data validation based upon a named range. If the namedRange + * is an empty string ("") then the cell is effectively made read only + * + * @param namedRange the workbook named range defining the validation + * boundaries + */ + public void setDataValidationRange(String namedRange) { + super.setDataValidationRange(namedRange); + } + + + /** + * Sets the numeric value against which to validate + * + * @param val the number + * @param c the validation condition + */ + public void setNumberValidation(double val, ValidationCondition c) { + super.setNumberValidation(val, c); + } + + /** + * Sets the numeric range against which to validate the data + * + * @param val1 the first number + * @param val2 the second number + * @param c the validation condition + */ + public void setNumberValidation(double val1, + double val2, + ValidationCondition c) { + super.setNumberValidation(val1, val2, c); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java b/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java new file mode 100755 index 0000000..7195e1a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableCellFormat.java @@ -0,0 +1,219 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.biff.DisplayFormat; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.biff.CellXFRecord; + +/** + * A user specified cell format, which may be reused across many cells. + * The constructors takes parameters, such as font details and the numerical + * date formats, which specify to Excel how cells with this format should be + * displayed. + * Once a CellFormat has been added to a Cell which has been added to + * a sheet, then the CellFormat becomes immutable (to prevent unforeseen + * effects on other cells which share the same format). Attempts to + * call the various set... functions on a WritableCellFormat after this + * time will result in a runtime exception. + */ +public class WritableCellFormat extends CellXFRecord { + /** + * A default constructor, which uses the default font and format. + * This constructor should be used in conjunction with the more + * advanced two-phase methods setAlignment, setBorder etc. + */ + public WritableCellFormat() { + this(WritableWorkbook.ARIAL_10_PT, NumberFormats.DEFAULT); + } + + /** + * A CellFormat which specifies the font for cells with this format + * + * @param font the font + */ + public WritableCellFormat(WritableFont font) { + this(font, NumberFormats.DEFAULT); + } + + /** + * A constructor which specifies a date/number format for Cells which + * use this format object + * + * @param format the format + */ + public WritableCellFormat(DisplayFormat format) { + this(WritableWorkbook.ARIAL_10_PT, format); + } + + /** + * A constructor which specifies the font and date/number format for cells + * which wish to use this format + * + * @param font the font + * @param format the date/number format + */ + public WritableCellFormat(WritableFont font, DisplayFormat format) { + super(font, format); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + * + * @param format the cell format to copy + */ + public WritableCellFormat(CellFormat format) { + super(format); + } + + /** + * Sets the horizontal alignment for this format + * + * @param a the alignment + * @throws WriteException + */ + public void setAlignment(Alignment a) throws WriteException { + super.setAlignment(a); + } + + /** + * Sets the vertical alignment for this format + * + * @param va the vertical alignment + * @throws WriteException + */ + public void setVerticalAlignment(VerticalAlignment va) throws WriteException { + super.setVerticalAlignment(va); + } + + /** + * Sets the text orientation for this format + * + * @param o the orientation + * @throws WriteException + */ + public void setOrientation(Orientation o) throws WriteException { + super.setOrientation(o); + } + + /** + * Sets the wrap indicator for this format. If the wrap is set to TRUE, then + * Excel will wrap data in cells with this format so that it fits within the + * cell boundaries + * + * @param w the wrap flag + * @throws WriteException + */ + public void setWrap(boolean w) throws WriteException { + super.setWrap(w); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls) throws WriteException { + super.setBorder(b, ls, Colour.BLACK); + } + + /** + * Sets the specified border for this format + * + * @param b the border + * @param ls the border line style + * @param c the colour of the specified border + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException { + super.setBorder(b, ls, c); + } + + /** + * Sets the background colour for this cell format + * + * @param c the bacground colour + * @throws WriteException + */ + public void setBackground(Colour c) throws WriteException { + this.setBackground(c, Pattern.SOLID); + } + + /** + * Sets the background colour and pattern for this cell format + * + * @param c the colour + * @param p the pattern + * @throws WriteException + */ + public void setBackground(Colour c, Pattern p) throws WriteException { + super.setBackground(c, p); + } + + /** + * Sets the shrink to fit flag + * + * @param s shrink to fit flag + * @throws WriteException + */ + public void setShrinkToFit(boolean s) throws WriteException { + super.setShrinkToFit(s); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException { + super.setIndentation(i); + } + + + /** + * Sets whether or not this XF record locks the cell. For this to + * have any effect, the sheet containing cells with this format must + * also be locke3d + * + * @param l the locked flag + * @throws WriteException + */ + public void setLocked(boolean l) throws WriteException { + super.setLocked(l); + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableFont.java b/datastructures-xslx/src/main/java/jxl/write/WritableFont.java new file mode 100755 index 0000000..692559c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableFont.java @@ -0,0 +1,333 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.format.Colour; +import jxl.format.Font; +import jxl.format.ScriptStyle; +import jxl.format.UnderlineStyle; +import jxl.write.biff.WritableFontRecord; + +/** + * A class which is instantiated when the user application wishes to specify + * the font for a particular cell + */ +public class WritableFont extends WritableFontRecord { + /** + * Objects created with this font name will be rendered within Excel as ARIAL + * fonts + */ + public static final FontName ARIAL = new FontName("Arial"); + /** + * Objects created with this font name will be rendered within Excel as TIMES + * fonts + */ + public static final FontName TIMES = new FontName("Times New Roman"); + /** + * Objects created with this font name will be rendered within Excel as + * COURIER fonts + */ + public static final FontName COURIER = new FontName("Courier New"); + /** + * Objects created with this font name will be rendered within Excel as + * TAHOMA fonts + */ + public static final FontName TAHOMA = new FontName("Tahoma"); + /** + * Indicates that this font should not be presented as bold + */ + public static final BoldStyle NO_BOLD = new BoldStyle(0x190); + /** + * Indicates that this font should be presented in a BOLD style + */ + public static final BoldStyle BOLD = new BoldStyle(0x2bc); + + // The bold styles + /** + * The default point size for all Fonts + */ + public static final int DEFAULT_POINT_SIZE = 10; + /** + * Creates a default font, vanilla font of the specified face and with + * default point size. + * + * @param fn the font name + */ + public WritableFont(FontName fn) { + this(fn, + DEFAULT_POINT_SIZE, + NO_BOLD, + false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Publicly available copy constructor + * + * @param f the font to copy + */ + public WritableFont(Font f) { + super(f); + } + + /** + * Constructs of font of the specified face and of size given by the + * specified point size + * + * @param ps the point size + * @param fn the font name + */ + public WritableFont(FontName fn, int ps) { + this(fn, ps, NO_BOLD, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size and bold style + * + * @param ps the point size + * @param bs the bold style + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs) { + this(fn, ps, bs, false, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight and + * italicised option. + * + * @param ps the point size + * @param bs the bold style + * @param italic italic flag + * @param fn the font name + */ + public WritableFont(FontName fn, int ps, BoldStyle bs, boolean italic) { + this(fn, ps, bs, italic, + UnderlineStyle.NO_UNDERLINE, + Colour.BLACK, + ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold weight, + * italicisation and underline style + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us) { + this(fn, ps, bs, it, us, Colour.BLACK, ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style and colour + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it italic flag + * @param c the colour + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c) { + this(fn, ps, bs, it, us, c, ScriptStyle.NORMAL_SCRIPT); + } + + /** + * Creates a font of the specified face, point size, bold style, + * italicisation, underline style, colour, and script + * style (superscript/subscript) + * + * @param ps the point size + * @param bs the bold style + * @param us the underline style + * @param fn the font name + * @param it the italic flag + * @param c the colour + * @param ss the script style + */ + public WritableFont(FontName fn, + int ps, + BoldStyle bs, + boolean it, + UnderlineStyle us, + Colour c, + ScriptStyle ss) { + super(fn.name, ps, bs.value, it, + us.getValue(), + c.getValue(), ss.getValue()); + } + + /** + * Factory method which creates the specified font name. This method + * should be used with care, since the string used to create the font + * name must be recognized by Excel's internal processing + * + * @param fontName the name of the Excel font + * @return the font name + */ + public static FontName createFont(String fontName) { + return new FontName(fontName); + } + + /** + * Sets the point size for this font, if the font hasn't been initialized + * + * @param pointSize the point size + * @throws WriteException, if this font is already in use elsewhere + */ + public void setPointSize(int pointSize) throws WriteException { + super.setPointSize(pointSize); + } + + /** + * Sets the bold style for this font, if the font hasn't been initialized + * + * @param boldStyle the bold style + * @throws WriteException, if this font is already in use elsewhere + */ + public void setBoldStyle(BoldStyle boldStyle) throws WriteException { + super.setBoldStyle(boldStyle.value); + } + + /** + * Sets the italic indicator for this font, if the font hasn't been + * initialized + * + * @param italic the italic flag + * @throws WriteException, if this font is already in use elsewhere + */ + public void setItalic(boolean italic) throws WriteException { + super.setItalic(italic); + } + + /** + * Sets the underline style for this font, if the font hasn't been + * initialized + * + * @param us the underline style + * @throws WriteException, if this font is already in use elsewhere + */ + public void setUnderlineStyle(UnderlineStyle us) throws WriteException { + super.setUnderlineStyle(us.getValue()); + } + + /** + * Sets the colour for this font, if the font hasn't been + * initialized + * + * @param colour the colour + * @throws WriteException, if this font is already in use elsewhere + */ + public void setColour(Colour colour) throws WriteException { + super.setColour(colour.getValue()); + } + + /** + * Sets the script style (eg. superscript, subscript) for this font, + * if the font hasn't been initialized + * + * @param scriptStyle the colour + * @throws WriteException, if this font is already in use elsewhere + */ + public void setScriptStyle(ScriptStyle scriptStyle) throws WriteException { + super.setScriptStyle(scriptStyle.getValue()); + } + + /** + * Accessor for the strike-out flag + * + * @return the strike-out flag + */ + public boolean isStruckout() { + return super.isStruckout(); + } + + /** + * Sets Accessor for the strike-out flag + * + * @param struckout TRUE if this is a struckout font + * @return the strike-out flag + * @throws WriteException, if this font is already in use elsewhere + */ + public void setStruckout(boolean struckout) throws WriteException { + super.setStruckout(struckout); + } + + /** + * Static inner class used for classifying the font names + */ + public static class FontName { + /** + * The name + */ + String name; + + /** + * Constructor + * + * @param s the font name + */ + FontName(String s) { + name = s; + } + } + + /** + * Static inner class used for the boldness of the fonts + */ + /*private*/ static class BoldStyle { + /** + * The value + */ + public int value; + + /** + * Constructor + * + * @param val the value + */ + BoldStyle(int val) { + value = val; + } + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java b/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java new file mode 100755 index 0000000..28ac95c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableHyperlink.java @@ -0,0 +1,232 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.File; +import java.net.URL; +import jxl.Hyperlink; +import jxl.write.biff.HyperlinkRecord; + +/** + * A writable hyperlink. Provides API to modify the contents of the hyperlink + */ +public class WritableHyperlink extends HyperlinkRecord implements Hyperlink { + /** + * Constructor used internally by the worksheet when making a copy + * of worksheet + * + * @param h the hyperlink being read in + * @param ws the writable sheet containing the hyperlink + */ + public WritableHyperlink(Hyperlink h, WritableSheet ws) { + super(h, ws); + } + + /** + * Constructs a URL hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, URL url) { + this(col, row, col, row, url); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, URL url) { + this(col, row, lastcol, lastrow, url, null); + } + + /** + * Constructs a url hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description text to place in the cell + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + URL url, + String desc) { + super(col, row, lastcol, lastrow, url, desc); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, File file) { + this(col, row, col, row, file, null); + } + + /** + * Constructs a file hyperlink in a single cell + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param file the hyperlink + * @param desc the hyperlink description + */ + public WritableHyperlink(int col, int row, File file, String desc) { + this(col, row, col, row, file, desc); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + */ + public WritableHyperlink(int col, int row, int lastcol, int lastrow, + File file) { + super(col, row, lastcol, lastrow, file, null); + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + public WritableHyperlink(int col, + int row, + int lastcol, + int lastrow, + File file, + String desc) { + super(col, row, lastcol, lastrow, file, desc); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + */ + public WritableHyperlink(int col, int row, + String desc, + WritableSheet sheet, + int destcol, int destrow) { + this(col, row, col, row, + desc, + sheet, destcol, destrow, destcol, destrow); + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the cell contents for this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public WritableHyperlink(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super(col, row, lastcol, lastrow, + desc, + sheet, destcol, destrow, + lastdestcol, lastdestrow); + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) { + super.setURL(url); + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) { + super.setFile(file); + } + + /** + * Sets the description to appear in the hyperlink cell + * + * @param desc the description + */ + public void setDescription(String desc) { + super.setContents(desc); + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + public void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super.setLocation(desc, sheet, destcol, destrow, lastdestcol, lastdestrow); + } + +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableImage.java b/datastructures-xslx/src/main/java/jxl/write/WritableImage.java new file mode 100755 index 0000000..151eb07 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableImage.java @@ -0,0 +1,207 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.File; +import jxl.biff.drawing.Drawing; +import jxl.biff.drawing.DrawingGroup; +import jxl.biff.drawing.DrawingGroupObject; + +/** + * Allows an image to be created, or an existing image to be manipulated + * Note that co-ordinates and dimensions are given in cells, so that if for + * example the width or height of a cell which the image spans is altered, + * the image will have a correspondign distortion + */ +public class WritableImage extends Drawing { + // Shadow these values from the superclass. The only practical reason + // for doing this is that they appear nicely in the javadoc + + /** + * Image anchor properties which will move and resize an image + * along with the cells + */ + public static ImageAnchorProperties MOVE_AND_SIZE_WITH_CELLS = + Drawing.MOVE_AND_SIZE_WITH_CELLS; + + /** + * Image anchor properties which will move an image + * when cells are inserted or deleted + */ + public static ImageAnchorProperties MOVE_WITH_CELLS = + Drawing.MOVE_WITH_CELLS; + + /** + * Image anchor properties which will leave an image unaffected when + * other cells are inserted, removed or resized + */ + public static ImageAnchorProperties NO_MOVE_OR_SIZE_WITH_CELLS = + Drawing.NO_MOVE_OR_SIZE_WITH_CELLS; + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param image the source image file + */ + public WritableImage(double x, double y, + double width, double height, + File image) { + super(x, y, width, height, image); + } + + /** + * Constructor + * + * @param x the column number at which to position the image + * @param y the row number at which to position the image + * @param width the number of columns cells which the image spans + * @param height the number of rows which the image spans + * @param imageData the image data + */ + public WritableImage(double x, + double y, + double width, + double height, + byte[] imageData) { + super(x, y, width, height, imageData); + } + + /** + * Constructor, used when copying sheets + * + * @param d the image to copy + * @param dg the drawing group + */ + public WritableImage(DrawingGroupObject d, DrawingGroup dg) { + super(d, dg); + } + + /** + * Accessor for the image position + * + * @return the column number at which the image is positioned + */ + public double getColumn() { + return super.getX(); + } + + /** + * Accessor for the image position + * + * @param c the column number at which the image should be positioned + */ + public void setColumn(double c) { + super.setX(c); + } + + /** + * Accessor for the image position + * + * @return the row number at which the image is positions + */ + public double getRow() { + return super.getY(); + } + + /** + * Accessor for the image position + * + * @param c the row number at which the image should be positioned + */ + public void setRow(double c) { + super.setY(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of columns this image spans + */ + public double getWidth() { + return super.getWidth(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * width of the columns it spans + * + * @param c the number of columns which this image spans + */ + public void setWidth(double c) { + super.setWidth(c); + } + + /** + * Accessor for the image dimensions + * + * @return the number of rows which this image spans + */ + public double getHeight() { + return super.getHeight(); + } + + /** + * Accessor for the image dimensions + * Note that the actual size of the rendered image will depend on the + * height of the rows it spans + * + * @param c the number of rows which this image should span + */ + public void setHeight(double c) { + super.setHeight(c); + } + + /** + * Accessor for the image file + * + * @return the file which the image references + */ + public File getImageFile() { + return super.getImageFile(); + } + + /** + * Accessor for the image data + * + * @return the image data + */ + public byte[] getImageData() { + return super.getImageData(); + } + + /** + * Accessor for the anchor properties + */ + public ImageAnchorProperties getImageAnchor() { + return super.getImageAnchor(); + } + + /** + * Accessor for the anchor properties + */ + public void setImageAnchor(ImageAnchorProperties iap) { + super.setImageAnchor(iap); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java b/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java new file mode 100755 index 0000000..0dfae07 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableSheet.java @@ -0,0 +1,433 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.CellView; +import jxl.Range; +import jxl.Sheet; +import jxl.format.CellFormat; +import jxl.format.PageOrientation; +import jxl.format.PaperSize; +import jxl.write.biff.RowsExceededException; + +/** + * Interface for a worksheet that may be modified. The most + * important modification for a sheet is to have cells added to it + */ +public interface WritableSheet extends Sheet { + /** + * Adds a cell to this sheet + * The RowsExceededException may be caught if client code wishes to + * explicitly trap the case where too many rows have been written + * to the current sheet. If this behaviour is not desired, it is + * sufficient simply to handle the WriteException, since this is a base + * class of RowsExceededException + * + * @param cell the cell to add + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + void addCell(WritableCell cell) + throws WriteException, RowsExceededException; + + /** + * Sets the name of this sheet + * + * @param name the name of the sheet + */ + void setName(String name); + + /** + * Indicates whether or not this sheet is hidden + * + * @param hidden hidden flag + * @deprecated use the SheetSettings bean instead + */ + void setHidden(boolean hidden); + + /** + * Indicates whether or not this sheet is protected + * + * @param prot Protected flag + * @deprecated use the SheetSettings bean instead + */ + void setProtected(boolean prot); + + /** + * Sets the width of the column on this sheet, in characters. This causes + * Excel to resize the entire column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param width the width of the column + */ + void setColumnView(int col, int width); + + /** + * Sets the width and style of every cell in the specified column. + * If the columns specified already has view information associated + * with it, then it is replaced by the new data + * + * @param col the column to be formatted + * @param format the format of every cell in the column + * @param width the width of the column, in characters + * @deprecated Use the CellView bean instead + */ + void setColumnView(int col, int width, CellFormat format); + + /** + * Sets the view for this column + * + * @param col the column on which to set the view + * @param view the view to set + */ + void setColumnView(int col, CellView view); + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in characters + * @throws RowsExceededException + */ + void setRowView(int row, int height) + throws RowsExceededException; + + /** + * Sets the properties of the specified row + * + * @param row the row to be formatted + * @param collapsed indicates whether the row is collapsed + * @throws RowsExceededException + */ + void setRowView(int row, boolean collapsed) + throws RowsExceededException; + + /** + * Sets the height of the specified row, as well as its collapse status + * + * @param row the row to be formatted + * @param height the row height in 1/20th of a point + * @param collapsed indicates whether the row is collapsed + * @throws RowsExceededException + */ + void setRowView(int row, int height, + boolean collapsed) + throws RowsExceededException; + + /** + * Sets the view for this column + * + * @param row the column on which to set the view + * @param view the view to set + * @throws RowsExceededException + */ + void setRowView(int row, CellView view) throws RowsExceededException; + + /** + * Gets the writable cell from this sheet. Use of this method allows + * the returned cell to be modified by the users application + * + * @param column the column + * @param row the row + * @return the cell at the specified position + */ + WritableCell getWritableCell(int column, int row); + + /** + * Returns the cell for the specified location eg. "A4". Note that this + * method is identical to calling getCell(CellReferenceHelper.getColumn(loc), + * CellReferenceHelper.getRow(loc)) and its implicit performance + * overhead for string parsing. As such,this method should therefore + * be used sparingly + * + * @param loc the cell reference + * @return the cell at the specified co-ordinates + */ + WritableCell getWritableCell(String loc); + + /** + * Gets the writable hyperlinks from this sheet. The hyperlinks + * that are returned may be modified by user applications + * + * @return the writable hyperlinks + */ + WritableHyperlink[] getWritableHyperlinks(); + + /** + * Inserts a blank row into this spreadsheet. If the row is out of range + * of the rows in the sheet, then no action is taken + * + * @param row the row to insert + */ + void insertRow(int row); + + /** + * Inserts a blank column into this spreadsheet. If the column is out of + * range of the columns in the sheet, then no action is taken + * + * @param col the column to insert + */ + void insertColumn(int col); + + /** + * Removes a column from this spreadsheet. If the column is out of range + * of the columns in the sheet, then no action is taken + * + * @param col the column to remove + */ + void removeColumn(int col); + + /** + * Removes a row from this spreadsheet. If the row is out of + * range of the columns in the sheet, then no action is taken + * + * @param row the row to remove + */ + void removeRow(int row); + + /** + * Merges the specified cells. Any clashes or intersections between + * merged cells are resolved when the spreadsheet is written out + * + * @param col1 the column number of the top left cell + * @param row1 the row number of the top left cell + * @param col2 the column number of the bottom right cell + * @param row2 the row number of the bottom right cell + * @return the Range object representing the merged cells + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + Range mergeCells(int col1, int row1, int col2, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a row grouping + * + * @param row1 the first row of the group + * @param row2 the last row of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + void setRowGroup(int row1, int row2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a row grouping + * + * @param row1 the first row to unset + * @param row2 the last row to unset + * @throws WriteException + * @throws RowsExceededException + */ + void unsetRowGroup(int row1, int row2) + throws WriteException, RowsExceededException; + + /** + * Sets a column grouping + * + * @param col1 the first column of the group + * @param col2 the last column of the group + * @param collapsed should the group be collapsed? + * @throws WriteException + * @throws RowsExceededException + */ + void setColumnGroup(int col1, int col2, boolean collapsed) + throws WriteException, RowsExceededException; + + /** + * Unsets a column grouping + * + * @param col1 the first column to unset + * @param col2 the last column to unset + * @throws WriteException + * @throws RowsExceededException + */ + void unsetColumnGroup(int col1, int col2) + throws WriteException, RowsExceededException; + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + void unmergeCells(Range r); + + /** + * Adds the specified hyperlink. Adding a hyperlink causes any populated + * cells in the range of the hyperlink to be set to empty + * If the cells which activate this hyperlink clash with any other cells, + * they are still added to the worksheet and it is left to Excel to + * handle this. + * + * @param h the hyperlink + * @throws jxl.write..WriteException + * @throws RowsExceededException + */ + void addHyperlink(WritableHyperlink h) + throws WriteException, RowsExceededException; + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. The contents of the cell which + * activate the hyperlink are removed. + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + */ + void removeHyperlink(WritableHyperlink h); + + /** + * Removes the specified hyperlink. Note that if you merely set the + * cell contents to be an Empty cell, then the cells containing the + * hyperlink will still be active. + * If the preserveLabel field is set, the cell contents of the + * hyperlink are preserved, although the hyperlink is deactivated. If + * this value is FALSE, the cell contents are removed + * The hyperlink passed in must be a hyperlink retrieved using the + * getHyperlinks method + * + * @param h the hyperlink to remove. + * @param preserveLabel if TRUE preserves the label contents, if FALSE + * removes them + */ + void removeHyperlink(WritableHyperlink h, boolean preserveLabel); + + /** + * Sets the header for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + void setHeader(String l, String c, String r); + + /** + * Sets the footer for this page + * + * @param l the print header to print on the left side + * @param c the print header to print in the centre + * @param r the print header to print on the right hand side + * @deprecated use the SheetSettings bean + */ + void setFooter(String l, String c, String r); + + /** + * Sets the page setup details + * + * @param p the page orientation + */ + void setPageSetup(PageOrientation p); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + void setPageSetup(PageOrientation p, double hm, double fm); + + /** + * Sets the page setup details + * + * @param p the page orientation + * @param ps the paper size + * @param hm the header margin, in inches + * @param fm the footer margin, in inches + */ + void setPageSetup(PageOrientation p, PaperSize ps, + double hm, double fm); + + /** + * Forces a page break at the specified row + * + * @param row the row to break at + */ + void addRowPageBreak(int row); + + /** + * Forces a page break at the specified column + * + * @param col the column to break at + */ + void addColumnPageBreak(int col); + + /** + * Adds an image to the sheet + * + * @param image the image to add + */ + void addImage(WritableImage image); + + /** + * Accessor for the number of images on the sheet + * + * @return the number of images on this sheet + */ + int getNumberOfImages(); + + /** + * Accessor for the image + * + * @param i the 0 based image number + * @return the image at the specified position + */ + WritableImage getImage(int i); + + /** + * Removes the specified image from the sheet. The image passed in + * must be the same instance as that previously retrieved using the + * getImage() method + * + * @param wi the image to remove + */ + void removeImage(WritableImage wi); + + /** + * Extend the data validation contained in the specified cell across and + * downwards. + * NOTE: The source cell (top left) must have been added to the sheet prior + * to this method being called + * + * @param col the number of cells accross to apply this data validation + * @param row the number of cells downwards to apply this data validation + */ + void applySharedDataValidation(WritableCell cell, int col, int row) + throws WriteException; + + /** + * Remove the shared data validation from multiple cells. The cell passed + * in is the top left cell. The data validation is removed from this + * cell and all cells which share the same validation. + * + * @param cell the top left cell containing the shared data validation + */ + void removeSharedDataValidation(WritableCell cell) + throws WriteException; +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java b/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java new file mode 100755 index 0000000..a8354f3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WritableWorkbook.java @@ -0,0 +1,306 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import java.io.IOException; +import jxl.Range; +import jxl.Sheet; +import jxl.Workbook; +import jxl.format.Colour; +import jxl.format.UnderlineStyle; + +/** + * A writable workbook + */ +public abstract class WritableWorkbook { + // Globally available stuff + + /** + * The default font for Cell formats + */ + public static final WritableFont ARIAL_10_PT = + new WritableFont(WritableFont.ARIAL); + + /** + * The font used for hyperlinks + */ + public static final WritableFont HYPERLINK_FONT = + new WritableFont(WritableFont.ARIAL, + WritableFont.DEFAULT_POINT_SIZE, + WritableFont.NO_BOLD, + false, + UnderlineStyle.SINGLE, + Colour.BLUE); + + /** + * The default style for cells + */ + public static final WritableCellFormat NORMAL_STYLE = + new WritableCellFormat(ARIAL_10_PT, NumberFormats.DEFAULT); + + /** + * The style used for hyperlinks + */ + public static final WritableCellFormat HYPERLINK_STYLE = + new WritableCellFormat(HYPERLINK_FONT); + + /** + * A cell format used to hide the cell contents + */ + public static final WritableCellFormat HIDDEN_STYLE = + new WritableCellFormat(new DateFormat(";;;")); + + /** + * Constructor used by the implemenation class + */ + protected WritableWorkbook() { + } + + /** + * Gets the sheets within this workbook. Use of this method for + * large worksheets can cause performance problems. + * + * @return an array of the individual sheets + */ + public abstract WritableSheet[] getSheets(); + + /** + * Gets the sheet names + * + * @return an array of strings containing the sheet names + */ + public abstract String[] getSheetNames(); + + /** + * Gets the specified sheet within this workbook + * + * @param index the zero based index of the reQuired sheet + * @return The sheet specified by the index + * @throws IndexOutOfBoundsException when index refers to a non-existent + * sheet + */ + public abstract WritableSheet getSheet(int index) + throws IndexOutOfBoundsException; + + /** + * Gets the sheet with the specified name from within this workbook + * + * @param name the sheet name + * @return The sheet with the specified name, or null if it is not found + */ + public abstract WritableSheet getSheet(String name); + + /** + * Returns the cell for the specified location eg. "Sheet1!A4". + * This is identical to using the CellReferenceHelper with its + * associated performance overheads, consequently it should + * be use sparingly + * + * @param loc the cell to retrieve + * @return the cell at the specified location + */ + public abstract WritableCell getWritableCell(String loc); + + /** + * Returns the number of sheets in this workbook + * + * @return the number of sheets in this workbook + */ + public abstract int getNumberOfSheets(); + + /** + * Closes this workbook, and makes any memory allocated available + * for garbage collection. Also closes the underlying output stream + * if necessary. + * + * @throws IOException + * @throws WriteException + */ + public abstract void close() throws IOException, WriteException; + + /** + * Creates, and returns a worksheet at the specified position + * with the specified name + * If the index specified is less than or equal to zero, the new sheet + * is created at the beginning of the workbook. If the index is greater + * than the number of sheet, then the sheet is created at the + * end of the workbook. + * + * @param name the sheet name + * @param index the index number at which to insert + * @return the new sheet + */ + public abstract WritableSheet createSheet(String name, int index); + + /** + * Imports a sheet from a different workbook. Does a deep copy on all + * elements within that sheet + * + * @param name the name of the new sheet + * @param index the position for the new sheet within this workbook + * @param sheet the sheet (from another workbook) to merge into this one + * @return the new sheet + */ + public abstract WritableSheet importSheet(String name, int index, Sheet sheet); + + /** + * Copy sheet within the same workbook. The sheet specified is copied to + * the new sheet name at the position + * + * @param s the index of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(int s, String name, int index); + + /** + * Copies the specified sheet and places it at the index + * specified by the parameter + * + * @param s the name of the sheet to copy + * @param name the name of the new sheet + * @param index the position of the new sheet + */ + public abstract void copySheet(String s, String name, int index); + + /** + * Removes the sheet at the specified index from this workbook + * + * @param index the sheet index to remove + */ + public abstract void removeSheet(int index); + + /** + * Moves the specified sheet within this workbook to another index + * position. + * + * @param fromIndex the zero based index of the required sheet + * @param toIndex the zero based index of the required sheet + * @return the sheet that has been moved + */ + public abstract WritableSheet moveSheet(int fromIndex, int toIndex); + + /** + * Writes out the data held in this workbook in Excel format + * + * @throws IOException + */ + public abstract void write() throws IOException; + + /** + * Indicates whether or not this workbook is protected + * + * @param prot Protected flag + */ + public abstract void setProtected(boolean prot); + + /** + * Sets the RGB value for the specified colour for this workbook + * + * @param c the colour whose RGB value is to be overwritten + * @param r the red portion to set (0-255) + * @param g the green portion to set (0-255) + * @param b the blue portion to set (0-255) + */ + public abstract void setColourRGB(Colour c, int r, int g, int b); + + /** + * This method can be used to create a writable clone of some other + * workbook + * + * @param w the workdoock to copy + * @deprecated Copying now occurs implicitly as part of the overloaded + * factory method Workbook.createWorkbood + */ + public void copy(Workbook w) { + // Was an abstract method - leave the method body blank + } + + /** + * Gets the named cell from this workbook. The name refers to a + * range of cells, then the cell on the top left is returned. If + * the name cannot be, null is returned + * + * @param name the name of the cell/range to search for + * @return the cell in the top left of the range if found, NULL + * otherwise + */ + public abstract WritableCell findCellByName(String name); + + /** + * Gets the named range from this workbook. The Range object returns + * contains all the cells from the top left to the bottom right + * of the range. + * If the named range comprises an adjacent range, + * the Range[] will contain one object; for non-adjacent + * ranges, it is necessary to return an array of length greater than + * one. + * If the named range contains a single cell, the top left and + * bottom right cell will be the same cell + * + * @param name the name of the cell/range to search for + * @return the range of cells + */ + public abstract Range[] findByName(String name); + + /** + * Gets the named ranges + * + * @return the list of named cells within the workbook + */ + public abstract String[] getRangeNames(); + + /** + * Removes the specified named range from the workbook. Note that + * removing a name could cause formulas which use that name to + * calculate their results incorrectly + * + * @param name the name to remove + */ + public abstract void removeRangeName(String name); + + /** + * Add new named area to this workbook with the given information. + * + * @param name name to be created. + * @param sheet sheet containing the name + * @param firstCol first column this name refers to. + * @param firstRow first row this name refers to. + * @param lastCol last column this name refers to. + * @param lastRow last row this name refers to. + */ + public abstract void addNameArea(String name, + WritableSheet sheet, + int firstCol, + int firstRow, + int lastCol, + int lastRow); + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param fileName the file name + * @throws IOException + */ + public abstract void setOutputFile(java.io.File fileName) + throws IOException; +} diff --git a/datastructures-xslx/src/main/java/jxl/write/WriteException.java b/datastructures-xslx/src/main/java/jxl/write/WriteException.java new file mode 100755 index 0000000..a8a7035 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/WriteException.java @@ -0,0 +1,36 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write; + +import jxl.JXLException; + +/** + * Exception thrown when using the API to generate an Excel file + */ +public abstract class WriteException extends JXLException { + /** + * Constructs this exception with the specified message + * + * @param s the message + */ + protected WriteException(String s) { + super(s); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java new file mode 100755 index 0000000..7038876 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ArbitraryRecord.java @@ -0,0 +1,69 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * Writes out some arbitrary record data. Used during the debug process + */ +class ArbitraryRecord extends WritableRecordData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(ArbitraryRecord.class); + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param type the biff code + * @param d the data + */ + public ArbitraryRecord(int type, byte[] d) { + super(Type.createType(type)); + + data = d; + logger.warn("ArbitraryRecord of type " + type + " created"); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java new file mode 100755 index 0000000..ec24f80 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BOFRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record to indicate the beginning of a new stream in the Compound + * File + */ +class BOFRecord extends WritableRecordData { + public final static WorkbookGlobalsBOF workbookGlobals + = new WorkbookGlobalsBOF(); + public final static SheetBOF sheet = new SheetBOF(); + + /** + * The data to write to the file + */ + private final byte[] data; + + /** + * Constructor for generating a workbook globals BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(WorkbookGlobalsBOF dummy) { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // workbook globals + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x5, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + /** + * Constructor for generating a sheet BOF record + * + * @param dummy - a dummy argument for overloading purposes + */ + public BOFRecord(SheetBOF dummy) { + super(Type.BOF); + + // Create the data as biff 8 format with a substream type of + // worksheet + data = new byte[] + {(byte) 0x0, + (byte) 0x6, + (byte) 0x10, // substream type + (byte) 0x0, // substream type + (byte) 0xf2, // rupBuild + (byte) 0x15, // rupBuild + (byte) 0xcc, // rupYear + (byte) 0x07, // rupYear + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x0, // bfh + (byte) 0x6, // sfo + (byte) 0x0, // sfo, + (byte) 0x0, // sfo + (byte) 0x0 // sfo + }; + } + + /** + * Gets the data for writing to the output file + * + * @return the binary data for writing + */ + public byte[] getData() { + return data; + } + + // Dummy types for constructor overloading + private static class WorkbookGlobalsBOF { + } + + private static class SheetBOF { + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java new file mode 100755 index 0000000..45da57d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BackupRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether Excel should save backup versions of the + * file + */ +class BackupRecord extends WritableRecordData { + /** + * Flag to indicate whether or not Excel should make backups + */ + private final boolean backup; + /** + * The data array + */ + private final byte[] data; + + /** + * Constructor + * + * @param bu backup flag + */ + public BackupRecord(boolean bu) { + super(Type.BACKUP); + + backup = bu; + + // Hard code in an unprotected workbook + data = new byte[2]; + + if (backup) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Returns the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java new file mode 100755 index 0000000..8cdab87 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BlankRecord.java @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.Cell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A blank record, which is used to contain formatting information + */ +public abstract class BlankRecord extends CellValue { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(BlankRecord.class); + + /** + * Consructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected BlankRecord(int c, int r) { + super(Type.BLANK, c, r); + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param r the row + * @param st the format applied to the cell + */ + protected BlankRecord(int c, int r, CellFormat st) { + super(Type.BLANK, c, r, st); + } + + /** + * Constructor used when copying a formatted blank cell from a read only + * spreadsheet + * + * @param c the blank cell to copy + */ + protected BlankRecord(Cell c) { + super(Type.BLANK, c); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param b the record to copy + */ + protected BlankRecord(int c, int r, BlankRecord br) { + super(Type.BLANK, c, r, br); + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.EMPTY; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * + * @return the contents of this cell as a string + */ + public String getContents() { + return ""; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java new file mode 100755 index 0000000..8d21708 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BookboolRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Writes out the workbook option flag (should it save the external + * link options) + */ +class BookboolRecord extends WritableRecordData { + /** + * The external link option flag + */ + private final boolean externalLink; + /** + * The binary data to write out + */ + private final byte[] data; + + /** + * Constructor + * + * @param extlink the external link options flag + */ + public BookboolRecord(boolean extlink) { + super(Type.BOOKBOOL); + + externalLink = extlink; + data = new byte[2]; + + if (!externalLink) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java new file mode 100755 index 0000000..cdbcfc1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BooleanRecord.java @@ -0,0 +1,150 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + + +import jxl.BooleanCell; +import jxl.CellType; +import jxl.biff.Type; +import jxl.format.CellFormat; + + +/** + * A boolean cell's last calculated value + */ +public abstract class BooleanRecord extends CellValue { + /** + * The boolean value of this cell. If this cell represents an error, + * this will be false + */ + private boolean value; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected BooleanRecord(int c, int r, boolean val) { + super(Type.BOOLERR, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected BooleanRecord(int c, int r, boolean val, CellFormat st) { + super(Type.BOOLERR, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected BooleanRecord(BooleanCell nc) { + super(Type.BOOLERR, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param br the record to copy + */ + protected BooleanRecord(int c, int r, BooleanRecord br) { + super(Type.BOOLERR, c, r, br); + value = br.value; + } + + /** + * Interface method which Gets the boolean value stored in this cell. If + * this cell contains an error, then returns FALSE. Always query this cell + * type using the accessor method isError() prior to calling this method + * + * @return TRUE if this cell contains TRUE, FALSE if it contains FALSE or + * an error code + */ + public boolean getValue() { + return value; + } + + /** + * Sets the value + * + * @param val the boolean value + */ + protected void setValue(boolean val) { + value = val; + } + + /** + * Returns the numerical value as a string + * + * @return The numerical value of the formula as a string + */ + public String getContents() { + // return Boolean.toString(value) - only available in 1.4 or later + return (new Boolean(value)).toString(); + } + + /** + * Returns the cell type + * + * @return The cell type + */ + public CellType getType() { + return CellType.BOOLEAN; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 2]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + + if (value) { + data[celldata.length] = 1; + } + + return data; + } + +} + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java new file mode 100755 index 0000000..d7b2bdd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BottomMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class BottomMarginRecord extends MarginRecord { + BottomMarginRecord(double v) { + super(Type.BOTTOMMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java new file mode 100755 index 0000000..a626301 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/BoundsheetRecord.java @@ -0,0 +1,110 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the sheet name, the sheet type and the stream + * position + */ +class BoundsheetRecord extends WritableRecordData { + /** + * Hidden flag + */ + private boolean hidden; + + /** + * Chart only flag + */ + private boolean chartOnly; + + /** + * The name of the sheet + */ + private final String name; + + /** + * The data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param n the sheet name + */ + public BoundsheetRecord(String n) { + super(Type.BOUNDSHEET); + name = n; + hidden = false; + chartOnly = false; + } + + /** + * Sets the hidden flag + */ + void setHidden() { + hidden = true; + } + + /** + * Sets the chart only flag + */ + void setChartOnly() { + chartOnly = true; + } + + /** + * Gets the data to write out to the binary file + * + * @return the data to write out + */ + public byte[] getData() { + data = new byte[name.length() * 2 + 8]; + + if (chartOnly) { + data[5] = 0x02; + } else { + data[5] = 0; // set stream type to worksheet + } + + if (hidden) { + data[4] = 0x1; + data[5] = 0x0; + } + + data[6] = (byte) name.length(); + data[7] = 1; + StringHelper.getUnicodeBytes(name, data, 8); + + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java new file mode 100755 index 0000000..8d1a8e6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ButtonPropertySetRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Any arbitrary excel record. Used during development only + */ +class ButtonPropertySetRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public ButtonPropertySetRecord(jxl.read.biff.ButtonPropertySetRecord bps) { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Constructor + */ + public ButtonPropertySetRecord(ButtonPropertySetRecord bps) { + super(Type.BUTTONPROPERTYSET); + + data = bps.getData(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java new file mode 100755 index 0000000..dda09ae --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CalcCountRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum iterations option from the Options + * dialog box + */ +class CalcCountRecord extends WritableRecordData { + /** + * The iteration count + */ + private final int calcCount; + /** + * The binary data to write to the output file + */ + private byte[] data; + + /** + * Constructor + * + * @param cnt the count indicator + */ + public CalcCountRecord(int cnt) { + super(Type.CALCCOUNT); + calcCount = cnt; + } + + + /** + * Gets the data to write out to the file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calcCount, data, 0); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java new file mode 100755 index 0000000..81ee47a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CalcModeRecord.java @@ -0,0 +1,87 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The calculation mode for the workbook, as set from the Options + * dialog box + */ +class CalcModeRecord extends WritableRecordData { + /** + * Manual calculation + */ + static CalcMode manual = new CalcMode(0); + /** + * Automatic calculation + */ + static CalcMode automatic = new CalcMode(1); + /** + * Automatic calculation, except tables + */ + static CalcMode automaticNoTables = new CalcMode(-1); + /** + * The calculation mode (manual, automatic) + */ + private final CalcMode calculationMode; + /** + * Constructor + * + * @param cm the calculation mode + */ + public CalcModeRecord(CalcMode cm) { + super(Type.CALCMODE); + calculationMode = cm; + } + + /** + * Gets the binary to data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2]; + + IntegerHelper.getTwoBytes(calculationMode.value, data, 0); + + return data; + } + + private static class CalcMode { + /** + * The indicator as written to the output file + */ + int value; + + /** + * Constructor + * + * @param m + */ + public CalcMode(int m) { + value = m; + } + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java b/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java new file mode 100755 index 0000000..87dbb52 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CellValue.java @@ -0,0 +1,595 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.Cell; +import jxl.CellFeatures; +import jxl.CellReferenceHelper; +import jxl.Sheet; +import jxl.biff.DVParser; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.NumFormatRecordsException; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; +import jxl.biff.drawing.ComboBox; +import jxl.biff.drawing.Comment; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.WritableCell; +import jxl.write.WritableCellFeatures; +import jxl.write.WritableWorkbook; + +/** + * Abstract class which stores the jxl.common.data used for cells, such + * as row, column and formatting information. + * Any record which directly represents the contents of a cell, such + * as labels and numbers, are derived from this class + * data store + */ +public abstract class CellValue extends WritableRecordData + implements WritableCell { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CellValue.class); + + /** + * The row in the worksheet at which this cell is located + */ + private int row; + + /** + * The column in the worksheet at which this cell is located + */ + private int column; + + /** + * The format applied to this cell + */ + private XFRecord format; + + /** + * A handle to the formatting records, used in case we want + * to change the format of the cell once it has been added + * to the spreadsheet + */ + private FormattingRecords formattingRecords; + + /** + * A flag to indicate that this record is already referenced within + * a worksheet + */ + private boolean referenced; + + /** + * A handle to the sheet + */ + private WritableSheetImpl sheet; + + /** + * The cell features + */ + private WritableCellFeatures features; + + /** + * Internal copied flag, to prevent cell features being added multiple + * times to the drawing array + */ + private boolean copied; + + /** + * Constructor used when building writable cells from the Java API + * + * @param c the column + * @param t the type indicator + * @param r the row + */ + protected CellValue(Type t, int c, int r) { + this(t, c, r, WritableWorkbook.NORMAL_STYLE); + copied = false; + } + + /** + * Constructor used when creating a writable cell from a read-only cell + * (when copying a workbook) + * + * @param c the cell to clone + * @param t the type of this cell + */ + protected CellValue(Type t, Cell c) { + this(t, c.getColumn(), c.getRow()); + copied = true; + + format = (XFRecord) c.getCellFormat(); + + if (c.getCellFeatures() != null) { + features = new WritableCellFeatures(c.getCellFeatures()); + features.setWritableCell(this); + } + } + + /** + * Overloaded constructor used when building writable cells from the + * Java API which also takes a format + * + * @param c the column + * @param t the cell type + * @param r the row + * @param st the format to apply to this cell + */ + protected CellValue(Type t, int c, int r, CellFormat st) { + super(t); + row = r; + column = c; + format = (XFRecord) st; + referenced = false; + copied = false; + } + + /** + * Copy constructor + * + * @param c the column + * @param t the cell type + * @param r the row + * @param cv the value to copy + */ + protected CellValue(Type t, int c, int r, CellValue cv) { + super(t); + row = r; + column = c; + format = cv.format; + referenced = false; + copied = false; // used during a deep copy, so the cell features need + // to be added again + + if (cv.features != null) { + features = new WritableCellFeatures(cv.features); + features.setWritableCell(this); + } + } + + /** + * Returns the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() { + return row; + } + + /** + * Returns the column number of this cell + * + * @return the column number of this cell + */ + public int getColumn() { + return column; + } + + /** + * Indicates whether or not this cell is hidden, by virtue of either + * the entire row or column being collapsed + * + * @return TRUE if this cell is hidden, FALSE otherwise + */ + public boolean isHidden() { + ColumnInfoRecord cir = sheet.getColumnInfo(column); + + if (cir != null && cir.getWidth() == 0) { + return true; + } + + RowRecord rr = sheet.getRowInfo(row); + + return rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed()); + } + + /** + * Gets the data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] mydata = new byte[6]; + IntegerHelper.getTwoBytes(row, mydata, 0); + IntegerHelper.getTwoBytes(column, mydata, 2); + IntegerHelper.getTwoBytes(format.getXFIndex(), mydata, 4); + return mydata; + } + + /** + * Called when the cell is added to the worksheet in order to indicate + * that this object is already added to the worksheet + * This method also verifies that the associated formats and formats + * have been initialized correctly + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + referenced = true; + sheet = s; + formattingRecords = fr; + + addCellFormat(); + addCellFeatures(); + } + + /** + * Internal method to see if this cell is referenced within the workbook. + * Once this has been placed in the workbook, it becomes immutable + * + * @return TRUE if this cell has been added to a sheet, FALSE otherwise + */ + final boolean isReferenced() { + return referenced; + } + + /** + * Gets the internal index of the formatting record + * + * @return the index of the format record + */ + final int getXFIndex() { + return format.getXFIndex(); + } + + /** + * API method which gets the format applied to this cell + * + * @return the format for this cell + */ + public CellFormat getCellFormat() { + return format; + } + + /** + * An API function which sets the format to apply to this cell + * + * @param cf the format to apply to this cell + */ + public void setCellFormat(CellFormat cf) { + format = (XFRecord) cf; + + // If the referenced flag has not been set, this cell has not + // been added to the spreadsheet, so we don't need to perform + // any further logic + if (!referenced) { + return; + } + + // The cell has already been added to the spreadsheet, so the + // formattingRecords reference must be initialized + Assert.verify(formattingRecords != null); + + addCellFormat(); + } + + /** + * Increments the row of this cell by one. Invoked by the sheet when + * inserting rows + */ + void incrementRow() { + row++; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + } + + /** + * Decrements the row of this cell by one. Invoked by the sheet when + * removing rows + */ + void decrementRow() { + row--; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + + if (features.hasDropDown()) { + logger.warn("need to change value for drop down drawing"); + } + } + } + + /** + * Increments the column of this cell by one. Invoked by the sheet when + * inserting columns + */ + void incrementColumn() { + column++; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Decrements the column of this cell by one. Invoked by the sheet when + * removing columns + */ + void decrementColumn() { + column--; + + if (features != null) { + Comment c = features.getCommentDrawing(); + if (c != null) { + c.setX(column); + c.setY(row); + } + } + + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) { + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) { + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) { + } + + /** + * Accessor for the sheet containing this cell + * + * @return the sheet containing this cell + */ + public WritableSheetImpl getSheet() { + return sheet; + } + + /** + * Adds the format information to the shared records. Performs the necessary + * checks (and clones) to ensure that the formats are not shared. + * Called from setCellDetails and setCellFormat + */ + private void addCellFormat() { + // Check to see if the format is one of the shared Workbook defaults. If + // so, then get hold of the Workbook's specific instance + Styles styles = sheet.getWorkbook().getStyles(); + format = styles.getFormat(format); + + try { + if (!format.isInitialized()) { + formattingRecords.addStyle(format); + } + } catch (NumFormatRecordsException e) { + logger.warn("Maximum number of format records exceeded. Using " + + "default format."); + format = styles.getNormalStyle(); + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public CellFeatures getCellFeatures() { + return features; + } + + /** + * Sets the cell features + * + * @param cf the cell features + */ + public void setCellFeatures(WritableCellFeatures cf) { + if (features != null) { + logger.warn("current cell features for " + + CellReferenceHelper.getCellReference(this) + + " not null - overwriting"); + + // Check to see if the features include a shared data validation + if (features.hasDataValidation() && + features.getDVParser() != null && + features.getDVParser().extendedCellsValidation()) { + DVParser dvp = features.getDVParser(); + logger.warn("Cannot add cell features to " + + CellReferenceHelper.getCellReference(this) + + " because it is part of the shared cell validation " + + "group " + + CellReferenceHelper.getCellReference(dvp.getFirstColumn(), + dvp.getFirstRow()) + + "-" + + CellReferenceHelper.getCellReference(dvp.getLastColumn(), + dvp.getLastRow())); + return; + } + } + + features = cf; + cf.setWritableCell(this); + + // If the cell is already on the worksheet, then add the cell features + // to the workbook + if (referenced) { + addCellFeatures(); + } + } + + /** + * Accessor for the cell features + * + * @return the cell features or NULL if this cell doesn't have any + */ + public WritableCellFeatures getWritableCellFeatures() { + return features; + } + + /** + * Handles any addition cell features, such as comments or data + * validation. Called internally from this class when a cell is + * added to the workbook, and also externally from BaseCellFeatures + * following a call to setComment + */ + public final void addCellFeatures() { + if (features == null) { + return; + } + + if (copied == true) { + copied = false; + + return; + } + + if (features.getComment() != null) { + Comment comment = new Comment(features.getComment(), + column, row); + comment.setWidth(features.getCommentWidth()); + comment.setHeight(features.getCommentHeight()); + sheet.addDrawing(comment); + sheet.getWorkbook().addDrawing(comment); + features.setCommentDrawing(comment); + } + + if (features.hasDataValidation()) { + try { + features.getDVParser().setCell(column, + row, + sheet.getWorkbook(), + sheet.getWorkbook(), + sheet.getWorkbookSettings()); + } catch (jxl.biff.formula.FormulaException e) { + Assert.verify(false); + } + + sheet.addValidationCell(this); + if (!features.hasDropDown()) { + return; + } + + // Get the combo box drawing object for list validations + if (sheet.getComboBox() == null) { + // Need to add the combo box the first time, since even though + // it doesn't need a separate Sp entry, it still needs to increment + // the shape id + ComboBox cb = new ComboBox(); + sheet.addDrawing(cb); + sheet.getWorkbook().addDrawing(cb); + sheet.setComboBox(cb); + } + + features.setComboBox(sheet.getComboBox()); + } + } + + /** + * Internal function invoked by WritableSheetImpl called when shared data + * validation is removed + */ + public final void removeCellFeatures() { + /* + // Remove the comment + features.removeComment(); + + // Remove the data validation + features.removeDataValidation(); + */ + + features = null; + } + + + /** + * Called by the cell features to remove a comment + * + * @param c the comment to remove + */ + public final void removeComment(Comment c) { + sheet.removeDrawing(c); + } + + /** + * Called by the cell features to remove the data validation + */ + public final void removeDataValidation() { + sheet.removeDataValidation(this); + } + + /** + * Called when doing a copy of a writable object to indicate the source + * was writable than a read only copy and certain things (most notably + * the comments will need to be re-evaluated) + * + * @param boolean the copied flag + */ + final void setCopied(boolean c) { + copied = c; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java new file mode 100755 index 0000000..07bd727 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CellXFRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DisplayFormat; +import jxl.biff.FontRecord; +import jxl.biff.XFRecord; +import jxl.format.Alignment; +import jxl.format.Border; +import jxl.format.BorderLineStyle; +import jxl.format.CellFormat; +import jxl.format.Colour; +import jxl.format.Orientation; +import jxl.format.Pattern; +import jxl.format.VerticalAlignment; +import jxl.write.WriteException; + +/** + * A cell XF Record + */ +public class CellXFRecord extends XFRecord { + /** + * Constructor + * + * @param fnt the font + * @param form the format + */ + protected CellXFRecord(FontRecord fnt, DisplayFormat form) { + super(fnt, form); + setXFDetails(XFRecord.cell, 0); + } + + /** + * Copy constructor. Invoked when copying formats to handle cell merging + * + * @param fmt the format to copy + */ + CellXFRecord(XFRecord fmt) { + super(fmt); + setXFDetails(XFRecord.cell, 0); + } + + /** + * A public copy constructor which can be used for copy formats between + * different sheets + */ + protected CellXFRecord(CellFormat format) { + super(format); + } + + /** + * Sets the alignment for the cell + * + * @param a the alignment + * @throws WriteException + */ + public void setAlignment(Alignment a) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFAlignment(a); + } + + /** + * Sets the background for the cell + * + * @param c the background colour + * @param p the background patter + * @throws WriteException + */ + public void setBackground(Colour c, Pattern p) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFBackground(c, p); + super.setXFCellOptions(0x4000); + } + + /** + * Sets whether or not this XF record locks the cell + * + * @param l the locked flag + * @throws WriteException + */ + public void setLocked(boolean l) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFLocked(l); + super.setXFCellOptions(0x8000); + } + + /** + * Sets the indentation of the cell text + * + * @param i the indentation + */ + public void setIndentation(int i) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFIndentation(i); + } + + /** + * Sets the shrink to fit flag + * + * @param b the shrink to fit flag + */ + public void setShrinkToFit(boolean s) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + super.setXFShrinkToFit(s); + } + + /** + * Sets the vertical alignment for cells with this style + * + * @param va the vertical alignment + * @throws WriteException + */ + public void setVerticalAlignment(VerticalAlignment va) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFVerticalAlignment(va); + } + + /** + * Sets the text orientation for cells with this style + * + * @param o the orientation + * @throws WriteException + */ + public void setOrientation(Orientation o) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFOrientation(o); + } + + /** + * Sets the text wrapping for cells with this style. If the parameter is + * set to TRUE, then data in this cell will be wrapped around, and the + * cell's height adjusted accordingly + * + * @param w the wrap + * @throws WriteException + */ + public void setWrap(boolean w) throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + super.setXFWrap(w); + } + + /** + * Sets the border style for cells with this format + * + * @param b the border + * @param ls the line for the specified border + * @throws WriteException + */ + public void setBorder(Border b, BorderLineStyle ls, Colour c) + throws WriteException { + if (isInitialized()) { + throw new JxlWriteException(JxlWriteException.formatInitialized); + } + + if (b == Border.ALL) { + // Apply to all + super.setXFBorder(Border.LEFT, ls, c); + super.setXFBorder(Border.RIGHT, ls, c); + super.setXFBorder(Border.TOP, ls, c); + super.setXFBorder(Border.BOTTOM, ls, c); + return; + } + + if (b == Border.NONE) { + // Apply to all + super.setXFBorder(Border.LEFT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.RIGHT, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.TOP, BorderLineStyle.NONE, Colour.BLACK); + super.setXFBorder(Border.BOTTOM, BorderLineStyle.NONE, Colour.BLACK); + return; + } + + super.setXFBorder(b, ls, c); + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java new file mode 100755 index 0000000..73ff9fe --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CodepageRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the default character set in operation when the workbook was + * saved + */ +class CodepageRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public CodepageRecord() { + super(Type.CODEPAGE); + + // Hard code inthe ANSI character set for Microsoft + data = new byte[]{(byte) 0xe4, (byte) 0x4}; + } + + /** + * Retrieves the data for output to binary file + * + * @return the data to be written + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java new file mode 100755 index 0000000..bc14e47 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnInfoRecord.java @@ -0,0 +1,389 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + + +import jxl.biff.FormattingRecords; +import jxl.biff.IndexMapping; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.biff.XFRecord; + +/** + * Describes the column formatting for a particular column + */ +class ColumnInfoRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The column number which this format applies to + */ + private int column; + /** + * The style for the column + */ + private XFRecord style; + /** + * The index for the style of this column + */ + private int xfIndex; + + /** + * The width of the column in 1/256 of a character + */ + private int width; + + /** + * Flag to indicate the hidden status of this column + */ + private boolean hidden; + + /** + * The column's outline level + */ + private int outlineLevel; + + /** + * Column collapsed flag + */ + private boolean collapsed; + + + /** + * Constructor used when setting column information from the user + * API + * + * @param w the width of the column in characters + * @param col the column to format + * @param xf the style for the column + */ + public ColumnInfoRecord(int col, int w, XFRecord xf) { + super(Type.COLINFO); + + column = col; + width = w; + style = xf; + xfIndex = style.getXFIndex(); + hidden = false; + } + + /** + * Copy constructor used when copying from sheet to sheet within the + * same workbook + * + * @param the record to copy + */ + public ColumnInfoRecord(ColumnInfoRecord cir) { + super(Type.COLINFO); + + column = cir.column; + width = cir.width; + style = cir.style; + xfIndex = cir.xfIndex; + hidden = cir.hidden; + outlineLevel = cir.outlineLevel; + collapsed = cir.collapsed; + + } + + + /** + * Constructor used when copying an existing spreadsheet + * + * @param col the column number + * @param cir the column info record read in + * @param fr the format records + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col, + FormattingRecords fr) { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + style = fr.getXFRecord(xfIndex); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Constructor used when importing a sheet from another + * spreadsheet + * + * @param col the column number + * @param cir the column info record read in + */ + public ColumnInfoRecord(jxl.read.biff.ColumnInfoRecord cir, + int col) { + super(Type.COLINFO); + + column = col; + width = cir.getWidth(); + xfIndex = cir.getXFIndex(); + outlineLevel = cir.getOutlineLevel(); + collapsed = cir.getCollapsed(); + } + + /** + * Gets the column this format applies to + * + * @return the column which is formatted + */ + public int getColumn() { + return column; + } + + /** + * Increments the column. Called when inserting a new column into + * the sheet + */ + public void incrementColumn() { + column++; + } + + /** + * Decrements the column. Called when removing a column from + * the sheet + */ + public void decrementColumn() { + column--; + } + + /** + * Accessor for the width + * + * @return the width + */ + int getWidth() { + return width; + } + + /** + * Sets the width. Used when autosizing columns + * + * @param w the new width + */ + void setWidth(int w) { + width = w; + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() { + data = new byte[0x0c]; + + IntegerHelper.getTwoBytes(column, data, 0); + IntegerHelper.getTwoBytes(column, data, 2); + IntegerHelper.getTwoBytes(width, data, 4); + IntegerHelper.getTwoBytes(xfIndex, data, 6); + + // int options = 0x2; + int options = 0x6 | (outlineLevel << 8); + if (hidden) { + options |= 0x1; + } + + outlineLevel = ((options & 0x700) / 0x100); + + if (collapsed) { + options |= 0x1000; + } + + IntegerHelper.getTwoBytes(options, data, 8); + // IntegerHelper.getTwoBytes(2, data, 10); + + return data; + } + + /** + * Gets the cell format associated with this column info record + * + * @return the cell format for this column + */ + public XFRecord getCellFormat() { + return style; + } + + /** + * Sets the cell format. Used when importing spreadsheets + * + * @param xfr the xf record + */ + public void setCellFormat(XFRecord xfr) { + style = xfr; + } + + /** + * Accessor for the xf index, used when importing from another spreadsheet + * + * @return the xf index + */ + public int getXfIndex() { + return xfIndex; + } + + /** + * Rationalizes the sheets xf index mapping + * + * @param xfmapping the index mapping + */ + void rationalize(IndexMapping xfmapping) { + xfIndex = xfmapping.getNewIndex(xfIndex); + } + + /** + * Accessor for the hidden flag + * + * @return TRUE if this column is hidden, FALSE otherwise + */ + boolean getHidden() { + return hidden; + } + + /** + * Sets this column to be hidden (or otherwise) + * + * @param h TRUE if the column is to be hidden, FALSE otherwise + */ + void setHidden(boolean h) { + hidden = h; + } + + /** + * Standard equals method + * + * @return TRUE if these objects are equal, FALSE otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ColumnInfoRecord)) { + return false; + } + + ColumnInfoRecord cir = (ColumnInfoRecord) o; + + if (column != cir.column || + xfIndex != cir.xfIndex || + width != cir.width || + hidden != cir.hidden || + outlineLevel != cir.outlineLevel || + collapsed != cir.collapsed) { + return false; + } + + if ((style == null && cir.style != null) || + (style != null && cir.style == null)) { + return false; + } + + return style.equals(cir.style); + } + + /** + * Standard hashCode method + * + * @return the hashCode + */ + public int hashCode() { + int hashValue = 137; + int oddPrimeNumber = 79; + + hashValue = hashValue * oddPrimeNumber + column; + hashValue = hashValue * oddPrimeNumber + xfIndex; + hashValue = hashValue * oddPrimeNumber + width; + hashValue = hashValue * oddPrimeNumber + (hidden ? 1 : 0); + + if (style != null) { + hashValue ^= style.hashCode(); + } + + return hashValue; + } + + /** + * Accessor for the column's outline level + * + * @return the column's outline level + */ + public int getOutlineLevel() { + return outlineLevel; + } + + /** + * Sets the column's outline level + * + * @param level the column's outline level + */ + public void setOutlineLevel(int level) { + outlineLevel = level; + } + + /** + * Accessor for whether the column is collapsed + * + * @return the column's collapsed state + */ + public boolean getCollapsed() { + return collapsed; + } + + /** + * Sets the column's collapsed state + * + * @param value the column's collapsed state + */ + public void setCollapsed(boolean value) { + collapsed = value; + } + + /** + * Increments the column's outline level. This is how groups are made + * as well + */ + public void incrementOutlineLevel() { + outlineLevel++; + } + + /** + * Decrements the column's outline level. This removes it from a + * grouping level. If + * all outline levels are gone the uncollapse the column. + */ + public void decrementOutlineLevel() { + if (0 < outlineLevel) { + outlineLevel--; + } + + if (0 == outlineLevel) { + collapsed = false; + } + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java new file mode 100755 index 0000000..e4b5cd0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ColumnsExceededException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2005 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to add a column to a spreadsheet which + * has already reached the maximum amount + */ +public class ColumnsExceededException extends JxlWriteException { + /** + * Constructor + */ + public ColumnsExceededException() { + super(maxColumnsExceeded); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java new file mode 100755 index 0000000..2cee9d6 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java @@ -0,0 +1,1056 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + *
+ * Header block is -1 + * Excel data is e..n (where e is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private final OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private final ExcelDataOutput excelData; + + /** + * The size of the array + */ + private final int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private final int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private final int numExtensionBlocks; + + /** + * The extension block for the header + */ + private final int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private final int excelDataBlocks; + + /** + * The start block of the root entry + */ + private final int rootStartBlock; + + /** + * The start block of the excel data + */ + private final int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private final int bbdStartBlock; + + /** + * The start block of the small block depot + */ + private int sbdStartBlockChain; + + /** + * The start block of the small block depot + */ + private int sbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The number of small blocks + */ + private int numSmallBlocks; + + /** + * The total number of property sets in this compound file + */ + private final int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * The map of standard property sets, keyed on name + */ + private HashMap standardPropertySets; + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(ExcelDataOutput data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + + if (additionalPropertySets != null) { + numSmallBlockDepotChainBlocks = getBigBlocksRequired(numSmallBlocks * 4); + numSmallBlockDepotBlocks = getBigBlocksRequired + (numSmallBlocks * SMALL_BLOCK_SIZE); + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) { + requiredSize = SMALL_BLOCK_THRESHOLD; + } else { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + + // Do the calculations + excelDataBlocks = requiredSize / BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS) / 4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numSmallBlockDepotBlocks + + numSmallBlockDepotChainBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1) { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE / 4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil((double) totalBlocks / + (double) (BIG_BLOCK_SIZE / 4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } else { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + // Set the start block of the small block depot + sbdStartBlock = -2; + if (additionalPropertySets != null && numSmallBlockDepotBlocks != 0) { + sbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the sbd chain start block to be after the excel data and the + // small block depot + sbdStartBlockChain = -2; + + if (sbdStartBlock != -2) { + sbdStartBlockChain = sbdStartBlock + numSmallBlockDepotBlocks; + } + + // Set the bbd start block to be after all the excel data + if (sbdStartBlockChain != -2) { + bbdStartBlock = sbdStartBlockChain + + numSmallBlockDepotChainBlocks; + } else { + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + } + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @param readCompoundFile the file read in + * @throws CopyAdditionalPropertySetsException + * @throws IOException + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException { + if (readCompoundFile == null) { + return; + } + + additionalPropertySets = new ArrayList(); + standardPropertySets = new HashMap(); + int blocksRequired = 0; + + int numPropertySets = readCompoundFile.getNumberOfPropertySets(); + + for (int i = 0; i < numPropertySets; i++) { + PropertyStorage ps = readCompoundFile.getPropertySet(i); + + boolean standard = false; + + if (ps.name.equalsIgnoreCase(ROOT_ENTRY_NAME)) { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(ROOT_ENTRY_NAME, rps); + } + + // See if it is a standard property set + for (int j = 0; j < STANDARD_PROPERTY_SETS.length && !standard; j++) { + if (ps.name.equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) { + // See if it comes directly off the root entry + PropertyStorage ps2 = readCompoundFile.findPropertyStorage(ps.name); + Assert.verify(ps2 != null); + + if (ps2 == ps) { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + standardPropertySets.put(STANDARD_PROPERTY_SETS[j], rps); + } + } + } + + if (!standard) { + try { + byte[] data = null; + if (ps.size > 0) { + data = readCompoundFile.getStream(i); + } else { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + additionalPropertySets.add(rps); + + if (data.length > SMALL_BLOCK_THRESHOLD) { + int blocks = getBigBlocksRequired(data.length); + blocksRequired += blocks; + } else { + int blocks = getSmallBlocksRequired(data.length); + numSmallBlocks += blocks; + } + } catch (BiffException e) { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + /** + * Writes out the excel file in OLE compound file format + * + * @throws IOException + */ + public void write() throws IOException { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeSmallBlockDepot(); + writeSmallBlockDepotChain(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException { + if (additionalPropertySets == null) { + return; + } + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + if (data.length > SMALL_BLOCK_THRESHOLD) { + int numBlocks = getBigBlocksRequired(data.length); + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @throws IOException + */ + private void writeExcelData() throws IOException { + excelData.writeData(out); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @throws IOException + */ + private void writeDocumentSummaryData() throws IOException { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @throws IOException + */ + private void writeSummaryData() throws IOException { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @throws IOException + */ + private void writeHeader() throws IOException { + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain + IntegerHelper.getFourBytes(sbdStartBlockChain, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the number of blocks in the small block depot chain + IntegerHelper.getFourBytes(numSmallBlockDepotChainBlocks, + headerBlock, + NUM_SMALL_BLOCK_DEPOT_BLOCKS_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS) / 4); + int blocksWritten = 0; + + for (int i = 0; i < blocksToWrite; i++) { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE / 4 - 1); + + for (int j = 0; j < blocksToWrite; j++) { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock + 1; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos += 4; + } + + if (numExtensionBlocks > 0) { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @throws IOException + */ + private void checkBbdPos() throws IOException { + if (bbdPos >= BIG_BLOCK_SIZE) { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @throws IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos) / 4); + + for (int i = 0; i < bbdBlocks; i++) { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos += 4; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @throws IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException { + if (additionalPropertySets == null) { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.data.length > SMALL_BLOCK_THRESHOLD) { + int numBlocks = getBigBlocksRequired(rps.data.length); + + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + } + + /** + * Writes out the chains for the small block depot + */ + private void writeSmallBlockDepotChain() throws IOException { + if (sbdStartBlockChain == -2) { + return; + } + + byte[] smallBlockDepotChain = + new byte[numSmallBlockDepotChainBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + int sbdBlockNumber = 1; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD && + rps.data.length != 0) { + int numSmallBlocks = getSmallBlocksRequired(rps.data.length); + for (int j = 0; j < numSmallBlocks - 1; j++) { + IntegerHelper.getFourBytes(sbdBlockNumber, + smallBlockDepotChain, + pos); + pos += 4; + sbdBlockNumber++; + } + + // Write out the end of chain + IntegerHelper.getFourBytes(-2, smallBlockDepotChain, pos); + pos += 4; + sbdBlockNumber++; + } + } + + out.write(smallBlockDepotChain); + } + + /** + * Writes out all the data in the small block depot + * + * @throws + */ + private void writeSmallBlockDepot() throws IOException { + if (additionalPropertySets == null) { + return; + } + + byte[] smallBlockDepot = + new byte[numSmallBlockDepotBlocks * BIG_BLOCK_SIZE]; + + int pos = 0; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + if (rps.data.length <= SMALL_BLOCK_THRESHOLD) { + int smallBlocks = getSmallBlocksRequired(rps.data.length); + int length = smallBlocks * SMALL_BLOCK_SIZE; + System.arraycopy(rps.data, 0, smallBlockDepot, pos, rps.data.length); + pos += length; + } + } + + out.write(smallBlockDepot); + } + + /** + * Writes out the Big Block Depot + * + * @throws IOException + */ + private void writeBigBlockDepot() throws IOException { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0; i < numExtensionBlocks; i++) { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + if (sbdStartBlock != -2) { + // Write out the block chain for the small block depot + writeBlockChain(sbdStartBlock, numSmallBlockDepotBlocks); + + // Write out the block chain for the small block depot chain + writeBlockChain(sbdStartBlockChain, numSmallBlockDepotChainBlocks); + } + + // The Big Block Depot immediately follows. Denote these as a special + // block + for (int i = 0; i < numBigBlockDepotBlocks; i++) { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0) ? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0) ? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @throws IOException + */ + private void writePropertySets() throws IOException { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0; i < STANDARD_PROPERTY_SETS.length; i++) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(STANDARD_PROPERTY_SETS[i]); + + if (rps != null) { + mappings[rps.number] = i; + } else { + logger.warn("Standard property set " + STANDARD_PROPERTY_SETS[i] + + " not present in source file"); + } + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int child = 0; + int previous = 0; + int next = 0; + + // Compute the size of the root property set + int size = 0; + + if (additionalPropertySets != null) { + // Workbook + size += getBigBlocksRequired(requiredSize) * BIG_BLOCK_SIZE; + + // The two information blocks + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + size += getBigBlocksRequired(SMALL_BLOCK_THRESHOLD) * BIG_BLOCK_SIZE; + + // Additional property sets + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + if (rps.propertyStorage.type != 1) { + if (rps.propertyStorage.size >= SMALL_BLOCK_THRESHOLD) { + size += getBigBlocksRequired(rps.propertyStorage.size) * + BIG_BLOCK_SIZE; + } else { + size += getSmallBlocksRequired(rps.propertyStorage.size) * + SMALL_BLOCK_SIZE; + } + } + } + } + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(sbdStartBlock); + ps.setSize(size); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + child = 1; + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(ROOT_ENTRY_NAME); + child = mappings[rps.propertyStorage.child]; + } + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // always use a big block stream - none of that messing around + // with small blocks + + previous = 3; + next = -1; + + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(WORKBOOK_NAME); + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) { + ReadPropertyStorage rps = (ReadPropertyStorage) + standardPropertySets.get(SUMMARY_INFORMATION_NAME); + + if (rps != null) { + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + } + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setChild(-1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Write out the additional property sets + if (additionalPropertySets == null) { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16; + int smallBlock = 0; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int block = rps.data.length > SMALL_BLOCK_THRESHOLD ? + bigBlock : smallBlock; + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(block); + ps.setSize(rps.propertyStorage.size); + // ps.setColour(rps.propertyStorage.colour); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + child = rps.propertyStorage.child != -1 ? + mappings[rps.propertyStorage.child] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setChild(child); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + if (rps.data.length > SMALL_BLOCK_THRESHOLD) { + bigBlock += getBigBlocksRequired(rps.data.length); + } else { + smallBlock += getSmallBlocksRequired(rps.data.length); + } + } + + out.write(propertySetStorage); + } + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) { + propertyStorage = ps; + data = d; + number = n; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 new file mode 100755 index 0000000..a54ac83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java3 @@ -0,0 +1,1020 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 new file mode 100755 index 0000000..30dc0f4 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CompoundFile.java4 @@ -0,0 +1,1025 @@ +/********************************************************************* +* +* Copyright (C) 2002 Andrew Khan +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +***************************************************************************/ + +package jxl.write.biff; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashMap; + +import common.Assert; +import common.Logger; +import jxl.biff.BaseCompoundFile; +import jxl.biff.IntegerHelper; +import jxl.read.biff.BiffException; + +/** + * Writes out a compound file + * + * Header block is -1 + * Excel data is e..n (where is the head extension blocks, normally 0 and + * n is at least 8) + * Summary information (8 blocks) + * Document summary (8 blocks) + * BBD is block p..q (where p=e+n+16 and q-p+1 is the number of BBD blocks) + * Property storage block is q+b...r (normally 1 block) (where b is the number + * of BBD blocks) + */ +final class CompoundFile extends BaseCompoundFile +{ + /** + * The logger + */ + private static Logger logger = Logger.getLogger(CompoundFile.class); + + /** + * The stream to which the jumbled up data is written to + */ + private OutputStream out; + /** + * The organized biff records which form the actual excel data + */ + private byte[] excelData; + + /** + * The size of the array + */ + private int size; + + /** + * The size the excel data should be in order to comply with the + * general compound file format + */ + private int requiredSize; + + /** + * The number of blocks it takes to store the big block depot + */ + private int numBigBlockDepotBlocks; + + /** + * The number of blocks it takes to store the small block depot chain + */ + private int numSmallBlockDepotChainBlocks; + + /** + * The number of blocks it takes to store the small block depot + */ + private int numSmallBlockDepotBlocks; + + /** + * The number of extension blocks required for the header to describe + * the BBD + */ + private int numExtensionBlocks; + + /** + * The extension block for the header + */ + private int extensionBlock; + + /** + * The number of blocks it takes to store the excel data + */ + private int excelDataBlocks; + + /** + * The start block of the root entry + */ + private int rootStartBlock; + + /** + * The start block of the excel data + */ + private int excelDataStartBlock; + + /** + * The start block of the big block depot + */ + private int bbdStartBlock; + + /** + * The number of big blocks required for additional property sets + */ + private int additionalPropertyBlocks; + + /** + * The total number of property sets in this compound file + */ + private int numPropertySets; + + /** + * The number of blocks required to store the root entry property sets + * and small block depot + */ + private int numRootEntryBlocks; + + /** + * The list of additional, non standard property sets names + */ + private ArrayList additionalPropertySets; + + /** + * A hash map of the original property sets keyed on name + */ + private HashMap readPropertySets; + + /** + * The array of standard property set mappings + */ + private int[] standardPropertySetMappings; + + private ReadPropertyStorage rootEntryPropertySet; + + /** + * Structure used to store the property set and the data + */ + private static final class ReadPropertyStorage + { + PropertyStorage propertyStorage; + byte[] data; + int number; + + ReadPropertyStorage(PropertyStorage ps, byte[] d, int n) + { + propertyStorage = ps; + data = d; + number = n; + } + } + + + // The following member variables are used across methods when + // writing out the big block depot + /** + * The current position within the bbd. Used when writing out the + * BBD + */ + private int bbdPos; + + /** + * The current bbd block + */ + private byte[] bigBlockDepot; + + + /** + * Constructor + * + * @param l the length of the data + * @param os the output stream to write to + * @param data the excel data + * @param rcf the read compound + */ + public CompoundFile(byte[] data, int l, OutputStream os, + jxl.read.biff.CompoundFile rcf) + throws CopyAdditionalPropertySetsException, IOException + { + super(); + size = l; + excelData = data; + + readAdditionalPropertySets(rcf); + + numRootEntryBlocks = 1; + numPropertySets = 4 + + (additionalPropertySets != null ? additionalPropertySets.size() : 0); + + if (additionalPropertySets != null) + { + /* + try + { + rootEntryPropertySet = new ReadPropertyStorage(rcf.getPropertySet(ROOT_ENTRY_NAME), rcf.getStream(ROOT_ENTRY_NAME), 0); + int blocks = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + additionalPropertyBlocks += blocks; + } + catch(BiffException e) + { + e.printStackTrace(); + } + */ + numRootEntryBlocks += getBigBlocksRequired + (additionalPropertySets.size() * PROPERTY_STORAGE_BLOCK_SIZE); + } + + logger.debug("root entry requires " + numRootEntryBlocks + " blocks"); + + int blocks = getBigBlocksRequired(l); + + // First pad the data out so that it fits nicely into a whole number + // of blocks + if (l < SMALL_BLOCK_THRESHOLD) + { + requiredSize = SMALL_BLOCK_THRESHOLD; + } + else + { + requiredSize = blocks * BIG_BLOCK_SIZE; + } + + out = os; + + // logger.debug("smallBlockDepot requires " + numSmallBlockDepotBlocks + " big blocks"); + + // Do the calculations + excelDataBlocks = requiredSize/BIG_BLOCK_SIZE; + numBigBlockDepotBlocks = 1; + + int blockChainLength = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + + int startTotalBlocks = excelDataBlocks + + 8 + // summary block + 8 + // document information + additionalPropertyBlocks + + numRootEntryBlocks; + + int totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // Calculate the number of BBD blocks needed to hold this info + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // And recalculate + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // Does this affect the total? + totalBlocks = startTotalBlocks + numBigBlockDepotBlocks; + + // See if the excel bbd chain can fit into the header block. + // Remember to allow for the end of chain indicator + if (numBigBlockDepotBlocks > blockChainLength - 1 ) + { + // Sod it - we need an extension block. We have to go through + // the whole tiresome calculation again + extensionBlock = 0; + + // Compute the number of extension blocks + int bbdBlocksLeft = numBigBlockDepotBlocks - blockChainLength + 1; + + numExtensionBlocks = (int) Math.ceil((double) bbdBlocksLeft / + (double) (BIG_BLOCK_SIZE/4 - 1)); + + // Modify the total number of blocks required and recalculate the + // the number of bbd blocks + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + numBigBlockDepotBlocks = (int) Math.ceil( (double) totalBlocks / + (double) (BIG_BLOCK_SIZE/4)); + + // The final total + totalBlocks = startTotalBlocks + + numExtensionBlocks + + numBigBlockDepotBlocks; + } + else + { + extensionBlock = -2; + numExtensionBlocks = 0; + } + + // Set the excel data start block to be after the header (and + // its extensions) + excelDataStartBlock = numExtensionBlocks; + + logger.debug("excelDataStartBlock " + excelDataStartBlock); + + // Set the bbd start block to be after all the excel data + bbdStartBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks + + 16; + + logger.debug("bbdStartBlock " + bbdStartBlock); + + // Set the root start block to be after all the big block depot blocks + rootStartBlock = bbdStartBlock + + numBigBlockDepotBlocks; + + + if (totalBlocks != rootStartBlock + numRootEntryBlocks) + { + logger.warn("Root start block and total blocks are inconsistent " + + " generated file may be corrupt"); + logger.warn("RootStartBlock " + rootStartBlock + " totalBlocks " + totalBlocks); + } + } + + /** + * Reads the additional property sets from the read in compound file + * + * @return the number of blocks needed to store these property sets + */ + private void readAdditionalPropertySets + (jxl.read.biff.CompoundFile readCompoundFile) + throws CopyAdditionalPropertySetsException, IOException + { + if (readCompoundFile == null) + { + return; + } + + additionalPropertySets = new ArrayList(); + readPropertySets = new HashMap(); + + String[] psnames = readCompoundFile.getPropertySetNames(); + int blocksRequired = 0; + standardPropertySetMappings = new int[STANDARD_PROPERTY_SETS.length]; + + for (int i = 0 ; i < psnames.length ; i++) + { + // Add it to the hash map for later + PropertyStorage ps = readCompoundFile.getPropertySet(psnames[i]); + + // If the name is non standard, then retrieve the property set + // information + boolean standard = false; + for (int j = 0 ; j < STANDARD_PROPERTY_SETS.length && !standard ; j++) + { + if (psnames[i].equalsIgnoreCase(STANDARD_PROPERTY_SETS[j])) + { + standard = true; + ReadPropertyStorage rps = new ReadPropertyStorage(ps, null, i); + readPropertySets.put(psnames[i], rps); + } + } + + if (!standard) + { + try + { + byte[] data = null; + if (ps.size > 0 ) + { + data = readCompoundFile.getStream(ps.name); + } + else + { + data = new byte[0]; + } + ReadPropertyStorage rps = new ReadPropertyStorage(ps, data, i); + readPropertySets.put(psnames[i], rps); + additionalPropertySets.add(rps); + + int blocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + blocksRequired += blocks; + } + catch (BiffException e) + { + logger.error(e); + throw new CopyAdditionalPropertySetsException(); + } + } + } + + additionalPropertyBlocks = blocksRequired; + } + + + /** + * Writes out the excel file in OLE compound file format + * + * @exception IOException + */ + public void write() throws IOException + { + writeHeader(); + writeExcelData(); + writeDocumentSummaryData(); + writeSummaryData(); + writeAdditionalPropertySets(); + writeBigBlockDepot(); + writePropertySets(); + + // Don't flush or close the stream - this is handled by the enclosing File + // object + } + + /** + * Writes out any additional property sets + */ + private void writeAdditionalPropertySets() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + + /* + logger.debug("Writing property set " + rootEntryPropertySet.propertyStorage.name); + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize2 = numBlocks2 * BIG_BLOCK_SIZE; + + out.write(rootEntryPropertySet.data, 0, rootEntryPropertySet.data.length); + + byte[] padding2 = new byte[requiredSize2 - rootEntryPropertySet.data.length]; + out.write(padding2, 0, padding2.length); + + logger.debug("data length " + rootEntryPropertySet.data.length + " Padding " + padding2.length); + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ;) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + byte[] data = rps.data; + + logger.debug("Writing property set " + rps.propertyStorage.name); + int numBlocks = data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + int requiredSize = numBlocks * BIG_BLOCK_SIZE; + + out.write(data, 0, data.length); + + byte[] padding = new byte[requiredSize - data.length]; + out.write(padding, 0, padding.length); + } + } + + /** + * Writes out the excel data, padding it out with empty bytes as + * necessary + * Also write out empty + * + * @exception IOException + */ + private void writeExcelData() throws IOException + { + logger.debug("num excel data blocks " + excelDataBlocks + " excelData size " + requiredSize); + out.write(excelData, 0, size); + + byte[] padding = new byte[requiredSize - size]; + out.write(padding); + } + + /** + * Write out the document summary data. This is just blank + * + * @exception IOException + */ + private void writeDocumentSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Write out the summary data. This is just blank + * + * @exception IOException + */ + private void writeSummaryData() throws IOException + { + byte[] padding = new byte[SMALL_BLOCK_THRESHOLD]; + + // Write out the summary information + out.write(padding); + } + + /** + * Writes the compound file header + * + * @exception IOException + */ + private void writeHeader() throws IOException + { + logger.debug("num extensions blocks for header: " + numExtensionBlocks); + // Build up the header array + byte[] headerBlock = new byte[BIG_BLOCK_SIZE]; + byte[] extensionBlockData = new byte[BIG_BLOCK_SIZE * numExtensionBlocks]; + + // Copy in the identifier + System.arraycopy(IDENTIFIER, 0, headerBlock, 0, IDENTIFIER.length); + + // Copy in some magic values - no idea what they mean + headerBlock[0x18] = 0x3e; + headerBlock[0x1a] = 0x3; + headerBlock[0x1c] = (byte) 0xfe; + headerBlock[0x1d] = (byte) 0xff; + headerBlock[0x1e] = 0x9; + headerBlock[0x20] = 0x6; + headerBlock[0x39] = 0x10; + + // Set the number of BBD blocks + IntegerHelper.getFourBytes(numBigBlockDepotBlocks, + headerBlock, + NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + + // Set the small block depot chain to -2 ie. no small block chain + IntegerHelper.getFourBytes(-2, + headerBlock, + SMALL_BLOCK_DEPOT_BLOCK_POS); + + // Set the extension block + IntegerHelper.getFourBytes(extensionBlock, + headerBlock, + EXTENSION_BLOCK_POS); + + // Set the number of extension blocks to be the number of BBD blocks - 1 + IntegerHelper.getFourBytes(numExtensionBlocks, + headerBlock, + NUM_EXTENSION_BLOCK_POS); + + // Set the root start block + IntegerHelper.getFourBytes(rootStartBlock, + headerBlock, + ROOT_START_BLOCK_POS); + + // Set the block numbers for the BBD. Set the BBD running + // after the excel data and summary information + int pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + + // See how many blocks fit into the header + int blocksToWrite = Math.min(numBigBlockDepotBlocks, + (BIG_BLOCK_SIZE - + BIG_BLOCK_DEPOT_BLOCKS_POS)/4); + int extensionBlock = 0; + int blocksWritten = 0; + + for (int i = 0 ; i < blocksToWrite; i++) + { + IntegerHelper.getFourBytes(bbdStartBlock + i, + headerBlock, + pos); + pos += 4; + blocksWritten++; + } + + // Pad out the rest of the header with blanks + for (int i = pos; i < BIG_BLOCK_SIZE; i++) + { + headerBlock[i] = (byte) 0xff; + } + + out.write(headerBlock); + + // Write out the extension blocks + pos = 0; + + for (int extBlock = 0; extBlock < numExtensionBlocks; extBlock++) + { + blocksToWrite = Math.min(numBigBlockDepotBlocks - blocksWritten, + BIG_BLOCK_SIZE/4 -1); + + for(int j = 0 ; j < blocksToWrite; j++) + { + IntegerHelper.getFourBytes(bbdStartBlock + blocksWritten + j, + extensionBlockData, + pos); + pos += 4; + } + + blocksWritten += blocksToWrite; + + // Indicate the next block, or the termination of the chain + int nextBlock = (blocksWritten == numBigBlockDepotBlocks) ? + -2 : extBlock+1 ; + IntegerHelper.getFourBytes(nextBlock, extensionBlockData, pos); + pos +=4; + } + + if (numExtensionBlocks > 0) + { + // Pad out the rest of the extension block with blanks + for (int i = pos; i < extensionBlockData.length; i++) + { + extensionBlockData[i] = (byte) 0xff; + } + + out.write(extensionBlockData); + } + } + + /** + * Checks that the data can fit into the current BBD block. If not, + * then it moves on to the next block + * + * @exception IOException + */ + private void checkBbdPos() throws IOException + { + if (bbdPos >= BIG_BLOCK_SIZE) + { + // Write out the extension block. This will simply be the next block + out.write(bigBlockDepot); + + // Create a new block + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + } + } + + /** + * Writes out the big block chain + * + * @param startBlock the starting block of the big block chain + * @param numBlocks the number of blocks in the chain + * @exception IOException + */ + private void writeBlockChain(int startBlock, int numBlocks) + throws IOException + { + int blocksToWrite = numBlocks - 1; + int blockNumber = startBlock + 1; + + while (blocksToWrite > 0) + { + int bbdBlocks = Math.min(blocksToWrite, (BIG_BLOCK_SIZE - bbdPos)/4); + + for (int i = 0 ; i < bbdBlocks; i++) + { + IntegerHelper.getFourBytes(blockNumber, bigBlockDepot, bbdPos); + bbdPos +=4 ; + blockNumber++; + } + + blocksToWrite -= bbdBlocks; + checkBbdPos(); + } + + // Write the end of the block chain + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + /** + * Writes the block chains for the additional property sets + * + * @exception IOException + */ + private void writeAdditionalPropertySetBlockChains() throws IOException + { + if (additionalPropertySets == null) + { + return; + } + + int blockNumber = excelDataStartBlock + excelDataBlocks + 16; + + /* + int numBlocks2 = rootEntryPropertySet.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rootEntryPropertySet.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname2 = rootEntryPropertySet.propertyStorage.name; + logger.debug("writing big block chain for " + psname2 + " block " + blockNumber + " numBlocks " + numBlocks2); + writeBlockChain(blockNumber, numBlocks2); + blockNumber += numBlocks2; + */ + + for (Iterator i = additionalPropertySets.iterator(); i.hasNext() ; ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + int numBlocks = rps.data.length >= SMALL_BLOCK_THRESHOLD ? + getBigBlocksRequired(rps.data.length) : + SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + + String psname = rps.propertyStorage.name; + logger.debug("writing big block chain for " + psname + " block " + blockNumber + " numBlocks " + numBlocks); + writeBlockChain(blockNumber, numBlocks); + blockNumber += numBlocks; + } + } + + /** + * Writes out the Big Block Depot + * + * @exception IOException + */ + private void writeBigBlockDepot() throws IOException + { + // This is after the excel data, the summary information, the + // big block property sets and the small block depot + bigBlockDepot = new byte[BIG_BLOCK_SIZE]; + bbdPos = 0; + + // Write out the extension blocks, indicating them as special blocks + for (int i = 0 ; i < numExtensionBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + writeBlockChain(excelDataStartBlock, excelDataBlocks); + + // The excel data has been written. Now write out the rest of it + + // Write the block chain for the summary information + int summaryInfoBlock = excelDataStartBlock + + excelDataBlocks + + additionalPropertyBlocks; + + for (int i = summaryInfoBlock; i < summaryInfoBlock + 7; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the summary info block + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write the block chain for the document summary information + for (int i = summaryInfoBlock + 8; i < summaryInfoBlock + 15; i++) + { + IntegerHelper.getFourBytes(i + 1, bigBlockDepot, bbdPos); + bbdPos +=4 ; + checkBbdPos(); + } + + // Write the end of the block chain for the document summary + IntegerHelper.getFourBytes(-2, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + + // Write out the block chain for the copied property sets, if present + writeAdditionalPropertySetBlockChains(); + + // The Big Block Depot immediately follows the document summary. Denote + // these as a special block + for (int i = 0; i < numBigBlockDepotBlocks; i++) + { + IntegerHelper.getFourBytes(-3, bigBlockDepot, bbdPos); + bbdPos += 4; + checkBbdPos(); + } + + // Write the root entry + writeBlockChain(rootStartBlock, numRootEntryBlocks); + + // Pad out the remainder of the block + if (bbdPos != 0) + { + for (int i = bbdPos; i < BIG_BLOCK_SIZE; i++) + { + bigBlockDepot[i] = (byte) 0xff; + } + out.write(bigBlockDepot); + } + } + + /** + * Calculates the number of big blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of big blocks required to store the data + */ + private int getBigBlocksRequired(int length) + { + int blocks = length / BIG_BLOCK_SIZE; + + return (length % BIG_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Calculates the number of small blocks required to store data of the + * specified length + * + * @param length the length of the data + * @return the number of small blocks required to store the data + */ + private int getSmallBlocksRequired(int length) + { + int blocks = length / SMALL_BLOCK_SIZE; + + return (length % SMALL_BLOCK_SIZE > 0 )? blocks + 1 : blocks; + } + + /** + * Writes out the property sets + * + * @exception IOException + */ + private void writePropertySets() throws IOException + { + byte[] propertySetStorage = new byte[BIG_BLOCK_SIZE * numRootEntryBlocks]; + + int pos = 0; + int[] mappings = null; + + // Build up the mappings array + if (additionalPropertySets != null) + { + mappings = new int[numPropertySets]; + + // Map the standard ones to the first four + for (int i = 0 ; i < STANDARD_PROPERTY_SETS.length ; i++) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(STANDARD_PROPERTY_SETS[i]); + mappings[rps.number] = i; + } + + // Now go through the original ones + int newMapping = STANDARD_PROPERTY_SETS.length; + for (Iterator i = additionalPropertySets.iterator(); i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + mappings[rps.number] = newMapping; + newMapping++; + } + } + + int dir = 0; + int previous = 0; + int next = 0; + + // Set the root entry property set + PropertyStorage ps = new PropertyStorage(ROOT_ENTRY_NAME); + ps.setType(5); + ps.setStartBlock(-2); + ps.setSize(0); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setColour(0); + + dir = 2; + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(ROOT_ENTRY_NAME); + dir = mappings[rps.propertyStorage.directory]; + } + ps.setDirectory(dir); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + // Set the workbook property set + ps = new PropertyStorage(WORKBOOK_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock); + // start the excel data after immediately after this block + ps.setSize(requiredSize); + // alway use a big block stream - none of that messing around + // with small blocks + ps.setColour(1); + + previous = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(WORKBOOK_NAME); + previous = mappings[rps.propertyStorage.previous]; + } + + ps.setPrevious(previous); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the summary information + ps = new PropertyStorage(SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks); + ps.setSize(SMALL_BLOCK_THRESHOLD); + // ps.setColour(1); + + previous = 1; + next = 3; + + if (additionalPropertySets != null) + { + ReadPropertyStorage rps = (ReadPropertyStorage) + readPropertySets.get(SUMMARY_INFORMATION_NAME); + + previous = rps.propertyStorage.previous != - 1 ? + mappings[rps.propertyStorage.previous] : -1 ; + next = rps.propertyStorage.next != - 1 ? + mappings[rps.propertyStorage.next] : -1 ; + } + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + // Set the document summary information + ps = new PropertyStorage(DOCUMENT_SUMMARY_INFORMATION_NAME); + ps.setType(2); + ps.setStartBlock(excelDataStartBlock + excelDataBlocks + 8); + ps.setSize(SMALL_BLOCK_THRESHOLD); + ps.setPrevious(-1); + ps.setNext(-1); + ps.setDirectory(-1); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + + // Write out the additional property sets + if (additionalPropertySets == null) + { + out.write(propertySetStorage); + return; + } + + int bigBlock = excelDataStartBlock + excelDataBlocks + 16 + + 18; + + for (Iterator i = additionalPropertySets.iterator() ; i.hasNext(); ) + { + ReadPropertyStorage rps = (ReadPropertyStorage) i.next(); + + ps = new PropertyStorage(rps.propertyStorage.name); + ps.setType(rps.propertyStorage.type); + ps.setStartBlock(bigBlock); + ps.setSize(Math.max(rps.propertyStorage.size, SMALL_BLOCK_THRESHOLD)); + + previous = rps.propertyStorage.previous != -1 ? + mappings[rps.propertyStorage.previous] : -1; + next = rps.propertyStorage.next != -1 ? + mappings[rps.propertyStorage.next] : -1; + dir = rps.propertyStorage.directory != -1 ? + mappings[rps.propertyStorage.directory] : -1; + + ps.setPrevious(previous); + ps.setNext(next); + ps.setDirectory(dir); + // ps.setColour(1); + + System.arraycopy(ps.data, 0, + propertySetStorage, pos, + PROPERTY_STORAGE_BLOCK_SIZE); + pos += PROPERTY_STORAGE_BLOCK_SIZE; + + + if (rps.data.length >= SMALL_BLOCK_THRESHOLD) + { + bigBlock += getBigBlocksRequired(rps.data.length); + } + else + { + bigBlock += SMALL_BLOCK_THRESHOLD / BIG_BLOCK_SIZE; + } + } + + out.write(propertySetStorage); + } +} + + + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java b/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java new file mode 100755 index 0000000..30799d1 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CopyAdditionalPropertySetsException.java @@ -0,0 +1,33 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +/** + * Exception thrown when attempting to copy a workbook which contains + * additional property sets + */ +public class CopyAdditionalPropertySetsException extends JxlWriteException { + /** + * Constructor + */ + public CopyAdditionalPropertySetsException() { + super(copyPropertySets); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java new file mode 100755 index 0000000..d5b7fc3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/CountryRecord.java @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.CountryCode; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + + +/** + * Record containing the localization information + */ +class CountryRecord extends WritableRecordData { + /** + * The user interface language + */ + private final int language; + + /** + * The regional settings + */ + private final int regionalSettings; + + /** + * Constructor + */ + public CountryRecord(CountryCode lang, CountryCode r) { + super(Type.COUNTRY); + + language = lang.getValue(); + regionalSettings = r.getValue(); + } + + public CountryRecord(jxl.read.biff.CountryRecord cr) { + super(Type.COUNTRY); + + language = cr.getLanguageCode(); + regionalSettings = cr.getRegionalSettingsCode(); + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[4]; + + IntegerHelper.getTwoBytes(language, data, 0); + IntegerHelper.getTwoBytes(regionalSettings, data, 2); + + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java new file mode 100755 index 0000000..dd514ad --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DBCellRecord.java @@ -0,0 +1,116 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indexes the first row record of the block and each individual cell. + * This is invoked by the sheets write process + */ +class DBCellRecord extends WritableRecordData { + /** + * The file position of the first Row record in this block + */ + private final int rowPos; + + /** + * The position of the start of the next cell after the first row. This + * is used as the offset for the cell positions + */ + private int cellOffset; + + /** + * The list of all cell positions in this block + */ + private final ArrayList cellRowPositions; + + /** + * The position of this record in the file. Vital for calculating offsets + */ + private int position; + + /** + * Constructor + * + * @param rp the position of this row + */ + public DBCellRecord(int rp) { + super(Type.DBCELL); + rowPos = rp; + cellRowPositions = new ArrayList(10); + } + + /** + * Sets the offset of this cell record within the sheet stream + * + * @param pos the offset + */ + void setCellOffset(int pos) { + cellOffset = pos; + } + + /** + * Adds a cell + * + * @param pos + */ + void addCellRowPosition(int pos) { + cellRowPositions.add(new Integer(pos)); + } + + /** + * Sets the position of this cell within the sheet stream + * + * @param pos the position + */ + void setPosition(int pos) { + position = pos; + } + + /** + * Gets the binary data for this cell record + * + * @return the binary data + */ + protected byte[] getData() { + byte[] data = new byte[4 + 2 * cellRowPositions.size()]; + + // Set the offset to the first row + IntegerHelper.getFourBytes(position - rowPos, data, 0); + + // Now add in all the cell offsets + int pos = 4; + int lastCellPos = cellOffset; + Iterator i = cellRowPositions.iterator(); + while (i.hasNext()) { + int cellPos = ((Integer) i.next()).intValue(); + IntegerHelper.getTwoBytes(cellPos - lastCellPos, data, pos); + lastCellPos = cellPos; + pos += 2; + } + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java new file mode 100755 index 0000000..8a3ff90 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DSFRecord.java @@ -0,0 +1,54 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores a flag which indicates whether the file is a double stream + * file. For files written by JExcelAPI, this FALSE + */ +class DSFRecord extends WritableRecordData { + /** + * The data to be written to the binary file + */ + private final byte[] data; + + /** + * Constructor + */ + public DSFRecord() { + super(Type.DSF); + + // Hard code the fact that this is most assuredly not a double + // stream file + data = new byte[]{0, 0}; + } + + /** + * The binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java new file mode 100755 index 0000000..2f370a2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DateFormatRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FormatRecord; + +/** + * A class which contains a date format + */ +public class DateFormatRecord extends FormatRecord { + /** + * Constructor + * + * @param fmt the date format + */ + protected DateFormatRecord(String fmt) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "a", "AM/PM"); + fs = replace(fs, "S", "0"); + + setFormatString(fs); + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java new file mode 100755 index 0000000..35ffbbb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DateRecord.java @@ -0,0 +1,320 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import jxl.CellType; +import jxl.DateCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.DateFormats; +import jxl.write.WritableCellFormat; + +/** + * A date stored in the database + */ +public abstract class DateRecord extends CellValue { + /** + * This is package protected so that the worksheet might detect + * whether or not to override it with the column cell format + */ + static final WritableCellFormat defaultDateFormat = + new WritableCellFormat(DateFormats.DEFAULT); + /** + * + */ + private final static int utcOffsetDays = 25569; + /** + * + */ + private final static long msInADay = 24 * 60 * 60 * 1000; + /** + * + */ + private final static int nonLeapDay = 61; + + // The number of days between 01 Jan 1900 and 01 Jan 1970 - this gives + // the UTC offset + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(DateRecord.class); + + // The number of milliseconds in a day + /** + * The excel value of the date + */ + private double value; + /** + * The java representation of the date + */ + private Date date; + + // The number of days between 1 Jan 1900 and 1 March 1900. Excel thinks + // the day before this was 29th Feb 1900, but it was 28th Feb 19000. + // I guess the programmers thought nobody would notice that they + // couldn't be bothered to program this dating anomaly properly + /** + * Indicates whether this is a full date, or just a time only + */ + private boolean time; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + */ + protected DateRecord(int c, int r, Date d) { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param d the date + * @param a adjust timezone + */ + protected DateRecord(int c, int r, Date d, GMTDate a) { + this(c, r, d, defaultDateFormat, false); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st) { + super(Type.NUMBER, c, r, st); + date = d; + calculateValue(true); + } + + /** + * Constructor invoked from the user API + * + * @param c the column + * @param r the row + * @param st the format for the date + * @param d the date + * @param a adjust for the timezone + */ + protected DateRecord(int c, int r, Date d, CellFormat st, GMTDate a) { + super(Type.NUMBER, c, r, st); + date = d; + calculateValue(false); + } + + /** + * Constructor invoked from the API + * + * @param c the column + * @param r the row + * @param st the date format + * @param tim time indicator + * @param d the date + */ + protected DateRecord(int c, int r, Date d, CellFormat st, boolean tim) { + super(Type.NUMBER, c, r, st); + date = d; + time = tim; + calculateValue(false); + } + + /** + * Constructor invoked when copying a readable spreadsheet + * + * @param dc the date to copy + */ + protected DateRecord(DateCell dc) { + super(Type.NUMBER, dc); + date = dc.getDate(); + time = dc.isTime(); + calculateValue(false); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param dr the record to copy + */ + protected DateRecord(int c, int r, DateRecord dr) { + super(Type.NUMBER, c, r, dr); + value = dr.value; + time = dr.time; + date = dr.date; + } + + /** + * Calculates the 1900 based numerical value based upon the utc value held + * in the date object + * + * @param adjust TRUE if we want to incorporate timezone information + * into the raw UTC date eg. when copying from a spreadsheet + */ + private void calculateValue(boolean adjust) { + // Offsets for current time zone + long zoneOffset = 0; + long dstOffset = 0; + + // Get the timezone and dst offsets if we want to take these into + // account + if (adjust) { + // Get the current calender, replete with timezone information + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + + zoneOffset = cal.get(Calendar.ZONE_OFFSET); + dstOffset = cal.get(Calendar.DST_OFFSET); + } + + long utcValue = date.getTime() + zoneOffset + dstOffset; + + // Convert this to the number of days, plus fractions of a day since + // 01 Jan 1970 + double utcDays = (double) utcValue / (double) msInADay; + + // Add in the offset to get the number of days since 01 Jan 1900 + value = utcDays + utcOffsetDays; + + // Work round a bug in excel. Excel seems to think there is a date + // called the 29th Feb, 1900 - but this was not a leap year. + // Therefore for values less than 61, we must subtract 1. Only do + // this for full dates, not times + if (!time && value < nonLeapDay) { + value -= 1; + } + + // If this refers to a time, then get rid of the integer part + if (time) { + value = value - (int) value; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.DATE; + } + + /** + * Gets the binary data for writing + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return date.toString(); + } + + /** + * Sets the date in this cell, taking the timezone into account + * + * @param d the date + * @param a adjust for timezone + */ + protected void setDate(Date d, GMTDate a) { + date = d; + calculateValue(false); + } + + /** + * Gets the date contained in this cell + * + * @return the cell contents + */ + public Date getDate() { + return date; + } + + /** + * Sets the date in this cell + * + * @param d the date + */ + protected void setDate(Date d) { + date = d; + calculateValue(true); + } + + /** + * Indicates whether the date value contained in this cell refers to a date, + * or merely a time. When writing a cell, all dates are fully defined, + * even if they refer to a time + * + * @return FALSE if this is full date, TRUE if a time + */ + public boolean isTime() { + return time; + } + + /** + * Gets the DateFormat used to format the cell. This will normally be + * the format specified in the excel spreadsheet, but in the event of any + * difficulty parsing this, it will revert to the default date/time format. + * + * @return the DateFormat object used to format the date in the original + * excel cell + */ + public DateFormat getDateFormat() { + return null; + } + + /** + * Class definition for a dummy variable + */ + protected static final class GMTDate { + public GMTDate() { + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java new file mode 100755 index 0000000..09bf5d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultColumnWidth.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default column width for a workbook + */ +class DefaultColumnWidth extends WritableRecordData { + /** + * The default column width + */ + private final int width; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param w the default column width + */ + public DefaultColumnWidth(int w) { + super(Type.DEFCOLWIDTH); + width = w; + data = new byte[2]; + IntegerHelper.getTwoBytes(width, data, 0); + } + + /** + * Gets the binary data for writing to the stream + * + * @return the binary data + */ + protected byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java new file mode 100755 index 0000000..5dccd54 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DefaultRowHeightRecord.java @@ -0,0 +1,73 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The default row height for cells in the workbook + */ +class DefaultRowHeightRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The default row height + */ + private final int rowHeight; + + /** + * Indicates whether or not the default row height has been changed + */ + private final boolean changed; + + /** + * Constructor + * + * @param height the default row height + * @param ch TRUE if the default value has been changed, false + * otherwise + */ + public DefaultRowHeightRecord(int h, boolean ch) { + super(Type.DEFAULTROWHEIGHT); + data = new byte[4]; + rowHeight = h; + changed = ch; + } + + /** + * Gets the binary data for writing to the output stream + * + * @return the binary data + */ + public byte[] getData() { + if (changed) { + data[0] |= 0x1; + } + + IntegerHelper.getTwoBytes(rowHeight, data, 2); + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java new file mode 100755 index 0000000..c84ab53 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DeltaRecord.java @@ -0,0 +1,76 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which stores the maximum change value from the Options + * dialog + */ +class DeltaRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The number of iterations + */ + private final double iterationValue; + + /** + * Constructor + * + * @param itval + */ + public DeltaRecord(double itval) { + super(Type.DELTA); + iterationValue = itval; + + data = new byte[8]; + } + + + /** + * Gets the binary data for writing to the output file + * + * @return the binary data + */ + public byte[] getData() { + DoubleHelper.getIEEEBytes(iterationValue, data, 0); + + /* long val = Double.doubleToLongBits(iterationValue); + data[0] = (byte) (val & 0xff); + data[1] = (byte) ((val & 0xff00) >> 8); + data[2] = (byte) ((val & 0xff0000) >> 16); + data[3] = (byte) ((val & 0xff000000) >> 24); + data[4] = (byte) ((val & 0xff00000000L) >> 32); + data[5] = (byte) ((val & 0xff0000000000L) >> 40); + data[6] = (byte) ((val & 0xff000000000000L) >> 48); + data[7] = (byte) ((val & 0xff00000000000000L) >> 56) ; + */ + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java new file mode 100755 index 0000000..2b81984 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/DimensionRecord.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the bounds of the sheet + */ +class DimensionRecord extends WritableRecordData { + /** + * The number of rows in the sheet + */ + private final int numRows; + /** + * The number of columns in the sheet + */ + private final int numCols; + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param c the number of columns + * @param r the number of rows + */ + public DimensionRecord(int r, int c) { + super(Type.DIMENSION); + numRows = r; + numCols = c; + + data = new byte[14]; + + IntegerHelper.getFourBytes(numRows, data, 4); + IntegerHelper.getTwoBytes(numCols, data, 10); + } + + /** + * Gets the binary data to be written to the output file + * + * @return the binary data + */ + protected byte[] getData() { + return data; + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java new file mode 100755 index 0000000..04747d0 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/EOFRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which marks the end of the current stream + */ +class EOFRecord extends WritableRecordData { + /** + * Constructor + */ + public EOFRecord() { + super(Type.EOF); + } + + /** + * Returns the binary data to be written to the output file + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java new file mode 100755 index 0000000..b098949 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/Excel9FileRecord.java @@ -0,0 +1,47 @@ +/********************************************************************* + * + * Copyright (C) 2009 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates whether this file has been generated by excel 2000 + * should be refreshed when the workbook is loaded + */ +class Excel9FileRecord extends WritableRecordData { + /** + * Constructor + * + * @param template flag + */ + public Excel9FileRecord() { + super(Type.EXCEL9FILE); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java new file mode 100755 index 0000000..9225028 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExcelDataOutput.java @@ -0,0 +1,61 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to abstract away an in-memory output or a temporary file + * output. Used by the File object + */ +interface ExcelDataOutput { + /** + * Appends the bytes to the end of the output + * + * @param d the data to write to the end of the array + */ + void write(byte[] bytes) throws IOException; + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + int getPosition() throws IOException; + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + void setData(byte[] newdata, int pos) throws IOException; + + /** + * Writes the data to the output stream + */ + void writeData(OutputStream out) throws IOException; + + /** + * Called when the final compound file has been written + */ + void close() throws IOException; +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java new file mode 100755 index 0000000..f4cf1bb --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExtendedSSTRecord.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Indicates an extension to the Shared String Table. Currently this + * contains blank records + *
+ * Thanks to Guenther for contributing a proper implementation of the EXTSST + * record, replacing my previous dummy version + */ +class ExtendedSSTRecord extends WritableRecordData { + private static final int infoRecordSize = 8; + private final int numberOfStrings; + private final int[] absoluteStreamPositions; + private final int[] relativeStreamPositions; + private int currentStringIndex = 0; + + /** + * Constructor + * + * @param numstrings the number of strings per bucket + * @param streampos the absolute stream position of the beginning of + * the SST record + */ + public ExtendedSSTRecord(int newNumberOfStrings) { + super(Type.EXTSST); + numberOfStrings = newNumberOfStrings; + int numberOfBuckets = getNumberOfBuckets(); + absoluteStreamPositions = new int[numberOfBuckets]; + relativeStreamPositions = new int[numberOfBuckets]; + currentStringIndex = 0; + } + + public int getNumberOfBuckets() { + int numberOfStringsPerBucket = getNumberOfStringsPerBucket(); + return numberOfStringsPerBucket != 0 ? + (numberOfStrings + numberOfStringsPerBucket - 1) / + numberOfStringsPerBucket : 0; + } + + public int getNumberOfStringsPerBucket() { + // XXX + // should come up with a more clever calculation + // bucket limit should not be bigger than 1024, otherwise we end + // up with too many buckets and would have to write continue records + // for the EXTSST record which we want to avoid for now. + final int bucketLimit = 128; + return (numberOfStrings + bucketLimit - 1) / bucketLimit; + } + + public void addString(int absoluteStreamPosition, + int relativeStreamPosition) { + absoluteStreamPositions[currentStringIndex] = + absoluteStreamPosition + relativeStreamPosition; + relativeStreamPositions[currentStringIndex] = relativeStreamPosition; + currentStringIndex++; + } + + /** + * Gets the binary data to be written out + * + * @return the binary data + */ + public byte[] getData() { + int numberOfBuckets = getNumberOfBuckets(); + byte[] data = new byte[2 + (8 * numberOfBuckets)]; + // number of strings per bucket + IntegerHelper.getTwoBytes(getNumberOfStringsPerBucket(), data, 0); + + for (int i = 0; i < numberOfBuckets; i++) { + // absolute stream position + IntegerHelper.getFourBytes(absoluteStreamPositions[i], + data, + 2 + (i * infoRecordSize)); + // relative offset + IntegerHelper.getTwoBytes(relativeStreamPositions[i], + data, + 6 + (i * infoRecordSize)); + // reserved + // IntegerHelper.getTwoBytes(0x0, data, 8 + (i * infoRecordSize)); + } + + return data; + } +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java new file mode 100755 index 0000000..38f4e1a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalNameRecord.java @@ -0,0 +1,70 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalNameRecord extends WritableRecordData { + /** + * The logger + */ + Logger logger = Logger.getLogger(ExternalNameRecord.class); + + /** + * The name of the addin + */ + private final String name; + + /** + * Constructor used for writable workbooks + */ + public ExternalNameRecord(String n) { + super(Type.EXTERNNAME); + name = n; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[name.length() * 2 + 12]; + + data[6] = (byte) name.length(); + data[7] = 0x1; + StringHelper.getUnicodeBytes(name, data, 8); + + int pos = 8 + name.length() * 2; + data[pos] = 0x2; + data[pos + 1] = 0x0; + data[pos + 2] = 0x1c; + data[pos + 3] = 0x17; + + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java new file mode 100755 index 0000000..b21aa83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ExternalSheetRecord.java @@ -0,0 +1,225 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import java.util.Iterator; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * An external sheet record, used to maintain integrity when formulas + * are copied from read databases + */ +class ExternalSheetRecord extends WritableRecordData { + /** + * The underlying external sheet data + */ + private byte[] data; + + /** + * The list of XTI structures + */ + private final ArrayList xtis; + + /** + * Constructor + * + * @param esf the external sheet record to copy + */ + public ExternalSheetRecord(jxl.read.biff.ExternalSheetRecord esf) { + super(Type.EXTERNSHEET); + + xtis = new ArrayList(esf.getNumRecords()); + XTI xti = null; + for (int i = 0; i < esf.getNumRecords(); i++) { + xti = new XTI(esf.getSupbookIndex(i), + esf.getFirstTabIndex(i), + esf.getLastTabIndex(i)); + xtis.add(xti); + } + } + + /** + * Constructor used for writable workbooks + */ + public ExternalSheetRecord() { + super(Type.EXTERNSHEET); + xtis = new ArrayList(); + } + + /** + * Gets the extern sheet index for the specified parameters, creating + * a new xti record if necessary + * + * @param supbookind the internal supbook reference + * @param sheetind the sheet index + */ + int getIndex(int supbookind, int sheetind) { + Iterator i = xtis.iterator(); + XTI xti = null; + boolean found = false; + int pos = 0; + while (i.hasNext() && !found) { + xti = (XTI) i.next(); + + if (xti.supbookIndex == supbookind && + xti.firstTab == sheetind) { + found = true; + } else { + pos++; + } + } + + if (!found) { + xti = new XTI(supbookind, sheetind, sheetind); + xtis.add(xti); + pos = xtis.size() - 1; + } + + return pos; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[2 + xtis.size() * 6]; + + int pos = 0; + IntegerHelper.getTwoBytes(xtis.size(), data, 0); + pos += 2; + + Iterator i = xtis.iterator(); + XTI xti = null; + while (i.hasNext()) { + xti = (XTI) i.next(); + IntegerHelper.getTwoBytes(xti.supbookIndex, data, pos); + IntegerHelper.getTwoBytes(xti.firstTab, data, pos + 2); + IntegerHelper.getTwoBytes(xti.lastTab, data, pos + 4); + pos += 6; + } + + return data; + } + + /** + * Gets the supbook index for the specified external sheet + * + * @param the index of the supbook record + * @return the supbook index + */ + public int getSupbookIndex(int index) { + return ((XTI) xtis.get(index)).supbookIndex; + } + + /** + * Gets the first tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the first tab index + */ + public int getFirstTabIndex(int index) { + return ((XTI) xtis.get(index)).firstTab; + } + + /** + * Gets the last tab index for the specified external sheet + * + * @param the index of the supbook record + * @return the last tab index + */ + public int getLastTabIndex(int index) { + return ((XTI) xtis.get(index)).lastTab; + } + + /** + * Called when a sheet has been inserted via the API + * + * @param the position of the insertion + */ + void sheetInserted(int index) { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext(); ) { + xti = (XTI) i.next(); + xti.sheetInserted(index); + } + } + + /** + * Called when a sheet has been removed via the API + * + * @param the position of the insertion + */ + void sheetRemoved(int index) { + XTI xti = null; + for (Iterator i = xtis.iterator(); i.hasNext(); ) { + xti = (XTI) i.next(); + xti.sheetRemoved(index); + } + } + + /** + * An XTI structure + */ + private static class XTI { + int supbookIndex; + int firstTab; + int lastTab; + + XTI(int s, int f, int l) { + supbookIndex = s; + firstTab = f; + lastTab = l; + } + + void sheetInserted(int index) { + if (firstTab >= index) { + firstTab++; + } + + if (lastTab >= index) { + lastTab++; + } + } + + void sheetRemoved(int index) { + if (firstTab == index) { + firstTab = 0; + } + + if (lastTab == index) { + lastTab = 0; + } + + if (firstTab > index) { + firstTab--; + } + + if (lastTab > index) { + lastTab--; + } + } + } + +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/File.java b/datastructures-xslx/src/main/java/jxl/write/biff/File.java new file mode 100755 index 0000000..8ef73be --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/File.java @@ -0,0 +1,174 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import jxl.WorkbookSettings; +import jxl.biff.ByteData; +import jxl.common.Logger; + +/** + * A file of excel data to be written out. All the excel data is held + * in memory, and when the close method is called a CompoundFile object + * is used to write the Biff oriented excel data in the CompoundFile + * format + */ +public final class File { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(File.class); + /** + * The read compound file. This will only be non-null if there are macros + * or other property sets of that ilk which that we should be copying + */ + jxl.read.biff.CompoundFile readCompoundFile; + /** + * The data from the excel 97 file + */ + private ExcelDataOutput data; + /** + * The current position within the file + */ + private int pos; + /** + * The output stream + */ + private OutputStream outputStream; + /** + * The initial file size + */ + private int initialFileSize; + /** + * The amount to increase the growable array by + */ + private int arrayGrowSize; + /** + * The workbook settings + */ + private final WorkbookSettings workbookSettings; + + /** + * Constructor + * + * @param os the output stream + * @param ws the configuration settings for this workbook + * @param rcf the rea compound file + */ + File(OutputStream os, WorkbookSettings ws, jxl.read.biff.CompoundFile rcf) + throws IOException { + outputStream = os; + workbookSettings = ws; + readCompoundFile = rcf; + createDataOutput(); + } + + private void createDataOutput() throws IOException { + if (workbookSettings.getUseTemporaryFileDuringWrite()) { + data = new FileDataOutput + (workbookSettings.getTemporaryFileDuringWriteDirectory()); + } else { + initialFileSize = workbookSettings.getInitialFileSize(); + arrayGrowSize = workbookSettings.getArrayGrowSize(); + + data = new MemoryDataOutput(initialFileSize, arrayGrowSize); + } + } + + /** + * Closes the file. In fact, this writes out all the excel data + * to disk using a CompoundFile object, and then frees up all the memory + * allocated to the workbook + * + * @param cs TRUE if this should close the stream, FALSE if the application + * closes it + * @throws IOException + */ + void close(boolean cs) throws IOException, JxlWriteException { + CompoundFile cf = new CompoundFile(data, + data.getPosition(), + outputStream, + readCompoundFile); + cf.write(); + + outputStream.flush(); + data.close(); + + if (cs) { + outputStream.close(); + } + + // Cleanup the memory a bit + data = null; + + if (!workbookSettings.getGCDisabled()) { + System.gc(); + } + } + + /** + * Adds the biff record data to the memory allocated for this File + * + * @param record the record to add to the excel data + * @throws IOException + */ + public void write(ByteData record) throws IOException { + byte[] bytes = record.getBytes(); + + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the current position + */ + int getPos() throws IOException { + return data.getPosition(); + } + + /** + * Used to manually alter the contents of the written out data. This + * is used when cross-referencing cell records + * + * @param pos the position to alter + * @param newdata the data to modify + */ + void setData(byte[] newdata, int pos) throws IOException { + data.setData(newdata, pos); + } + + /** + * Sets a new output file. This allows the same workbook to be + * written to various different output files without having to + * read in any templates again + * + * @param os the output stream + */ + public void setOutputFile(OutputStream os) throws IOException { + if (data != null) { + logger.warn("Rewriting a workbook with non-empty data"); + } + + outputStream = os; + createDataOutput(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java new file mode 100755 index 0000000..c3026be --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FileDataOutput.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import jxl.common.Logger; + +/** + * Used to generate the excel biff data using a temporary file. This + * class wraps a RandomAccessFile + */ +class FileDataOutput implements ExcelDataOutput { + // The logger + private static final Logger logger = Logger.getLogger(FileDataOutput.class); + + /** + * The temporary file + */ + private final File temporaryFile; + + /** + * The excel data + */ + private final RandomAccessFile data; + + /** + * Constructor + * + * @param tmpdir the temporary directory used to write files. If this is + * NULL then the sytem temporary directory will be used + */ + public FileDataOutput(File tmpdir) throws IOException { + temporaryFile = File.createTempFile("jxl", ".tmp", tmpdir); + temporaryFile.deleteOnExit(); + data = new RandomAccessFile(temporaryFile, "rw"); + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) throws IOException { + data.write(bytes); + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() throws IOException { + // As all excel data structures are four bytes anyway, it's ok to + // truncate the long to an int + return (int) data.getFilePointer(); + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) throws IOException { + long curpos = data.getFilePointer(); + data.seek(pos); + data.write(newdata); + data.seek(curpos); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int length = 0; + data.seek(0); + while ((length = data.read(buffer)) != -1) { + out.write(buffer, 0, length); + } + } + + /** + * Called when the final compound file has been written + */ + public void close() throws IOException { + data.close(); + + // Explicitly delete the temporary file, since sometimes it is the case + // that a single process may be generating multiple different excel files + temporaryFile.delete(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java new file mode 100755 index 0000000..665c827 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FooterRecord.java @@ -0,0 +1,84 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Places a string at the bottom of each page when the file is printed. + * JExcelApi sets this to be blank + */ +class FooterRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The footer string + */ + private final String footer; + + /** + * Consructor + * + * @param s the footer + */ + public FooterRecord(String s) { + super(Type.FOOTER); + + footer = s; + } + + /** + * Consructor invoked when copying a sheets + * + * @param fr the read footer record + */ + public FooterRecord(FooterRecord fr) { + super(Type.FOOTER); + + footer = fr.footer; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + if (footer == null || footer.length() == 0) { + data = new byte[0]; + return data; + } + + data = new byte[footer.length() * 2 + 3]; + IntegerHelper.getTwoBytes(footer.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(footer, data, 3); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java new file mode 100755 index 0000000..16fb8bd --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FormulaRecord.java @@ -0,0 +1,344 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.CellReferenceHelper; +import jxl.CellType; +import jxl.Sheet; +import jxl.WorkbookSettings; +import jxl.biff.FormattingRecords; +import jxl.biff.FormulaData; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WorkbookMethods; +import jxl.biff.formula.ExternalSheet; +import jxl.biff.formula.FormulaException; +import jxl.biff.formula.FormulaParser; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; +import jxl.write.WritableCell; + +/** + * A formula record. Parses the string passed in to deduce the set of + * formula records + */ +public class FormulaRecord extends CellValue implements FormulaData { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(FormulaRecord.class); + + /** + * The formula to parse + */ + private String formulaToParse; + + /** + * The formula parser + */ + private FormulaParser parser; + + /** + * The parsed formula string + */ + private String formulaString; + + /** + * The parsed formula bytes + */ + private byte[] formulaBytes; + + /** + * The location where this formula was copied from. It is used subsequently + * to adjust relative cell references + */ + private CellValue copiedFrom; + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f) { + super(Type.FORMULA2, c, r); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Constructor + * + * @param f the formula to copy + */ + public FormulaRecord(int c, int r, String f, CellFormat st) { + super(Type.FORMULA, c, r, st); + formulaToParse = f; + copiedFrom = null; + } + + /** + * Copy constructor for writable formulas + * + * @param c the column + * @param r the row + * @param fr the record to copy + */ + protected FormulaRecord(int c, int r, FormulaRecord fr) { + super(Type.FORMULA, c, r, fr); + copiedFrom = fr; + formulaBytes = new byte[fr.formulaBytes.length]; + System.arraycopy(fr.formulaBytes, 0, formulaBytes, 0, formulaBytes.length); + } + + /** + * Copy constructor for formulas read in - invoked from writable formulas + * + * @param c the column + * @param r the row + * @param rfr the formula data to copy + */ + protected FormulaRecord(int c, int r, ReadFormulaRecord rfr) { + super(Type.FORMULA, c, r, rfr); + try { + copiedFrom = rfr; + formulaBytes = rfr.getFormulaBytes(); + } catch (FormulaException e) { + // Fail silently + logger.error("", e); + } + } + + /** + * Initializes the string and the formula bytes. In order to get + * access to the workbook settings, the object is not initialized until + * it is added to the sheet + * + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initialize(WorkbookSettings ws, ExternalSheet es, + WorkbookMethods nt) { + if (copiedFrom != null) { + initializeCopiedFormula(ws, es, nt); + return; + } + + parser = new FormulaParser(formulaToParse, es, nt, ws); + + try { + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e) { + logger.warn + (e.getMessage() + + " when parsing formula " + formulaToParse + " in cell " + + getSheet().getName() + "!" + + CellReferenceHelper.getCellReference(getColumn(), getRow())); + + try { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e2) { + // fail silently + logger.error("", e2); + } + } + } + + /** + * This formula was copied from a formula already present in the writable + * workbook. Requires special handling to sort out the cell references + * + * @param ws the workbook settings + * @param es the external sheet + * @param nt the name table + */ + private void initializeCopiedFormula(WorkbookSettings ws, + ExternalSheet es, WorkbookMethods nt) { + try { + parser = new FormulaParser(formulaBytes, this, es, nt, ws); + parser.parse(); + parser.adjustRelativeCellReferences + (getColumn() - copiedFrom.getColumn(), + getRow() - copiedFrom.getRow()); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + } catch (FormulaException e) { + try { + // try again, with an error formula + formulaToParse = "ERROR(1)"; + parser = new FormulaParser(formulaToParse, es, nt, ws); + parser.parse(); + formulaString = parser.getFormula(); + formulaBytes = parser.getBytes(); + + } catch (FormulaException e2) { + // fail silently + logger.error("", e2); + } + } + } + + /** + * Called when the cell is added to the worksheet. Overrides the + * method in the base class in order to get a handle to the + * WorkbookSettings so that this formula may be initialized + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s the sheet this is being added to + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + super.setCellDetails(fr, ss, s); + initialize(s.getWorkbookSettings(), s.getWorkbook(), s.getWorkbook()); + s.getWorkbook().addRCIRCell(this); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] formulaData = getFormulaData(); + byte[] data = new byte[formulaData.length + celldata.length]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + System.arraycopy(formulaData, 0, data, celldata.length, + formulaData.length); + return data; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.ERROR; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return formulaString; + } + + /** + * Gets the raw bytes for the formula. This will include the + * parsed tokens array + * + * @return the raw record data + */ + public byte[] getFormulaData() { + byte[] data = new byte[formulaBytes.length + 16]; + System.arraycopy(formulaBytes, 0, data, 16, formulaBytes.length); + + data[6] = (byte) 0x10; + data[7] = (byte) 0x40; + data[12] = (byte) 0xe0; + data[13] = (byte) 0xfc; + // Set the recalculate on load bit + data[8] |= 0x02; + + // Set the length of the rpn array + IntegerHelper.getTwoBytes(formulaBytes.length, data, 14); + + return data; + } + + /** + * A dummy implementation to keep the compiler quiet. This object needs + * to be instantiated from ReadFormulaRecord + * + * @param col the column which the new cell will occupy + * @param row the row which the new cell will occupy + * @return NOTHING + */ + public WritableCell copyTo(int col, int row) { + Assert.verify(false); + return null; + } + + /** + * Called when a column is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(Sheet s, int sheetIndex, int col) { + parser.columnInserted(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a column is removed on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnRemoved(Sheet s, int sheetIndex, int col) { + parser.columnRemoved(sheetIndex, col, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the column was inserted + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(Sheet s, int sheetIndex, int row) { + parser.rowInserted(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } + + /** + * Called when a row is inserted on the specified sheet. Notifies all + * RCIR cells of this change. The default implementation here does nothing + * + * @param s the sheet on which the row was removed + * @param sheetIndex the sheet index on which the column was removed + * @param row the column number which was removed + */ + void rowRemoved(Sheet s, int sheetIndex, int row) { + parser.rowRemoved(sheetIndex, row, s == getSheet()); + formulaBytes = parser.getBytes(); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java new file mode 100755 index 0000000..aeb2c47 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/FunctionGroupCountRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of build in function groups + */ +class FunctionGroupCountRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * The number of built in function groups + */ + private final int numFunctionGroups; + + /** + * Constructor + */ + public FunctionGroupCountRecord() { + super(Type.FNGROUPCOUNT); + + numFunctionGroups = 0xe; + + data = new byte[2]; + + IntegerHelper.getTwoBytes(numFunctionGroups, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java new file mode 100755 index 0000000..0041104 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/GridSetRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates that the user changed the stae of the + * GridLines option + */ +class GridSetRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * Gridset flag + */ + private final boolean gridSet; + + /** + * Constructor + * + * @param gs grid set flag + */ + public GridSetRecord(boolean gs) { + super(Type.GRIDSET); + gridSet = gs; + + data = new byte[2]; + + if (gridSet) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java new file mode 100755 index 0000000..6ceb6ef --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/GuttersRecord.java @@ -0,0 +1,112 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which contains the size of the row and column gutters. These are + * all set to zero by default + */ +class GuttersRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The rowGutter + */ + private int rowGutter; + /** + * The column gutter + */ + private int colGutter; + /** + * The maximum outline level for the row gutter + */ + private int maxRowOutline; + /** + * The maximum row outline level for the column gutter + */ + private int maxColumnOutline; + + /** + * Constructor + */ + public GuttersRecord() { + super(Type.GUTS); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + data = new byte[8]; + IntegerHelper.getTwoBytes(rowGutter, data, 0); + IntegerHelper.getTwoBytes(colGutter, data, 2); + IntegerHelper.getTwoBytes(maxRowOutline, data, 4); + IntegerHelper.getTwoBytes(maxColumnOutline, data, 6); + return data; + } + + /** + * Accessor for the maximum row outline + * + * @return the maximum row outline + */ + public int getMaxRowOutline() { + return maxRowOutline; + } + + /** + * Sets the maximum row outline + * + * @param value the maximum row outline + */ + public void setMaxRowOutline(int value) { + maxRowOutline = value; + rowGutter = 1 + 14 * value; + } + + /** + * Accessor for the maximum column outline + * + * @return the maximum column outline + */ + public int getMaxColumnOutline() { + return maxColumnOutline; + } + + /** + * Sets the maximum column outline + * + * @param value the maximum row outline + */ + public void setMaxColumnOutline(int value) { + maxColumnOutline = value; + colGutter = 1 + 14 * value; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java new file mode 100755 index 0000000..7b8431d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HeaderRecord.java @@ -0,0 +1,83 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class HeaderRecord extends WritableRecordData { + /** + * The binary data + */ + private byte[] data; + /** + * The print header string + */ + private final String header; + + /** + * Constructor + * + * @param s the header string + */ + public HeaderRecord(String h) { + super(Type.HEADER); + + header = h; + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public HeaderRecord(HeaderRecord hr) { + super(Type.HEADER); + + header = hr.header; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + if (header == null || header.length() == 0) { + data = new byte[0]; + return data; + } + + data = new byte[header.length() * 2 + 3]; + IntegerHelper.getTwoBytes(header.length(), data, 0); + data[2] = (byte) 0x1; + + StringHelper.getUnicodeBytes(header, data, 3); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java new file mode 100755 index 0000000..b1fd757 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HideobjRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores options selected in the Options dialog box + *
+ * =2 if the Hide All option is turned on + * =1 if the Show Placeholders option is turned on + * =0 if the Show All option is turned on + */ +class HideobjRecord extends WritableRecordData { + /** + * Hide object mode + */ + private final int hidemode; + + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param newHideMode the hide all flag + */ + public HideobjRecord(int newHideMode) { + super(Type.HIDEOBJ); + + hidemode = newHideMode; + data = new byte[2]; + + IntegerHelper.getTwoBytes(hidemode, data, 0); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java new file mode 100755 index 0000000..2e88e0f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalCentreRecord.java @@ -0,0 +1,66 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which indicates the whether the horizontal center option has + * been set + */ +class HorizontalCentreRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The centre flag + */ + private final boolean centre; + + /** + * Constructor + * + * @param ce the centre flag + */ + public HorizontalCentreRecord(boolean ce) { + super(Type.HCENTER); + + centre = ce; + + data = new byte[2]; + + if (centre) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java new file mode 100755 index 0000000..752ad1d --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HorizontalPageBreaksRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the list of explicit horizontal page breaks on the current sheet + */ +class HorizontalPageBreaksRecord extends WritableRecordData { + /** + * The row breaks + */ + private final int[] rowBreaks; + + /** + * Constructor + * + * @param break the row breaks + */ + public HorizontalPageBreaksRecord(int[] breaks) { + super(Type.HORIZONTALPAGEBREAKS); + + rowBreaks = breaks; + } + + /** + * Gets the binary data to write to the output file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[rowBreaks.length * 6 + 2]; + + // The number of breaks on the list + IntegerHelper.getTwoBytes(rowBreaks.length, data, 0); + int pos = 2; + + for (int i = 0; i < rowBreaks.length; i++) { + IntegerHelper.getTwoBytes(rowBreaks[i], data, pos); + IntegerHelper.getTwoBytes(0xff, data, pos + 4); + pos += 6; + } + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java new file mode 100755 index 0000000..9c99607 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/HyperlinkRecord.java @@ -0,0 +1,1140 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import jxl.CellType; +import jxl.Hyperlink; +import jxl.Range; +import jxl.biff.CellReferenceHelper; +import jxl.biff.IntegerHelper; +import jxl.biff.SheetRangeImpl; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.Label; +import jxl.write.WritableCell; +import jxl.write.WritableSheet; + + +/** + * A hyperlink + */ +public class HyperlinkRecord extends WritableRecordData { + private static final LinkType urlLink = new LinkType(); + private static final LinkType fileLink = new LinkType(); + private static final LinkType uncLink = new LinkType(); + private static final LinkType workbookLink = new LinkType(); + private static final LinkType unknown = new LinkType(); + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(HyperlinkRecord.class); + /** + * The first row + */ + private int firstRow; + /** + * The last row + */ + private int lastRow; + /** + * The first column + */ + private int firstColumn; + /** + * The last column + */ + private int lastColumn; + /** + * The URL referred to by this hyperlink + */ + private URL url; + /** + * The local file referred to by this hyperlink + */ + private File file; + /** + * The location in this workbook referred to by this hyperlink + */ + private String location; + /** + * The cell contents of the cell which activate this hyperlink + */ + private String contents; + /** + * The type of this hyperlink + */ + private LinkType linkType; + + /** + * The data for this hyperlink + */ + private byte[] data; + /** + * The range of this hyperlink. When creating a hyperlink, this will + * be null until the hyperlink is added to the sheet + */ + private Range range; + /** + * The sheet containing this hyperlink + */ + private WritableSheet sheet; + /** + * Indicates whether this record has been modified since it was copied + */ + private boolean modified; + /** + * Constructs this object from the readable spreadsheet + * + * @param hl the hyperlink from the read spreadsheet + */ + protected HyperlinkRecord(Hyperlink h, WritableSheet s) { + super(Type.HLINK); + + if (h instanceof jxl.read.biff.HyperlinkRecord) { + copyReadHyperlink(h, s); + } else { + copyWritableHyperlink(h, s); + } + } + + /** + * Constructs a URL hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param url the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + URL url, + String desc) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + this.url = url; + contents = desc; + + linkType = urlLink; + + modified = true; + } + + /** + * Constructs a File hyperlink to a range of cells + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param file the hyperlink + * @param desc the description + */ + protected HyperlinkRecord(int col, int row, int lastcol, int lastrow, + File file, String desc) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + contents = desc; + + this.file = file; + + if (file.getPath().startsWith("\\\\")) { + linkType = uncLink; + } else { + linkType = fileLink; + } + + modified = true; + } + + /** + * Constructs a hyperlink to some cells within this workbook + * + * @param col the column containing this hyperlink + * @param row the row containing this hyperlink + * @param lastcol the last column which activates this hyperlink + * @param lastrow the last row which activates this hyperlink + * @param desc the contents of the cell which describe this hyperlink + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected HyperlinkRecord(int col, int row, + int lastcol, int lastrow, + String desc, + WritableSheet s, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + super(Type.HLINK); + + firstColumn = col; + firstRow = row; + + lastColumn = Math.max(firstColumn, lastcol); + lastRow = Math.max(firstRow, lastrow); + + setLocation(s, destcol, destrow, lastdestcol, lastdestrow); + contents = desc; + + linkType = workbookLink; + + modified = true; + } + + /** + * Copies a hyperlink read in from a read only sheet + */ + private void copyReadHyperlink(Hyperlink h, WritableSheet s) { + jxl.read.biff.HyperlinkRecord hl = (jxl.read.biff.HyperlinkRecord) h; + + data = hl.getRecord().getData(); + sheet = s; + + // Populate this hyperlink with the copied data + firstRow = hl.getRow(); + firstColumn = hl.getColumn(); + lastRow = hl.getLastRow(); + lastColumn = hl.getLastColumn(); + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + + linkType = unknown; + + if (hl.isFile()) { + linkType = fileLink; + file = hl.getFile(); + } else if (hl.isURL()) { + linkType = urlLink; + url = hl.getURL(); + } else if (hl.isLocation()) { + linkType = workbookLink; + location = hl.getLocation(); + } + + modified = false; + } + + /** + * Copies a hyperlink read in from a writable sheet. + * Used when copying writable sheets + * + * @param hl the hyperlink from the read spreadsheet + */ + private void copyWritableHyperlink(Hyperlink hl, WritableSheet s) { + HyperlinkRecord h = (HyperlinkRecord) hl; + + firstRow = h.firstRow; + lastRow = h.lastRow; + firstColumn = h.firstColumn; + lastColumn = h.lastColumn; + + if (h.url != null) { + try { + url = new URL(h.url.toString()); + } catch (MalformedURLException e) { + // should never get a malformed url as a result url.toString() + Assert.verify(false); + } + } + + if (h.file != null) { + file = new File(h.file.getPath()); + } + + location = h.location; + contents = h.contents; + linkType = h.linkType; + modified = true; + + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Determines whether this is a hyperlink to a file + * + * @return TRUE if this is a hyperlink to a file, FALSE otherwise + */ + public boolean isFile() { + return linkType == fileLink; + } + + /** + * Determines whether this is a hyperlink to a UNC + * + * @return TRUE if this is a hyperlink to a UNC, FALSE otherwise + */ + public boolean isUNC() { + return linkType == uncLink; + } + + /** + * Determines whether this is a hyperlink to a web resource + * + * @return TRUE if this is a URL + */ + public boolean isURL() { + return linkType == urlLink; + } + + /** + * Determines whether this is a hyperlink to a location in this workbook + * + * @return TRUE if this is a link to an internal location + */ + public boolean isLocation() { + return linkType == workbookLink; + } + + /** + * Returns the row number of the top left cell + * + * @return the row number of this cell + */ + public int getRow() { + return firstRow; + } + + /** + * Returns the column number of the top left cell + * + * @return the column number of this cell + */ + public int getColumn() { + return firstColumn; + } + + /** + * Returns the row number of the bottom right cell + * + * @return the row number of this cell + */ + public int getLastRow() { + return lastRow; + } + + /** + * Returns the column number of the bottom right cell + * + * @return the column number of this cell + */ + public int getLastColumn() { + return lastColumn; + } + + /** + * Gets the URL referenced by this Hyperlink + * + * @return the URL, or NULL if this hyperlink is not a URL + */ + public URL getURL() { + return url; + } + + /** + * Sets the URL of this hyperlink + * + * @param url the url + */ + public void setURL(URL url) { + URL prevurl = this.url; + linkType = urlLink; + file = null; + location = null; + contents = null; + this.url = url; + modified = true; + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet if it was a string representation of the + // URL + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + if (wc.getType() == CellType.LABEL) { + Label l = (Label) wc; + String prevurlString = prevurl.toString(); + String prevurlString2 = ""; + if (prevurlString.charAt(prevurlString.length() - 1) == '/' || + prevurlString.charAt(prevurlString.length() - 1) == '\\') { + prevurlString2 = prevurlString.substring(0, + prevurlString.length() - 1); + } + + if (l.getString().equals(prevurlString) || + l.getString().equals(prevurlString2)) { + l.setString(url.toString()); + } + } + } + + /** + * Returns the local file eferenced by this Hyperlink + * + * @return the file, or NULL if this hyperlink is not a file + */ + public File getFile() { + return file; + } + + /** + * Sets the file activated by this hyperlink + * + * @param file the file + */ + public void setFile(File file) { + linkType = fileLink; + url = null; + location = null; + contents = null; + this.file = file; + modified = true; + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(file.toString()); + } + + /** + * Gets the binary data to be written to the output file + * + * @return the data to write to file + */ + public byte[] getData() { + if (!modified) { + return data; + } + + // Build up the jxl.common.data + byte[] commonData = new byte[32]; + + // Set the range of cells this hyperlink applies to + IntegerHelper.getTwoBytes(firstRow, commonData, 0); + IntegerHelper.getTwoBytes(lastRow, commonData, 2); + IntegerHelper.getTwoBytes(firstColumn, commonData, 4); + IntegerHelper.getTwoBytes(lastColumn, commonData, 6); + + // Some inexplicable byte sequence + commonData[8] = (byte) 0xd0; + commonData[9] = (byte) 0xc9; + commonData[10] = (byte) 0xea; + commonData[11] = (byte) 0x79; + commonData[12] = (byte) 0xf9; + commonData[13] = (byte) 0xba; + commonData[14] = (byte) 0xce; + commonData[15] = (byte) 0x11; + commonData[16] = (byte) 0x8c; + commonData[17] = (byte) 0x82; + commonData[18] = (byte) 0x0; + commonData[19] = (byte) 0xaa; + commonData[20] = (byte) 0x0; + commonData[21] = (byte) 0x4b; + commonData[22] = (byte) 0xa9; + commonData[23] = (byte) 0x0b; + commonData[24] = (byte) 0x2; + commonData[25] = (byte) 0x0; + commonData[26] = (byte) 0x0; + commonData[27] = (byte) 0x0; + + // Set up the option flags to indicate the type of this URL. There + // is no description + int optionFlags = 0; + if (isURL()) { + optionFlags = 3; + + if (contents != null) { + optionFlags |= 0x14; + } + } else if (isFile()) { + optionFlags = 1; + + if (contents != null) { + optionFlags |= 0x14; + } + } else if (isLocation()) { + optionFlags = 8; + } else if (isUNC()) { + optionFlags = 259; + } + + IntegerHelper.getFourBytes(optionFlags, commonData, 28); + + if (isURL()) { + data = getURLData(commonData); + } else if (isFile()) { + data = getFileData(commonData); + } else if (isLocation()) { + data = getLocationData(commonData); + } else if (isUNC()) { + data = getUNCData(commonData); + } + + return data; + } + + /** + * A standard toString method + * + * @return the contents of this object as a string + */ + public String toString() { + if (isFile()) { + return file.toString(); + } else if (isURL()) { + return url.toString(); + } else if (isUNC()) { + return file.toString(); + } else { + return ""; + } + } + + /** + * Gets the range of cells which activate this hyperlink + * The get sheet index methods will all return -1, because the + * cells will all be present on the same sheet + * + * @return the range of cells which activate the hyperlink or NULL + * if this hyperlink has not been added to the sheet + */ + public Range getRange() { + return range; + } + + /** + * Sets the location of the cells to be linked to within this workbook + * + * @param desc the label describing the link + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + protected void setLocation(String desc, + WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + linkType = workbookLink; + url = null; + file = null; + modified = true; + contents = desc; + + setLocation(sheet, destcol, destrow, lastdestcol, lastdestrow); + + if (sheet == null) { + // hyperlink has not been added to the sheet yet, so simply return + return; + } + + // Change the label on the sheet + WritableCell wc = sheet.getWritableCell(firstColumn, firstRow); + + Assert.verify(wc.getType() == CellType.LABEL); + + Label l = (Label) wc; + l.setString(desc); + } + + /** + * Initializes the location from the data passed in + * + * @param sheet the sheet containing the cells to be linked to + * @param destcol the column number of the first destination linked cell + * @param destrow the row number of the first destination linked cell + * @param lastdestcol the column number of the last destination linked cell + * @param lastdestrow the row number of the last destination linked cell + */ + private void setLocation(WritableSheet sheet, + int destcol, int destrow, + int lastdestcol, int lastdestrow) { + StringBuffer sb = new StringBuffer(); + sb.append('\''); + + if (sheet.getName().indexOf('\'') == -1) { + sb.append(sheet.getName()); + } else { + // sb.append(sheet.getName().replaceAll("'", "''")); + + // Can't use replaceAll as it is only 1.4 compatible, so have to + // do this the tedious way + String sheetName = sheet.getName(); + int pos = 0; + int nextPos = sheetName.indexOf('\'', pos); + + while (nextPos != -1 && pos < sheetName.length()) { + sb.append(sheetName, pos, nextPos); + sb.append("''"); + pos = nextPos + 1; + nextPos = sheetName.indexOf('\'', pos); + } + sb.append(sheetName.substring(pos)); + } + + sb.append('\''); + sb.append('!'); + + lastdestcol = Math.max(destcol, lastdestcol); + lastdestrow = Math.max(destrow, lastdestrow); + + CellReferenceHelper.getCellReference(destcol, destrow, sb); + sb.append(':'); + CellReferenceHelper.getCellReference(lastdestcol, lastdestrow, sb); + + location = sb.toString(); + } + + /** + * A row has been inserted, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void insertRow(int r) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) { + return; + } + + if (r <= firstRow) { + firstRow++; + modified = true; + } + + if (r <= lastRow) { + lastRow++; + modified = true; + } + + if (modified) { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been inserted, so adjust the range objects accordingly + * + * @param c the column which has been inserted + */ + void insertColumn(int c) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) { + return; + } + + if (c <= firstColumn) { + firstColumn++; + modified = true; + } + + if (c <= lastColumn) { + lastColumn++; + modified = true; + } + + if (modified) { + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A row has been removed, so adjust the range objects accordingly + * + * @param r the row which has been inserted + */ + void removeRow(int r) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (r > lastRow) { + return; + } + + if (r < firstRow) { + firstRow--; + modified = true; + } + + if (r < lastRow) { + lastRow--; + modified = true; + } + + if (modified) { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * A column has been removed, so adjust the range objects accordingly + * + * @param c the column which has been removed + */ + void removeColumn(int c) { + // This will not be called unless the hyperlink has been added to the + // sheet + Assert.verify(sheet != null && range != null); + + if (c > lastColumn) { + return; + } + + if (c < firstColumn) { + firstColumn--; + modified = true; + } + + if (c < lastColumn) { + lastColumn--; + modified = true; + } + + if (modified) { + Assert.verify(range != null); + range = new SheetRangeImpl(sheet, + firstColumn, firstRow, + lastColumn, lastRow); + } + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getURLData(byte[] cd) { + String urlString = url.toString(); + + int dataLength = cd.length + 20 + (urlString.length() + 1) * 2; + + if (contents != null) { + dataLength += 4 + (contents.length() + 1) * 2; + } + + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + if (contents != null) { + IntegerHelper.getFourBytes(contents.length() + 1, d, urlPos); + StringHelper.getUnicodeBytes(contents, d, urlPos + 4); + urlPos += (contents.length() + 1) * 2 + 4; + } + + // Inexplicable byte sequence + d[urlPos] = (byte) 0xe0; + d[urlPos + 1] = (byte) 0xc9; + d[urlPos + 2] = (byte) 0xea; + d[urlPos + 3] = (byte) 0x79; + d[urlPos + 4] = (byte) 0xf9; + d[urlPos + 5] = (byte) 0xba; + d[urlPos + 6] = (byte) 0xce; + d[urlPos + 7] = (byte) 0x11; + d[urlPos + 8] = (byte) 0x8c; + d[urlPos + 9] = (byte) 0x82; + d[urlPos + 10] = (byte) 0x0; + d[urlPos + 11] = (byte) 0xaa; + d[urlPos + 12] = (byte) 0x0; + d[urlPos + 13] = (byte) 0x4b; + d[urlPos + 14] = (byte) 0xa9; + d[urlPos + 15] = (byte) 0x0b; + + // Number of characters in the url, including a zero trailing character + IntegerHelper.getFourBytes((urlString.length() + 1) * 2, d, urlPos + 16); + + // Put the url into the data string + StringHelper.getUnicodeBytes(urlString, d, urlPos + 20); + + return d; + } + + /** + * Gets the hyperlink stream specific to a URL link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getUNCData(byte[] cd) { + String uncString = file.getPath(); + + byte[] d = new byte[cd.length + uncString.length() * 2 + 2 + 4]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int urlPos = cd.length; + + // The length of the unc string, including zero terminator + int length = uncString.length() + 1; + IntegerHelper.getFourBytes(length, d, urlPos); + + // Place the string into the stream + StringHelper.getUnicodeBytes(uncString, d, urlPos + 4); + + return d; + } + + /** + * Gets the hyperlink stream specific to a local file link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getFileData(byte[] cd) { + // Build up the directory hierarchy in reverse order + ArrayList path = new ArrayList(); + ArrayList shortFileName = new ArrayList(); + path.add(file.getName()); + shortFileName.add(getShortName(file.getName())); + + File parent = file.getParentFile(); + while (parent != null) { + path.add(parent.getName()); + shortFileName.add(getShortName(parent.getName())); + parent = parent.getParentFile(); + } + + // Deduce the up directory level count and remove the directory from + // the path + int upLevelCount = 0; + int pos = path.size() - 1; + boolean upDir = true; + + while (upDir) { + String s = (String) path.get(pos); + if (s.equals("..")) { + upLevelCount++; + path.remove(pos); + shortFileName.remove(pos); + } else { + upDir = false; + } + + pos--; + } + + StringBuffer filePathSB = new StringBuffer(); + StringBuffer shortFilePathSB = new StringBuffer(); + + if (file.getPath().charAt(1) == ':') { + char driveLetter = file.getPath().charAt(0); + if (driveLetter != 'C' && driveLetter != 'c') { + filePathSB.append(driveLetter); + filePathSB.append(':'); + shortFilePathSB.append(driveLetter); + shortFilePathSB.append(':'); + } + } + + for (int i = path.size() - 1; i >= 0; i--) { + filePathSB.append((String) path.get(i)); + shortFilePathSB.append((String) shortFileName.get(i)); + + if (i != 0) { + filePathSB.append("\\"); + shortFilePathSB.append("\\"); + } + } + + + String filePath = filePathSB.toString(); + String shortFilePath = shortFilePathSB.toString(); + + int dataLength = cd.length + + 4 + (shortFilePath.length() + 1) + // short file name + 16 + // inexplicable byte sequence + 2 + // up directory level count + 8 + (filePath.length() + 1) * 2 + // long file name + 24; // inexplicable byte sequence + + + if (contents != null) { + dataLength += 4 + (contents.length() + 1) * 2; + } + + // Copy across the jxl.common.data into the new array + byte[] d = new byte[dataLength]; + + System.arraycopy(cd, 0, d, 0, cd.length); + + int filePos = cd.length; + + // Add in the description text + if (contents != null) { + IntegerHelper.getFourBytes(contents.length() + 1, d, filePos); + StringHelper.getUnicodeBytes(contents, d, filePos + 4); + filePos += (contents.length() + 1) * 2 + 4; + } + + int curPos = filePos; + + // Inexplicable byte sequence + d[curPos] = (byte) 0x03; + d[curPos + 1] = (byte) 0x03; + d[curPos + 2] = (byte) 0x0; + d[curPos + 3] = (byte) 0x0; + d[curPos + 4] = (byte) 0x0; + d[curPos + 5] = (byte) 0x0; + d[curPos + 6] = (byte) 0x0; + d[curPos + 7] = (byte) 0x0; + d[curPos + 8] = (byte) 0xc0; + d[curPos + 9] = (byte) 0x0; + d[curPos + 10] = (byte) 0x0; + d[curPos + 11] = (byte) 0x0; + d[curPos + 12] = (byte) 0x0; + d[curPos + 13] = (byte) 0x0; + d[curPos + 14] = (byte) 0x0; + d[curPos + 15] = (byte) 0x46; + + curPos += 16; + + // The directory up level count + IntegerHelper.getTwoBytes(upLevelCount, d, curPos); + curPos += 2; + + // The number of bytes in the short file name, including zero terminator + IntegerHelper.getFourBytes((shortFilePath.length() + 1), d, curPos); + + // The short file name + StringHelper.getBytes(shortFilePath, d, curPos + 4); + + curPos += 4 + (shortFilePath.length() + 1); + + // Inexplicable byte sequence + d[curPos] = (byte) 0xff; + d[curPos + 1] = (byte) 0xff; + d[curPos + 2] = (byte) 0xad; + d[curPos + 3] = (byte) 0xde; + d[curPos + 4] = (byte) 0x0; + d[curPos + 5] = (byte) 0x0; + d[curPos + 6] = (byte) 0x0; + d[curPos + 7] = (byte) 0x0; + d[curPos + 8] = (byte) 0x0; + d[curPos + 9] = (byte) 0x0; + d[curPos + 10] = (byte) 0x0; + d[curPos + 11] = (byte) 0x0; + d[curPos + 12] = (byte) 0x0; + d[curPos + 13] = (byte) 0x0; + d[curPos + 14] = (byte) 0x0; + d[curPos + 15] = (byte) 0x0; + d[curPos + 16] = (byte) 0x0; + d[curPos + 17] = (byte) 0x0; + d[curPos + 18] = (byte) 0x0; + d[curPos + 19] = (byte) 0x0; + d[curPos + 20] = (byte) 0x0; + d[curPos + 21] = (byte) 0x0; + d[curPos + 22] = (byte) 0x0; + d[curPos + 23] = (byte) 0x0; + + curPos += 24; + + // Size of the long file name data in bytes, including inexplicable data + // fields + int size = 6 + filePath.length() * 2; + IntegerHelper.getFourBytes(size, d, curPos); + curPos += 4; + + // The number of bytes in the long file name + // NOT including zero terminator + IntegerHelper.getFourBytes((filePath.length()) * 2, d, curPos); + curPos += 4; + + // Inexplicable bytes + d[curPos] = (byte) 0x3; + d[curPos + 1] = (byte) 0x0; + + curPos += 2; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos); + curPos += (filePath.length() + 1) * 2; + + + /* + curPos += 24; + int nameLength = filePath.length() * 2; + + // Size of the file link + IntegerHelper.getFourBytes(nameLength+6, d, curPos); + + // Number of characters + IntegerHelper.getFourBytes(nameLength, d, curPos+4); + + // Inexplicable byte sequence + d[curPos+8] = 0x03; + + // The long file name + StringHelper.getUnicodeBytes(filePath, d, curPos+10); + */ + + return d; + } + + /** + * Gets the DOS short file name in 8.3 format of the name passed in + * + * @param s the name + * @return the dos short name + */ + private String getShortName(String s) { + int sep = s.indexOf('.'); + + String prefix = null; + String suffix = null; + + if (sep == -1) { + prefix = s; + suffix = ""; + } else { + prefix = s.substring(0, sep); + suffix = s.substring(sep + 1); + } + + if (prefix.length() > 8) { + prefix = prefix.substring(0, 6) + "~" + (prefix.length() - 8); + prefix = prefix.substring(0, 8); + } + + suffix = suffix.substring(0, Math.min(3, suffix.length())); + + if (suffix.length() > 0) { + return prefix + '.' + suffix; + } else { + return prefix; + } + } + + /** + * Gets the hyperlink stream specific to a location link + * + * @param cd the data jxl.common.for all types of hyperlink + * @return the raw data for a URL hyperlink + */ + private byte[] getLocationData(byte[] cd) { + byte[] d = new byte[cd.length + 4 + (location.length() + 1) * 2]; + System.arraycopy(cd, 0, d, 0, cd.length); + + int locPos = cd.length; + + // The number of chars in the location string, plus a 0 terminator + IntegerHelper.getFourBytes(location.length() + 1, d, locPos); + + // Get the location + StringHelper.getUnicodeBytes(location, d, locPos + 4); + + return d; + } + + /** + * Initializes the range when this hyperlink is added to the sheet + * + * @param s the sheet containing this hyperlink + */ + void initialize(WritableSheet s) { + sheet = s; + range = new SheetRangeImpl(s, + firstColumn, firstRow, + lastColumn, lastRow); + } + + /** + * Called by the worksheet. Gets the string contents to put into the cell + * containing this hyperlink + * + * @return the string contents for the hyperlink cell + */ + String getContents() { + return contents; + } + + /** + * Sets the description + * + * @param desc the description + */ + protected void setContents(String desc) { + contents = desc; + modified = true; + } + + /** + * The excel type of hyperlink + */ + private static class LinkType { + } +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java new file mode 100755 index 0000000..4337f96 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/IndexRecord.java @@ -0,0 +1,99 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Index into the cell rows in an worksheet + */ +class IndexRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + /** + * The numbe of rows served by this index record + */ + private final int rows; + /** + * The position of the BOF record in the excel output stream + */ + private final int bofPosition; + /** + * The number of blocks needed to hold all the rows + */ + private final int blocks; + + /** + * The position of the current 'pointer' within the byte array + */ + private int dataPos; + + /** + * Constructor + * + * @param pos the position of the BOF record + * @param bl the number of blocks + * @param r the number of rows + */ + public IndexRecord(int pos, int r, int bl) { + super(Type.INDEX); + bofPosition = pos; + rows = r; + blocks = bl; + + // Allocate the amount of bytes required to hold all the blocks + data = new byte[16 + 4 * blocks]; + dataPos = 16; + } + + /** + * Gets the binary data for output. This writes out an empty data block, and + * the information is filled in later on when the information becomes + * available + * + * @return the binary data + */ + protected byte[] getData() { + IntegerHelper.getFourBytes(rows, data, 8); + return data; + } + + /** + * Adds another index record into the array + * + * @param pos the position in the output file + */ + void addBlockPosition(int pos) { + IntegerHelper.getFourBytes(pos - bofPosition, data, dataPos); + dataPos += 4; + } + + /** + * Sets the position of the data start. This happens to be the position + * of the DEFCOLWIDTH record + */ + void setDataStartPosition(int pos) { + IntegerHelper.getFourBytes(pos - bofPosition, data, 12); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java new file mode 100755 index 0000000..8fb52af --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceEndRecord.java @@ -0,0 +1,45 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the end of the user interface section of the Book stream. + * It has no data field + */ +class InterfaceEndRecord extends WritableRecordData { + /** + * Consructor + */ + public InterfaceEndRecord() { + super(Type.INTERFACEEND); + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + return new byte[0]; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java new file mode 100755 index 0000000..54acfd8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/InterfaceHeaderRecord.java @@ -0,0 +1,49 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Marks the beginning of the user interface record + */ +class InterfaceHeaderRecord extends WritableRecordData { + /** + * Constructor + */ + public InterfaceHeaderRecord() { + super(Type.INTERFACEHDR); + } + + /** + * Gets the binary data + * + * @return the binary data + */ + public byte[] getData() { + // Return the character encoding + byte[] data = new byte[] + {(byte) 0xb0, (byte) 0x04}; + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java new file mode 100755 index 0000000..bad702f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/IterationRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the iteration option from the dialog box + */ +class IterationRecord extends WritableRecordData { + /** + * The iterate flag + */ + private final boolean iterate; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param it the iterator flag + */ + public IterationRecord(boolean it) { + super(Type.ITERATION); + + iterate = it; + + data = new byte[2]; + + if (iterate) { + data[0] = 1; + } + } + + /** + * Gets the binary data for output + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java b/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java new file mode 100755 index 0000000..07c4575 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/JxlWriteException.java @@ -0,0 +1,71 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.write.WriteException; + +/** + * Exception thrown when reading a biff file + */ +public class JxlWriteException extends WriteException { + /** + * + */ + static WriteMessage formatInitialized = + new WriteMessage("Attempt to modify a referenced format"); + /** + * + */ + static WriteMessage cellReferenced = + new WriteMessage("Cell has already been added to a worksheet"); + static WriteMessage maxRowsExceeded = + new WriteMessage("The maximum number of rows permitted on a worksheet " + + "been exceeded"); + static WriteMessage maxColumnsExceeded = + new WriteMessage("The maximum number of columns permitted on a " + + "worksheet has been exceeded"); + static WriteMessage copyPropertySets = + new WriteMessage("Error encounted when copying additional property sets"); + + /** + * Constructs this exception with the specified message + * + * @param m the message + */ + public JxlWriteException(WriteMessage m) { + super(m.message); + } + + private static class WriteMessage { + /** + * + */ + public String message; + + /** + * Constructs this exception with the specified message + * + * @param m the messageA + */ + WriteMessage(String m) { + message = m; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java new file mode 100755 index 0000000..4e90a7e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/LabelRecord.java @@ -0,0 +1,214 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.CellType; +import jxl.LabelCell; +import jxl.biff.FormattingRecords; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.format.CellFormat; + +/** + * A label record, used for writing out string + */ +public abstract class LabelRecord extends CellValue { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(LabelRecord.class); + + /** + * The string + */ + private String contents; + + /** + * A handle to the shared strings used within this workbook + */ + private SharedStrings sharedStrings; + + /** + * The index of the string in the shared string table + */ + private int index; + + /** + * Constructor used when creating a label from the user API + * + * @param c the column + * @param cont the contents + * @param r the row + */ + protected LabelRecord(int c, int r, String cont) { + super(Type.LABELSST, c, r); + contents = cont; + if (contents == null) { + contents = ""; + } + } + + /** + * Constructor used when creating a label from the API. This is + * overloaded to allow formatting information to be passed to the record + * + * @param c the column + * @param cont the contents + * @param r the row + * @param st the format applied to the cell + */ + protected LabelRecord(int c, int r, String cont, CellFormat st) { + super(Type.LABELSST, c, r, st); + contents = cont; + + if (contents == null) { + contents = ""; + } + } + + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected LabelRecord(int c, int r, LabelRecord lr) { + super(Type.LABELSST, c, r, lr); + contents = lr.contents; + } + + /** + * Constructor used when copying a label from a read only + * spreadsheet + * + * @param lc the label to copy + */ + protected LabelRecord(LabelCell lc) { + super(Type.LABELSST, lc); + contents = lc.getString(); + if (contents == null) { + contents = ""; + } + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.LABEL; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 4]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + IntegerHelper.getFourBytes(index, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + return contents; + } + + /** + * Gets the label for this cell. The value returned will be the same + * as for the getContents method in the base class + * + * @return the cell contents + */ + public String getString() { + return contents; + } + + /** + * Sets the string contents of this cell + * + * @param s the new string contents + */ + protected void setString(String s) { + if (s == null) { + s = ""; + } + + contents = s; + + // Don't bother doing anything if this cell has not been referenced + // yet - everything will be set up in due course + if (!isReferenced()) { + return; + } + + Assert.verify(sharedStrings != null); + + // Initalize the shared string index + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + + /** + * Overrides the method in the base class in order to add the string + * content to the shared string table, and to store its shared string + * index + * + * @param fr the formatting records + * @param ss the shared strings used within the workbook + * @param s + */ + void setCellDetails(FormattingRecords fr, SharedStrings ss, + WritableSheetImpl s) { + super.setCellDetails(fr, ss, s); + + sharedStrings = ss; + + index = sharedStrings.getIndex(contents); + + // Use the sharedStrings reference instead of this object's own + // handle - this means that the bespoke copy becomes eligible for + // garbage collection + contents = sharedStrings.get(index); + } + +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java new file mode 100755 index 0000000..329e9b8 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/LeftMarginRecord.java @@ -0,0 +1,31 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; + +/** + * The settings for the left margin + */ +class LeftMarginRecord extends MarginRecord { + LeftMarginRecord(double v) { + super(Type.LEFTMARGIN, v); + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java new file mode 100755 index 0000000..ce38048 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MMSRecord.java @@ -0,0 +1,68 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the number of addmen and delmenu groups in the book stream + */ +class MMSRecord extends WritableRecordData { + /** + * The number of menu items added + */ + private final byte numMenuItemsAdded; + /** + * The number of menu items deleted + */ + private final byte numMenuItemsDeleted; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param menuItemsAdded the number of menu items added + * @param menuItemsDeleted the number of menu items deleted + */ + public MMSRecord(int menuItemsAdded, int menuItemsDeleted) { + super(Type.MMS); + + numMenuItemsAdded = (byte) menuItemsAdded; + numMenuItemsDeleted = (byte) menuItemsDeleted; + + data = new byte[2]; + + data[0] = numMenuItemsAdded; + data[1] = numMenuItemsDeleted; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java new file mode 100755 index 0000000..7ead8e9 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MarginRecord.java @@ -0,0 +1,57 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a margin value + */ +abstract class MarginRecord extends WritableRecordData { + /** + * The margin + */ + private final double margin; + + /** + * Constructor + */ + public MarginRecord(Type t, double v) { + super(t); + margin = v; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[8]; + + DoubleHelper.getIEEEBytes(margin, data, 0); + + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java b/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java new file mode 100755 index 0000000..1b6ccf3 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MemoryDataOutput.java @@ -0,0 +1,108 @@ +/********************************************************************* + * + * Copyright (C) 2007 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.io.OutputStream; +import jxl.common.Logger; + +/** + * Used to generate the excel biff data in memory. This class wraps a byte + * array + */ +class MemoryDataOutput implements ExcelDataOutput { + // The logger + private static final Logger logger = Logger.getLogger(MemoryDataOutput.class); + + /** + * The excel data + */ + private byte[] data; + + /** + * The grow size for the array + */ + private final int growSize; + + /** + * The current position within the array + */ + private int pos; + + /** + * Constructor + */ + public MemoryDataOutput(int initialSize, int gs) { + data = new byte[initialSize]; + growSize = gs; + pos = 0; + } + + /** + * Writes the bytes to the end of the array, growing the array + * as needs dictate + * + * @param d the data to write to the end of the array + */ + public void write(byte[] bytes) { + while (pos + bytes.length > data.length) { + // Grow the array + byte[] newdata = new byte[data.length + growSize]; + System.arraycopy(data, 0, newdata, 0, pos); + data = newdata; + } + + System.arraycopy(bytes, 0, data, pos, bytes.length); + pos += bytes.length; + } + + /** + * Gets the current position within the file + * + * @return the position within the file + */ + public int getPosition() { + return pos; + } + + /** + * Sets the data at the specified position to the contents of the array + * + * @param pos the position to alter + * @param newdata the data to modify + */ + public void setData(byte[] newdata, int pos) { + System.arraycopy(newdata, 0, data, pos, newdata.length); + } + + /** + * Writes the data to the output stream + */ + public void writeData(OutputStream out) throws IOException { + out.write(data, 0, pos); + } + + /** + * Called when the final compound file has been written. No cleanup is + * necessary for in-memory file generation + */ + public void close() throws IOException { + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java new file mode 100755 index 0000000..76573ac --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCells.java @@ -0,0 +1,278 @@ +/********************************************************************* + * + * Copyright (C) 2003 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import jxl.Cell; +import jxl.CellType; +import jxl.Range; +import jxl.WorkbookSettings; +import jxl.biff.SheetRangeImpl; +import jxl.common.Assert; +import jxl.common.Logger; +import jxl.write.Blank; +import jxl.write.WritableSheet; +import jxl.write.WriteException; + +/** + * Contains all the merged cells, and the necessary logic for checking + * for intersections and for handling very large amounts of merging + */ +class MergedCells { + /** + * The maximum number of ranges per sheet + */ + private static final int maxRangesPerSheet = 1020; + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(MergedCells.class); + /** + * The list of merged cells + */ + private ArrayList ranges; + /** + * The sheet containing the cells + */ + private final WritableSheet sheet; + + /** + * Constructor + */ + public MergedCells(WritableSheet ws) { + ranges = new ArrayList(); + sheet = ws; + } + + /** + * Adds the range to the list of merged cells. Does no checking + * at this stage + * + * @param range the range to add + */ + void add(Range r) { + ranges.add(r); + } + + /** + * Used to adjust the merged cells following a row insertion + */ + void insertRow(int row) { + // Adjust any merged cells + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + sr.insertRow(row); + } + } + + /** + * Used to adjust the merged cells following a column insertion + */ + void insertColumn(int col) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + sr.insertColumn(col); + } + } + + /** + * Used to adjust the merged cells following a column removal + */ + void removeColumn(int col) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getColumn() == col && + sr.getBottomRight().getColumn() == col) { + // The column with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } else { + sr.removeColumn(col); + } + } + } + + /** + * Used to adjust the merged cells following a row removal + */ + void removeRow(int row) { + SheetRangeImpl sr = null; + Iterator i = ranges.iterator(); + while (i.hasNext()) { + sr = (SheetRangeImpl) i.next(); + if (sr.getTopLeft().getRow() == row && + sr.getBottomRight().getRow() == row) { + // The row with the merged cells on has been removed, so get + // rid of it from the list + i.remove(); + } else { + sr.removeRow(row); + } + } + } + + /** + * Gets the cells which have been merged on this sheet + * + * @return an array of range objects + */ + Range[] getMergedCells() { + Range[] cells = new Range[ranges.size()]; + + for (int i = 0; i < cells.length; i++) { + cells[i] = (Range) ranges.get(i); + } + + return cells; + } + + /** + * Unmerges the specified cells. The Range passed in should be one that + * has been previously returned as a result of the getMergedCells method + * + * @param r the range of cells to unmerge + */ + void unmergeCells(Range r) { + int index = ranges.indexOf(r); + + if (index != -1) { + ranges.remove(index); + } + } + + /** + * Called prior to writing out in order to check for intersections + */ + private void checkIntersections() { + ArrayList newcells = new ArrayList(ranges.size()); + + for (Iterator mci = ranges.iterator(); mci.hasNext(); ) { + SheetRangeImpl r = (SheetRangeImpl) mci.next(); + + // Check that the range doesn't intersect with any existing range + Iterator i = newcells.iterator(); + SheetRangeImpl range = null; + boolean intersects = false; + while (i.hasNext() && !intersects) { + range = (SheetRangeImpl) i.next(); + + if (range.intersects(r)) { + logger.warn("Could not merge cells " + r + + " as they clash with an existing set of merged cells."); + + intersects = true; + } + } + + if (!intersects) { + newcells.add(r); + } + } + + ranges = newcells; + } + + /** + * Checks the cell ranges for intersections, or if the merged cells + * contains more than one item of data + */ + private void checkRanges() { + try { + SheetRangeImpl range = null; + + // Check all the ranges to make sure they only contain one entry + for (int i = 0; i < ranges.size(); i++) { + range = (SheetRangeImpl) ranges.get(i); + + // Get the cell in the top left + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + boolean found = false; + + for (int c = tl.getColumn(); c <= br.getColumn(); c++) { + for (int r = tl.getRow(); r <= br.getRow(); r++) { + Cell cell = sheet.getCell(c, r); + if (cell.getType() != CellType.EMPTY) { + if (!found) { + found = true; + } else { + logger.warn("Range " + range + + " contains more than one data cell. " + + "Setting the other cells to blank."); + Blank b = new Blank(c, r); + sheet.addCell(b); + } + } + } + } + } + } catch (WriteException e) { + // This should already have been checked - bomb out + Assert.verify(false); + } + } + + void write(File outputFile) throws IOException { + if (ranges.size() == 0) { + return; + } + + WorkbookSettings ws = + ((WritableSheetImpl) sheet).getWorkbookSettings(); + + if (!ws.getMergedCellCheckingDisabled()) { + checkIntersections(); + checkRanges(); + } + + // If they will all fit into one record, then create a single + // record, write them and get out + if (ranges.size() < maxRangesPerSheet) { + MergedCellsRecord mcr = new MergedCellsRecord(ranges); + outputFile.write(mcr); + return; + } + + int numRecordsRequired = ranges.size() / maxRangesPerSheet + 1; + int pos = 0; + + for (int i = 0; i < numRecordsRequired; i++) { + int numranges = Math.min(maxRangesPerSheet, ranges.size() - pos); + + ArrayList cells = new ArrayList(numranges); + for (int j = 0; j < numranges; j++) { + cells.add(ranges.get(pos + j)); + } + + MergedCellsRecord mcr = new MergedCellsRecord(cells); + outputFile.write(mcr); + + pos += numranges; + } + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java new file mode 100755 index 0000000..f06cba2 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MergedCellsRecord.java @@ -0,0 +1,88 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.ArrayList; +import jxl.Cell; +import jxl.Range; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A number record. This is stored as 8 bytes, as opposed to the + * 4 byte RK record + */ +public class MergedCellsRecord extends WritableRecordData { + /** + * The ranges of all the cells which are merged on this sheet + */ + private final ArrayList ranges; + + /** + * Constructs a merged cell record + * + * @param ws the sheet containing the merged cells + */ + protected MergedCellsRecord(ArrayList mc) { + super(Type.MERGEDCELLS); + + ranges = mc; + } + + /** + * Gets the raw data for output to file + * + * @return the data to write to file + */ + public byte[] getData() { + byte[] data = new byte[ranges.size() * 8 + 2]; + + // Set the number of ranges + IntegerHelper.getTwoBytes(ranges.size(), data, 0); + + int pos = 2; + Range range = null; + for (int i = 0; i < ranges.size(); i++) { + range = (Range) ranges.get(i); + + // Set the various cell records + Cell tl = range.getTopLeft(); + Cell br = range.getBottomRight(); + + IntegerHelper.getTwoBytes(tl.getRow(), data, pos); + IntegerHelper.getTwoBytes(br.getRow(), data, pos + 2); + IntegerHelper.getTwoBytes(tl.getColumn(), data, pos + 4); + IntegerHelper.getTwoBytes(br.getColumn(), data, pos + 6); + + pos += 8; + } + + return data; + } + +} + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java new file mode 100755 index 0000000..9e4a9b5 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/MulRKRecord.java @@ -0,0 +1,114 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.util.List; +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.write.Number; + +/** + * Contains an array of RK numbers + */ +class MulRKRecord extends WritableRecordData { + /** + * The row containing these numbers + */ + private final int row; + /** + * The first column these rk number occur on + */ + private final int colFirst; + /** + * The last column these rk number occur on + */ + private final int colLast; + /** + * The array of rk numbers + */ + private final int[] rknumbers; + /** + * The array of xf indices + */ + private final int[] xfIndices; + + /** + * Constructs the rk numbers from the integer cells + * + * @param numbers A list of jxl.write.Number objects + */ + public MulRKRecord(List numbers) { + super(Type.MULRK); + row = ((Number) numbers.get(0)).getRow(); + colFirst = ((Number) numbers.get(0)).getColumn(); + colLast = colFirst + numbers.size() - 1; + + rknumbers = new int[numbers.size()]; + xfIndices = new int[numbers.size()]; + + for (int i = 0; i < numbers.size(); i++) { + rknumbers[i] = (int) ((Number) numbers.get(i)).getValue(); + xfIndices[i] = ((CellValue) numbers.get(i)).getXFIndex(); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] data = new byte[rknumbers.length * 6 + 6]; + + // Set up the row and the first column + IntegerHelper.getTwoBytes(row, data, 0); + IntegerHelper.getTwoBytes(colFirst, data, 2); + + // Add all the rk numbers + int pos = 4; + int rkValue = 0; + byte[] rkBytes = new byte[4]; + for (int i = 0; i < rknumbers.length; i++) { + IntegerHelper.getTwoBytes(xfIndices[i], data, pos); + + // To represent an int as an Excel RK value, we have to + // undergo some outrageous jiggery pokery, as follows: + + // Gets the bit representation of the number + rkValue = rknumbers[i] << 2; + + // Set the integer bit + rkValue |= 0x2; + IntegerHelper.getFourBytes(rkValue, data, pos + 2); + + pos += 6; + } + + // Write the number of rk numbers in this record + IntegerHelper.getTwoBytes(colLast, data, pos); + + return data; + } +} + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java new file mode 100755 index 0000000..bec192c --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NameRecord.java @@ -0,0 +1,618 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.BuiltInName; +import jxl.biff.IntegerHelper; +import jxl.biff.StringHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; +import jxl.common.Logger; + +/** + * A name record. Simply takes the binary data from the name + * record read in + */ +class NameRecord extends WritableRecordData { + // Constants which refer to the parse tokens after the string + private static final int cellReference = 0x3a; + private static final int areaReference = 0x3b; + private static final int subExpression = 0x29; + private static final int union = 0x10; + // An empty range + private static final NameRange EMPTY_RANGE = new NameRange(0, 0, 0, 0, 0); + // The logger + private static final Logger logger = Logger.getLogger(NameRecord.class); + /** + * The binary data for output to file + */ + private byte[] data; + /** + * The name + */ + private String name; + /** + * The built in name + */ + private BuiltInName builtInName; + /** + * The index into the name table + */ + private final int index; + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + */ + private int sheetRef = 0; + /** + * Modified flag + */ + private boolean modified; + /** + * The ranges covered by this name + */ + private NameRange[] ranges; + + /** + * Constructor - used when copying sheets + * + * @param index the index into the name table + */ + public NameRecord(jxl.read.biff.NameRecord sr, int ind) { + super(Type.NAME); + + data = sr.getData(); + name = sr.getName(); + sheetRef = sr.getSheetRef(); + index = ind; + modified = false; + + // Copy the ranges + jxl.read.biff.NameRecord.NameRange[] r = sr.getRanges(); + ranges = new NameRange[r.length]; + for (int i = 0; i < ranges.length; i++) { + ranges[i] = new NameRange(r[i]); + } + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(String theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) { + super(Type.NAME); + + name = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + modified = true; + } + + /** + * Create a new name record with the given information. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + boolean global) { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[1]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + } + + /** + * Create a new name record with the given information for 2-range entities. + * + * @param theName Name to be created. + * @param theIndex Index of this name. + * @param extSheet External sheet index this name refers to. + * @param theStartRow First row this name refers to. + * @param theEndRow Last row this name refers to. + * @param theStartCol First column this name refers to. + * @param theEndCol Last column this name refers to. + * @param theStartRow2 First row this name refers to (2nd instance). + * @param theEndRow2 Last row this name refers to (2nd instance). + * @param theStartCol2 First column this name refers to (2nd instance). + * @param theEndCol2 Last column this name refers to (2nd instance). + * @param global TRUE if this is a global name + */ + NameRecord(BuiltInName theName, + int theIndex, + int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol, + int theStartRow2, + int theEndRow2, + int theStartCol2, + int theEndCol2, + boolean global) { + super(Type.NAME); + + builtInName = theName; + index = theIndex; + sheetRef = global ? 0 : index + 1; // 0 indicates a global name, otherwise + // the 1-based index of the sheet + + ranges = new NameRange[2]; + ranges[0] = new NameRange(extSheet, + theStartRow, + theEndRow, + theStartCol, + theEndCol); + ranges[1] = new NameRange(extSheet, + theStartRow2, + theEndRow2, + theStartCol2, + theEndCol2); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + if (data != null && !modified) { + // this is a copy + return data; + } + + final int NAME_HEADER_LENGTH = 15; + final byte AREA_RANGE_LENGTH = 11; + final byte AREA_REFERENCE = 0x3b; + + int detailLength; + + if (ranges.length > 1) { + detailLength = (ranges.length * AREA_RANGE_LENGTH) + 4; + } else { + detailLength = AREA_RANGE_LENGTH; + } + + int length = NAME_HEADER_LENGTH + detailLength; + length += builtInName != null ? 1 : name.length(); + data = new byte[length]; + + // Options + int options = 0; + + if (builtInName != null) { + options |= 0x20; + } + IntegerHelper.getTwoBytes(options, data, 0); + + // Keyboard shortcut + data[2] = 0; + + // Length of the name in chars + if (builtInName != null) { + data[3] = (byte) 0x1; + } else { + data[3] = (byte) name.length(); + } + + // Size of the definitions + IntegerHelper.getTwoBytes(detailLength, data, 4); + + // Sheet index + IntegerHelper.getTwoBytes(sheetRef, data, 6); + IntegerHelper.getTwoBytes(sheetRef, data, 8); + + // Byte 10-13 are optional lengths [0,0,0,0] + // Byte 14 is length of name which is not used. + + // The name + if (builtInName != null) { + data[15] = (byte) builtInName.getValue(); + } else { + StringHelper.getBytes(name, data, 15); + } + + // The actual range definition. + int pos = builtInName != null ? 16 : name.length() + 15; + + // If there are multiple ranges for the name, we must specify a + // subExpression type rather than areaReference and then put out + // multiple areaReference entries with an end byte. + if (ranges.length > 1) { + data[pos++] = subExpression; + // Length of remaining bytes excluding itself + IntegerHelper.getTwoBytes(detailLength - 3, data, pos); + pos += 2; + byte[] rd; + for (int i = 0; i < ranges.length; i++) { + data[pos++] = areaReference; + rd = ranges[i].getData(); + System.arraycopy(rd, 0, data, pos, rd.length); + pos += rd.length; + } + data[pos] = 0x10; + } else { + // Range format - area + data[pos] = areaReference; + + // The range data + byte[] rd = ranges[0].getData(); + System.arraycopy(rd, 0, data, pos + 1, rd.length); + } + + return data; + } + + /** + * Accessor for the name + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Accessor for the index of this name in the name table + * + * @return the index of this name in the name table + */ + public int getIndex() { + return index; + } + + /** + * The 0-based index sheet reference for a record name + * 0 is for a global reference + * + * @return the sheet reference for name formula + */ + public int getSheetRef() { + return sheetRef; + } + + /** + * Set the index sheet reference for a record name + * 0 is for a global reference + */ + public void setSheetRef(int i) { + sheetRef = i; + IntegerHelper.getTwoBytes(sheetRef, data, 8); + } + + /** + * Gets the array of ranges for this name + * + * @return the ranges + */ + public NameRange[] getRanges() { + return ranges; + } + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + */ + void rowInserted(int sheetIndex, int row) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (row <= ranges[i].getFirstRow()) { + ranges[i].incrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) { + ranges[i].incrementLastRow(); + modified = true; + } + } + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean rowRemoved(int sheetIndex, int row) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (row == ranges[i].getFirstRow() && row == ranges[i].getLastRow()) { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (row < ranges[i].getFirstRow() && row > 0) { + ranges[i].decrementFirstRow(); + modified = true; + } + + if (row <= ranges[i].getLastRow()) { + ranges[i].decrementLastRow(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] == EMPTY_RANGE) { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] != EMPTY_RANGE) { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + /** + * Called when a row is removed on the worksheet + * + * @param sheetIndex the sheet index on which the column was inserted + * @param row the column number which was inserted + * @reeturn TRUE if the name is to be removed entirely, FALSE otherwise + */ + boolean columnRemoved(int sheetIndex, int col) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (col == ranges[i].getFirstColumn() && col == + ranges[i].getLastColumn()) { + // remove the range + ranges[i] = EMPTY_RANGE; + } + + if (col < ranges[i].getFirstColumn() && col > 0) { + ranges[i].decrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) { + ranges[i].decrementLastColumn(); + modified = true; + } + } + + // If all ranges are empty, then remove the name + int emptyRanges = 0; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] == EMPTY_RANGE) { + emptyRanges++; + } + } + + if (emptyRanges == ranges.length) { + return true; + } + + // otherwise just remove the empty ones + NameRange[] newRanges = new NameRange[ranges.length - emptyRanges]; + for (int i = 0; i < ranges.length; i++) { + if (ranges[i] != EMPTY_RANGE) { + newRanges[i] = ranges[i]; + } + } + + ranges = newRanges; + + return false; + } + + /** + * Called when a row is inserted on the + * + * @param sheetIndex the sheet index on which the column was inserted + * @param col the column number which was inserted + */ + void columnInserted(int sheetIndex, int col) { + for (int i = 0; i < ranges.length; i++) { + if (sheetIndex != ranges[i].getExternalSheet()) { + continue; // shame on me - this is no better than a goto + } + + if (col <= ranges[i].getFirstColumn()) { + ranges[i].incrementFirstColumn(); + modified = true; + } + + if (col <= ranges[i].getLastColumn()) { + ranges[i].incrementLastColumn(); + modified = true; + } + } + } + + /** + * A nested class to hold range information + */ + static class NameRange { + private int columnFirst; + private int rowFirst; + private int columnLast; + private int rowLast; + private final int externalSheet; + + NameRange(jxl.read.biff.NameRecord.NameRange nr) { + columnFirst = nr.getFirstColumn(); + rowFirst = nr.getFirstRow(); + columnLast = nr.getLastColumn(); + rowLast = nr.getLastRow(); + externalSheet = nr.getExternalSheet(); + } + + /** + * Create a new range for the name record. + */ + NameRange(int extSheet, + int theStartRow, + int theEndRow, + int theStartCol, + int theEndCol) { + columnFirst = theStartCol; + rowFirst = theStartRow; + columnLast = theEndCol; + rowLast = theEndRow; + externalSheet = extSheet; + } + + int getFirstColumn() { + return columnFirst; + } + + int getFirstRow() { + return rowFirst; + } + + int getLastColumn() { + return columnLast; + } + + int getLastRow() { + return rowLast; + } + + int getExternalSheet() { + return externalSheet; + } + + void incrementFirstRow() { + rowFirst++; + } + + void incrementLastRow() { + rowLast++; + } + + void decrementFirstRow() { + rowFirst--; + } + + void decrementLastRow() { + rowLast--; + } + + void incrementFirstColumn() { + columnFirst++; + } + + void incrementLastColumn() { + columnLast++; + } + + void decrementFirstColumn() { + columnFirst--; + } + + void decrementLastColumn() { + columnLast--; + } + + byte[] getData() { + byte[] d = new byte[10]; + + // Sheet index + IntegerHelper.getTwoBytes(externalSheet, d, 0); + + // Starting row + IntegerHelper.getTwoBytes(rowFirst, d, 2); + + // End row + IntegerHelper.getTwoBytes(rowLast, d, 4); + + // Start column + IntegerHelper.getTwoBytes(columnFirst & 0xff, d, 6); + + // End columns + IntegerHelper.getTwoBytes(columnLast & 0xff, d, 8); + + return d; + } + } + +} + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java new file mode 100755 index 0000000..d87052b --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NineteenFourRecord.java @@ -0,0 +1,65 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * A record which indicates whether or the 1904 date system is + * in use + */ +class NineteenFourRecord extends WritableRecordData { + /** + * Flag which indicates whether the 1904 date system is being used + */ + private final boolean nineteenFourDate; + + /** + * The binary data for output to file + */ + private final byte[] data; + + /** + * Constructor + * + * @param oldDate flag indicating whether the 1904 date system is in use + */ + public NineteenFourRecord(boolean oldDate) { + super(Type.NINETEENFOUR); + + nineteenFourDate = oldDate; + data = new byte[2]; + + if (nineteenFourDate) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * The binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java new file mode 100755 index 0000000..c918c83 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NumberFormatRecord.java @@ -0,0 +1,131 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.FormatRecord; +import jxl.common.Logger; + +/** + * A class which contains a number format + */ +public class NumberFormatRecord extends FormatRecord { + /** + * The logger + */ + private static final Logger logger = Logger.getLogger(NumberFormatRecord.class); + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + fs = trimInvalidChars(fs); + + setFormatString(fs); + } + + + /** + * Constructor. Replaces some of the characters in the java number + * format string with the appropriate excel format characters + * + * @param fmt the number format + */ + protected NumberFormatRecord(String fmt, NonValidatingFormat dummy) { + super(); + + // Do the replacements in the format string + String fs = fmt; + + fs = replace(fs, "E0", "E+0"); + + setFormatString(fs); + } + + /** + * Remove all but the first characters preceding the # or the 0. + * Remove all characters after the # or the 0, unless it is a ) + * + * @param fs the candidate number format + * @return the string with spurious characters removed + */ + private String trimInvalidChars(String fs) { + int firstHash = fs.indexOf('#'); + int firstZero = fs.indexOf('0'); + int firstValidChar = 0; + + if (firstHash == -1 && firstZero == -1) { + // The string is complete nonsense. Return a default string + return "#.###"; + } + + if (firstHash != 0 && firstZero != 0 && + firstHash != 1 && firstZero != 1) { + // The string is dodgy. Find the first valid char + firstHash = firstHash == -1 ? firstHash = Integer.MAX_VALUE : firstHash; + firstZero = firstZero == -1 ? firstZero = Integer.MAX_VALUE : firstZero; + firstValidChar = Math.min(firstHash, firstZero); + + StringBuffer tmp = new StringBuffer(); + tmp.append(fs.charAt(0)); + tmp.append(fs.substring(firstValidChar)); + fs = tmp.toString(); + } + + // Now strip of everything at the end that isn't a # or 0 + int lastHash = fs.lastIndexOf('#'); + int lastZero = fs.lastIndexOf('0'); + + if (lastHash == fs.length() || + lastZero == fs.length()) { + return fs; + } + + // Find the last valid character + int lastValidChar = Math.max(lastHash, lastZero); + + // Check for the existence of a ) or % + while ((fs.length() > lastValidChar + 1) && + (fs.charAt(lastValidChar + 1) == ')' || + (fs.charAt(lastValidChar + 1) == '%'))) { + lastValidChar++; + } + + return fs.substring(0, lastValidChar + 1); + } + + // Dummy class to specify non validation + protected static class NonValidatingFormat { + public NonValidatingFormat() { + } + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java new file mode 100755 index 0000000..49f771f --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/NumberRecord.java @@ -0,0 +1,167 @@ +/********************************************************************* + * + * Copyright (C) 2001 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import jxl.CellType; +import jxl.NumberCell; +import jxl.biff.DoubleHelper; +import jxl.biff.Type; +import jxl.biff.XFRecord; +import jxl.format.CellFormat; + +/** + * The record which contains numerical values. All values are stored + * as 64bit IEEE floating point values + */ +public abstract class NumberRecord extends CellValue { + /** + * The formatter to convert the value into a string + */ + private static final DecimalFormat defaultFormat = new DecimalFormat("#.###"); + /** + * The number + */ + private double value; + /** + * The java equivalent of the excel format + */ + private NumberFormat format; + + /** + * Constructor invoked by the user API + * + * @param c the column + * @param r the row + * @param val the value + */ + protected NumberRecord(int c, int r, double val) { + super(Type.NUMBER, c, r); + value = val; + } + + /** + * Overloaded constructor invoked from the API, which takes a cell + * format + * + * @param c the column + * @param r the row + * @param val the value + * @param st the cell format + */ + protected NumberRecord(int c, int r, double val, CellFormat st) { + super(Type.NUMBER, c, r, st); + value = val; + } + + /** + * Constructor used when copying a workbook + * + * @param nc the number to copy + */ + protected NumberRecord(NumberCell nc) { + super(Type.NUMBER, nc); + value = nc.getValue(); + } + + /** + * Copy constructor + * + * @param c the column + * @param r the row + * @param nr the record to copy + */ + protected NumberRecord(int c, int r, NumberRecord nr) { + super(Type.NUMBER, c, r, nr); + value = nr.value; + } + + /** + * Returns the content type of this cell + * + * @return the content type for this cell + */ + public CellType getType() { + return CellType.NUMBER; + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + byte[] celldata = super.getData(); + byte[] data = new byte[celldata.length + 8]; + System.arraycopy(celldata, 0, data, 0, celldata.length); + DoubleHelper.getIEEEBytes(value, data, celldata.length); + + return data; + } + + /** + * Quick and dirty function to return the contents of this cell as a string. + * For more complex manipulation of the contents, it is necessary to cast + * this interface to correct subinterface + * + * @return the contents of this cell as a string + */ + public String getContents() { + if (format == null) { + format = ((XFRecord) getCellFormat()).getNumberFormat(); + if (format == null) { + format = defaultFormat; + } + } + return format.format(value); + } + + /** + * Gets the double contents for this cell. + * + * @return the cell contents + */ + public double getValue() { + return value; + } + + /** + * Sets the value of the contents for this cell + * + * @param val the new value + */ + public void setValue(double val) { + value = val; + } + + /** + * Gets the NumberFormat used to format this cell. This is the java + * equivalent of the Excel format + * + * @return the NumberFormat used to format the cell + */ + public NumberFormat getNumberFormat() { + return null; + } +} + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java new file mode 100755 index 0000000..c5d7010 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ObjProjRecord.java @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record containing the obj proj record + */ +class ObjProjRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + */ + public ObjProjRecord() { + super(Type.OBJPROJ); + + data = new byte[4]; + } + + /** + * Retrieves the data to be written to the binary file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + + + + + + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java new file mode 100755 index 0000000..dd58911 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/ObjectProtectRecord.java @@ -0,0 +1,64 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * The protection state for a sheet or workbook + */ +class ObjectProtectRecord extends WritableRecordData { + /** + * The protection state + */ + private final boolean protection; + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param prot the protection state + */ + public ObjectProtectRecord(boolean prot) { + super(Type.OBJPROTECT); + + protection = prot; + + data = new byte[2]; + + if (protection) { + IntegerHelper.getTwoBytes(1, data, 0); + } + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java new file mode 100755 index 0000000..45bf80e --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PLSRecord.java @@ -0,0 +1,67 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Record which specifies a print header for a work sheet + */ +class PLSRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Consructor invoked when copying a spreadsheet + * + * @param hr the read header record + */ + public PLSRecord(jxl.read.biff.PLSRecord hr) { + super(Type.PLS); + + data = hr.getData(); + } + + /** + * Consructor invoked when copying a sheets + * + * @param hr the read header record + */ + public PLSRecord(PLSRecord hr) { + super(Type.PLS); + + data = new byte[hr.data.length]; + System.arraycopy(hr.data, 0, data, 0, data.length); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} + + diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java new file mode 100755 index 0000000..f5b983a --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PaletteRecord.java @@ -0,0 +1,53 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Stores the colour palette + */ +class PaletteRecord extends WritableRecordData { + /** + * The binary data + */ + private final byte[] data; + + /** + * Constructor + * + * @param p the palette record + */ + public PaletteRecord(jxl.read.biff.PaletteRecord p) { + super(Type.PALETTE); + + data = p.getData(); + } + + /** + * Gets the binary data for output to file + * + * @return the binary data + */ + public byte[] getData() { + return data; + } +} diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java new file mode 100755 index 0000000..45da541 --- /dev/null +++ b/datastructures-xslx/src/main/java/jxl/write/biff/PaneRecord.java @@ -0,0 +1,98 @@ +/********************************************************************* + * + * Copyright (C) 2002 Andrew Khan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +package jxl.write.biff; + +import jxl.biff.IntegerHelper; +import jxl.biff.Type; +import jxl.biff.WritableRecordData; + +/** + * Contains the window attributes for a worksheet + */ +class PaneRecord extends WritableRecordData { + /** + * The pane codes + */ + private final static int topLeftPane = 0x3; + private final static int bottomLeftPane = 0x2; + private final static int topRightPane = 0x1; + private final static int bottomRightPane = 0x0; + /** + * The number of rows visible in the top left pane + */ + private final int rowsVisible; + /** + * The number of columns visible in the top left pane + */ + private final int columnsVisible; + + /** + * Code + *
+ * /**
+ * Constructor
+ */
+ public PaneRecord(int cols, int rows) {
+ super(Type.PANE);
+
+ rowsVisible = rows;
+ columnsVisible = cols;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[10];
+
+ // The x position
+ IntegerHelper.getTwoBytes(columnsVisible, data, 0);
+
+ // The y position
+ IntegerHelper.getTwoBytes(rowsVisible, data, 2);
+
+ // The top row visible in the bottom pane
+ if (rowsVisible > 0) {
+ IntegerHelper.getTwoBytes(rowsVisible, data, 4);
+ }
+
+ // The left most column visible in the right pane
+ if (columnsVisible > 0) {
+ IntegerHelper.getTwoBytes(columnsVisible, data, 6);
+ }
+
+ // The active pane
+ int activePane = topLeftPane;
+
+ if (rowsVisible > 0 && columnsVisible == 0) {
+ activePane = bottomLeftPane;
+ } else if (rowsVisible == 0 && columnsVisible > 0) {
+ activePane = topRightPane;
+ } else if (rowsVisible > 0 && columnsVisible > 0) {
+ activePane = bottomRightPane;
+ }
+ // always present
+ IntegerHelper.getTwoBytes(activePane, data, 8);
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java
new file mode 100755
index 0000000..958cae9
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/PasswordRecord.java
@@ -0,0 +1,109 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * A password record. Thanks to Michael Matthews for sending me the
+ * code to actually store the password for the sheet
+ */
+class PasswordRecord extends WritableRecordData {
+ /**
+ * The password
+ */
+ private String password;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param pw the password
+ */
+ public PasswordRecord(String pw) {
+ super(Type.PASSWORD);
+
+ password = pw;
+
+ if (pw == null) {
+ data = new byte[2];
+ IntegerHelper.getTwoBytes(0, data, 0);
+ } else {
+ byte[] passwordBytes = pw.getBytes();
+ int passwordHash = 0;
+ for (int a = 0; a < passwordBytes.length; a++) {
+ int shifted = rotLeft15Bit(passwordBytes[a], a + 1);
+ passwordHash ^= shifted;
+ }
+ passwordHash ^= passwordBytes.length;
+ passwordHash ^= 0xCE4B;
+
+ data = new byte[2];
+ IntegerHelper.getTwoBytes(passwordHash, data, 0);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param ph the password hash code
+ */
+ public PasswordRecord(int ph) {
+ super(Type.PASSWORD);
+
+ data = new byte[2];
+ IntegerHelper.getTwoBytes(ph, data, 0);
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Rotate the value by 15 bits. Thanks to Michael for this
+ *
+ * @param val
+ * @param rotate
+ * @return int
+ */
+ private int rotLeft15Bit(int val, int rotate) {
+ val = val & 0x7FFF;
+
+ for (; rotate > 0; rotate--) {
+ if ((val & 0x4000) != 0) {
+ val = ((val << 1) & 0x7FFF) + 1;
+ } else {
+ val = (val << 1) & 0x7FFF;
+ }
+ }
+
+ return val;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java
new file mode 100755
index 0000000..c20ecfd
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrecisionRecord.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the Precision As Displayed option from the dialog box
+ */
+class PrecisionRecord extends WritableRecordData {
+ /**
+ * The precision as displayed flag
+ */
+ private final boolean asDisplayed;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param disp the precision as displayed flag
+ */
+ public PrecisionRecord(boolean disp) {
+ super(Type.PRECISION);
+
+ asDisplayed = disp;
+ data = new byte[2];
+
+ if (!asDisplayed) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java
new file mode 100755
index 0000000..a5c5489
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrintGridLinesRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The grid lines option from the Page Setup dialog box
+ */
+class PrintGridLinesRecord extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+ /**
+ * The print grid lines option
+ */
+ private final boolean printGridLines;
+
+ /**
+ * Constructor
+ *
+ * @param pgl the grid lines option
+ */
+ public PrintGridLinesRecord(boolean pgl) {
+ super(Type.PRINTGRIDLINES);
+ printGridLines = pgl;
+
+ data = new byte[2];
+
+ if (printGridLines) {
+ data[0] = 1;
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java
new file mode 100755
index 0000000..253cd43
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/PrintHeadersRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The headings options from the Page Setup dialog box
+ */
+class PrintHeadersRecord extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+ /**
+ * Flag to print headers
+ */
+ private final boolean printHeaders;
+
+ /**
+ * Constructor
+ *
+ * @param ph print headers flag
+ */
+ public PrintHeadersRecord(boolean ph) {
+ super(Type.PRINTHEADERS);
+ printHeaders = ph;
+
+ data = new byte[2];
+
+ if (printHeaders) {
+ data[0] = 1;
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java
new file mode 100755
index 0000000..fe785d0
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevPassRecord.java
@@ -0,0 +1,52 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the shared workbook protection flag
+ */
+class Prot4RevPassRecord extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ */
+ public Prot4RevPassRecord() {
+ super(Type.PROT4REVPASS);
+
+ // Hard code in an unprotected workbook
+ data = new byte[2];
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java
new file mode 100755
index 0000000..133a4b6
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Prot4RevRecord.java
@@ -0,0 +1,65 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The shared workbook protection flag
+ */
+class Prot4RevRecord extends WritableRecordData {
+ /**
+ * The protection flag
+ */
+ private final boolean protection;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param prot protection flag
+ */
+ public Prot4RevRecord(boolean prot) {
+ super(Type.PROT4REV);
+
+ protection = prot;
+
+ // Hard code in an unprotected workbook
+ data = new byte[2];
+
+ if (protection) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java
new file mode 100755
index 0000000..e08e067
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ProtectRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The protection state for a sheet or workbook
+ */
+class ProtectRecord extends WritableRecordData {
+ /**
+ * The protection state
+ */
+ private final boolean protection;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param prot the protection state
+ */
+ public ProtectRecord(boolean prot) {
+ super(Type.PROTECT);
+
+ protection = prot;
+
+ data = new byte[2];
+
+ if (protection) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java
new file mode 100755
index 0000000..ff700fe
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadBooleanFormulaRecord.java
@@ -0,0 +1,47 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.BooleanFormulaCell;
+import jxl.biff.FormulaData;
+
+/**
+ * Class for read number formula records
+ */
+class ReadBooleanFormulaRecord extends ReadFormulaRecord
+ implements BooleanFormulaCell {
+ /**
+ * Constructor
+ *
+ * @param f
+ */
+ public ReadBooleanFormulaRecord(FormulaData f) {
+ super(f);
+ }
+
+ /**
+ * Gets the boolean contents for this cell.
+ *
+ * @return the cell contents
+ */
+ public boolean getValue() {
+ return ((BooleanFormulaCell) getReadFormula()).getValue();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java
new file mode 100755
index 0000000..66580f5
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadDateFormulaRecord.java
@@ -0,0 +1,70 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.text.DateFormat;
+import java.util.Date;
+import jxl.DateFormulaCell;
+import jxl.biff.FormulaData;
+
+/**
+ * Class for read number formula records
+ */
+class ReadDateFormulaRecord extends ReadFormulaRecord
+ implements DateFormulaCell {
+ /**
+ * Constructor
+ *
+ * @param f
+ */
+ public ReadDateFormulaRecord(FormulaData f) {
+ super(f);
+ }
+
+ /**
+ * Gets the Date contents for this cell.
+ *
+ * @return the cell contents
+ */
+ public Date getDate() {
+ return ((DateFormulaCell) getReadFormula()).getDate();
+ }
+
+ /**
+ * Indicates whether the date value contained in this cell refers to a date,
+ * or merely a time
+ *
+ * @return TRUE if the value refers to a time
+ */
+ public boolean isTime() {
+ return ((DateFormulaCell) getReadFormula()).isTime();
+ }
+
+
+ /**
+ * Gets the DateFormat used to format this cell. This is the java
+ * equivalent of the Excel format
+ *
+ * @return the DateFormat used to format the cell
+ */
+ public DateFormat getDateFormat() {
+ return ((DateFormulaCell) getReadFormula()).getDateFormat();
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java
new file mode 100755
index 0000000..c03ce5e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadErrorFormulaRecord.java
@@ -0,0 +1,118 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.ErrorFormulaCell;
+import jxl.biff.FormulaData;
+import jxl.biff.IntegerHelper;
+import jxl.biff.formula.FormulaErrorCode;
+import jxl.biff.formula.FormulaException;
+import jxl.biff.formula.FormulaParser;
+import jxl.common.Logger;
+
+
+/**
+ * Class for read number formula records
+ */
+class ReadErrorFormulaRecord extends ReadFormulaRecord
+ implements ErrorFormulaCell {
+ // The logger
+ private static final Logger logger = Logger.getLogger(ReadErrorFormulaRecord.class);
+
+ /**
+ * Constructor
+ *
+ * @param f
+ */
+ public ReadErrorFormulaRecord(FormulaData f) {
+ super(f);
+ }
+
+ /**
+ * Gets the error code for this cell.
+ *
+ * @return the cell contents
+ */
+ public int getErrorCode() {
+ return ((ErrorFormulaCell) getReadFormula()).getErrorCode();
+ }
+
+ /**
+ * Error formula specific exception handling. Can't really create
+ * a formula (as it will look for a cell of that name, so just
+ * create a STRING record containing the contents
+ *
+ * @return the bodged data
+ */
+ protected byte[] handleFormulaException() {
+ byte[] expressiondata = null;
+ byte[] celldata = super.getCellData();
+
+ int errorCode = getErrorCode();
+ String formulaString = null;
+
+ if (errorCode == FormulaErrorCode.DIV0.getCode()) {
+ formulaString = "1/0";
+ } else if (errorCode == FormulaErrorCode.VALUE.getCode()) {
+ formulaString = "\"\"/0";
+ } else if (errorCode == FormulaErrorCode.REF.getCode()) {
+ formulaString = "\"#REF!\"";
+ } else {
+ formulaString = "\"ERROR\"";
+ }
+
+ // Generate an appropriate dummy formula
+ WritableWorkbookImpl w = getSheet().getWorkbook();
+ FormulaParser parser = new FormulaParser(formulaString, w, w,
+ w.getSettings());
+
+ // Get the bytes for the dummy formula
+ try {
+ parser.parse();
+ } catch (FormulaException e2) {
+ logger.warn(e2.getMessage());
+ }
+ byte[] formulaBytes = parser.getBytes();
+ expressiondata = new byte[formulaBytes.length + 16];
+ IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
+ System.arraycopy(formulaBytes, 0, expressiondata, 16,
+ formulaBytes.length);
+
+ // Set the recalculate on load bit
+ expressiondata[8] |= 0x02;
+
+ byte[] data = new byte[celldata.length +
+ expressiondata.length];
+ System.arraycopy(celldata, 0, data, 0, celldata.length);
+ System.arraycopy(expressiondata, 0, data,
+ celldata.length, expressiondata.length);
+
+ // Set the type bits to indicate an error
+ data[6] = 2;
+ data[12] = -1;
+ data[13] = -1;
+
+ // Set the error code
+ data[8] = (byte) errorCode;
+
+ return data;
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java
new file mode 100755
index 0000000..9a5e075
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadFormulaRecord.java
@@ -0,0 +1,402 @@
+/*************************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.CellReferenceHelper;
+import jxl.CellType;
+import jxl.FormulaCell;
+import jxl.Sheet;
+import jxl.WorkbookSettings;
+import jxl.biff.FormattingRecords;
+import jxl.biff.FormulaData;
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WorkbookMethods;
+import jxl.biff.formula.ExternalSheet;
+import jxl.biff.formula.FormulaException;
+import jxl.biff.formula.FormulaParser;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.write.WritableCell;
+
+/**
+ * A formula record. This is invoked when copying a formula from a
+ * read only spreadsheet
+ * This method implements the FormulaData interface to allow the copying
+ * of writable sheets
+ */
+class ReadFormulaRecord extends CellValue implements FormulaData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(ReadFormulaRecord.class);
+
+ /**
+ * The underlying formula from the read sheet
+ */
+ private final FormulaData formula;
+
+ /**
+ * The formula parser
+ */
+ private FormulaParser parser;
+
+ /**
+ * Constructor
+ *
+ * @param f the formula to copy
+ */
+ protected ReadFormulaRecord(FormulaData f) {
+ super(Type.FORMULA, f);
+ formula = f;
+ }
+
+ protected final byte[] getCellData() {
+ return super.getData();
+ }
+
+ /**
+ * An exception has occurred, so produce some appropriate dummy
+ * cell contents. This may be overridden by subclasses
+ * if they require specific handling
+ *
+ * @return the bodged data
+ */
+ protected byte[] handleFormulaException() {
+ byte[] expressiondata = null;
+ byte[] celldata = super.getData();
+
+ // Generate an appropriate dummy formula
+ WritableWorkbookImpl w = getSheet().getWorkbook();
+ parser = new FormulaParser(getContents(), w, w,
+ w.getSettings());
+
+ // Get the bytes for the dummy formula
+ try {
+ parser.parse();
+ } catch (FormulaException e2) {
+ logger.warn(e2.getMessage());
+ parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings());
+ try {
+ parser.parse();
+ } catch (FormulaException e3) {
+ Assert.verify(false);
+ }
+ }
+ byte[] formulaBytes = parser.getBytes();
+ expressiondata = new byte[formulaBytes.length + 16];
+ IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
+ System.arraycopy(formulaBytes, 0, expressiondata, 16,
+ formulaBytes.length);
+
+ // Set the recalculate on load bit
+ expressiondata[8] |= 0x02;
+
+ byte[] data = new byte[celldata.length +
+ expressiondata.length];
+ System.arraycopy(celldata, 0, data, 0, celldata.length);
+ System.arraycopy(expressiondata, 0, data,
+ celldata.length, expressiondata.length);
+ return data;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ // Take the superclass cell data to take into account cell
+ // rationalization
+ byte[] celldata = super.getData();
+ byte[] expressiondata = null;
+
+ try {
+ if (parser == null) {
+ expressiondata = formula.getFormulaData();
+ } else {
+ byte[] formulaBytes = parser.getBytes();
+ expressiondata = new byte[formulaBytes.length + 16];
+ IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
+ System.arraycopy(formulaBytes, 0, expressiondata, 16,
+ formulaBytes.length);
+ }
+
+ // Set the recalculate on load bit
+ expressiondata[8] |= 0x02;
+
+ byte[] data = new byte[celldata.length +
+ expressiondata.length];
+ System.arraycopy(celldata, 0, data, 0, celldata.length);
+ System.arraycopy(expressiondata, 0, data,
+ celldata.length, expressiondata.length);
+ return data;
+ } catch (FormulaException e) {
+ // Something has gone wrong trying to read the formula data eg. it
+ // might be unsupported biff7 data
+ logger.warn
+ (CellReferenceHelper.getCellReference(getColumn(), getRow()) +
+ " " + e.getMessage());
+ return handleFormulaException();
+ }
+ }
+
+ /**
+ * Returns the content type of this cell
+ *
+ * @return the content type for this cell
+ */
+ public CellType getType() {
+ return formula.getType();
+ }
+
+ /**
+ * Quick and dirty function to return the contents of this cell as a string.
+ *
+ * @return the contents of this cell as a string
+ */
+ public String getContents() {
+ return formula.getContents();
+ }
+
+ /**
+ * Gets the raw bytes for the formula. This will include the
+ * parsed tokens array. Used when copying spreadsheets
+ *
+ * @return the raw record data
+ */
+ public byte[] getFormulaData() throws FormulaException {
+ byte[] d = formula.getFormulaData();
+ byte[] data = new byte[d.length];
+
+ System.arraycopy(d, 0, data, 0, d.length);
+
+ // Set the recalculate on load bit
+ data[8] |= 0x02;
+
+ return data;
+ }
+
+ /**
+ * Gets the formula bytes
+ *
+ * @return the formula bytes
+ */
+ public byte[] getFormulaBytes() throws FormulaException {
+ // If the formula has been parsed, then get the parsed bytes
+ if (parser != null) {
+ return parser.getBytes();
+ }
+
+ // otherwise get the bytes from the original formula
+ byte[] readFormulaData = getFormulaData();
+ byte[] formulaBytes = new byte[readFormulaData.length - 16];
+ System.arraycopy(readFormulaData, 16, formulaBytes, 0,
+ formulaBytes.length);
+ return formulaBytes;
+ }
+
+ /**
+ * Implementation of the deep copy function
+ *
+ * @param col the column which the new cell will occupy
+ * @param row the row which the new cell will occupy
+ * @return a copy of this cell, which can then be added to the sheet
+ */
+ public WritableCell copyTo(int col, int row) {
+ return new FormulaRecord(col, row, this);
+ }
+
+ /**
+ * Overrides the method in the base class to add this to the Workbook's
+ * list of maintained formulas
+ *
+ * @param fr the formatting records
+ * @param ss the shared strings used within the workbook
+ * @param s the sheet this is being added to
+ */
+ void setCellDetails(FormattingRecords fr, SharedStrings ss,
+ WritableSheetImpl s) {
+ super.setCellDetails(fr, ss, s);
+ s.getWorkbook().addRCIRCell(this);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Notifies all
+ * RCIR cells of this change. The default implementation here does nothing
+ *
+ * @param s the sheet on which the column was inserted
+ * @param sheetIndex the sheet index on which the column was inserted
+ * @param col the column number which was inserted
+ */
+ void columnInserted(Sheet s, int sheetIndex, int col) {
+ try {
+ if (parser == null) {
+ byte[] formulaData = formula.getFormulaData();
+ byte[] formulaBytes = new byte[formulaData.length - 16];
+ System.arraycopy(formulaData, 16,
+ formulaBytes, 0, formulaBytes.length);
+ parser = new FormulaParser(formulaBytes,
+ this,
+ getSheet().getWorkbook(),
+ getSheet().getWorkbook(),
+ getSheet().getWorkbookSettings());
+ parser.parse();
+ }
+
+ parser.columnInserted(sheetIndex, col, s == getSheet());
+ } catch (FormulaException e) {
+ logger.warn("cannot insert column within formula: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Called when a column is removed on the specified sheet. Notifies all
+ * RCIR cells of this change. The default implementation here does nothing
+ *
+ * @param s the sheet on which the column was inserted
+ * @param sheetIndex the sheet index on which the column was inserted
+ * @param col the column number which was inserted
+ */
+ void columnRemoved(Sheet s, int sheetIndex, int col) {
+ try {
+ if (parser == null) {
+ byte[] formulaData = formula.getFormulaData();
+ byte[] formulaBytes = new byte[formulaData.length - 16];
+ System.arraycopy(formulaData, 16,
+ formulaBytes, 0, formulaBytes.length);
+ parser = new FormulaParser(formulaBytes,
+ this,
+ getSheet().getWorkbook(),
+ getSheet().getWorkbook(),
+ getSheet().getWorkbookSettings());
+ parser.parse();
+ }
+
+ parser.columnRemoved(sheetIndex, col, s == getSheet());
+ } catch (FormulaException e) {
+ logger.warn("cannot remove column within formula: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Called when a row is inserted on the specified sheet. Notifies all
+ * RCIR cells of this change. The default implementation here does nothing
+ *
+ * @param s the sheet on which the column was inserted
+ * @param sheetIndex the sheet index on which the column was inserted
+ * @param row the column number which was inserted
+ */
+ void rowInserted(Sheet s, int sheetIndex, int row) {
+ try {
+ if (parser == null) {
+ byte[] formulaData = formula.getFormulaData();
+ byte[] formulaBytes = new byte[formulaData.length - 16];
+ System.arraycopy(formulaData, 16,
+ formulaBytes, 0, formulaBytes.length);
+ parser = new FormulaParser(formulaBytes,
+ this,
+ getSheet().getWorkbook(),
+ getSheet().getWorkbook(),
+ getSheet().getWorkbookSettings());
+ parser.parse();
+ }
+
+ parser.rowInserted(sheetIndex, row, s == getSheet());
+ } catch (FormulaException e) {
+ logger.warn("cannot insert row within formula: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Called when a row is inserted on the specified sheet. Notifies all
+ * RCIR cells of this change. The default implementation here does nothing
+ *
+ * @param s the sheet on which the row was removed
+ * @param sheetIndex the sheet index on which the column was removed
+ * @param row the column number which was removed
+ */
+ void rowRemoved(Sheet s, int sheetIndex, int row) {
+ try {
+ if (parser == null) {
+ byte[] formulaData = formula.getFormulaData();
+ byte[] formulaBytes = new byte[formulaData.length - 16];
+ System.arraycopy(formulaData, 16,
+ formulaBytes, 0, formulaBytes.length);
+ parser = new FormulaParser(formulaBytes,
+ this,
+ getSheet().getWorkbook(),
+ getSheet().getWorkbook(),
+ getSheet().getWorkbookSettings());
+ parser.parse();
+ }
+
+ parser.rowRemoved(sheetIndex, row, s == getSheet());
+ } catch (FormulaException e) {
+ logger.warn("cannot remove row within formula: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Accessor for the read formula
+ *
+ * @return the read formula
+ */
+ protected FormulaData getReadFormula() {
+ return formula;
+ }
+
+ /**
+ * Accessor for the read formula
+ *
+ * @return the read formula
+ */
+ public String getFormula() throws FormulaException {
+ return ((FormulaCell) formula).getFormula();
+ }
+
+ /**
+ * If this formula was on an imported sheet, check that
+ * cell references to another sheet are warned appropriately
+ *
+ * @return TRUE if this formula was able to be imported, FALSE otherwise
+ */
+ public boolean handleImportedCellReferences(ExternalSheet es,
+ WorkbookMethods mt,
+ WorkbookSettings ws) {
+ try {
+ if (parser == null) {
+ byte[] formulaData = formula.getFormulaData();
+ byte[] formulaBytes = new byte[formulaData.length - 16];
+ System.arraycopy(formulaData, 16,
+ formulaBytes, 0, formulaBytes.length);
+ parser = new FormulaParser(formulaBytes,
+ this,
+ es, mt, ws);
+ parser.parse();
+ }
+
+ return parser.handleImportedCellReferences();
+ } catch (FormulaException e) {
+ logger.warn("cannot import formula: " + e.getMessage());
+ return false;
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java
new file mode 100755
index 0000000..9304be6
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadNumberFormulaRecord.java
@@ -0,0 +1,110 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.text.NumberFormat;
+import jxl.NumberFormulaCell;
+import jxl.biff.DoubleHelper;
+import jxl.biff.FormulaData;
+import jxl.biff.IntegerHelper;
+import jxl.biff.formula.FormulaException;
+import jxl.biff.formula.FormulaParser;
+import jxl.common.Logger;
+
+/**
+ * Class for read number formula records
+ */
+class ReadNumberFormulaRecord extends ReadFormulaRecord
+ implements NumberFormulaCell {
+ // The logger
+ private static final Logger logger = Logger.getLogger(ReadNumberFormulaRecord.class);
+
+ /**
+ * Constructor
+ *
+ * @param f
+ */
+ public ReadNumberFormulaRecord(FormulaData f) {
+ super(f);
+ }
+
+ /**
+ * Gets the double contents for this cell.
+ *
+ * @return the cell contents
+ */
+ public double getValue() {
+ return ((NumberFormulaCell) getReadFormula()).getValue();
+ }
+
+ /**
+ * Gets the NumberFormat used to format this cell. This is the java
+ * equivalent of the Excel format
+ *
+ * @return the NumberFormat used to format the cell
+ */
+ public NumberFormat getNumberFormat() {
+ return ((NumberFormulaCell) getReadFormula()).getNumberFormat();
+ }
+
+ /**
+ * Error formula specific exception handling. Can't really create
+ * a formula (as it will look for a cell of that name, so just
+ * create a STRING record containing the contents
+ *
+ * @return the bodged data
+ */
+ protected byte[] handleFormulaException() {
+ byte[] expressiondata = null;
+ byte[] celldata = super.getCellData();
+
+ // Generate an appropriate dummy formula
+ WritableWorkbookImpl w = getSheet().getWorkbook();
+ FormulaParser parser = new FormulaParser(Double.toString(getValue()), w, w,
+ w.getSettings());
+
+ // Get the bytes for the dummy formula
+ try {
+ parser.parse();
+ } catch (FormulaException e2) {
+ logger.warn(e2.getMessage());
+ }
+ byte[] formulaBytes = parser.getBytes();
+ expressiondata = new byte[formulaBytes.length + 16];
+ IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
+ System.arraycopy(formulaBytes, 0, expressiondata, 16,
+ formulaBytes.length);
+
+ // Set the recalculate on load bit
+ expressiondata[8] |= 0x02;
+
+ byte[] data = new byte[celldata.length +
+ expressiondata.length];
+ System.arraycopy(celldata, 0, data, 0, celldata.length);
+ System.arraycopy(expressiondata, 0, data,
+ celldata.length, expressiondata.length);
+
+ // Store the value in the formula
+ DoubleHelper.getIEEEBytes(getValue(), data, 6);
+
+ return data;
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java
new file mode 100755
index 0000000..b2fc7b3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ReadStringFormulaRecord.java
@@ -0,0 +1,106 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.StringFormulaCell;
+import jxl.biff.FormulaData;
+import jxl.biff.IntegerHelper;
+import jxl.biff.formula.FormulaException;
+import jxl.biff.formula.FormulaParser;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * Class for read number formula records
+ */
+class ReadStringFormulaRecord extends ReadFormulaRecord
+ implements StringFormulaCell {
+ // the logger
+ private static final Logger logger = Logger.getLogger(ReadFormulaRecord.class);
+
+ /**
+ * Constructor
+ *
+ * @param f
+ */
+ public ReadStringFormulaRecord(FormulaData f) {
+ super(f);
+ }
+
+ /**
+ * Gets the string contents for this cell.
+ *
+ * @return the cell contents
+ */
+ public String getString() {
+ return ((StringFormulaCell) getReadFormula()).getString();
+ }
+
+ /**
+ * String formula specific exception handling. Can't really create
+ * a formula (as it will look for a cell of that name, so just
+ * create a STRING record containing the contents
+ *
+ * @return the bodged data
+ */
+ protected byte[] handleFormulaException() {
+ byte[] expressiondata = null;
+ byte[] celldata = super.getCellData();
+
+ // Generate an appropriate dummy formula
+ WritableWorkbookImpl w = getSheet().getWorkbook();
+ FormulaParser parser = new FormulaParser("\"" + getContents() + "\"", w, w,
+ w.getSettings());
+
+ // Get the bytes for the dummy formula
+ try {
+ parser.parse();
+ } catch (FormulaException e2) {
+ logger.warn(e2.getMessage());
+ parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings());
+ try {
+ parser.parse();
+ } catch (FormulaException e3) {
+ Assert.verify(false);
+ }
+ }
+ byte[] formulaBytes = parser.getBytes();
+ expressiondata = new byte[formulaBytes.length + 16];
+ IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
+ System.arraycopy(formulaBytes, 0, expressiondata, 16,
+ formulaBytes.length);
+
+ // Set the recalculate on load bit
+ expressiondata[8] |= 0x02;
+
+ byte[] data = new byte[celldata.length +
+ expressiondata.length];
+ System.arraycopy(celldata, 0, data, 0, celldata.length);
+ System.arraycopy(expressiondata, 0, data,
+ celldata.length, expressiondata.length);
+
+ // Set the type bits to indicate a string formula
+ data[6] = 0;
+ data[12] = -1;
+ data[13] = -1;
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java
new file mode 100755
index 0000000..aff642b
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/RefModeRecord.java
@@ -0,0 +1,51 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the reference style option from the options dialog box
+ */
+class RefModeRecord extends WritableRecordData {
+ /**
+ * Constructor
+ */
+ public RefModeRecord() {
+ super(Type.REFMODE);
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[2];
+
+ // Hard code in the style A1 for now
+ data[0] = 0x1;
+
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java
new file mode 100755
index 0000000..3764fe9
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/RefreshAllRecord.java
@@ -0,0 +1,66 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Record which indicates whether or not data ranges and pivot tables
+ * should be refreshed when the workbook is loaded
+ */
+class RefreshAllRecord extends WritableRecordData {
+ /**
+ * The refresh all flag
+ */
+ private final boolean refreshall;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param refresh refresh all flag
+ */
+ public RefreshAllRecord(boolean refresh) {
+ super(Type.REFRESHALL);
+
+ refreshall = refresh;
+
+ // Hard code in an unprotected workbook
+ data = new byte[2];
+
+ if (refreshall) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java
new file mode 100755
index 0000000..8a9b903
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/RightMarginRecord.java
@@ -0,0 +1,31 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+
+/**
+ * The settings for the left margin
+ */
+class RightMarginRecord extends MarginRecord {
+ RightMarginRecord(double v) {
+ super(Type.RIGHTMARGIN, v);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java
new file mode 100755
index 0000000..8a794ac
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/RowRecord.java
@@ -0,0 +1,659 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.CellType;
+import jxl.SheetSettings;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.IndexMapping;
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.biff.XFRecord;
+import jxl.common.Logger;
+import jxl.write.Number;
+import jxl.write.WritableCellFeatures;
+import jxl.write.WritableSheet;
+
+/**
+ * Contains all the cells for a given row in a sheet
+ */
+class RowRecord extends WritableRecordData {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(RowRecord.class);
+ /**
+ * The amount to grow the cells array by
+ */
+ private static final int growSize = 10;
+ /**
+ * The maximum integer value that can be squeezed into 30 bits
+ */
+ private static final int maxRKValue = 0x1fffffff;
+ /**
+ * The minimum integer value that can be squeezed into 30 bits
+ */
+ private static final int minRKValue = -0x20000000;
+ /**
+ * Indicates that the row is default height
+ */
+ private static final int defaultHeightIndicator = 0xff;
+ /**
+ * The maximum number of columns
+ */
+ private static final int maxColumns = 256;
+ /**
+ * The binary data
+ */
+ private byte[] data;
+ /**
+ * The cells which comprise this row
+ */
+ private CellValue[] cells;
+ /**
+ * The height of this row in 1/20ths of a point
+ */
+ private int rowHeight;
+ /**
+ * Flag to indicate whether this row is outline collapsed or not
+ */
+ private boolean collapsed;
+ /**
+ * The number of this row within the worksheet
+ */
+ private int rowNumber;
+ /**
+ * The number of columns in this row. This is the largest column value + 1
+ */
+ private int numColumns;
+ /**
+ * The xfIndex for this row
+ */
+ private int xfIndex;
+ /**
+ * The style for this row
+ */
+ private XFRecord style;
+ /**
+ * Flag indicating that this row record has an default format
+ */
+ private boolean defaultFormat;
+ /**
+ * Flag indicating whether this row matches the default font height
+ */
+ private boolean matchesDefFontHeight;
+ /**
+ * The outline level of the row
+ */
+ private int outlineLevel;
+
+ /**
+ * Is this the icon indicator row of a group?
+ */
+ private boolean groupStart;
+
+ /**
+ * A handle back to the sheet
+ */
+ private final WritableSheet sheet;
+
+ /**
+ * Constructs an empty row which has the specified row number
+ *
+ * @param rn the row number of this row
+ */
+ public RowRecord(int rn, WritableSheet ws) {
+ super(Type.ROW);
+ rowNumber = rn;
+ cells = new CellValue[0];
+ numColumns = 0;
+ rowHeight = defaultHeightIndicator;
+ collapsed = false;
+ matchesDefFontHeight = true;
+ sheet = ws;
+ }
+
+ /**
+ * Sets the row details based upon the readable row record passed in
+ * Called when copying spreadsheets
+ *
+ * @param height the height of the row record in 1/20ths of a point
+ * @param mdfh matches the default font height
+ * @param col the collapsed status of the row
+ * @param ol the outline level
+ * @param gs the group start
+ * @param xf the xfrecord for the row (NULL if no default is set)
+ */
+ void setRowDetails(int height,
+ boolean mdfh,
+ boolean col,
+ int ol,
+ boolean gs,
+ XFRecord xfr) {
+ rowHeight = height;
+ collapsed = col;
+ matchesDefFontHeight = mdfh;
+ outlineLevel = ol;
+ groupStart = gs;
+
+ if (xfr != null) {
+ defaultFormat = true;
+ style = xfr;
+ xfIndex = style.getXFIndex();
+ }
+ }
+
+ /**
+ * Gets the row number of this row
+ *
+ * @return the row number
+ */
+ public int getRowNumber() {
+ return rowNumber;
+ }
+
+ /**
+ * Adds a cell to this row, growing the array of cells as required
+ *
+ * @param cv the cell to add
+ */
+ public void addCell(CellValue cv) {
+ int col = cv.getColumn();
+
+ if (col >= maxColumns) {
+ logger.warn("Could not add cell at " +
+ CellReferenceHelper.getCellReference(cv.getRow(),
+ cv.getColumn()) +
+ " because it exceeds the maximum column limit");
+ return;
+ }
+
+ // Grow the array if needs be
+ if (col >= cells.length) {
+ CellValue[] oldCells = cells;
+ cells = new CellValue[Math.max(oldCells.length + growSize, col + 1)];
+ System.arraycopy(oldCells, 0, cells, 0, oldCells.length);
+ oldCells = null;
+ }
+
+ // Remove any cell features from the cell being replaced
+ if (cells[col] != null) {
+ WritableCellFeatures wcf = cells[col].getWritableCellFeatures();
+ if (wcf != null) {
+ wcf.removeComment();
+
+ // if the cell is part of a shared data validation,then don't remove
+ // the validation
+ if (wcf.getDVParser() != null &&
+ !wcf.getDVParser().extendedCellsValidation()) {
+ wcf.removeDataValidation();
+ }
+ }
+
+ }
+
+ cells[col] = cv;
+
+ numColumns = Math.max(col + 1, numColumns);
+ }
+
+ /**
+ * Removes a cell from this row
+ *
+ * @param col the column at which to remove the cell
+ */
+ public void removeCell(int col) {
+ // Grow the array if needs be
+ if (col >= numColumns) {
+ return;
+ }
+
+ cells[col] = null;
+ }
+
+ /**
+ * Writes out the row information data (but not the individual cells)
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void write(File outputFile) throws IOException {
+ outputFile.write(this);
+ }
+
+ /**
+ * Writes out all the cells in this row. If more than three integer
+ * values occur consecutively, then a MulRK record is used to group the
+ * numbers
+ *
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public void writeCells(File outputFile)
+ throws IOException {
+ // This is the list for integer values
+ ArrayList integerValues = new ArrayList();
+ boolean integerValue = false;
+
+ // Write out all the records
+ for (int i = 0; i < numColumns; i++) {
+ integerValue = false;
+ if (cells[i] != null) {
+ // See if this cell is a 30-bit integer value (without additional
+ // cell features)
+ if (cells[i].getType() == CellType.NUMBER) {
+ Number nc = (Number) cells[i];
+ if (nc.getValue() == (int) nc.getValue() &&
+ nc.getValue() < maxRKValue &&
+ nc.getValue() > minRKValue &&
+ nc.getCellFeatures() == null) {
+ integerValue = true;
+ }
+ }
+
+ if (integerValue) {
+ // This cell is an integer, add it to the list
+ integerValues.add(cells[i]);
+ } else {
+ // This cell is not an integer. Write out whatever integers we
+ // have, and then write out this cell
+ writeIntegerValues(integerValues, outputFile);
+ outputFile.write(cells[i]);
+
+ // If the cell is a string formula, write out the string record
+ // immediately afterwards
+ if (cells[i].getType() == CellType.STRING_FORMULA) {
+ StringRecord sr = new StringRecord(cells[i].getContents());
+ outputFile.write(sr);
+ }
+ }
+ } else {
+ // Cell does not exist. Write out the list of integers that
+ // we have
+ writeIntegerValues(integerValues, outputFile);
+ }
+ }
+
+ // All done. Write out any remaining integer values
+ writeIntegerValues(integerValues, outputFile);
+ }
+
+ /**
+ * Writes out the list of integer values. If there are more than three,
+ * a MulRK record is used, otherwise a sequence of Numbers is used
+ *
+ * @param outputFile the output file
+ * @param integerValues the array of integer values
+ * @throws IOException
+ */
+ private void writeIntegerValues(ArrayList integerValues, File outputFile)
+ throws IOException {
+ if (integerValues.size() == 0) {
+ return;
+ }
+
+ if (integerValues.size() >= 3) {
+ // Write out as a MulRK record
+ MulRKRecord mulrk = new MulRKRecord(integerValues);
+ outputFile.write(mulrk);
+ } else {
+ // Write out as number records
+ Iterator i = integerValues.iterator();
+ while (i.hasNext()) {
+ outputFile.write((CellValue) i.next());
+ }
+ }
+
+ // Clear out the list of integerValues
+ integerValues.clear();
+ }
+
+ /**
+ * Gets the row data to output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ // Write out the row record
+ byte[] data = new byte[16];
+
+ // If the default row height has been changed in the sheet settings,
+ // then we need to set the rowHeight on this row explicitly, as
+ // specifying the "match default" flag doesn't work
+ int rh = rowHeight;
+ if (sheet.getSettings().getDefaultRowHeight() !=
+ SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT) {
+ // the default row height has been changed. If this row does not
+ // have a specific row height set on it, then set it to the default
+ if (rh == defaultHeightIndicator) {
+ rh = sheet.getSettings().getDefaultRowHeight();
+ }
+ }
+
+ IntegerHelper.getTwoBytes(rowNumber, data, 0);
+ IntegerHelper.getTwoBytes(numColumns, data, 4);
+ IntegerHelper.getTwoBytes(rh, data, 6);
+
+ int options = 0x100 + outlineLevel;
+
+ if (groupStart) {
+ options |= 0x10;
+ }
+
+ if (collapsed) {
+ options |= 0x20;
+ }
+
+ if (!matchesDefFontHeight) {
+ options |= 0x40;
+ }
+
+ if (defaultFormat) {
+ options |= 0x80;
+ options |= (xfIndex << 16);
+ }
+
+ IntegerHelper.getFourBytes(options, data, 12);
+
+ return data;
+ }
+
+ /**
+ * Gets the maximum column value which occurs in this row
+ *
+ * @return the maximum column value
+ */
+ public int getMaxColumn() {
+ return numColumns;
+ }
+
+ /**
+ * Gets the cell which occurs at the specified column value
+ *
+ * @param col the colun for which to return the cell
+ * @return the cell value at the specified position, or null if the column
+ * is invalid
+ */
+ public CellValue getCell(int col) {
+ return (col >= 0 && col < numColumns) ? cells[col] : null;
+ }
+
+ /**
+ * Increments the row of this cell by one. Invoked by the sheet when
+ * inserting rows
+ */
+ void incrementRow() {
+ rowNumber++;
+
+ for (int i = 0; i < cells.length; i++) {
+ if (cells[i] != null) {
+ cells[i].incrementRow();
+ }
+ }
+ }
+
+ /**
+ * Decrements the row of this cell by one. Invoked by the sheet when
+ * removing rows
+ */
+ void decrementRow() {
+ rowNumber--;
+ for (int i = 0; i < cells.length; i++) {
+ if (cells[i] != null) {
+ cells[i].decrementRow();
+ }
+ }
+ }
+
+ /**
+ * Inserts a new column at the position specified. If the max column length
+ * is already reached, then the last column simply gets dropped
+ *
+ * @param col the column to insert
+ */
+ void insertColumn(int col) {
+ // Don't bother doing anything unless there are cells after the
+ // column to be inserted
+ if (col >= numColumns) {
+ return;
+ }
+
+ // Create a new array to hold the new column. Grow it if need be
+ CellValue[] oldCells = cells;
+
+ if (numColumns >= cells.length - 1) {
+ cells = new CellValue[oldCells.length + growSize];
+ } else {
+ cells = new CellValue[oldCells.length];
+ }
+
+ // Copy in everything up to the new column
+ System.arraycopy(oldCells, 0, cells, 0, col);
+
+ // Copy in the remaining cells
+ System.arraycopy(oldCells, col, cells, col + 1, numColumns - col);
+
+ // Increment all the internal column numbers by one
+ for (int i = col + 1; i <= numColumns; i++) {
+ if (cells[i] != null) {
+ cells[i].incrementColumn();
+ }
+ }
+
+ // Adjust the maximum column record
+ numColumns = Math.min(numColumns + 1, maxColumns);
+ }
+
+ /**
+ * Remove the new column at the position specified
+ *
+ * @param col the column to remove
+ */
+ void removeColumn(int col) {
+ // Don't bother doing anything unless there are cells after the
+ // column to be inserted
+ if (col >= numColumns) {
+ return;
+ }
+
+ // Create a new array to hold the new columns
+ CellValue[] oldCells = cells;
+
+ cells = new CellValue[oldCells.length];
+
+ // Copy in everything up to the column
+ System.arraycopy(oldCells, 0, cells, 0, col);
+
+ // Copy in the remaining cells after the column
+ System.arraycopy(oldCells, col + 1, cells, col, numColumns - (col + 1));
+
+ // Decrement all the internal column numbers by one
+ for (int i = col; i < numColumns; i++) {
+ if (cells[i] != null) {
+ cells[i].decrementColumn();
+ }
+ }
+
+ // Adjust the maximum column record
+ numColumns--;
+ }
+
+ /**
+ * Interrogates whether this row is of default height
+ *
+ * @return TRUE if this is set to the default height, FALSE otherwise
+ */
+ public boolean isDefaultHeight() {
+ return rowHeight == defaultHeightIndicator;
+ }
+
+ /**
+ * Gets the height of the row
+ *
+ * @return the row height
+ */
+ public int getRowHeight() {
+ return rowHeight;
+ }
+
+ /**
+ * Sets the height of this row
+ *
+ * @param h the row height
+ */
+ public void setRowHeight(int h) {
+ if (h == 0) {
+ setCollapsed(true);
+ matchesDefFontHeight = false;
+ } else {
+ rowHeight = h;
+ matchesDefFontHeight = false;
+ }
+ }
+
+ /**
+ * Queries whether the row is collapsed
+ *
+ * @return the collapsed indicator
+ */
+ public boolean isCollapsed() {
+ return collapsed;
+ }
+
+ /**
+ * Sets the collapsed status of this row
+ *
+ * @param c the collapsed flag
+ */
+ public void setCollapsed(boolean c) {
+ collapsed = c;
+ }
+
+ /**
+ * Rationalizes the sheets xf index mapping
+ *
+ * @param xfmapping the index mapping
+ */
+ void rationalize(IndexMapping xfmapping) {
+ if (defaultFormat) {
+ xfIndex = xfmapping.getNewIndex(xfIndex);
+ }
+ }
+
+ /**
+ * Accessor for the style. The returned value is only non-null if the
+ * default style is overridden
+ *
+ * @return the style
+ */
+ XFRecord getStyle() {
+ return style;
+ }
+
+ /**
+ * Accessor for the default format flag
+ *
+ * @return TRUE if this row has its own default format
+ */
+ boolean hasDefaultFormat() {
+ return defaultFormat;
+ }
+
+ /**
+ * Accessor for the matches default font height flag
+ *
+ * @return TRUE if this row matches the default font height
+ */
+ boolean matchesDefaultFontHeight() {
+ return matchesDefFontHeight;
+ }
+
+ /**
+ * Accessor for the column's outline level
+ *
+ * @return the column's outline level
+ */
+ public int getOutlineLevel() {
+ return outlineLevel;
+ }
+
+ /**
+ * Sets the row's outline level
+ *
+ * @param level the row's outline level
+ */
+ public void setOutlineLevel(int level) {
+ outlineLevel = level;
+ }
+
+ /**
+ * Accessor for row's groupStart state
+ *
+ * @return the row's groupStart state
+ */
+ public boolean getGroupStart() {
+ return groupStart;
+ }
+
+ /**
+ * Sets the row's group start state
+ *
+ * @param value the group start state
+ */
+ public void setGroupStart(boolean value) {
+ groupStart = value;
+ }
+
+ /**
+ * Increments the row's outline level. This is how groups are made as well
+ */
+ public void incrementOutlineLevel() {
+ outlineLevel++;
+ }
+
+ /**
+ * Decrements the row's outline level. This removes it from a grouping
+ * level. If
+ * all outline levels are gone the uncollapse the row.
+ */
+ public void decrementOutlineLevel() {
+ if (0 < outlineLevel) {
+ outlineLevel--;
+ }
+
+ if (0 == outlineLevel) {
+ collapsed = false;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java b/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java
new file mode 100755
index 0000000..b1762e4
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/RowsExceededException.java
@@ -0,0 +1,33 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+/**
+ * Exception thrown when attempting to add a row to a spreadsheet which
+ * has already reached the maximum amount
+ */
+public class RowsExceededException extends JxlWriteException {
+ /**
+ * Constructor
+ */
+ public RowsExceededException() {
+ super(maxRowsExceeded);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java
new file mode 100755
index 0000000..122e43c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SCLRecord.java
@@ -0,0 +1,62 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2003 Andrew Khan, Adam Caldwell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Record which specifies a margin value
+ */
+class SCLRecord extends WritableRecordData {
+ /**
+ * The zoom factor
+ */
+ private final int zoomFactor;
+
+ /**
+ * Constructor
+ *
+ * @param zf the zoom factor as a percentage
+ */
+ public SCLRecord(int zf) {
+ super(Type.SCL);
+
+ zoomFactor = zf;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[4];
+
+ int numerator = zoomFactor;
+ int denominator = 100;
+
+ IntegerHelper.getTwoBytes(numerator, data, 0);
+ IntegerHelper.getTwoBytes(denominator, data, 2);
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java
new file mode 100755
index 0000000..37d27b3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SSTContinueRecord.java
@@ -0,0 +1,207 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * A continuation of a shared string table record.
+ */
+class SSTContinueRecord extends WritableRecordData {
+ /**
+ * The maximum amount of bytes available for the SST record
+ */
+ private static final int maxBytes = 8228 - // max length
+ 4; // standard biff record stuff
+ /**
+ * The first string is a special case
+ */
+ private String firstString;
+ /**
+ * Indicates whether or not we need to include the length information
+ * for the first string
+ */
+ private boolean includeLength;
+ /**
+ * The length of the first string
+ */
+ private int firstStringLength;
+ /**
+ * The list of strings
+ */
+ private final ArrayList strings;
+ /**
+ * The list of string lengths
+ */
+ private final ArrayList stringLengths;
+ /**
+ * The binary data
+ */
+ private byte[] data;
+ /**
+ * The count of bytes needed so far to contain this record
+ */
+ private int byteCount;
+
+ /**
+ * Constructor
+ *
+ * @param numRefs the number of string references in the workbook
+ * @param s the number of strings
+ */
+ public SSTContinueRecord() {
+ super(Type.CONTINUE);
+
+ byteCount = 0;
+ strings = new ArrayList(50);
+ stringLengths = new ArrayList(50);
+ }
+
+ /**
+ * Adds the first string to this SST record
+ *
+ * @param s the string to add
+ * @param b include the length information for the first string
+ * @return the number of characters not added
+ */
+ public int setFirstString(String s, boolean b) {
+ includeLength = b;
+ firstStringLength = s.length();
+
+ int bytes = 0;
+
+ if (!includeLength) {
+ bytes = s.length() * 2 + 1;
+ } else {
+ bytes = s.length() * 2 + 3;
+ }
+
+ if (bytes <= maxBytes) {
+ firstString = s;
+ byteCount += bytes;
+ return 0;
+ }
+
+ // Calculate the number of characters we can add
+ // The bytes variable will always be an odd number
+ int charsAvailable = includeLength ? (maxBytes - 4) / 2 :
+ (maxBytes - 2) / 2;
+
+ // Add what part of the string we can
+ firstString = s.substring(0, charsAvailable);
+ byteCount = maxBytes - 1;
+
+ return s.length() - charsAvailable;
+ }
+
+ /**
+ * Gets the current offset into this record, excluding the header fields
+ *
+ * @return the number of bytes after the header field
+ */
+ public int getOffset() {
+ return byteCount;
+ }
+
+ /**
+ * Adds a string to this record. It returns the number of string
+ * characters not added, due to space constraints. In the event
+ * of this being non-zero, a continue record will be needed
+ *
+ * @param s the string to add
+ * @return the number of characters not added
+ */
+ public int add(String s) {
+ int bytes = s.length() * 2 + 3;
+
+ // Must be able to add at least the first character of the string
+ // onto the SST
+ if (byteCount >= maxBytes - 5) {
+ return s.length();
+ }
+
+ stringLengths.add(new Integer(s.length()));
+
+ if (bytes + byteCount < maxBytes) {
+ // add the string and return
+ strings.add(s);
+ byteCount += bytes;
+ return 0;
+ }
+
+ // Calculate the number of characters we can add
+ int bytesLeft = maxBytes - 3 - byteCount;
+ int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 :
+ (bytesLeft - 1) / 2;
+
+ // Add what part of the string we can
+ strings.add(s.substring(0, charsAvailable));
+ byteCount += charsAvailable * 2 + 3;
+
+ return s.length() - charsAvailable;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ data = new byte[byteCount];
+
+ int pos = 0;
+
+ // Write out the first string
+ if (includeLength) {
+ IntegerHelper.getTwoBytes(firstStringLength, data, 0);
+ data[2] = 0x01;
+ pos = 3;
+ } else {
+ // Just include the unicode indicator
+ data[0] = 0x01;
+ pos = 1;
+ }
+
+ StringHelper.getUnicodeBytes(firstString, data, pos);
+ pos += firstString.length() * 2;
+
+ // Now write out the remainder of the strings
+ Iterator i = strings.iterator();
+ String s = null;
+ int length = 0;
+ int count = 0;
+ while (i.hasNext()) {
+ s = (String) i.next();
+ length = ((Integer) stringLengths.get(count)).intValue();
+ IntegerHelper.getTwoBytes(length, data, pos);
+ data[pos + 2] = 0x01;
+ StringHelper.getUnicodeBytes(s, data, pos + 3);
+ pos += s.length() * 2 + 3;
+ count++;
+ }
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java
new file mode 100755
index 0000000..2dd5d9e
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SSTRecord.java
@@ -0,0 +1,156 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * A shared string table record.
+ */
+class SSTRecord extends WritableRecordData {
+ /**
+ * The maximum amount of bytes available for the SST record
+ */
+ private static final int maxBytes = 8228 - // max length
+ 8 - // bytes for string count fields
+ 4; // standard biff record header
+ /**
+ * The number of string references in the workbook
+ */
+ private final int numReferences;
+ /**
+ * The number of strings in this table
+ */
+ private final int numStrings;
+ /**
+ * The list of strings
+ */
+ private final ArrayList strings;
+ /**
+ * The list of string lengths
+ */
+ private final ArrayList stringLengths;
+ /**
+ * The binary data
+ */
+ private byte[] data;
+ /**
+ * The count of bytes needed so far to contain this record
+ */
+ private int byteCount;
+
+ /**
+ * Constructor
+ *
+ * @param numRefs the number of string references in the workbook
+ * @param s the number of strings
+ */
+ public SSTRecord(int numRefs, int s) {
+ super(Type.SST);
+
+ numReferences = numRefs;
+ numStrings = s;
+ byteCount = 0;
+ strings = new ArrayList(50);
+ stringLengths = new ArrayList(50);
+ }
+
+ /**
+ * Adds a string to this SST record. It returns the number of string
+ * characters not added, due to space constraints. In the event
+ * of this being non-zero, a continue record will be needed
+ *
+ * @param s the string to add
+ * @return the number of characters not added
+ */
+ public int add(String s) {
+ int bytes = s.length() * 2 + 3;
+
+ // Must be able to add at least the first character of the string
+ // onto the SST
+ if (byteCount >= maxBytes - 5) {
+ return s.length() > 0 ? s.length() : -1; // need to return some non-zero
+ // value in order to force the creation of a continue record
+ }
+
+ stringLengths.add(new Integer(s.length()));
+
+ if (bytes + byteCount < maxBytes) {
+ // add the string and return
+ strings.add(s);
+ byteCount += bytes;
+ return 0;
+ }
+
+ // Calculate the number of characters we can add
+ int bytesLeft = maxBytes - 3 - byteCount;
+ int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 :
+ (bytesLeft - 1) / 2;
+
+ // Add what strings we can
+ strings.add(s.substring(0, charsAvailable));
+ byteCount += charsAvailable * 2 + 3;
+
+ return s.length() - charsAvailable;
+ }
+
+ /**
+ * Gets the current offset into this record, excluding the header fields
+ *
+ * @return the number of bytes after the header field
+ */
+ public int getOffset() {
+ return byteCount + 8;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ data = new byte[byteCount + 8];
+ IntegerHelper.getFourBytes(numReferences, data, 0);
+ IntegerHelper.getFourBytes(numStrings, data, 4);
+
+ int pos = 8;
+ int count = 0;
+
+ Iterator i = strings.iterator();
+ String s = null;
+ int length = 0;
+ while (i.hasNext()) {
+ s = (String) i.next();
+ length = ((Integer) stringLengths.get(count)).intValue();
+ IntegerHelper.getTwoBytes(length, data, pos);
+ data[pos + 2] = 0x01;
+ StringHelper.getUnicodeBytes(s, data, pos + 3);
+ pos += s.length() * 2 + 3;
+ count++;
+ }
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java
new file mode 100755
index 0000000..dce67f2
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SaveRecalcRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the recalculate before save flag as set in the options dialog box
+ */
+class SaveRecalcRecord extends WritableRecordData {
+ /**
+ * The binary data for output to file
+ */
+ private final byte[] data;
+ /**
+ * The recalculate before save flag
+ */
+ private final boolean recalc;
+
+ /**
+ * Constructor
+ *
+ * @param r recalculate flag
+ */
+ public SaveRecalcRecord(boolean r) {
+ super(Type.SAVERECALC);
+ recalc = r;
+
+ data = new byte[2];
+
+ if (recalc) {
+ data[0] = 1;
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java
new file mode 100755
index 0000000..17dcd31
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/ScenarioProtectRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The protection state for a sheet or workbook
+ */
+class ScenarioProtectRecord extends WritableRecordData {
+ /**
+ * The protection state
+ */
+ private final boolean protection;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param prot the protection state
+ */
+ public ScenarioProtectRecord(boolean prot) {
+ super(Type.SCENPROTECT);
+
+ protection = prot;
+
+ data = new byte[2];
+
+ if (protection) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java
new file mode 100755
index 0000000..31acc7a
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SelectionRecord.java
@@ -0,0 +1,88 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the current selection
+ */
+class SelectionRecord extends WritableRecordData {
+ // The pane types
+ public final static PaneType lowerRight = new PaneType(0);
+ public final static PaneType upperRight = new PaneType(1);
+ public final static PaneType lowerLeft = new PaneType(2);
+ public final static PaneType upperLeft = new PaneType(3);
+ /**
+ * The pane type
+ */
+ private final PaneType pane;
+ /**
+ * The top left column in this pane
+ */
+ private final int column;
+ /**
+ * The top left row in this pane
+ */
+ private final int row;
+ /**
+ * Constructor
+ */
+ public SelectionRecord(PaneType pt, int col, int r) {
+ super(Type.SELECTION);
+ column = col;
+ row = r;
+ pane = pt;
+ }
+
+ /**
+ * Gets the binary data
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ // hard code the data in for now
+ byte[] data = new byte[15];
+
+ data[0] = (byte) pane.val;
+ IntegerHelper.getTwoBytes(row, data, 1);
+ IntegerHelper.getTwoBytes(column, data, 3);
+
+ data[7] = (byte) 0x01;
+
+ IntegerHelper.getTwoBytes(row, data, 9);
+ IntegerHelper.getTwoBytes(row, data, 11);
+ data[13] = (byte) column;
+ data[14] = (byte) column;
+
+ return data;
+ }
+
+ // Enumeration for the pane type
+ private static class PaneType {
+ int val;
+
+ PaneType(int v) {
+ val = v;
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java
new file mode 100755
index 0000000..242d2a1
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SetupRecord.java
@@ -0,0 +1,235 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.SheetSettings;
+import jxl.biff.DoubleHelper;
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Logger;
+import jxl.format.PageOrder;
+import jxl.format.PageOrientation;
+import jxl.format.PaperSize;
+
+/**
+ * Stores the options and measurements from the Page Setup dialog box
+ */
+class SetupRecord extends WritableRecordData {
+ /**
+ * The logger
+ */
+ Logger logger = Logger.getLogger(SetupRecord.class);
+
+ /**
+ * The binary data for output to file
+ */
+ private byte[] data;
+
+ /**
+ * The header margin
+ */
+ private double headerMargin;
+
+ /**
+ * The footer margin
+ */
+ private double footerMargin;
+
+ /**
+ * The page orientation
+ */
+ private PageOrientation orientation;
+
+ /**
+ * The page order
+ */
+ private PageOrder order;
+
+ /**
+ * The paper size
+ */
+ private int paperSize;
+
+ /**
+ * The scale factor
+ */
+ private final int scaleFactor;
+
+ /**
+ * The page start
+ */
+ private final int pageStart;
+
+ /**
+ * The fit width
+ */
+ private final int fitWidth;
+
+ /**
+ * The fit height
+ */
+ private final int fitHeight;
+
+ /**
+ * The horizontal print resolution
+ */
+ private final int horizontalPrintResolution;
+
+ /**
+ * The vertical print resolution
+ */
+ private final int verticalPrintResolution;
+
+ /**
+ * The number of copies
+ */
+ private final int copies;
+
+ /**
+ * Indicates whether the setup data should be initiliazed in the setup
+ * box
+ */
+ private final boolean initialized;
+
+ /**
+ * Constructor, taking the sheet settings. This object just
+ * takes the various fields from the bean in which it is interested
+ *
+ * @param the sheet settings
+ */
+ public SetupRecord(SheetSettings s) {
+ super(Type.SETUP);
+
+ orientation = s.getOrientation();
+ order = s.getPageOrder();
+ headerMargin = s.getHeaderMargin();
+ footerMargin = s.getFooterMargin();
+ paperSize = s.getPaperSize().getValue();
+ horizontalPrintResolution = s.getHorizontalPrintResolution();
+ verticalPrintResolution = s.getVerticalPrintResolution();
+ fitWidth = s.getFitWidth();
+ fitHeight = s.getFitHeight();
+ pageStart = s.getPageStart();
+ scaleFactor = s.getScaleFactor();
+ copies = s.getCopies();
+ initialized = true;
+ }
+
+ /**
+ * Sets the orientation
+ *
+ * @param o the orientation
+ */
+ public void setOrientation(PageOrientation o) {
+ orientation = o;
+ }
+
+ /**
+ * Sets the page order
+ *
+ * @param o
+ */
+ public void setOrder(PageOrder o) {
+ order = o;
+ }
+
+ /**
+ * Sets the header and footer margins
+ *
+ * @param hm the header margin
+ * @param fm the footer margin
+ */
+ public void setMargins(double hm, double fm) {
+ headerMargin = hm;
+ footerMargin = fm;
+ }
+
+ /**
+ * Sets the paper size
+ *
+ * @param ps the paper size
+ */
+ public void setPaperSize(PaperSize ps) {
+ paperSize = ps.getValue();
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ data = new byte[34];
+
+ // Paper size
+ IntegerHelper.getTwoBytes(paperSize, data, 0);
+
+ // Scale factor
+ IntegerHelper.getTwoBytes(scaleFactor, data, 2);
+
+ // Page start
+ IntegerHelper.getTwoBytes(pageStart, data, 4);
+
+ // Fit width
+ IntegerHelper.getTwoBytes(fitWidth, data, 6);
+
+ // Fit height
+ IntegerHelper.getTwoBytes(fitHeight, data, 8);
+
+ // grbit
+ int options = 0;
+ if (order == PageOrder.RIGHT_THEN_DOWN) {
+ options |= 0x01;
+ }
+
+ if (orientation == PageOrientation.PORTRAIT) {
+ options |= 0x02;
+ }
+
+ if (pageStart != 0) {
+ options |= 0x80;
+ }
+
+ if (!initialized) {
+ options |= 0x04;
+ }
+
+ IntegerHelper.getTwoBytes(options, data, 10);
+
+ // print resolution
+ IntegerHelper.getTwoBytes(horizontalPrintResolution, data, 12);
+
+ // vertical print resolution
+ IntegerHelper.getTwoBytes(verticalPrintResolution, data, 14);
+
+ // header margin
+ DoubleHelper.getIEEEBytes(headerMargin, data, 16);
+
+ // footer margin
+ DoubleHelper.getIEEEBytes(footerMargin, data, 24);
+
+ // Number of copies
+ IntegerHelper.getTwoBytes(copies, data, 32);
+
+ return data;
+ }
+}
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java b/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java
new file mode 100755
index 0000000..2b9a2cd
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SharedStrings.java
@@ -0,0 +1,171 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * The list of available shared strings. This class contains
+ * the labels used for the entire spreadsheet
+ */
+class SharedStrings {
+ /**
+ * All the strings in the spreadsheet, keyed on the string itself
+ */
+ private final HashMap strings;
+
+ /**
+ * Contains the same strings, held in a list
+ */
+ private final ArrayList stringList;
+
+ /**
+ * The total occurrence of strings in the workbook
+ */
+ private int totalOccurrences;
+
+ /**
+ * Constructor
+ */
+ public SharedStrings() {
+ strings = new HashMap(100);
+ stringList = new ArrayList(100);
+ totalOccurrences = 0;
+ }
+
+ /**
+ * Gets the index for the string passed in. If the string is already
+ * present, then returns the index of that string, otherwise
+ * creates a new key-index mapping
+ *
+ * @param s the string whose index we want
+ * @return the index of the string
+ */
+ public int getIndex(String s) {
+ Integer i = (Integer) strings.get(s);
+
+ if (i == null) {
+ i = new Integer(strings.size());
+ strings.put(s, i);
+ stringList.add(s);
+ }
+
+ totalOccurrences++;
+
+ return i.intValue();
+ }
+
+ /**
+ * Gets the string at the specified index
+ *
+ * @param i the index of the string
+ * @return the string at the specified index
+ */
+ public String get(int i) {
+ return (String) stringList.get(i);
+ }
+
+ /**
+ * Writes out the shared string table
+ *
+ * @param outputFile the binary output file
+ * @throws IOException
+ */
+ public void write(File outputFile) throws IOException {
+ // Thanks to Guenther for contributing the ExtSST implementation portion
+ // of this method
+ int charsLeft = 0;
+ String curString = null;
+ SSTRecord sst = new SSTRecord(totalOccurrences, stringList.size());
+ ExtendedSSTRecord extsst = new ExtendedSSTRecord(stringList.size());
+ int bucketSize = extsst.getNumberOfStringsPerBucket();
+
+ Iterator i = stringList.iterator();
+ int stringIndex = 0;
+ while (i.hasNext() && charsLeft == 0) {
+ curString = (String) i.next();
+ // offset + header bytes
+ int relativePosition = sst.getOffset() + 4;
+ charsLeft = sst.add(curString);
+ if ((stringIndex % bucketSize) == 0) {
+ extsst.addString(outputFile.getPos(), relativePosition);
+ }
+ stringIndex++;
+ }
+ outputFile.write(sst);
+
+ if (charsLeft != 0 || i.hasNext()) {
+ // Add the remainder of the string to the continue record
+ SSTContinueRecord cont = createContinueRecord(curString,
+ charsLeft,
+ outputFile);
+
+ // Carry on looping through the array until all the strings are done
+ while (i.hasNext()) {
+ curString = (String) i.next();
+ int relativePosition = cont.getOffset() + 4;
+ charsLeft = cont.add(curString);
+ if ((stringIndex % bucketSize) == 0) {
+ extsst.addString(outputFile.getPos(), relativePosition);
+ }
+ stringIndex++;
+
+ if (charsLeft != 0) {
+ outputFile.write(cont);
+ cont = createContinueRecord(curString, charsLeft, outputFile);
+ }
+ }
+
+ outputFile.write(cont);
+ }
+
+ outputFile.write(extsst);
+ }
+
+ /**
+ * Creates and returns a continue record using the left over bits and
+ * pieces
+ */
+ private SSTContinueRecord createContinueRecord
+ (String curString, int charsLeft, File outputFile) throws IOException {
+ // Set up the remainder of the string in the continue record
+ SSTContinueRecord cont = null;
+ while (charsLeft != 0) {
+ cont = new SSTContinueRecord();
+
+ if (charsLeft == curString.length() || curString.length() == 0) {
+ charsLeft = cont.setFirstString(curString, true);
+ } else {
+ charsLeft = cont.setFirstString
+ (curString.substring(curString.length() - charsLeft), false);
+ }
+
+ if (charsLeft != 0) {
+ outputFile.write(cont);
+ cont = new SSTContinueRecord();
+ }
+ }
+
+ return cont;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java b/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java
new file mode 100755
index 0000000..3419945
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SheetCopier.java
@@ -0,0 +1,965 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.TreeSet;
+import jxl.BooleanCell;
+import jxl.Cell;
+import jxl.CellType;
+import jxl.DateCell;
+import jxl.Hyperlink;
+import jxl.LabelCell;
+import jxl.NumberCell;
+import jxl.Range;
+import jxl.Sheet;
+import jxl.WorkbookSettings;
+import jxl.biff.AutoFilter;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.ConditionalFormat;
+import jxl.biff.DataValidation;
+import jxl.biff.FormattingRecords;
+import jxl.biff.FormulaData;
+import jxl.biff.NumFormatRecordsException;
+import jxl.biff.SheetRangeImpl;
+import jxl.biff.XFRecord;
+import jxl.biff.drawing.Chart;
+import jxl.biff.drawing.ComboBox;
+import jxl.biff.drawing.DrawingGroupObject;
+import jxl.biff.formula.FormulaException;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.format.CellFormat;
+import jxl.read.biff.NameRecord;
+import jxl.read.biff.SheetImpl;
+import jxl.read.biff.WorkbookParser;
+import jxl.write.Blank;
+import jxl.write.Boolean;
+import jxl.write.DateTime;
+import jxl.write.Formula;
+import jxl.write.Label;
+import jxl.write.Number;
+import jxl.write.WritableCell;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableHyperlink;
+import jxl.write.WritableImage;
+import jxl.write.WritableSheet;
+import jxl.write.WritableWorkbook;
+import jxl.write.WriteException;
+
+/**
+ * A transient utility object used to copy sheets. This
+ * functionality has been farmed out to a different class
+ * in order to reduce the bloat of the WritableSheetImpl
+ */
+class SheetCopier {
+ private static final Logger logger = Logger.getLogger(SheetCopier.class);
+
+ private final SheetImpl fromSheet;
+ private final WritableSheetImpl toSheet;
+ private final WorkbookSettings workbookSettings;
+
+ // Objects used by the sheet
+ private TreeSet columnFormats;
+ private FormattingRecords formatRecords;
+ private ArrayList hyperlinks;
+ private MergedCells mergedCells;
+ private ArrayList rowBreaks;
+ private ArrayList columnBreaks;
+ private SheetWriter sheetWriter;
+ private ArrayList drawings;
+ private ArrayList images;
+ private ArrayList conditionalFormats;
+ private ArrayList validatedCells;
+ private AutoFilter autoFilter;
+ private DataValidation dataValidation;
+ private ComboBox comboBox;
+ private PLSRecord plsRecord;
+ private boolean chartOnly;
+ private ButtonPropertySetRecord buttonPropertySet;
+ private int numRows;
+ private int maxRowOutlineLevel;
+ private int maxColumnOutlineLevel;
+
+ // Objects used to maintain state during the copy process
+ private HashMap xfRecords;
+ private HashMap fonts;
+ private HashMap formats;
+
+ public SheetCopier(Sheet f, WritableSheet t) {
+ fromSheet = (SheetImpl) f;
+ toSheet = (WritableSheetImpl) t;
+ workbookSettings = toSheet.getWorkbook().getSettings();
+ chartOnly = false;
+ }
+
+ void setColumnFormats(TreeSet cf) {
+ columnFormats = cf;
+ }
+
+ void setFormatRecords(FormattingRecords fr) {
+ formatRecords = fr;
+ }
+
+ void setHyperlinks(ArrayList h) {
+ hyperlinks = h;
+ }
+
+ void setMergedCells(MergedCells mc) {
+ mergedCells = mc;
+ }
+
+ void setRowBreaks(ArrayList rb) {
+ rowBreaks = rb;
+ }
+
+ void setColumnBreaks(ArrayList cb) {
+ columnBreaks = cb;
+ }
+
+ void setSheetWriter(SheetWriter sw) {
+ sheetWriter = sw;
+ }
+
+ void setDrawings(ArrayList d) {
+ drawings = d;
+ }
+
+ void setImages(ArrayList i) {
+ images = i;
+ }
+
+ void setConditionalFormats(ArrayList cf) {
+ conditionalFormats = cf;
+ }
+
+ void setValidatedCells(ArrayList vc) {
+ validatedCells = vc;
+ }
+
+ AutoFilter getAutoFilter() {
+ return autoFilter;
+ }
+
+ DataValidation getDataValidation() {
+ return dataValidation;
+ }
+
+ ComboBox getComboBox() {
+ return comboBox;
+ }
+
+ PLSRecord getPLSRecord() {
+ return plsRecord;
+ }
+
+ boolean isChartOnly() {
+ return chartOnly;
+ }
+
+ ButtonPropertySetRecord getButtonPropertySet() {
+ return buttonPropertySet;
+ }
+
+ /**
+ * Copies a sheet from a read-only version to the writable version.
+ * Performs shallow copies
+ */
+ public void copySheet() {
+ shallowCopyCells();
+
+ // Copy the column info records
+ jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos();
+
+ for (int i = 0; i < readCirs.length; i++) {
+ jxl.read.biff.ColumnInfoRecord rcir = readCirs[i];
+ for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn(); j++) {
+ ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j,
+ formatRecords);
+ cir.setHidden(rcir.getHidden());
+ columnFormats.add(cir);
+ }
+ }
+
+ // Copy the hyperlinks
+ Hyperlink[] hls = fromSheet.getHyperlinks();
+ for (int i = 0; i < hls.length; i++) {
+ WritableHyperlink hr = new WritableHyperlink
+ (hls[i], toSheet);
+ hyperlinks.add(hr);
+ }
+
+ // Copy the merged cells
+ Range[] merged = fromSheet.getMergedCells();
+
+ for (int i = 0; i < merged.length; i++) {
+ mergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i], toSheet));
+ }
+
+ // Copy the row properties
+ try {
+ jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties();
+
+ for (int i = 0; i < rowprops.length; i++) {
+ RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber());
+ XFRecord format = rowprops[i].hasDefaultFormat() ?
+ formatRecords.getXFRecord(rowprops[i].getXFIndex()) : null;
+ rr.setRowDetails(rowprops[i].getRowHeight(),
+ rowprops[i].matchesDefaultFontHeight(),
+ rowprops[i].isCollapsed(),
+ rowprops[i].getOutlineLevel(),
+ rowprops[i].getGroupStart(),
+ format);
+ numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1);
+ }
+ } catch (RowsExceededException e) {
+ // Handle the rows exceeded exception - this cannot occur since
+ // the sheet we are copying from will have a valid number of rows
+ Assert.verify(false);
+ }
+
+ // Copy the headers and footers
+ // sheetWriter.setHeader(new HeaderRecord(si.getHeader()));
+ // sheetWriter.setFooter(new FooterRecord(si.getFooter()));
+
+ // Copy the page breaks
+ int[] rowbreaks = fromSheet.getRowPageBreaks();
+
+ if (rowbreaks != null) {
+ for (int i = 0; i < rowbreaks.length; i++) {
+ rowBreaks.add(new Integer(rowbreaks[i]));
+ }
+ }
+
+ int[] columnbreaks = fromSheet.getColumnPageBreaks();
+
+ if (columnbreaks != null) {
+ for (int i = 0; i < columnbreaks.length; i++) {
+ columnBreaks.add(new Integer(columnbreaks[i]));
+ }
+ }
+
+ // Copy the charts
+ sheetWriter.setCharts(fromSheet.getCharts());
+
+ // Copy the drawings
+ DrawingGroupObject[] dr = fromSheet.getDrawings();
+ for (int i = 0; i < dr.length; i++) {
+ if (dr[i] instanceof jxl.biff.drawing.Drawing) {
+ WritableImage wi = new WritableImage
+ (dr[i], toSheet.getWorkbook().getDrawingGroup());
+ drawings.add(wi);
+ images.add(wi);
+ } else if (dr[i] instanceof jxl.biff.drawing.Comment) {
+ jxl.biff.drawing.Comment c =
+ new jxl.biff.drawing.Comment(dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(c);
+
+ // Set up the reference on the cell value
+ CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(),
+ c.getRow());
+ Assert.verify(cv.getCellFeatures() != null);
+ cv.getWritableCellFeatures().setCommentDrawing(c);
+ } else if (dr[i] instanceof jxl.biff.drawing.Button) {
+ jxl.biff.drawing.Button b =
+ new jxl.biff.drawing.Button
+ (dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(b);
+ } else if (dr[i] instanceof jxl.biff.drawing.ComboBox) {
+ jxl.biff.drawing.ComboBox cb =
+ new jxl.biff.drawing.ComboBox
+ (dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(cb);
+ } else if (dr[i] instanceof jxl.biff.drawing.CheckBox) {
+ jxl.biff.drawing.CheckBox cb =
+ new jxl.biff.drawing.CheckBox
+ (dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(cb);
+ }
+
+ }
+
+ // Copy the data validations
+ DataValidation rdv = fromSheet.getDataValidation();
+ if (rdv != null) {
+ dataValidation = new DataValidation(rdv,
+ toSheet.getWorkbook(),
+ toSheet.getWorkbook(),
+ workbookSettings);
+ int objid = dataValidation.getComboBoxObjectId();
+
+ if (objid != 0) {
+ comboBox = (ComboBox) drawings.get(objid);
+ }
+ }
+
+ // Copy the conditional formats
+ ConditionalFormat[] cf = fromSheet.getConditionalFormats();
+ if (cf.length > 0) {
+ for (int i = 0; i < cf.length; i++) {
+ conditionalFormats.add(cf[i]);
+ }
+ }
+
+ // Get the autofilter
+ autoFilter = fromSheet.getAutoFilter();
+
+ // Copy the workspace options
+ sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions());
+
+ // Set a flag to indicate if it contains a chart only
+ if (fromSheet.getSheetBof().isChart()) {
+ chartOnly = true;
+ sheetWriter.setChartOnly();
+ }
+
+ // Copy the environment specific print record
+ if (fromSheet.getPLS() != null) {
+ if (fromSheet.getWorkbookBof().isBiff7()) {
+ logger.warn("Cannot copy Biff7 print settings record - ignoring");
+ } else {
+ plsRecord = new PLSRecord(fromSheet.getPLS());
+ }
+ }
+
+ // Copy the button property set
+ if (fromSheet.getButtonPropertySet() != null) {
+ buttonPropertySet = new ButtonPropertySetRecord
+ (fromSheet.getButtonPropertySet());
+ }
+
+ // Copy the outline levels
+ maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel();
+ maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel();
+ }
+
+ /**
+ * Copies a sheet from a read-only version to the writable version.
+ * Performs shallow copies
+ */
+ public void copyWritableSheet() {
+ shallowCopyCells();
+
+ /*
+ // Copy the column formats
+ Iterator cfit = fromWritableSheet.columnFormats.iterator();
+ while (cfit.hasNext())
+ {
+ ColumnInfoRecord cv = new ColumnInfoRecord
+ ((ColumnInfoRecord) cfit.next());
+ columnFormats.add(cv);
+ }
+
+ // Copy the merged cells
+ Range[] merged = fromWritableSheet.getMergedCells();
+
+ for (int i = 0; i < merged.length; i++)
+ {
+ mergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i], this));
+ }
+
+ // Copy the row properties
+ try
+ {
+ RowRecord[] copyRows = fromWritableSheet.rows;
+ RowRecord row = null;
+ for (int i = 0; i < copyRows.length ; i++)
+ {
+ row = copyRows[i];
+
+ if (row != null &&
+ (!row.isDefaultHeight() ||
+ row.isCollapsed()))
+ {
+ RowRecord rr = getRowRecord(i);
+ rr.setRowDetails(row.getRowHeight(),
+ row.matchesDefaultFontHeight(),
+ row.isCollapsed(),
+ row.getStyle());
+ }
+ }
+ }
+ catch (RowsExceededException e)
+ {
+ // Handle the rows exceeded exception - this cannot occur since
+ // the sheet we are copying from will have a valid number of rows
+ Assert.verify(false);
+ }
+
+ // Copy the horizontal page breaks
+ rowBreaks = new ArrayList(fromWritableSheet.rowBreaks);
+
+ // Copy the vertical page breaks
+ columnBreaks = new ArrayList(fromWritableSheet.columnBreaks);
+
+ // Copy the data validations
+ DataValidation rdv = fromWritableSheet.dataValidation;
+ if (rdv != null)
+ {
+ dataValidation = new DataValidation(rdv,
+ workbook,
+ workbook,
+ workbookSettings);
+ }
+
+ // Copy the charts
+ sheetWriter.setCharts(fromWritableSheet.getCharts());
+
+ // Copy the drawings
+ DrawingGroupObject[] dr = si.getDrawings();
+ for (int i = 0 ; i < dr.length ; i++)
+ {
+ if (dr[i] instanceof jxl.biff.drawing.Drawing)
+ {
+ WritableImage wi = new WritableImage(dr[i],
+ workbook.getDrawingGroup());
+ drawings.add(wi);
+ images.add(wi);
+ }
+
+ // Not necessary to copy the comments, as they will be handled by
+ // the deep copy of the individual cells
+ }
+
+ // Copy the workspace options
+ sheetWriter.setWorkspaceOptions(fromWritableSheet.getWorkspaceOptions());
+
+ // Copy the environment specific print record
+ if (fromWritableSheet.plsRecord != null)
+ {
+ plsRecord = new PLSRecord(fromWritableSheet.plsRecord);
+ }
+
+ // Copy the button property set
+ if (fromWritableSheet.buttonPropertySet != null)
+ {
+ buttonPropertySet = new ButtonPropertySetRecord
+ (fromWritableSheet.buttonPropertySet);
+ }
+ */
+ }
+
+ /**
+ * Imports a sheet from a different workbook, doing a deep copy
+ */
+ public void importSheet() {
+ xfRecords = new HashMap();
+ fonts = new HashMap();
+ formats = new HashMap();
+
+ deepCopyCells();
+
+ // Copy the column info records
+ jxl.read.biff.ColumnInfoRecord[] readCirs = fromSheet.getColumnInfos();
+
+ for (int i = 0; i < readCirs.length; i++) {
+ jxl.read.biff.ColumnInfoRecord rcir = readCirs[i];
+ for (int j = rcir.getStartColumn(); j <= rcir.getEndColumn(); j++) {
+ ColumnInfoRecord cir = new ColumnInfoRecord(rcir, j);
+ int xfIndex = cir.getXfIndex();
+ XFRecord cf = (WritableCellFormat) xfRecords.get(new Integer(xfIndex));
+
+ if (cf == null) {
+ CellFormat readFormat = fromSheet.getColumnView(j).getFormat();
+ WritableCellFormat wcf = copyCellFormat(readFormat);
+ }
+
+ cir.setCellFormat(cf);
+ cir.setHidden(rcir.getHidden());
+ columnFormats.add(cir);
+ }
+ }
+
+ // Copy the hyperlinks
+ Hyperlink[] hls = fromSheet.getHyperlinks();
+ for (int i = 0; i < hls.length; i++) {
+ WritableHyperlink hr = new WritableHyperlink
+ (hls[i], toSheet);
+ hyperlinks.add(hr);
+ }
+
+ // Copy the merged cells
+ Range[] merged = fromSheet.getMergedCells();
+
+ for (int i = 0; i < merged.length; i++) {
+ mergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i], toSheet));
+ }
+
+ // Copy the row properties
+ try {
+ jxl.read.biff.RowRecord[] rowprops = fromSheet.getRowProperties();
+
+ for (int i = 0; i < rowprops.length; i++) {
+ RowRecord rr = toSheet.getRowRecord(rowprops[i].getRowNumber());
+ XFRecord format = null;
+ jxl.read.biff.RowRecord rowrec = rowprops[i];
+ if (rowrec.hasDefaultFormat()) {
+ format = (WritableCellFormat)
+ xfRecords.get(new Integer(rowrec.getXFIndex()));
+
+ if (format == null) {
+ int rownum = rowrec.getRowNumber();
+ CellFormat readFormat = fromSheet.getRowView(rownum).getFormat();
+ WritableCellFormat wcf = copyCellFormat(readFormat);
+ }
+ }
+
+ rr.setRowDetails(rowrec.getRowHeight(),
+ rowrec.matchesDefaultFontHeight(),
+ rowrec.isCollapsed(),
+ rowrec.getOutlineLevel(),
+ rowrec.getGroupStart(),
+ format);
+ numRows = Math.max(numRows, rowprops[i].getRowNumber() + 1);
+ }
+ } catch (RowsExceededException e) {
+ // Handle the rows exceeded exception - this cannot occur since
+ // the sheet we are copying from will have a valid number of rows
+ Assert.verify(false);
+ }
+
+ // Copy the headers and footers
+ // sheetWriter.setHeader(new HeaderRecord(si.getHeader()));
+ // sheetWriter.setFooter(new FooterRecord(si.getFooter()));
+
+ // Copy the page breaks
+ int[] rowbreaks = fromSheet.getRowPageBreaks();
+
+ if (rowbreaks != null) {
+ for (int i = 0; i < rowbreaks.length; i++) {
+ rowBreaks.add(new Integer(rowbreaks[i]));
+ }
+ }
+
+ int[] columnbreaks = fromSheet.getColumnPageBreaks();
+
+ if (columnbreaks != null) {
+ for (int i = 0; i < columnbreaks.length; i++) {
+ columnBreaks.add(new Integer(columnbreaks[i]));
+ }
+ }
+
+ // Copy the charts
+ Chart[] fromCharts = fromSheet.getCharts();
+ if (fromCharts != null && fromCharts.length > 0) {
+ logger.warn("Importing of charts is not supported");
+ /*
+ sheetWriter.setCharts(fromSheet.getCharts());
+ IndexMapping xfMapping = new IndexMapping(200);
+ for (Iterator i = xfRecords.keySet().iterator(); i.hasNext();)
+ {
+ Integer key = (Integer) i.next();
+ XFRecord xfmapping = (XFRecord) xfRecords.get(key);
+ xfMapping.setMapping(key.intValue(), xfmapping.getXFIndex());
+ }
+
+ IndexMapping fontMapping = new IndexMapping(200);
+ for (Iterator i = fonts.keySet().iterator(); i.hasNext();)
+ {
+ Integer key = (Integer) i.next();
+ Integer fontmap = (Integer) fonts.get(key);
+ fontMapping.setMapping(key.intValue(), fontmap.intValue());
+ }
+
+ IndexMapping formatMapping = new IndexMapping(200);
+ for (Iterator i = formats.keySet().iterator(); i.hasNext();)
+ {
+ Integer key = (Integer) i.next();
+ Integer formatmap = (Integer) formats.get(key);
+ formatMapping.setMapping(key.intValue(), formatmap.intValue());
+ }
+
+ // Now reuse the rationalization feature on each chart to
+ // handle the new fonts
+ for (int i = 0; i < fromCharts.length ; i++)
+ {
+ fromCharts[i].rationalize(xfMapping, fontMapping, formatMapping);
+ }
+ */
+ }
+
+ // Copy the drawings
+ DrawingGroupObject[] dr = fromSheet.getDrawings();
+
+ // Make sure the destination workbook has a drawing group
+ // created in it
+ if (dr.length > 0 &&
+ toSheet.getWorkbook().getDrawingGroup() == null) {
+ toSheet.getWorkbook().createDrawingGroup();
+ }
+
+ for (int i = 0; i < dr.length; i++) {
+ if (dr[i] instanceof jxl.biff.drawing.Drawing) {
+ WritableImage wi = new WritableImage
+ (dr[i].getX(), dr[i].getY(),
+ dr[i].getWidth(), dr[i].getHeight(),
+ dr[i].getImageData());
+ toSheet.getWorkbook().addDrawing(wi);
+ drawings.add(wi);
+ images.add(wi);
+ } else if (dr[i] instanceof jxl.biff.drawing.Comment) {
+ jxl.biff.drawing.Comment c =
+ new jxl.biff.drawing.Comment(dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(c);
+
+ // Set up the reference on the cell value
+ CellValue cv = (CellValue) toSheet.getWritableCell(c.getColumn(),
+ c.getRow());
+ Assert.verify(cv.getCellFeatures() != null);
+ cv.getWritableCellFeatures().setCommentDrawing(c);
+ } else if (dr[i] instanceof jxl.biff.drawing.Button) {
+ jxl.biff.drawing.Button b =
+ new jxl.biff.drawing.Button
+ (dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(b);
+ } else if (dr[i] instanceof jxl.biff.drawing.ComboBox) {
+ jxl.biff.drawing.ComboBox cb =
+ new jxl.biff.drawing.ComboBox
+ (dr[i],
+ toSheet.getWorkbook().getDrawingGroup(),
+ workbookSettings);
+ drawings.add(cb);
+ }
+ }
+
+ // Copy the data validations
+ DataValidation rdv = fromSheet.getDataValidation();
+ if (rdv != null) {
+ dataValidation = new DataValidation(rdv,
+ toSheet.getWorkbook(),
+ toSheet.getWorkbook(),
+ workbookSettings);
+ int objid = dataValidation.getComboBoxObjectId();
+ if (objid != 0) {
+ comboBox = (ComboBox) drawings.get(objid);
+ }
+ }
+
+ // Copy the workspace options
+ sheetWriter.setWorkspaceOptions(fromSheet.getWorkspaceOptions());
+
+ // Set a flag to indicate if it contains a chart only
+ if (fromSheet.getSheetBof().isChart()) {
+ chartOnly = true;
+ sheetWriter.setChartOnly();
+ }
+
+ // Copy the environment specific print record
+ if (fromSheet.getPLS() != null) {
+ if (fromSheet.getWorkbookBof().isBiff7()) {
+ logger.warn("Cannot copy Biff7 print settings record - ignoring");
+ } else {
+ plsRecord = new PLSRecord(fromSheet.getPLS());
+ }
+ }
+
+ // Copy the button property set
+ if (fromSheet.getButtonPropertySet() != null) {
+ buttonPropertySet = new ButtonPropertySetRecord
+ (fromSheet.getButtonPropertySet());
+ }
+
+ importNames();
+
+ // Copy the outline levels
+ maxRowOutlineLevel = fromSheet.getMaxRowOutlineLevel();
+ maxColumnOutlineLevel = fromSheet.getMaxColumnOutlineLevel();
+ }
+
+ /**
+ * Performs a shallow copy of the specified cell
+ */
+ private WritableCell shallowCopyCell(Cell cell) {
+ CellType ct = cell.getType();
+ WritableCell newCell = null;
+
+ if (ct == CellType.LABEL) {
+ newCell = new Label((LabelCell) cell);
+ } else if (ct == CellType.NUMBER) {
+ newCell = new Number((NumberCell) cell);
+ } else if (ct == CellType.DATE) {
+ newCell = new DateTime((DateCell) cell);
+ } else if (ct == CellType.BOOLEAN) {
+ newCell = new Boolean((BooleanCell) cell);
+ } else if (ct == CellType.NUMBER_FORMULA) {
+ newCell = new ReadNumberFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.STRING_FORMULA) {
+ newCell = new ReadStringFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.BOOLEAN_FORMULA) {
+ newCell = new ReadBooleanFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.DATE_FORMULA) {
+ newCell = new ReadDateFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.FORMULA_ERROR) {
+ newCell = new ReadErrorFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.EMPTY) {
+ if (cell.getCellFormat() != null) {
+ // It is a blank cell, rather than an empty cell, so
+ // it may have formatting information, so
+ // it must be copied
+ newCell = new Blank(cell);
+ }
+ }
+
+ return newCell;
+ }
+
+ /**
+ * Performs a deep copy of the specified cell, handling the cell format
+ *
+ * @param cell the cell to copy
+ */
+ private WritableCell deepCopyCell(Cell cell) {
+ WritableCell c = shallowCopyCell(cell);
+
+ if (c == null) {
+ return c;
+ }
+
+ if (c instanceof ReadFormulaRecord) {
+ ReadFormulaRecord rfr = (ReadFormulaRecord) c;
+ boolean crossSheetReference = !rfr.handleImportedCellReferences
+ (fromSheet.getWorkbook(),
+ fromSheet.getWorkbook(),
+ workbookSettings);
+
+ if (crossSheetReference) {
+ try {
+ logger.warn("Formula " + rfr.getFormula() +
+ " in cell " +
+ CellReferenceHelper.getCellReference(cell.getColumn(),
+ cell.getRow()) +
+ " cannot be imported because it references another " +
+ " sheet from the source workbook");
+ } catch (FormulaException e) {
+ logger.warn("Formula in cell " +
+ CellReferenceHelper.getCellReference(cell.getColumn(),
+ cell.getRow()) +
+ " cannot be imported: " + e.getMessage());
+ }
+
+ // Create a new error formula and add it instead
+ c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\"");
+ }
+ }
+
+ // Copy the cell format
+ CellFormat cf = c.getCellFormat();
+ int index = ((XFRecord) cf).getXFIndex();
+ WritableCellFormat wcf = (WritableCellFormat)
+ xfRecords.get(new Integer(index));
+
+ if (wcf == null) {
+ wcf = copyCellFormat(cf);
+ }
+
+ c.setCellFormat(wcf);
+
+ return c;
+ }
+
+ /**
+ * Perform a shallow copy of the cells from the specified sheet into this one
+ */
+ void shallowCopyCells() {
+ // Copy the cells
+ int cells = fromSheet.getRows();
+ Cell[] row = null;
+ Cell cell = null;
+ for (int i = 0; i < cells; i++) {
+ row = fromSheet.getRow(i);
+
+ for (int j = 0; j < row.length; j++) {
+ cell = row[j];
+ WritableCell c = shallowCopyCell(cell);
+
+ // Encase the calls to addCell in a try-catch block
+ // These should not generate any errors, because we are
+ // copying from an existing spreadsheet. In the event of
+ // errors, catch the exception and then bomb out with an
+ // assertion
+ try {
+ if (c != null) {
+ toSheet.addCell(c);
+
+ // Cell.setCellFeatures short circuits when the cell is copied,
+ // so make sure the copy logic handles the validated cells
+ if (c.getCellFeatures() != null &&
+ c.getCellFeatures().hasDataValidation()) {
+ validatedCells.add(c);
+ }
+ }
+ } catch (WriteException e) {
+ Assert.verify(false);
+ }
+ }
+ }
+ numRows = toSheet.getRows();
+ }
+
+ /**
+ * Perform a deep copy of the cells from the specified sheet into this one
+ */
+ void deepCopyCells() {
+ // Copy the cells
+ int cells = fromSheet.getRows();
+ Cell[] row = null;
+ Cell cell = null;
+ for (int i = 0; i < cells; i++) {
+ row = fromSheet.getRow(i);
+
+ for (int j = 0; j < row.length; j++) {
+ cell = row[j];
+ WritableCell c = deepCopyCell(cell);
+
+ // Encase the calls to addCell in a try-catch block
+ // These should not generate any errors, because we are
+ // copying from an existing spreadsheet. In the event of
+ // errors, catch the exception and then bomb out with an
+ // assertion
+ try {
+ if (c != null) {
+ toSheet.addCell(c);
+
+ // Cell.setCellFeatures short circuits when the cell is copied,
+ // so make sure the copy logic handles the validated cells
+ if (c.getCellFeatures() != null &
+ c.getCellFeatures().hasDataValidation()) {
+ validatedCells.add(c);
+ }
+ }
+ } catch (WriteException e) {
+ Assert.verify(false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an initialized copy of the cell format
+ *
+ * @param cf the cell format to copy
+ * @return a deep copy of the cell format
+ */
+ private WritableCellFormat copyCellFormat(CellFormat cf) {
+ try {
+ // just do a deep copy of the cell format for now. This will create
+ // a copy of the format and font also - in the future this may
+ // need to be sorted out
+ XFRecord xfr = (XFRecord) cf;
+ WritableCellFormat f = new WritableCellFormat(xfr);
+ formatRecords.addStyle(f);
+
+ // Maintain the local list of formats
+ int xfIndex = xfr.getXFIndex();
+ xfRecords.put(new Integer(xfIndex), f);
+
+ int fontIndex = xfr.getFontIndex();
+ fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex()));
+
+ int formatIndex = xfr.getFormatRecord();
+ formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord()));
+
+ return f;
+ } catch (NumFormatRecordsException e) {
+ logger.warn("Maximum number of format records exceeded. Using " +
+ "default format.");
+
+ return WritableWorkbook.NORMAL_STYLE;
+ }
+ }
+
+ /**
+ * Imports any names defined on the source sheet to the destination workbook
+ */
+ private void importNames() {
+ WorkbookParser fromWorkbook = fromSheet.getWorkbook();
+ WritableWorkbook toWorkbook = toSheet.getWorkbook();
+ int fromSheetIndex = fromWorkbook.getIndex(fromSheet);
+ NameRecord[] nameRecords = fromWorkbook.getNameRecords();
+ String[] names = toWorkbook.getRangeNames();
+
+ for (int i = 0; i < nameRecords.length; i++) {
+ NameRecord.NameRange[] nameRanges = nameRecords[i].getRanges();
+
+ for (int j = 0; j < nameRanges.length; j++) {
+ int nameSheetIndex = fromWorkbook.getExternalSheetIndex
+ (nameRanges[j].getExternalSheet());
+
+ if (fromSheetIndex == nameSheetIndex) {
+ String name = nameRecords[i].getName();
+ if (Arrays.binarySearch(names, name) < 0) {
+ toWorkbook.addNameArea(name,
+ toSheet,
+ nameRanges[j].getFirstColumn(),
+ nameRanges[j].getFirstRow(),
+ nameRanges[j].getLastColumn(),
+ nameRanges[j].getLastRow());
+ } else {
+ logger.warn("Named range " + name +
+ " is already present in the destination workbook");
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the number of rows - allows for the case where formatting has
+ * been applied to rows, even though the row has no data
+ *
+ * @return the number of rows
+ */
+ int getRows() {
+ return numRows;
+ }
+
+ /**
+ * Accessor for the maximum column outline level
+ *
+ * @return the maximum column outline level, or 0 if no outlines/groups
+ */
+ public int getMaxColumnOutlineLevel() {
+ return maxColumnOutlineLevel;
+ }
+
+ /**
+ * Accessor for the maximum row outline level
+ *
+ * @return the maximum row outline level, or 0 if no outlines/groups
+ */
+ public int getMaxRowOutlineLevel() {
+ return maxRowOutlineLevel;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java b/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java
new file mode 100755
index 0000000..062f326
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SheetWriter.java
@@ -0,0 +1,1045 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeSet;
+import jxl.Cell;
+import jxl.CellFeatures;
+import jxl.Range;
+import jxl.SheetSettings;
+import jxl.WorkbookSettings;
+import jxl.biff.AutoFilter;
+import jxl.biff.ConditionalFormat;
+import jxl.biff.DataValidation;
+import jxl.biff.DataValiditySettingsRecord;
+import jxl.biff.WorkspaceInformationRecord;
+import jxl.biff.XFRecord;
+import jxl.biff.drawing.Chart;
+import jxl.biff.drawing.SheetDrawingWriter;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.format.Border;
+import jxl.format.BorderLineStyle;
+import jxl.format.Colour;
+import jxl.write.Blank;
+import jxl.write.WritableCell;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableHyperlink;
+import jxl.write.WriteException;
+
+/**
+ * Contains the functionality necessary for writing out a sheet. Originally
+ * this was incorporated in WritableSheetImpl, but was moved out into
+ * a dedicated class in order to reduce the over bloated nature of that
+ * class
+ */
+final class SheetWriter {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(SheetWriter.class);
+
+ /**
+ * A handle to the output file which the binary data is written to
+ */
+ private final File outputFile;
+
+ /**
+ * The rows within this sheet
+ */
+ private RowRecord[] rows;
+
+ /**
+ * A number of rows. This is a count of the maximum row number + 1
+ */
+ private int numRows;
+
+ /**
+ * The number of columns. This is a count of the maximum column number + 1
+ */
+ private int numCols;
+
+ /**
+ * The page header
+ */
+ private HeaderRecord header;
+ /**
+ * The page footer
+ */
+ private FooterRecord footer;
+ /**
+ * The settings for the sheet
+ */
+ private SheetSettings settings;
+ /**
+ * The settings for the workbook
+ */
+ private final WorkbookSettings workbookSettings;
+ /**
+ * Array of row page breaks
+ */
+ private ArrayList rowBreaks;
+ /**
+ * Array of column page breaks
+ */
+ private ArrayList columnBreaks;
+ /**
+ * Array of hyperlinks
+ */
+ private ArrayList hyperlinks;
+ /**
+ * Array of conditional formats
+ */
+ private ArrayList conditionalFormats;
+ /**
+ * The autofilter info
+ */
+ private AutoFilter autoFilter;
+ /**
+ * Array of validated cells
+ */
+ private ArrayList validatedCells;
+ /**
+ * The data validation validations
+ */
+ private DataValidation dataValidation;
+
+ /**
+ * The list of merged ranges
+ */
+ private MergedCells mergedCells;
+
+ /**
+ * The environment specific print record
+ */
+ private PLSRecord plsRecord;
+
+ /**
+ * The button property ste
+ */
+ private ButtonPropertySetRecord buttonPropertySet;
+
+ /**
+ * The workspace options
+ */
+ private WorkspaceInformationRecord workspaceOptions;
+ /**
+ * The column format overrides
+ */
+ private TreeSet columnFormats;
+
+ /**
+ * The list of drawings
+ */
+ private final SheetDrawingWriter drawingWriter;
+
+ /**
+ * Flag indicates that this sheet contains just a chart, and nothing
+ * else
+ */
+ private boolean chartOnly;
+
+ /**
+ * The maximum row outline level
+ */
+ private int maxRowOutlineLevel;
+
+ /**
+ * The maximum column outline level
+ */
+ private int maxColumnOutlineLevel;
+
+ /**
+ * A handle back to the writable sheet, in order for this class
+ * to invoke the get accessor methods
+ */
+ private final WritableSheetImpl sheet;
+
+
+ /**
+ * Creates a new SheetWriter
instance.
+ *
+ * @param of the output file
+ */
+ public SheetWriter(File of,
+ WritableSheetImpl wsi,
+ WorkbookSettings ws) {
+ outputFile = of;
+ sheet = wsi;
+ workspaceOptions = new WorkspaceInformationRecord();
+ workbookSettings = ws;
+ chartOnly = false;
+ drawingWriter = new SheetDrawingWriter(ws);
+ }
+
+ /**
+ * Writes out this sheet. First writes out the standard sheet
+ * information then writes out each row in turn.
+ * Once all the rows have been written out, it retrospectively adjusts
+ * the offset references in the file
+ *
+ * @throws IOException
+ */
+ public void write() throws IOException {
+ Assert.verify(rows != null);
+
+ // This worksheet consists of just one chart, so write it and return
+ if (chartOnly) {
+ drawingWriter.write(outputFile);
+ return;
+ }
+
+ BOFRecord bof = new BOFRecord(BOFRecord.sheet);
+ outputFile.write(bof);
+
+ // Compute the number of blocks of 32 rows that will be needed
+ int numBlocks = numRows / 32;
+ if (numRows - numBlocks * 32 != 0) {
+ numBlocks++;
+ }
+
+ int indexPos = outputFile.getPos();
+
+ // Write the index record out now in order to serve as a place holder
+ // The bof passed in is the bof of the workbook, not this sheet
+ IndexRecord indexRecord = new IndexRecord(0, numRows, numBlocks);
+ outputFile.write(indexRecord);
+
+ if (settings.getAutomaticFormulaCalculation()) {
+ CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.automatic);
+ outputFile.write(cmr);
+ } else {
+ CalcModeRecord cmr = new CalcModeRecord(CalcModeRecord.manual);
+ outputFile.write(cmr);
+ }
+
+ CalcCountRecord ccr = new CalcCountRecord(0x64);
+ outputFile.write(ccr);
+
+ RefModeRecord rmr = new RefModeRecord();
+ outputFile.write(rmr);
+
+ IterationRecord itr = new IterationRecord(false);
+ outputFile.write(itr);
+
+ DeltaRecord dtr = new DeltaRecord(0.001);
+ outputFile.write(dtr);
+
+ SaveRecalcRecord srr = new SaveRecalcRecord
+ (settings.getRecalculateFormulasBeforeSave());
+ outputFile.write(srr);
+
+ PrintHeadersRecord phr = new PrintHeadersRecord
+ (settings.getPrintHeaders());
+ outputFile.write(phr);
+
+ PrintGridLinesRecord pglr = new PrintGridLinesRecord
+ (settings.getPrintGridLines());
+ outputFile.write(pglr);
+
+ GridSetRecord gsr = new GridSetRecord(true);
+ outputFile.write(gsr);
+
+ GuttersRecord gutr = new GuttersRecord();
+ gutr.setMaxColumnOutline(maxColumnOutlineLevel + 1);
+ gutr.setMaxRowOutline(maxRowOutlineLevel + 1);
+
+ outputFile.write(gutr);
+
+ DefaultRowHeightRecord drhr = new DefaultRowHeightRecord
+ (settings.getDefaultRowHeight(),
+ settings.getDefaultRowHeight() !=
+ SheetSettings.DEFAULT_DEFAULT_ROW_HEIGHT);
+ outputFile.write(drhr);
+
+ if (maxRowOutlineLevel > 0) {
+ workspaceOptions.setRowOutlines(true);
+ }
+
+ if (maxColumnOutlineLevel > 0) {
+ workspaceOptions.setColumnOutlines(true);
+ }
+
+ workspaceOptions.setFitToPages(settings.getFitToPages());
+ outputFile.write(workspaceOptions);
+
+ if (rowBreaks.size() > 0) {
+ int[] rb = new int[rowBreaks.size()];
+
+ for (int i = 0; i < rb.length; i++) {
+ rb[i] = ((Integer) rowBreaks.get(i)).intValue();
+ }
+
+ HorizontalPageBreaksRecord hpbr = new HorizontalPageBreaksRecord(rb);
+ outputFile.write(hpbr);
+ }
+
+ if (columnBreaks.size() > 0) {
+ int[] rb = new int[columnBreaks.size()];
+
+ for (int i = 0; i < rb.length; i++) {
+ rb[i] = ((Integer) columnBreaks.get(i)).intValue();
+ }
+
+ VerticalPageBreaksRecord hpbr = new VerticalPageBreaksRecord(rb);
+ outputFile.write(hpbr);
+ }
+
+ HeaderRecord header = new HeaderRecord(settings.getHeader().toString());
+ outputFile.write(header);
+
+ FooterRecord footer = new FooterRecord(settings.getFooter().toString());
+ outputFile.write(footer);
+
+ HorizontalCentreRecord hcr = new HorizontalCentreRecord
+ (settings.isHorizontalCentre());
+ outputFile.write(hcr);
+
+ VerticalCentreRecord vcr = new VerticalCentreRecord
+ (settings.isVerticalCentre());
+ outputFile.write(vcr);
+
+ // Write out the margins if they don't equal the default
+ if (settings.getLeftMargin() != settings.getDefaultWidthMargin()) {
+ MarginRecord mr = new LeftMarginRecord(settings.getLeftMargin());
+ outputFile.write(mr);
+ }
+
+ if (settings.getRightMargin() != settings.getDefaultWidthMargin()) {
+ MarginRecord mr = new RightMarginRecord(settings.getRightMargin());
+ outputFile.write(mr);
+ }
+
+ if (settings.getTopMargin() != settings.getDefaultHeightMargin()) {
+ MarginRecord mr = new TopMarginRecord(settings.getTopMargin());
+ outputFile.write(mr);
+ }
+
+ if (settings.getBottomMargin() != settings.getDefaultHeightMargin()) {
+ MarginRecord mr = new BottomMarginRecord(settings.getBottomMargin());
+ outputFile.write(mr);
+ }
+
+ if (plsRecord != null) {
+ outputFile.write(plsRecord);
+ }
+
+ SetupRecord setup = new SetupRecord(settings);
+ outputFile.write(setup);
+
+ if (settings.isProtected()) {
+ ProtectRecord pr = new ProtectRecord(settings.isProtected());
+ outputFile.write(pr);
+
+ ScenarioProtectRecord spr = new ScenarioProtectRecord
+ (settings.isProtected());
+ outputFile.write(spr);
+
+ ObjectProtectRecord opr = new ObjectProtectRecord
+ (settings.isProtected());
+ outputFile.write(opr);
+
+ if (settings.getPassword() != null) {
+ PasswordRecord pw = new PasswordRecord(settings.getPassword());
+ outputFile.write(pw);
+ } else if (settings.getPasswordHash() != 0) {
+ PasswordRecord pw = new PasswordRecord(settings.getPasswordHash());
+ outputFile.write(pw);
+ }
+ }
+
+ indexRecord.setDataStartPosition(outputFile.getPos());
+ DefaultColumnWidth dcw =
+ new DefaultColumnWidth(settings.getDefaultColumnWidth());
+ outputFile.write(dcw);
+
+ // Get a handle to the normal styles
+ WritableCellFormat normalStyle =
+ sheet.getWorkbook().getStyles().getNormalStyle();
+ WritableCellFormat defaultDateFormat =
+ sheet.getWorkbook().getStyles().getDefaultDateFormat();
+
+ // Write out all the column formats
+ ColumnInfoRecord cir = null;
+ for (Iterator colit = columnFormats.iterator(); colit.hasNext(); ) {
+ cir = (ColumnInfoRecord) colit.next();
+
+ // Writing out the column info with index 0x100 causes excel to crash
+ if (cir.getColumn() < 0x100) {
+ outputFile.write(cir);
+ }
+
+ XFRecord xfr = cir.getCellFormat();
+
+ if (xfr != normalStyle && cir.getColumn() < 0x100) {
+ // Make this the format for every cell in the column
+ Cell[] cells = getColumn(cir.getColumn());
+
+ for (int i = 0; i < cells.length; i++) {
+ if (cells[i] != null &&
+ (cells[i].getCellFormat() == normalStyle ||
+ cells[i].getCellFormat() == defaultDateFormat)) {
+ // The cell has no overriding format specified, so
+ // set it to the column default
+ ((WritableCell) cells[i]).setCellFormat(xfr);
+ }
+ }
+ }
+ }
+
+ // Write out the auto filter
+ if (autoFilter != null) {
+ autoFilter.write(outputFile);
+ }
+
+ DimensionRecord dr = new DimensionRecord(numRows, numCols);
+ outputFile.write(dr);
+
+ // Write out all the rows, in blocks of 32
+ for (int block = 0; block < numBlocks; block++) {
+ DBCellRecord dbcell = new DBCellRecord(outputFile.getPos());
+
+ int blockRows = Math.min(32, numRows - block * 32);
+ boolean firstRow = true;
+
+ // First write out all the row records
+ for (int i = block * 32; i < block * 32 + blockRows; i++) {
+ if (rows[i] != null) {
+ rows[i].write(outputFile);
+ if (firstRow) {
+ dbcell.setCellOffset(outputFile.getPos());
+ firstRow = false;
+ }
+ }
+ }
+
+ // Now write out all the cells
+ for (int i = block * 32; i < block * 32 + blockRows; i++) {
+ if (rows[i] != null) {
+ dbcell.addCellRowPosition(outputFile.getPos());
+ rows[i].writeCells(outputFile);
+ }
+ }
+
+ // Now set the current file position in the index record
+ indexRecord.addBlockPosition(outputFile.getPos());
+
+ // Set the position of the file pointer and write out the DBCell
+ // record
+ dbcell.setPosition(outputFile.getPos());
+ outputFile.write(dbcell);
+ }
+
+ // Do the drawings and charts if enabled
+ if (!workbookSettings.getDrawingsDisabled()) {
+ drawingWriter.write(outputFile);
+ }
+
+ Window2Record w2r = new Window2Record(settings);
+ outputFile.write(w2r);
+
+ // Handle the frozen panes
+ if (settings.getHorizontalFreeze() != 0 ||
+ settings.getVerticalFreeze() != 0) {
+ PaneRecord pr = new PaneRecord(settings.getHorizontalFreeze(),
+ settings.getVerticalFreeze());
+ outputFile.write(pr);
+
+ // Handle the selection record. First, there will always be a top left
+ SelectionRecord sr = new SelectionRecord
+ (SelectionRecord.upperLeft, 0, 0);
+ outputFile.write(sr);
+
+ // Top right
+ if (settings.getHorizontalFreeze() != 0) {
+ sr = new SelectionRecord
+ (SelectionRecord.upperRight, settings.getHorizontalFreeze(), 0);
+ outputFile.write(sr);
+ }
+
+ // Bottom left
+ if (settings.getVerticalFreeze() != 0) {
+ sr = new SelectionRecord
+ (SelectionRecord.lowerLeft, 0, settings.getVerticalFreeze());
+ outputFile.write(sr);
+ }
+
+ // Bottom right
+ if (settings.getHorizontalFreeze() != 0 &&
+ settings.getVerticalFreeze() != 0) {
+ sr = new SelectionRecord
+ (SelectionRecord.lowerRight,
+ settings.getHorizontalFreeze(),
+ settings.getVerticalFreeze());
+ outputFile.write(sr);
+ }
+
+ Weird1Record w1r = new Weird1Record();
+ outputFile.write(w1r);
+ } else {
+ // No frozen panes - just write out the selection record for the
+ // whole sheet
+ SelectionRecord sr = new SelectionRecord
+ (SelectionRecord.upperLeft, 0, 0);
+ outputFile.write(sr);
+ }
+
+ // Handle the zoom factor
+ if (settings.getZoomFactor() != 100) {
+ SCLRecord sclr = new SCLRecord(settings.getZoomFactor());
+ outputFile.write(sclr);
+ }
+
+ // Now write out all the merged cells
+ mergedCells.write(outputFile);
+
+ // Write out all the hyperlinks
+ Iterator hi = hyperlinks.iterator();
+ WritableHyperlink hlr = null;
+ while (hi.hasNext()) {
+ hlr = (WritableHyperlink) hi.next();
+ outputFile.write(hlr);
+ }
+
+ if (buttonPropertySet != null) {
+ outputFile.write(buttonPropertySet);
+ }
+
+ // Write out the data validations
+ if (dataValidation != null || validatedCells.size() > 0) {
+ writeDataValidation();
+ }
+
+ // Write out the conditional formats
+ if (conditionalFormats != null && conditionalFormats.size() > 0) {
+ for (Iterator i = conditionalFormats.iterator(); i.hasNext(); ) {
+ ConditionalFormat cf = (ConditionalFormat) i.next();
+ cf.write(outputFile);
+ }
+ }
+
+ EOFRecord eof = new EOFRecord();
+ outputFile.write(eof);
+
+ // Now the various cross reference offsets have been calculated,
+ // retrospectively set the values in the output file
+ outputFile.setData(indexRecord.getData(), indexPos + 4);
+ }
+
+ /**
+ * Gets the header. Called when copying sheets
+ *
+ * @return the page header
+ */
+ final HeaderRecord getHeader() {
+ return header;
+ }
+
+ /**
+ * Gets the footer. Called when copying sheets
+ *
+ * @return the page footer
+ */
+ final FooterRecord getFooter() {
+ return footer;
+ }
+
+ /**
+ * Sets the data necessary for writing out the sheet. This method must
+ * be called immediately prior to writing
+ *
+ * @param rws the rows in the spreadsheet
+ */
+ void setWriteData(RowRecord[] rws,
+ ArrayList rb,
+ ArrayList cb,
+ ArrayList hl,
+ MergedCells mc,
+ TreeSet cf,
+ int mrol,
+ int mcol) {
+ rows = rws;
+ rowBreaks = rb;
+ columnBreaks = cb;
+ hyperlinks = hl;
+ mergedCells = mc;
+ columnFormats = cf;
+ maxRowOutlineLevel = mrol;
+ maxColumnOutlineLevel = mcol;
+ }
+
+ /**
+ * Sets the dimensions of this spreadsheet. This method must be called
+ * immediately prior to writing
+ *
+ * @param rws the number of rows
+ * @param cls the number of columns
+ */
+ void setDimensions(int rws, int cls) {
+ numRows = rws;
+ numCols = cls;
+ }
+
+ /**
+ * Sets the sheet settings for this particular sheet. Must be
+ * called immediately prior to writing
+ *
+ * @param sr the sheet settings
+ */
+ void setSettings(SheetSettings sr) {
+ settings = sr;
+ }
+
+ /**
+ * Accessor for the workspace options
+ *
+ * @return the workspace options
+ */
+ WorkspaceInformationRecord getWorkspaceOptions() {
+ return workspaceOptions;
+ }
+
+ /**
+ * Accessor for the workspace options
+ *
+ * @param wo the workspace options
+ */
+ void setWorkspaceOptions(WorkspaceInformationRecord wo) {
+ if (wo != null) {
+ workspaceOptions = wo;
+ }
+ }
+
+ /**
+ * Sets the drawings on this sheet
+ *
+ * @param dr the list of drawings
+ * @param mod a modified flag
+ */
+ void setDrawings(ArrayList dr, boolean mod) {
+ drawingWriter.setDrawings(dr, mod);
+ }
+
+ /**
+ * Accessor for the charts on this sheet
+ *
+ * @return the charts
+ */
+ Chart[] getCharts() {
+ return drawingWriter.getCharts();
+ }
+
+ /**
+ * Sets the charts for this sheet
+ *
+ * @param ch the charts
+ */
+ void setCharts(Chart[] ch) {
+ drawingWriter.setCharts(ch);
+ }
+
+ /**
+ * Check all the merged cells for borders. If the merge record has
+ * borders, then we need to rejig the cell formats to take account of this.
+ * This is called by the write method of the WritableWorkbookImpl, so that
+ * any new XFRecords that are created may be written out with the others
+ */
+ void checkMergedBorders() {
+ Range[] mcells = mergedCells.getMergedCells();
+ ArrayList borderFormats = new ArrayList();
+ for (int mci = 0; mci < mcells.length; mci++) {
+ Range range = mcells[mci];
+ Cell topLeft = range.getTopLeft();
+ XFRecord tlformat = (XFRecord) topLeft.getCellFormat();
+
+ if (tlformat != null &&
+ tlformat.hasBorders() == true &&
+ !tlformat.isRead()) {
+ try {
+ CellXFRecord cf1 = new CellXFRecord(tlformat);
+ Cell bottomRight = range.getBottomRight();
+
+ cf1.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf1.setBorder(Border.LEFT,
+ tlformat.getBorderLine(Border.LEFT),
+ tlformat.getBorderColour(Border.LEFT));
+ cf1.setBorder(Border.TOP,
+ tlformat.getBorderLine(Border.TOP),
+ tlformat.getBorderColour(Border.TOP));
+
+ if (topLeft.getRow() == bottomRight.getRow()) {
+ cf1.setBorder(Border.BOTTOM,
+ tlformat.getBorderLine(Border.BOTTOM),
+ tlformat.getBorderColour(Border.BOTTOM));
+ }
+
+ if (topLeft.getColumn() == bottomRight.getColumn()) {
+ cf1.setBorder(Border.RIGHT,
+ tlformat.getBorderLine(Border.RIGHT),
+ tlformat.getBorderColour(Border.RIGHT));
+ }
+
+ int index = borderFormats.indexOf(cf1);
+ if (index != -1) {
+ cf1 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf1);
+ }
+ ((WritableCell) topLeft).setCellFormat(cf1);
+
+ // Handle the bottom left corner
+ if (bottomRight.getRow() > topLeft.getRow()) {
+ // Handle the corner cell
+ if (bottomRight.getColumn() != topLeft.getColumn()) {
+ CellXFRecord cf2 = new CellXFRecord(tlformat);
+ cf2.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf2.setBorder(Border.LEFT,
+ tlformat.getBorderLine(Border.LEFT),
+ tlformat.getBorderColour(Border.LEFT));
+ cf2.setBorder(Border.BOTTOM,
+ tlformat.getBorderLine(Border.BOTTOM),
+ tlformat.getBorderColour(Border.BOTTOM));
+
+ index = borderFormats.indexOf(cf2);
+ if (index != -1) {
+ cf2 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf2);
+ }
+
+ sheet.addCell(new Blank(topLeft.getColumn(),
+ bottomRight.getRow(), cf2));
+ }
+
+ // Handle the cells down the left hand side (and along the
+ // right too, if necessary)
+ for (int i = topLeft.getRow() + 1; i < bottomRight.getRow(); i++) {
+ CellXFRecord cf3 = new CellXFRecord(tlformat);
+ cf3.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf3.setBorder(Border.LEFT,
+ tlformat.getBorderLine(Border.LEFT),
+ tlformat.getBorderColour(Border.LEFT));
+
+ if (topLeft.getColumn() == bottomRight.getColumn()) {
+ cf3.setBorder(Border.RIGHT,
+ tlformat.getBorderLine(Border.RIGHT),
+ tlformat.getBorderColour(Border.RIGHT));
+ }
+
+ index = borderFormats.indexOf(cf3);
+ if (index != -1) {
+ cf3 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf3);
+ }
+
+ sheet.addCell(new Blank(topLeft.getColumn(), i, cf3));
+ }
+ }
+
+ // Handle the top right corner
+ if (bottomRight.getColumn() > topLeft.getColumn()) {
+ if (bottomRight.getRow() != topLeft.getRow()) {
+ // Handle the corner cell
+ CellXFRecord cf6 = new CellXFRecord(tlformat);
+ cf6.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf6.setBorder(Border.RIGHT,
+ tlformat.getBorderLine(Border.RIGHT),
+ tlformat.getBorderColour(Border.RIGHT));
+ cf6.setBorder(Border.TOP,
+ tlformat.getBorderLine(Border.TOP),
+ tlformat.getBorderColour(Border.TOP));
+ index = borderFormats.indexOf(cf6);
+ if (index != -1) {
+ cf6 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf6);
+ }
+
+ sheet.addCell(new Blank(bottomRight.getColumn(),
+ topLeft.getRow(), cf6));
+ }
+
+ // Handle the cells along the right
+ for (int i = topLeft.getRow() + 1;
+ i < bottomRight.getRow(); i++) {
+ CellXFRecord cf7 = new CellXFRecord(tlformat);
+ cf7.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf7.setBorder(Border.RIGHT,
+ tlformat.getBorderLine(Border.RIGHT),
+ tlformat.getBorderColour(Border.RIGHT));
+
+ index = borderFormats.indexOf(cf7);
+ if (index != -1) {
+ cf7 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf7);
+ }
+
+ sheet.addCell(new Blank(bottomRight.getColumn(), i, cf7));
+ }
+
+ // Handle the cells along the top, and along the bottom too
+ for (int i = topLeft.getColumn() + 1;
+ i < bottomRight.getColumn(); i++) {
+ CellXFRecord cf8 = new CellXFRecord(tlformat);
+ cf8.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf8.setBorder(Border.TOP,
+ tlformat.getBorderLine(Border.TOP),
+ tlformat.getBorderColour(Border.TOP));
+
+ if (topLeft.getRow() == bottomRight.getRow()) {
+ cf8.setBorder(Border.BOTTOM,
+ tlformat.getBorderLine(Border.BOTTOM),
+ tlformat.getBorderColour(Border.BOTTOM));
+ }
+
+ index = borderFormats.indexOf(cf8);
+ if (index != -1) {
+ cf8 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf8);
+ }
+
+ sheet.addCell(new Blank(i, topLeft.getRow(), cf8));
+ }
+ }
+
+ // Handle the bottom right corner
+ if (bottomRight.getColumn() > topLeft.getColumn() ||
+ bottomRight.getRow() > topLeft.getRow()) {
+ // Handle the corner cell
+ CellXFRecord cf4 = new CellXFRecord(tlformat);
+ cf4.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf4.setBorder(Border.RIGHT,
+ tlformat.getBorderLine(Border.RIGHT),
+ tlformat.getBorderColour(Border.RIGHT));
+ cf4.setBorder(Border.BOTTOM,
+ tlformat.getBorderLine(Border.BOTTOM),
+ tlformat.getBorderColour(Border.BOTTOM));
+
+ if (bottomRight.getRow() == topLeft.getRow()) {
+ cf4.setBorder(Border.TOP,
+ tlformat.getBorderLine(Border.TOP),
+ tlformat.getBorderColour(Border.TOP));
+ }
+
+ if (bottomRight.getColumn() == topLeft.getColumn()) {
+ cf4.setBorder(Border.LEFT,
+ tlformat.getBorderLine(Border.LEFT),
+ tlformat.getBorderColour(Border.LEFT));
+ }
+
+ index = borderFormats.indexOf(cf4);
+ if (index != -1) {
+ cf4 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf4);
+ }
+
+ sheet.addCell(new Blank(bottomRight.getColumn(),
+ bottomRight.getRow(), cf4));
+
+ // Handle the cells along the bottom (and along the top
+ // as well, if appropriate)
+ for (int i = topLeft.getColumn() + 1;
+ i < bottomRight.getColumn(); i++) {
+ CellXFRecord cf5 = new CellXFRecord(tlformat);
+ cf5.setBorder(Border.ALL, BorderLineStyle.NONE, Colour.BLACK);
+ cf5.setBorder(Border.BOTTOM,
+ tlformat.getBorderLine(Border.BOTTOM),
+ tlformat.getBorderColour(Border.BOTTOM));
+
+ if (topLeft.getRow() == bottomRight.getRow()) {
+ cf5.setBorder(Border.TOP,
+ tlformat.getBorderLine(Border.TOP),
+ tlformat.getBorderColour(Border.TOP));
+ }
+
+ index = borderFormats.indexOf(cf5);
+ if (index != -1) {
+ cf5 = (CellXFRecord) borderFormats.get(index);
+ } else {
+ borderFormats.add(cf5);
+ }
+
+ sheet.addCell(new Blank(i, bottomRight.getRow(), cf5));
+ }
+ }
+ } catch (WriteException e) {
+ // just log e.toString(), not the whole stack trace
+ logger.warn(e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the cells in the column. Don't use the interface method
+ * getColumn for this as this will create loads of empty cells,
+ * and we could do without that overhead
+ */
+ private Cell[] getColumn(int col) {
+ // Find the last non-null cell
+ boolean found = false;
+ int row = numRows - 1;
+
+ while (row >= 0 && !found) {
+ if (rows[row] != null &&
+ rows[row].getCell(col) != null) {
+ found = true;
+ } else {
+ row--;
+ }
+ }
+
+ // Only create entries for non-empty cells
+ Cell[] cells = new Cell[row + 1];
+
+ for (int i = 0; i <= row; i++) {
+ cells[i] = rows[i] != null ? rows[i].getCell(col) : null;
+ }
+
+ return cells;
+ }
+
+ /**
+ * Sets a flag to indicate that this sheet contains a chart only
+ */
+ void setChartOnly() {
+ chartOnly = true;
+ }
+
+ /**
+ * Sets the environment specific print record
+ *
+ * @param pls the print record
+ */
+ void setPLS(PLSRecord pls) {
+ plsRecord = pls;
+ }
+
+ /**
+ * Sets the button property set record
+ *
+ * @param bps the button property set
+ */
+ void setButtonPropertySet(ButtonPropertySetRecord bps) {
+ buttonPropertySet = bps;
+ }
+
+ /**
+ * Sets the data validations
+ *
+ * @param dv the read-in list of data validations
+ * @param vc the api manipulated set of data validations
+ */
+ void setDataValidation(DataValidation dv, ArrayList vc) {
+ dataValidation = dv;
+ validatedCells = vc;
+ }
+
+ /**
+ * Sets the conditional formats
+ *
+ * @param cf the conditonal formats
+ */
+ void setConditionalFormats(ArrayList cf) {
+ conditionalFormats = cf;
+ }
+
+ /**
+ * Sets the auto filter
+ *
+ * @param af the autofilter
+ */
+ void setAutoFilter(AutoFilter af) {
+ autoFilter = af;
+ }
+
+ /**
+ * Writes out the data validations
+ */
+ private void writeDataValidation() throws IOException {
+ if (dataValidation != null && validatedCells.size() == 0) {
+ // the only data validations are those read in - this should
+ // never be the case now that shared data validations add
+ // to the validatedCells list
+ dataValidation.write(outputFile);
+ return;
+ }
+
+ if (dataValidation == null && validatedCells.size() > 0) {
+ // the only data validations are those which have been added by the
+ // write API. Need to sort out the combo box id
+ int comboBoxId = sheet.getComboBox() != null ?
+ sheet.getComboBox().getObjectId() : DataValidation.DEFAULT_OBJECT_ID;
+ dataValidation = new DataValidation(comboBoxId,
+ sheet.getWorkbook(),
+ sheet.getWorkbook(),
+ workbookSettings);
+ }
+
+ for (Iterator i = validatedCells.iterator(); i.hasNext(); ) {
+ CellValue cv = (CellValue) i.next();
+ CellFeatures cf = cv.getCellFeatures();
+
+ // Do not do anything if the DVParser has been copied, as it
+ // will already by on the DataValidation record as a result
+ // of the SheetCopier process
+ if (!cf.getDVParser().copied()) {
+ if (!cf.getDVParser().extendedCellsValidation()) {
+ // DVParser is specific for a single cell validation - just add it
+ DataValiditySettingsRecord dvsr =
+ new DataValiditySettingsRecord(cf.getDVParser());
+ dataValidation.add(dvsr);
+ } else {
+ // Only add the DVParser once for shared validations
+ // only add it if it is the top left cell
+ if (cv.getColumn() == cf.getDVParser().getFirstColumn() &&
+ cv.getRow() == cf.getDVParser().getFirstRow()) {
+ DataValiditySettingsRecord dvsr =
+ new DataValiditySettingsRecord(cf.getDVParser());
+ dataValidation.add(dvsr);
+ }
+ }
+ }
+ }
+ dataValidation.write(outputFile);
+ }
+ /*
+
+ // There is a mixture of read and write validations
+ for (Iterator i = validatedCells.iterator(); i.hasNext(); )
+ {
+ CellValue cv = (CellValue) i.next();
+ CellFeatures cf = cv.getCellFeatures();
+ DataValiditySettingsRecord dvsr =
+ new DataValiditySettingsRecord(cf.getDVParser());
+ dataValidation.add(dvsr);
+ }
+ dataValidation.write(outputFile);
+ return;
+ }
+ */
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java
new file mode 100755
index 0000000..12b8c12
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SortRecord.java
@@ -0,0 +1,113 @@
+/*********************************************************************
+ *
+ * Copyright (C) 200r Andrew Khan, Al Mantei
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Record which specifies sort dialog box values
+ */
+class SortRecord extends WritableRecordData {
+ private final String column1Name;
+ private final String column2Name;
+ private final String column3Name;
+ private final boolean sortColumns;
+ private final boolean sortKey1Desc;
+ private final boolean sortKey2Desc;
+ private final boolean sortKey3Desc;
+ private final boolean sortCaseSensitive;
+
+ /**
+ * Constructor
+ *
+ * @param a Sort Column 1 Name
+ * @param b Sort Column 2 Name
+ * @param c Sort Column 3 Name
+ * @param sc Sort Columns
+ * @param sk1d Sort Key 1 Descending
+ * @param sk2d Sort Key 2 Descending
+ * @param sk3d Sort Key 3 Descending
+ * @param scs Sort Case Sensitive
+ */
+ public SortRecord(String a, String b, String c,
+ boolean sc, boolean sk1d,
+ boolean sk2d, boolean sk3d, boolean scs) {
+ super(Type.SORT);
+
+ column1Name = a;
+ column2Name = b;
+ column3Name = c;
+ sortColumns = sc;
+ sortKey1Desc = sk1d;
+ sortKey2Desc = sk2d;
+ sortKey3Desc = sk3d;
+ sortCaseSensitive = scs;
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ int byteCount = 5 + (column1Name.length() * 2) + 1;
+ if (column2Name.length() > 0)
+ byteCount += (column2Name.length() * 2) + 1;
+ if (column3Name.length() > 0)
+ byteCount += (column3Name.length() * 2) + 1;
+ byte[] data = new byte[byteCount + 1];
+ // there is supposed to be an extra "unused" byte at the end
+ int optionFlag = 0;
+ if (sortColumns)
+ optionFlag = optionFlag | 0x01;
+ if (sortKey1Desc)
+ optionFlag = optionFlag | 0x02;
+ if (sortKey2Desc)
+ optionFlag = optionFlag | 0x04;
+ if (sortKey3Desc)
+ optionFlag = optionFlag | 0x08;
+ if (sortCaseSensitive)
+ optionFlag = optionFlag | 0x10;
+
+ data[0] = (byte) optionFlag;
+ // data[1] is an index for sorting by a list - not implemented
+ data[2] = (byte) column1Name.length();
+ data[3] = (byte) column2Name.length();
+ data[4] = (byte) column3Name.length();
+ // always write the headings in unicode
+ data[5] = 0x01;
+ StringHelper.getUnicodeBytes(column1Name, data, 6);
+ int curPos = 6 + (column1Name.length() * 2);
+ if (column2Name.length() > 0) {
+ data[curPos++] = 0x01;
+ StringHelper.getUnicodeBytes(column2Name, data, curPos);
+ curPos += column2Name.length() * 2;
+ }
+ if (column3Name.length() > 0) {
+ data[curPos++] = 0x01;
+ StringHelper.getUnicodeBytes(column3Name, data, curPos);
+ curPos += column3Name.length() * 2;
+ }
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java
new file mode 100755
index 0000000..ef3e3d5
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/StringRecord.java
@@ -0,0 +1,59 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the string result of a formula calculation. This record
+ * occurs immediately after the formula
+ */
+class StringRecord extends WritableRecordData {
+ /**
+ * The string value
+ */
+ private final String value;
+
+ /**
+ * Constructor
+ */
+ public StringRecord(String val) {
+ super(Type.STRING);
+
+ value = val;
+ }
+
+ /**
+ * The binary data to be written out
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[value.length() * 2 + 3];
+ IntegerHelper.getTwoBytes(value.length(), data, 0);
+ data[2] = 0x01; // unicode
+ StringHelper.getUnicodeBytes(value, data, 3);
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java
new file mode 100755
index 0000000..ee76c46
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/StyleXFRecord.java
@@ -0,0 +1,63 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.DisplayFormat;
+import jxl.biff.FontRecord;
+import jxl.biff.XFRecord;
+
+/**
+ * A style XF Record
+ */
+public class StyleXFRecord extends XFRecord {
+ /**
+ * Constructor
+ *
+ * @param fnt the font for this style
+ * @param form the format of this style
+ */
+ public StyleXFRecord(FontRecord fnt, DisplayFormat form) {
+ super(fnt, form);
+
+ setXFDetails(XFRecord.style, 0xfff0);
+ }
+
+
+ /**
+ * Sets the raw cell options. Called by WritableFormattingRecord
+ * when setting the built in cell formats
+ *
+ * @param opt the cell options
+ */
+ public final void setCellOptions(int opt) {
+ super.setXFCellOptions(opt);
+ }
+
+ /**
+ * Sets whether or not this XF record locks the cell
+ *
+ * @param l the locked flag
+ * @throws WriteException
+ */
+ public void setLocked(boolean l) {
+ super.setXFLocked(l);
+ }
+
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java b/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java
new file mode 100755
index 0000000..a7a468d
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Styles.java
@@ -0,0 +1,193 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2004 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.XFRecord;
+import jxl.common.Logger;
+import jxl.write.DateFormat;
+import jxl.write.DateFormats;
+import jxl.write.NumberFormats;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableFont;
+import jxl.write.WritableWorkbook;
+
+/**
+ * A structure containing the styles used by this workbook. This is used
+ * to enforce thread safety by tying the default styles to a workbook
+ * instance rather than by initializing them statically
+ */
+class Styles {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(Styles.class);
+
+ /**
+ * The default font for Cell formats
+ */
+ private WritableFont arial10pt;
+
+ /**
+ * The font used for hyperlinks
+ */
+ private WritableFont hyperlinkFont;
+
+ /**
+ * The default style for cells
+ */
+ private WritableCellFormat normalStyle;
+
+ /**
+ * The style used for hyperlinks
+ */
+ private WritableCellFormat hyperlinkStyle;
+
+ /**
+ * A cell format used to hide the cell contents
+ */
+ private WritableCellFormat hiddenStyle;
+
+ /**
+ * A cell format used for the default date format
+ */
+ private WritableCellFormat defaultDateFormat;
+
+ /**
+ * Constructor
+ */
+ public Styles() {
+ arial10pt = null;
+ hyperlinkFont = null;
+ normalStyle = null;
+ hyperlinkStyle = null;
+ hiddenStyle = null;
+ }
+
+ private synchronized void initNormalStyle() {
+ normalStyle = new WritableCellFormat(getArial10Pt(),
+ NumberFormats.DEFAULT);
+ normalStyle.setFont(getArial10Pt());
+ }
+
+ public WritableCellFormat getNormalStyle() {
+ if (normalStyle == null) {
+ initNormalStyle();
+ }
+
+ return normalStyle;
+ }
+
+ private synchronized void initHiddenStyle() {
+ hiddenStyle = new WritableCellFormat
+ (getArial10Pt(), new DateFormat(";;;"));
+ }
+
+ public WritableCellFormat getHiddenStyle() {
+ if (hiddenStyle == null) {
+ initHiddenStyle();
+ }
+
+ return hiddenStyle;
+ }
+
+ private synchronized void initHyperlinkStyle() {
+ hyperlinkStyle = new WritableCellFormat(getHyperlinkFont(),
+ NumberFormats.DEFAULT);
+ }
+
+ public WritableCellFormat getHyperlinkStyle() {
+ if (hyperlinkStyle == null) {
+ initHyperlinkStyle();
+ }
+
+ return hyperlinkStyle;
+ }
+
+ private synchronized void initArial10Pt() {
+ arial10pt = new WritableFont(WritableWorkbook.ARIAL_10_PT);
+ }
+
+ public WritableFont getArial10Pt() {
+ if (arial10pt == null) {
+ initArial10Pt();
+ }
+
+ return arial10pt;
+ }
+
+ private synchronized void initHyperlinkFont() {
+ hyperlinkFont = new WritableFont(WritableWorkbook.HYPERLINK_FONT);
+ }
+
+ public WritableFont getHyperlinkFont() {
+ if (hyperlinkFont == null) {
+ initHyperlinkFont();
+ }
+
+ return hyperlinkFont;
+ }
+
+ private synchronized void initDefaultDateFormat() {
+ defaultDateFormat = new WritableCellFormat(DateFormats.DEFAULT);
+ }
+
+ public WritableCellFormat getDefaultDateFormat() {
+ if (defaultDateFormat == null) {
+ initDefaultDateFormat();
+ }
+
+ return defaultDateFormat;
+ }
+
+ /**
+ * Gets the thread safe version of the cell format passed in. If the
+ * format is already thread safe (ie. it doesn't use a statically initialized
+ * format or font) then the same object is simply returned
+ * This object is already tied to a workbook instance, so no synchronisation
+ * is necesasry
+ *
+ * @param wf a format to verify
+ * @return the thread safe format
+ */
+ public XFRecord getFormat(XFRecord wf) {
+ XFRecord format = wf;
+
+ // Check to see if the format is one of the shared Workbook defaults. If
+ // so, then get hold of the Workbook's specific instance
+ if (format == WritableWorkbook.NORMAL_STYLE) {
+ format = getNormalStyle();
+ } else if (format == WritableWorkbook.HYPERLINK_STYLE) {
+ format = getHyperlinkStyle();
+ } else if (format == WritableWorkbook.HIDDEN_STYLE) {
+ format = getHiddenStyle();
+ } else if (format == DateRecord.defaultDateFormat) {
+ format = getDefaultDateFormat();
+ }
+
+ // Do the same with the statically shared fonts
+ if (format.getFont() == WritableWorkbook.ARIAL_10_PT) {
+ format.setFont(getArial10Pt());
+ } else if (format.getFont() == WritableWorkbook.HYPERLINK_FONT) {
+ format.setFont(getHyperlinkFont());
+ }
+
+ return format;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java
new file mode 100755
index 0000000..d2c130a
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/SupbookRecord.java
@@ -0,0 +1,301 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.WorkbookSettings;
+import jxl.biff.EncodedURLHelper;
+import jxl.biff.IntegerHelper;
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+import jxl.common.Assert;
+import jxl.common.Logger;
+
+/**
+ * Stores the supporting workbook information. For files written by
+ * JExcelApi this will only reference internal sheets
+ */
+class SupbookRecord extends WritableRecordData {
+ public final static SupbookType INTERNAL = new SupbookType();
+ public final static SupbookType EXTERNAL = new SupbookType();
+ public final static SupbookType ADDIN = new SupbookType();
+ public final static SupbookType LINK = new SupbookType();
+ public final static SupbookType UNKNOWN = new SupbookType();
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(SupbookRecord.class);
+ /**
+ * The type of this supbook record
+ */
+ private SupbookType type;
+ /**
+ * The data to be written to the binary file
+ */
+ private byte[] data;
+
+ /**
+ * The number of sheets - internal & external supbooks only
+ */
+ private int numSheets;
+ /**
+ * The name of the external file
+ */
+ private String fileName;
+ /**
+ * The names of the external sheets
+ */
+ private String[] sheetNames;
+ /**
+ * The workbook settings
+ */
+ private WorkbookSettings workbookSettings;
+ /**
+ * Constructor for add in function names
+ */
+ public SupbookRecord() {
+ super(Type.SUPBOOK);
+ type = ADDIN;
+ }
+
+ /**
+ * Constructor for internal sheets
+ */
+ public SupbookRecord(int sheets, WorkbookSettings ws) {
+ super(Type.SUPBOOK);
+
+ numSheets = sheets;
+ type = INTERNAL;
+ workbookSettings = ws;
+ }
+
+ /**
+ * Constructor for external sheets
+ *
+ * @param fn the filename of the external supbook
+ * @param ws the workbook settings
+ */
+ public SupbookRecord(String fn, WorkbookSettings ws) {
+ super(Type.SUPBOOK);
+
+ fileName = fn;
+ numSheets = 1;
+ sheetNames = new String[0];
+ workbookSettings = ws;
+
+ type = EXTERNAL;
+ }
+
+ /**
+ * Constructor used when copying from an external workbook
+ */
+ public SupbookRecord(jxl.read.biff.SupbookRecord sr, WorkbookSettings ws) {
+ super(Type.SUPBOOK);
+
+ workbookSettings = ws;
+ if (sr.getType() == jxl.read.biff.SupbookRecord.INTERNAL) {
+ type = INTERNAL;
+ numSheets = sr.getNumberOfSheets();
+ } else if (sr.getType() == jxl.read.biff.SupbookRecord.EXTERNAL) {
+ type = EXTERNAL;
+ numSheets = sr.getNumberOfSheets();
+ fileName = sr.getFileName();
+ sheetNames = new String[numSheets];
+
+ for (int i = 0; i < numSheets; i++) {
+ sheetNames[i] = sr.getSheetName(i);
+ }
+ }
+
+ if (sr.getType() == jxl.read.biff.SupbookRecord.ADDIN) {
+ logger.warn("Supbook type is addin");
+ }
+ }
+
+ /**
+ * Initializes an internal supbook record
+ *
+ * @param sr the read supbook record to copy from
+ */
+ private void initInternal(jxl.read.biff.SupbookRecord sr) {
+ numSheets = sr.getNumberOfSheets();
+ initInternal();
+ }
+
+ /**
+ * Initializes an internal supbook record
+ */
+ private void initInternal() {
+ data = new byte[4];
+
+ IntegerHelper.getTwoBytes(numSheets, data, 0);
+ data[2] = 0x1;
+ data[3] = 0x4;
+ type = INTERNAL;
+ }
+
+ /**
+ * Adjust the number of internal sheets. Called by WritableSheet when
+ * a sheet is added or or removed to the workbook
+ *
+ * @param sheets the new number of sheets
+ */
+ void adjustInternal(int sheets) {
+ Assert.verify(type == INTERNAL);
+ numSheets = sheets;
+ initInternal();
+ }
+
+ /**
+ * Initializes an external supbook record
+ */
+ private void initExternal() {
+ int totalSheetNameLength = 0;
+ for (int i = 0; i < numSheets; i++) {
+ totalSheetNameLength += sheetNames[i].length();
+ }
+
+ byte[] fileNameData = EncodedURLHelper.getEncodedURL(fileName,
+ workbookSettings);
+ int dataLength = 2 + // numsheets
+ 4 + fileNameData.length +
+ numSheets * 3 + totalSheetNameLength * 2;
+
+ data = new byte[dataLength];
+
+ IntegerHelper.getTwoBytes(numSheets, data, 0);
+
+ // Add in the file name. Precede with a byte denoting that it is a
+ // file name
+ int pos = 2;
+ IntegerHelper.getTwoBytes(fileNameData.length + 1, data, pos);
+ data[pos + 2] = 0; // ascii indicator
+ data[pos + 3] = 1; // file name indicator
+ System.arraycopy(fileNameData, 0, data, pos + 4, fileNameData.length);
+
+ pos += 4 + fileNameData.length;
+
+ // Get the sheet names
+ for (int i = 0; i < sheetNames.length; i++) {
+ IntegerHelper.getTwoBytes(sheetNames[i].length(), data, pos);
+ data[pos + 2] = 1; // unicode indicator
+ StringHelper.getUnicodeBytes(sheetNames[i], data, pos + 3);
+ pos += 3 + sheetNames[i].length() * 2;
+ }
+ }
+
+ /**
+ * Initializes the supbook record for add in functions
+ */
+ private void initAddin() {
+ data = new byte[]{0x1, 0x0, 0x1, 0x3a};
+ }
+
+ /**
+ * The binary data to be written out
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ if (type == INTERNAL) {
+ initInternal();
+ } else if (type == EXTERNAL) {
+ initExternal();
+ } else if (type == ADDIN) {
+ initAddin();
+ } else {
+ logger.warn("unsupported supbook type - defaulting to internal");
+ initInternal();
+ }
+
+ return data;
+ }
+
+ /**
+ * Gets the type of this supbook record
+ *
+ * @return the type of this supbook
+ */
+ public SupbookType getType() {
+ return type;
+ }
+
+ /**
+ * Gets the number of sheets. This will only be non-zero for internal
+ * and external supbooks
+ *
+ * @return the number of sheets
+ */
+ public int getNumberOfSheets() {
+ return numSheets;
+ }
+
+ /**
+ * Accessor for the file name
+ *
+ * @return the file name
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Adds the worksheet name to this supbook
+ *
+ * @param name the worksheet name
+ * @return the index of this sheet in the supbook record
+ */
+ public int getSheetIndex(String s) {
+ boolean found = false;
+ int sheetIndex = 0;
+ for (int i = 0; i < sheetNames.length && !found; i++) {
+ if (sheetNames[i].equals(s)) {
+ found = true;
+ sheetIndex = 0;
+ }
+ }
+
+ if (found) {
+ return sheetIndex;
+ }
+
+ // Grow the array
+ String[] names = new String[sheetNames.length + 1];
+ System.arraycopy(sheetNames, 0, names, 0, sheetNames.length);
+ names[sheetNames.length] = s;
+ sheetNames = names;
+ return sheetNames.length - 1;
+ }
+
+ /**
+ * Accessor for the sheet name
+ *
+ * @param s the sheet index
+ */
+ public String getSheetName(int s) {
+ return sheetNames[s];
+ }
+
+ /**
+ * The type of supbook this refers to
+ */
+ private static class SupbookType {
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java
new file mode 100755
index 0000000..7a74d09
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/TabIdRecord.java
@@ -0,0 +1,58 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Contains an array of sheet tab index numbers
+ */
+class TabIdRecord extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param sheets the number of sheets
+ */
+ public TabIdRecord(int sheets) {
+ super(Type.TABID);
+
+ data = new byte[sheets * 2];
+
+ for (int i = 0; i < sheets; i++) {
+ IntegerHelper.getTwoBytes(i + 1, data, i * 2);
+ }
+ }
+
+ /**
+ * Gets the data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java
new file mode 100755
index 0000000..9501d55
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/TemplateRecord.java
@@ -0,0 +1,47 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2009 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Record which indicates whether or not this file is a template (Usually saved with .xlt file name extension)
+ * should be refreshed when the workbook is loaded
+ */
+class TemplateRecord extends WritableRecordData {
+ /**
+ * Constructor
+ *
+ * @param template flag
+ */
+ public TemplateRecord() {
+ super(Type.TEMPLATE);
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return new byte[0];
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java
new file mode 100755
index 0000000..3c47191
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/TopMarginRecord.java
@@ -0,0 +1,31 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+
+/**
+ * The settings for the left margin
+ */
+class TopMarginRecord extends MarginRecord {
+ TopMarginRecord(double v) {
+ super(Type.TOPMARGIN, v);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java
new file mode 100755
index 0000000..d2c31fc
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/UsesElfsRecord.java
@@ -0,0 +1,62 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores the flag which indicates whether the version of excel can
+ * understand natural language input for formulae
+ */
+class UsesElfsRecord extends WritableRecordData {
+ /**
+ * The binary data for output to file
+ */
+ private final byte[] data;
+ /**
+ * The uses ELFs flag
+ */
+ private final boolean usesElfs;
+
+ /**
+ * Constructor
+ */
+ public UsesElfsRecord() {
+ super(Type.USESELFS);
+
+ usesElfs = true;
+
+ data = new byte[2];
+
+ if (usesElfs) {
+ data[0] = 1;
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java
new file mode 100755
index 0000000..9fd32e7
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalCentreRecord.java
@@ -0,0 +1,66 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Indicates whether the centre vertically on page option has been
+ * set from the options dialog box
+ */
+class VerticalCentreRecord extends WritableRecordData {
+ /**
+ * The binary data for output to file
+ */
+ private final byte[] data;
+ /**
+ * The centre flag
+ */
+ private final boolean centre;
+
+ /**
+ * Constructor
+ *
+ * @param ce the centre flag
+ */
+ public VerticalCentreRecord(boolean ce) {
+ super(Type.VCENTER);
+
+ centre = ce;
+
+ data = new byte[2];
+
+ if (centre) {
+ data[0] = 1;
+ }
+ }
+
+ /**
+ * Gets the data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java
new file mode 100755
index 0000000..e61d24f
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/VerticalPageBreaksRecord.java
@@ -0,0 +1,68 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Contains the list of explicit horizontal page breaks on the current sheet
+ */
+class VerticalPageBreaksRecord extends WritableRecordData {
+ /**
+ * The row breaks
+ */
+ private final int[] columnBreaks;
+
+ /**
+ * Constructor
+ *
+ * @param break the row breaks
+ */
+ public VerticalPageBreaksRecord(int[] breaks) {
+ super(Type.VERTICALPAGEBREAKS);
+
+ columnBreaks = breaks;
+ }
+
+ /**
+ * Gets the binary data to write to the output file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[columnBreaks.length * 6 + 2];
+
+ // The number of breaks on the list
+ IntegerHelper.getTwoBytes(columnBreaks.length, data, 0);
+ int pos = 2;
+
+ for (int i = 0; i < columnBreaks.length; i++) {
+ IntegerHelper.getTwoBytes(columnBreaks[i], data, pos);
+ IntegerHelper.getTwoBytes(0xff, data, pos + 4);
+ pos += 6;
+ }
+
+ return data;
+ }
+}
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java
new file mode 100755
index 0000000..5431987
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Weird1Record.java
@@ -0,0 +1,48 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Don't know what this does - something to do with freezing panes I think
+ */
+class Weird1Record extends WritableRecordData {
+ /**
+ * Constructor
+ */
+ public Weird1Record() {
+ super(Type.WEIRD1);
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ byte[] data = new byte[6];
+
+ data[2] = 0x37;
+
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java
new file mode 100755
index 0000000..2ed5ce5
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Window1Record.java
@@ -0,0 +1,80 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Contains workbook level windowing attributes
+ */
+class Window1Record extends WritableRecordData {
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * The selected sheet
+ */
+ private final int selectedSheet;
+
+ /**
+ * Constructor
+ */
+ public Window1Record(int selSheet) {
+ super(Type.WINDOW1);
+
+ selectedSheet = selSheet;
+
+ // hard code the data in for now
+ data = new byte[]
+ {(byte) 0x68,
+ (byte) 0x1,
+ (byte) 0xe,
+ (byte) 0x1,
+ (byte) 0x5c,
+ (byte) 0x3a,
+ (byte) 0xbe,
+ (byte) 0x23,
+ (byte) 0x38,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0,
+ (byte) 0x1,
+ (byte) 0,
+ (byte) 0x58,
+ (byte) 0x2};
+
+ IntegerHelper.getTwoBytes(selectedSheet, data, 10);
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java b/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java
new file mode 100755
index 0000000..f0281c3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/Window2Record.java
@@ -0,0 +1,98 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.SheetSettings;
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Contains the window attributes for a worksheet
+ */
+class Window2Record extends WritableRecordData {
+ /**
+ * The binary data for output to file
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ */
+ public Window2Record(SheetSettings settings) {
+ super(Type.WINDOW2);
+
+ int options = 0;
+
+ options |= 0x0; // display formula values, not formulas
+
+ if (settings.getShowGridLines()) {
+ options |= 0x02;
+ }
+
+ options |= 0x04; // display row and column headings
+
+ options |= 0x0; // panes should be not frozen
+
+ if (settings.getDisplayZeroValues()) {
+ options |= 0x10;
+ }
+
+ options |= 0x20; // default header
+
+ options |= 0x80; // display outline symbols
+
+ // Handle the freeze panes
+ if (settings.getHorizontalFreeze() != 0 ||
+ settings.getVerticalFreeze() != 0) {
+ options |= 0x08;
+ options |= 0x100;
+ }
+
+ // Handle the selected flag
+ if (settings.isSelected()) {
+ options |= 0x600;
+ }
+
+ // Handle the view mode
+ if (settings.getPageBreakPreviewMode()) {
+ options |= 0x800;
+ }
+
+ // hard code the data in for now
+ data = new byte[18];
+ IntegerHelper.getTwoBytes(options, data, 0);
+ IntegerHelper.getTwoBytes(0x40, data, 6); // grid line colour
+ IntegerHelper.getTwoBytes(settings.getPageBreakPreviewMagnification(),
+ data, 10);
+ IntegerHelper.getTwoBytes(settings.getNormalMagnification(),
+ data, 12);
+
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java
new file mode 100755
index 0000000..2041ab7
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WindowProtectRecord.java
@@ -0,0 +1,64 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.IntegerHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * Stores an option from the Protect Workbook dialog box
+ */
+class WindowProtectRecord extends WritableRecordData {
+ /**
+ * Protect flag
+ */
+ private final boolean protection;
+ /**
+ * The binary data
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor
+ *
+ * @param prot the protect flag
+ */
+ public WindowProtectRecord(boolean prot) {
+ super(Type.WINDOWPROTECT);
+
+ protection = prot;
+
+ data = new byte[2];
+
+ if (protection) {
+ IntegerHelper.getTwoBytes(1, data, 0);
+ }
+ }
+
+ /**
+ * Gets the binary data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java
new file mode 100755
index 0000000..d4d8304
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFontRecord.java
@@ -0,0 +1,157 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.FontRecord;
+import jxl.format.Font;
+import jxl.write.WriteException;
+
+/**
+ * A writable Font record. This class intercepts any set accessor calls
+ * and throws and exception if the Font is already initialized
+ */
+public class WritableFontRecord extends FontRecord {
+ /**
+ * Constructor, used when creating a new font for writing out.
+ *
+ * @param bold the bold indicator
+ * @param ps the point size
+ * @param us the underline style
+ * @param fn the name
+ * @param it italicised indicator
+ * @param c the colour
+ * @param ss the script style
+ */
+ protected WritableFontRecord(String fn, int ps, int bold, boolean it,
+ int us, int ci, int ss) {
+ super(fn, ps, bold, it, us, ci, ss);
+ }
+
+ /**
+ * Publicly available copy constructor
+ *
+ * @param the font to copy
+ */
+ protected WritableFontRecord(Font f) {
+ super(f);
+ }
+
+
+ /**
+ * Sets the point size for this font, if the font hasn't been initialized
+ *
+ * @param pointSize the point size
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setPointSize(int pointSize) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontPointSize(pointSize);
+ }
+
+ /**
+ * Sets the bold style for this font, if the font hasn't been initialized
+ *
+ * @param boldStyle the bold style
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setBoldStyle(int boldStyle) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontBoldStyle(boldStyle);
+ }
+
+ /**
+ * Sets the italic indicator for this font, if the font hasn't been
+ * initialized
+ *
+ * @param italic the italic flag
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setItalic(boolean italic) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontItalic(italic);
+ }
+
+ /**
+ * Sets the underline style for this font, if the font hasn't been
+ * initialized
+ *
+ * @param us the underline style
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setUnderlineStyle(int us) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontUnderlineStyle(us);
+ }
+
+ /**
+ * Sets the colour for this font, if the font hasn't been
+ * initialized
+ *
+ * @param colour the colour
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setColour(int colour) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontColour(colour);
+ }
+
+ /**
+ * Sets the script style (eg. superscript, subscript) for this font,
+ * if the font hasn't been initialized
+ *
+ * @param scriptStyle the colour
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setScriptStyle(int scriptStyle) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+
+ super.setFontScriptStyle(scriptStyle);
+ }
+
+ /**
+ * Sets the struck out flag
+ *
+ * @param so TRUE if the font is struck out, false otherwise
+ * @throws WriteException, if this font is already in use elsewhere
+ */
+ protected void setStruckout(boolean os) throws WriteException {
+ if (isInitialized()) {
+ throw new JxlWriteException(JxlWriteException.formatInitialized);
+ }
+ super.setFontStruckout(os);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java
new file mode 100755
index 0000000..190f2b3
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFonts.java
@@ -0,0 +1,49 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Fonts;
+import jxl.write.WritableFont;
+
+/**
+ * A container for the list of fonts used in this workbook The writable
+ * subclass instantiates the predetermined list of fonts available to
+ * users of the writable API
+ */
+public class WritableFonts extends Fonts {
+ /**
+ * Constructor. Creates the predetermined list of fonts
+ */
+ public WritableFonts(WritableWorkbookImpl w) {
+ super();
+
+ addFont(w.getStyles().getArial10Pt());
+
+ // Create the default fonts
+ WritableFont f = new WritableFont(WritableFont.ARIAL);
+ addFont(f);
+
+ f = new WritableFont(WritableFont.ARIAL);
+ addFont(f);
+
+ f = new WritableFont(WritableFont.ARIAL);
+ addFont(f);
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java
new file mode 100755
index 0000000..294c560
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableFormattingRecords.java
@@ -0,0 +1,225 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.biff.Fonts;
+import jxl.biff.FormattingRecords;
+import jxl.biff.NumFormatRecordsException;
+import jxl.common.Assert;
+import jxl.write.NumberFormats;
+import jxl.write.WritableCellFormat;
+
+
+/**
+ * Handles the Format and XF record indexing. The writable subclass
+ * instantiates the predetermined list of XF records and formats
+ * present in every Excel Workbook
+ */
+public class WritableFormattingRecords extends FormattingRecords {
+ /**
+ * The statically defined normal style
+ */
+ public static WritableCellFormat normalStyle;
+
+ /**
+ * Constructor. Instantiates the prerequisite list of formats and
+ * styles required by all Excel workbooks
+ *
+ * @param f the list of Fonts
+ * @param styles the list of style clones
+ */
+ public WritableFormattingRecords(Fonts f, Styles styles) {
+ super(f);
+
+ try {
+ // Hard code all the styles
+ StyleXFRecord sxf = new StyleXFRecord
+ (styles.getArial10Pt(), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(2), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(3), NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(styles.getArial10Pt(),
+ NumberFormats.DEFAULT);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf400);
+ addStyle(sxf);
+
+ // That's the end of the built ins. Write the normal style
+ // cell XF here
+ addStyle(styles.getNormalStyle());
+
+ // Continue with "user defined" styles
+ sxf = new StyleXFRecord(getFonts().getFont(1),
+ NumberFormats.FORMAT7);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf800);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1),
+ NumberFormats.FORMAT5);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf800);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1),
+ NumberFormats.FORMAT8);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf800);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1),
+ NumberFormats.FORMAT6);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf800);
+ addStyle(sxf);
+
+ sxf = new StyleXFRecord(getFonts().getFont(1),
+ NumberFormats.PERCENT_INTEGER);
+ sxf.setLocked(true);
+ sxf.setCellOptions(0xf800);
+ addStyle(sxf);
+
+ // Hard code in the pre-defined number formats for now
+ /*
+ FormatRecord fr = new FormatRecord
+ ("\"$\"#,##0_);\\(\"$\"#,##0\\)",5);
+ addFormat(fr);
+
+ fr = new FormatRecord
+ ("\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)", 6);
+ addFormat(fr);
+
+ fr = new FormatRecord
+ ("\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)", 7);
+ addFormat(fr);
+
+ fr = new FormatRecord
+ ("\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)", 8);
+ addFormat(fr);
+
+ fr = new FormatRecord
+ ("_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)",
+ 0x2a);
+ // outputFile.write(fr);
+
+ fr = new FormatRecord
+ ("_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)",
+ 0x2e);
+ // outputFile.write(fr);
+
+ fr = new FormatRecord
+ ("_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)",
+ 0x2c);
+ // outputFile.write(fr);
+
+ fr = new FormatRecord
+ ("_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)",
+ 0x2b);
+ // outputFile.write(fr);
+ */
+ } catch (NumFormatRecordsException e) {
+ // This should not happen yet, since we are just creating the file.
+ // Bomb out
+ Assert.verify(false, e.getMessage());
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java
new file mode 100755
index 0000000..d43c8ca
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetCopier.java
@@ -0,0 +1,513 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2006 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeSet;
+import jxl.BooleanCell;
+import jxl.Cell;
+import jxl.CellType;
+import jxl.DateCell;
+import jxl.LabelCell;
+import jxl.NumberCell;
+import jxl.Range;
+import jxl.WorkbookSettings;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.DataValidation;
+import jxl.biff.FormattingRecords;
+import jxl.biff.FormulaData;
+import jxl.biff.NumFormatRecordsException;
+import jxl.biff.SheetRangeImpl;
+import jxl.biff.WorkspaceInformationRecord;
+import jxl.biff.XFRecord;
+import jxl.biff.formula.FormulaException;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.format.CellFormat;
+import jxl.write.Blank;
+import jxl.write.Boolean;
+import jxl.write.DateTime;
+import jxl.write.Formula;
+import jxl.write.Label;
+import jxl.write.Number;
+import jxl.write.WritableCell;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableHyperlink;
+import jxl.write.WritableImage;
+import jxl.write.WritableSheet;
+import jxl.write.WritableWorkbook;
+import jxl.write.WriteException;
+
+/**
+ * A transient utility object used to copy sheets. This
+ * functionality has been farmed out to a different class
+ * in order to reduce the bloat of the WritableSheetImpl
+ */
+class WritableSheetCopier {
+ private static final Logger logger = Logger.getLogger(SheetCopier.class);
+
+ private final WritableSheetImpl fromSheet;
+ private final WritableSheetImpl toSheet;
+ private final WorkbookSettings workbookSettings;
+
+ // Objects used by the sheet
+ private TreeSet fromColumnFormats;
+ private TreeSet toColumnFormats;
+ private MergedCells fromMergedCells;
+ private MergedCells toMergedCells;
+ private RowRecord[] fromRows;
+ private ArrayList fromRowBreaks;
+ private ArrayList fromColumnBreaks;
+ private ArrayList toRowBreaks;
+ private ArrayList toColumnBreaks;
+ private DataValidation fromDataValidation;
+ private DataValidation toDataValidation;
+ private SheetWriter sheetWriter;
+ private ArrayList fromDrawings;
+ private ArrayList toDrawings;
+ private ArrayList toImages;
+ private WorkspaceInformationRecord fromWorkspaceOptions;
+ private PLSRecord fromPLSRecord;
+ private PLSRecord toPLSRecord;
+ private ButtonPropertySetRecord fromButtonPropertySet;
+ private ButtonPropertySetRecord toButtonPropertySet;
+ private ArrayList fromHyperlinks;
+ private ArrayList toHyperlinks;
+ private ArrayList validatedCells;
+ private int numRows;
+ private int maxRowOutlineLevel;
+ private int maxColumnOutlineLevel;
+
+
+ private final boolean chartOnly;
+ private FormattingRecords formatRecords;
+
+
+ // Objects used to maintain state during the copy process
+ private HashMap xfRecords;
+ private HashMap fonts;
+ private HashMap formats;
+
+ public WritableSheetCopier(WritableSheet f, WritableSheet t) {
+ fromSheet = (WritableSheetImpl) f;
+ toSheet = (WritableSheetImpl) t;
+ workbookSettings = toSheet.getWorkbook().getSettings();
+ chartOnly = false;
+ }
+
+ void setColumnFormats(TreeSet fcf, TreeSet tcf) {
+ fromColumnFormats = fcf;
+ toColumnFormats = tcf;
+ }
+
+ void setMergedCells(MergedCells fmc, MergedCells tmc) {
+ fromMergedCells = fmc;
+ toMergedCells = tmc;
+ }
+
+ void setRows(RowRecord[] r) {
+ fromRows = r;
+ }
+
+ void setValidatedCells(ArrayList vc) {
+ validatedCells = vc;
+ }
+
+ void setRowBreaks(ArrayList frb, ArrayList trb) {
+ fromRowBreaks = frb;
+ toRowBreaks = trb;
+ }
+
+ void setColumnBreaks(ArrayList fcb, ArrayList tcb) {
+ fromColumnBreaks = fcb;
+ toColumnBreaks = tcb;
+ }
+
+ void setDrawings(ArrayList fd, ArrayList td, ArrayList ti) {
+ fromDrawings = fd;
+ toDrawings = td;
+ toImages = ti;
+ }
+
+ void setHyperlinks(ArrayList fh, ArrayList th) {
+ fromHyperlinks = fh;
+ toHyperlinks = th;
+ }
+
+ void setWorkspaceOptions(WorkspaceInformationRecord wir) {
+ fromWorkspaceOptions = wir;
+ }
+
+ void setButtonPropertySetRecord(ButtonPropertySetRecord bpsr) {
+ fromButtonPropertySet = bpsr;
+ }
+
+ void setSheetWriter(SheetWriter sw) {
+ sheetWriter = sw;
+ }
+
+ DataValidation getDataValidation() {
+ return toDataValidation;
+ }
+
+ void setDataValidation(DataValidation dv) {
+ fromDataValidation = dv;
+ }
+
+ PLSRecord getPLSRecord() {
+ return toPLSRecord;
+ }
+
+ void setPLSRecord(PLSRecord plsr) {
+ fromPLSRecord = plsr;
+ }
+
+ boolean isChartOnly() {
+ return chartOnly;
+ }
+
+ ButtonPropertySetRecord getButtonPropertySet() {
+ return toButtonPropertySet;
+ }
+
+ /**
+ * Copies a sheet from a read-only version to the writable version.
+ * Performs shallow copies
+ */
+ public void copySheet() {
+ shallowCopyCells();
+
+ // Copy the column formats
+ Iterator cfit = fromColumnFormats.iterator();
+ while (cfit.hasNext()) {
+ ColumnInfoRecord cv = new ColumnInfoRecord
+ ((ColumnInfoRecord) cfit.next());
+ toColumnFormats.add(cv);
+ }
+
+ // Copy the merged cells
+ Range[] merged = fromMergedCells.getMergedCells();
+
+ for (int i = 0; i < merged.length; i++) {
+ toMergedCells.add(new SheetRangeImpl((SheetRangeImpl) merged[i],
+ toSheet));
+ }
+
+ try {
+ RowRecord row = null;
+ RowRecord newRow = null;
+ for (int i = 0; i < fromRows.length; i++) {
+ row = fromRows[i];
+
+ if (row != null &&
+ (!row.isDefaultHeight() ||
+ row.isCollapsed())) {
+ newRow = toSheet.getRowRecord(i);
+ newRow.setRowDetails(row.getRowHeight(),
+ row.matchesDefaultFontHeight(),
+ row.isCollapsed(),
+ row.getOutlineLevel(),
+ row.getGroupStart(),
+ row.getStyle());
+ }
+ }
+ } catch (RowsExceededException e) {
+ // Handle the rows exceeded exception - this cannot occur since
+ // the sheet we are copying from will have a valid number of rows
+ Assert.verify(false);
+ }
+
+ // Copy the horizontal page breaks
+ toRowBreaks = new ArrayList(fromRowBreaks);
+
+ // Copy the vertical page breaks
+ toColumnBreaks = new ArrayList(fromColumnBreaks);
+
+ // Copy the data validations
+ if (fromDataValidation != null) {
+ toDataValidation = new DataValidation
+ (fromDataValidation,
+ toSheet.getWorkbook(),
+ toSheet.getWorkbook(),
+ toSheet.getWorkbook().getSettings());
+ }
+
+ // Copy the charts
+ sheetWriter.setCharts(fromSheet.getCharts());
+
+ // Copy the drawings
+ for (Iterator i = fromDrawings.iterator(); i.hasNext(); ) {
+ Object o = i.next();
+ if (o instanceof jxl.biff.drawing.Drawing) {
+ WritableImage wi = new WritableImage
+ ((jxl.biff.drawing.Drawing) o,
+ toSheet.getWorkbook().getDrawingGroup());
+ toDrawings.add(wi);
+ toImages.add(wi);
+ }
+
+ // Not necessary to copy the comments, as they will be handled by
+ // the deep copy of the individual cells
+ }
+
+ // Copy the workspace options
+ sheetWriter.setWorkspaceOptions(fromWorkspaceOptions);
+
+ // Copy the environment specific print record
+ if (fromPLSRecord != null) {
+ toPLSRecord = new PLSRecord(fromPLSRecord);
+ }
+
+ // Copy the button property set
+ if (fromButtonPropertySet != null) {
+ toButtonPropertySet = new ButtonPropertySetRecord(fromButtonPropertySet);
+ }
+
+ // Copy the hyperlinks
+ for (Iterator i = fromHyperlinks.iterator(); i.hasNext(); ) {
+ WritableHyperlink hr = new WritableHyperlink
+ ((WritableHyperlink) i.next(), toSheet);
+ toHyperlinks.add(hr);
+ }
+ }
+
+ /**
+ * Performs a shallow copy of the specified cell
+ */
+ private WritableCell shallowCopyCell(Cell cell) {
+ CellType ct = cell.getType();
+ WritableCell newCell = null;
+
+ if (ct == CellType.LABEL) {
+ newCell = new Label((LabelCell) cell);
+ } else if (ct == CellType.NUMBER) {
+ newCell = new Number((NumberCell) cell);
+ } else if (ct == CellType.DATE) {
+ newCell = new DateTime((DateCell) cell);
+ } else if (ct == CellType.BOOLEAN) {
+ newCell = new Boolean((BooleanCell) cell);
+ } else if (ct == CellType.NUMBER_FORMULA) {
+ newCell = new ReadNumberFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.STRING_FORMULA) {
+ newCell = new ReadStringFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.BOOLEAN_FORMULA) {
+ newCell = new ReadBooleanFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.DATE_FORMULA) {
+ newCell = new ReadDateFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.FORMULA_ERROR) {
+ newCell = new ReadErrorFormulaRecord((FormulaData) cell);
+ } else if (ct == CellType.EMPTY) {
+ if (cell.getCellFormat() != null) {
+ // It is a blank cell, rather than an empty cell, so
+ // it may have formatting information, so
+ // it must be copied
+ newCell = new Blank(cell);
+ }
+ }
+
+ return newCell;
+ }
+
+ /**
+ * Performs a deep copy of the specified cell, handling the cell format
+ *
+ * @param cell the cell to copy
+ */
+ private WritableCell deepCopyCell(Cell cell) {
+ WritableCell c = shallowCopyCell(cell);
+
+ if (c == null) {
+ return c;
+ }
+
+ if (c instanceof ReadFormulaRecord) {
+ ReadFormulaRecord rfr = (ReadFormulaRecord) c;
+ boolean crossSheetReference = !rfr.handleImportedCellReferences
+ (fromSheet.getWorkbook(),
+ fromSheet.getWorkbook(),
+ workbookSettings);
+
+ if (crossSheetReference) {
+ try {
+ logger.warn("Formula " + rfr.getFormula() +
+ " in cell " +
+ CellReferenceHelper.getCellReference(cell.getColumn(),
+ cell.getRow()) +
+ " cannot be imported because it references another " +
+ " sheet from the source workbook");
+ } catch (FormulaException e) {
+ logger.warn("Formula in cell " +
+ CellReferenceHelper.getCellReference(cell.getColumn(),
+ cell.getRow()) +
+ " cannot be imported: " + e.getMessage());
+ }
+
+ // Create a new error formula and add it instead
+ c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\"");
+ }
+ }
+
+ // Copy the cell format
+ CellFormat cf = c.getCellFormat();
+ int index = ((XFRecord) cf).getXFIndex();
+ WritableCellFormat wcf = (WritableCellFormat)
+ xfRecords.get(new Integer(index));
+
+ if (wcf == null) {
+ wcf = copyCellFormat(cf);
+ }
+
+ c.setCellFormat(wcf);
+
+ return c;
+ }
+
+ /**
+ * Perform a shallow copy of the cells from the specified sheet into this one
+ */
+ void shallowCopyCells() {
+ // Copy the cells
+ int cells = fromSheet.getRows();
+ Cell[] row = null;
+ Cell cell = null;
+ for (int i = 0; i < cells; i++) {
+ row = fromSheet.getRow(i);
+
+ for (int j = 0; j < row.length; j++) {
+ cell = row[j];
+ WritableCell c = shallowCopyCell(cell);
+
+ // Encase the calls to addCell in a try-catch block
+ // These should not generate any errors, because we are
+ // copying from an existing spreadsheet. In the event of
+ // errors, catch the exception and then bomb out with an
+ // assertion
+ try {
+ if (c != null) {
+ toSheet.addCell(c);
+
+ // Cell.setCellFeatures short circuits when the cell is copied,
+ // so make sure the copy logic handles the validated cells
+ if (c.getCellFeatures() != null &
+ c.getCellFeatures().hasDataValidation()) {
+ validatedCells.add(c);
+ }
+ }
+ } catch (WriteException e) {
+ Assert.verify(false);
+ }
+ }
+ }
+ numRows = toSheet.getRows();
+ }
+
+ /**
+ * Perform a deep copy of the cells from the specified sheet into this one
+ */
+ void deepCopyCells() {
+ // Copy the cells
+ int cells = fromSheet.getRows();
+ Cell[] row = null;
+ Cell cell = null;
+ for (int i = 0; i < cells; i++) {
+ row = fromSheet.getRow(i);
+
+ for (int j = 0; j < row.length; j++) {
+ cell = row[j];
+ WritableCell c = deepCopyCell(cell);
+
+ // Encase the calls to addCell in a try-catch block
+ // These should not generate any errors, because we are
+ // copying from an existing spreadsheet. In the event of
+ // errors, catch the exception and then bomb out with an
+ // assertion
+ try {
+ if (c != null) {
+ toSheet.addCell(c);
+
+ // Cell.setCellFeatures short circuits when the cell is copied,
+ // so make sure the copy logic handles the validated cells
+ if (c.getCellFeatures() != null &
+ c.getCellFeatures().hasDataValidation()) {
+ validatedCells.add(c);
+ }
+ }
+ } catch (WriteException e) {
+ Assert.verify(false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an initialized copy of the cell format
+ *
+ * @param cf the cell format to copy
+ * @return a deep copy of the cell format
+ */
+ private WritableCellFormat copyCellFormat(CellFormat cf) {
+ try {
+ // just do a deep copy of the cell format for now. This will create
+ // a copy of the format and font also - in the future this may
+ // need to be sorted out
+ XFRecord xfr = (XFRecord) cf;
+ WritableCellFormat f = new WritableCellFormat(xfr);
+ formatRecords.addStyle(f);
+
+ // Maintain the local list of formats
+ int xfIndex = xfr.getXFIndex();
+ xfRecords.put(new Integer(xfIndex), f);
+
+ int fontIndex = xfr.getFontIndex();
+ fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex()));
+
+ int formatIndex = xfr.getFormatRecord();
+ formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord()));
+
+ return f;
+ } catch (NumFormatRecordsException e) {
+ logger.warn("Maximum number of format records exceeded. Using " +
+ "default format.");
+
+ return WritableWorkbook.NORMAL_STYLE;
+ }
+ }
+
+
+ /**
+ * Accessor for the maximum column outline level
+ *
+ * @return the maximum column outline level, or 0 if no outlines/groups
+ */
+ public int getMaxColumnOutlineLevel() {
+ return maxColumnOutlineLevel;
+ }
+
+ /**
+ * Accessor for the maximum row outline level
+ *
+ * @return the maximum row outline level, or 0 if no outlines/groups
+ */
+ public int getMaxRowOutlineLevel() {
+ return maxRowOutlineLevel;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java
new file mode 100755
index 0000000..e4478ba
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableSheetImpl.java
@@ -0,0 +1,2535 @@
+/**********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import jxl.Cell;
+import jxl.CellFeatures;
+import jxl.CellReferenceHelper;
+import jxl.CellType;
+import jxl.CellView;
+import jxl.HeaderFooter;
+import jxl.Hyperlink;
+import jxl.Image;
+import jxl.LabelCell;
+import jxl.Range;
+import jxl.Sheet;
+import jxl.SheetSettings;
+import jxl.WorkbookSettings;
+import jxl.biff.AutoFilter;
+import jxl.biff.CellFinder;
+import jxl.biff.ConditionalFormat;
+import jxl.biff.DVParser;
+import jxl.biff.DataValidation;
+import jxl.biff.EmptyCell;
+import jxl.biff.FormattingRecords;
+import jxl.biff.IndexMapping;
+import jxl.biff.NumFormatRecordsException;
+import jxl.biff.SheetRangeImpl;
+import jxl.biff.WorkspaceInformationRecord;
+import jxl.biff.XFRecord;
+import jxl.biff.drawing.Chart;
+import jxl.biff.drawing.ComboBox;
+import jxl.biff.drawing.Drawing;
+import jxl.biff.drawing.DrawingGroupObject;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.format.CellFormat;
+import jxl.format.Font;
+import jxl.format.PageOrientation;
+import jxl.format.PaperSize;
+import jxl.write.Blank;
+import jxl.write.Label;
+import jxl.write.WritableCell;
+import jxl.write.WritableCellFeatures;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableHyperlink;
+import jxl.write.WritableImage;
+import jxl.write.WritableSheet;
+import jxl.write.WritableWorkbook;
+import jxl.write.WriteException;
+
+/**
+ * A writable sheet. This class contains implementation of all the
+ * writable sheet methods which may be invoke by the API
+ */
+class WritableSheetImpl implements WritableSheet {
+ /**
+ * The amount by which to grow the rows array
+ */
+ private final static int rowGrowSize = 10;
+ /**
+ * The maximum number of rows excel allows in a worksheet
+ */
+ private final static int numRowsPerSheet = 65536;
+ /**
+ * The maximum number of characters permissible for a sheet name
+ */
+ private final static int maxSheetNameLength = 31;
+ /**
+ * The illegal characters for a sheet name
+ */
+ private final static char[] illegalSheetNameCharacters =
+ new char[]{'*', ':', '?', '\\'};
+ /**
+ * The supported file types
+ */
+ private static final String[] imageTypes = new String[]{"png"};
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(WritableSheetImpl.class);
+ /**
+ * The name of this sheet
+ */
+ private String name;
+ /**
+ * A handle to the output file which the binary data is written to
+ */
+ private final File outputFile;
+ /**
+ * The rows within this sheet
+ */
+ private RowRecord[] rows;
+ /**
+ * A handle to workbook format records
+ */
+ private final FormattingRecords formatRecords;
+ /**
+ * A handle to the shared strings used by this workbook
+ */
+ private final SharedStrings sharedStrings;
+ /**
+ * The list of non-default column formats
+ */
+ private final TreeSet columnFormats;
+ /**
+ * The list of autosized columns
+ */
+ private TreeSet autosizedColumns;
+ /**
+ * The list of hyperlinks
+ */
+ private final ArrayList hyperlinks;
+ /**
+ * The list of merged ranged
+ */
+ private final MergedCells mergedCells;
+ /**
+ * A number of rows. This is a count of the maximum row number + 1
+ */
+ private int numRows;
+ /**
+ * The number of columns. This is a count of the maximum column number + 1
+ */
+ private int numColumns;
+ /**
+ * The environment specific print record, copied from the read spreadsheet
+ */
+ private PLSRecord plsRecord;
+ /**
+ * The buttons property set
+ */
+ private ButtonPropertySetRecord buttonPropertySet;
+ /**
+ * A flag indicating that this sheet is a chart only
+ */
+ private boolean chartOnly;
+ /**
+ * The data validations on this page. Used to store data validations
+ * from a read sheet
+ */
+ private DataValidation dataValidation;
+ /**
+ * Array of row page breaks
+ */
+ private ArrayList rowBreaks;
+ /**
+ * Array of column page breaks
+ */
+ private ArrayList columnBreaks;
+ /**
+ * The drawings on this sheet
+ */
+ private final ArrayList drawings;
+ /**
+ * The images on this sheet. This is a subset of the drawings list
+ */
+ private final ArrayList images;
+ /**
+ * The conditional formats on this sheet
+ */
+ private final ArrayList conditionalFormats;
+ /**
+ * The autofilter
+ */
+ private AutoFilter autoFilter;
+ /**
+ * The writable cells on this sheet which may have validation added
+ * to them
+ */
+ private final ArrayList validatedCells;
+ /**
+ * The combo box object used for list validations on this sheet
+ */
+ private ComboBox comboBox;
+ /**
+ * Drawings modified flag. Set to true if the drawings list has
+ * been modified
+ */
+ private boolean drawingsModified;
+ /**
+ * The maximum row outline level
+ */
+ private int maxRowOutlineLevel;
+ /**
+ * The maximum column outline level
+ */
+ private int maxColumnOutlineLevel;
+ /**
+ * The settings for this sheet
+ */
+ private SheetSettings settings;
+ /**
+ * The sheet writer engine
+ */
+ private final SheetWriter sheetWriter;
+ /**
+ * The settings for the workbook
+ */
+ private final WorkbookSettings workbookSettings;
+ /**
+ * The workbook
+ */
+ private final WritableWorkbookImpl workbook;
+
+ /**
+ * Constructor
+ *
+ * @param fr the formatting records used by the workbook
+ * @param of the output file to write the binary data
+ * @param f the fonts used by the workbook
+ * @param n the name of this sheet
+ * @param ss the shared strings used by the workbook
+ * @param ws the workbook settings
+ */
+ public WritableSheetImpl(String n,
+ File of,
+ FormattingRecords fr,
+ SharedStrings ss,
+ WorkbookSettings ws,
+ WritableWorkbookImpl ww) {
+ name = validateName(n);
+ outputFile = of;
+ rows = new RowRecord[0];
+ numRows = 0;
+ numColumns = 0;
+ chartOnly = false;
+ workbook = ww;
+
+ formatRecords = fr;
+ sharedStrings = ss;
+ workbookSettings = ws;
+ drawingsModified = false;
+ columnFormats = new TreeSet(new ColumnInfoComparator());
+ autosizedColumns = new TreeSet();
+ hyperlinks = new ArrayList();
+ mergedCells = new MergedCells(this);
+ rowBreaks = new ArrayList();
+ columnBreaks = new ArrayList();
+ drawings = new ArrayList();
+ images = new ArrayList();
+ conditionalFormats = new ArrayList();
+ validatedCells = new ArrayList();
+ settings = new SheetSettings(this);
+
+
+ sheetWriter = new SheetWriter(outputFile,
+ this,
+ workbookSettings);
+ }
+
+ /**
+ * Returns the cell for the specified location eg. "A4", using the
+ * CellReferenceHelper
+ *
+ * @param loc the cell reference
+ * @return the cell at the specified co-ordinates
+ */
+ public Cell getCell(String loc) {
+ return getCell(CellReferenceHelper.getColumn(loc),
+ CellReferenceHelper.getRow(loc));
+ }
+
+ /**
+ * Returns the cell specified at this row and at this column
+ *
+ * @param column the column number
+ * @param row the row number
+ * @return the cell at the specified co-ordinates
+ */
+ public Cell getCell(int column, int row) {
+ return getWritableCell(column, row);
+ }
+
+ /**
+ * Returns the cell for the specified location eg. "A4". Note that this
+ * method is identical to calling getCell(CellReferenceHelper.getColumn(loc),
+ * CellReferenceHelper.getRow(loc)) and its implicit performance
+ * overhead for string parsing. As such,this method should therefore
+ * be used sparingly
+ *
+ * @param loc the cell reference
+ * @return the cell at the specified co-ordinates
+ */
+ public WritableCell getWritableCell(String loc) {
+ return getWritableCell(CellReferenceHelper.getColumn(loc),
+ CellReferenceHelper.getRow(loc));
+ }
+
+ /**
+ * Returns the cell specified at this row and at this column
+ *
+ * @param column the column number
+ * @param row the row number
+ * @return the cell at the specified co-ordinates
+ */
+ public WritableCell getWritableCell(int column, int row) {
+ WritableCell c = null;
+
+ if (row < rows.length && rows[row] != null) {
+ c = rows[row].getCell(column);
+ }
+
+ if (c == null) {
+ c = new EmptyCell(column, row);
+ }
+
+ return c;
+ }
+
+ /**
+ * Returns the number of rows in this sheet
+ *
+ * @return the number of rows in this sheet
+ */
+ public int getRows() {
+ return numRows;
+ }
+
+ /**
+ * Returns the number of columns in this sheet
+ *
+ * @return the number of columns in this sheet
+ */
+ public int getColumns() {
+ return numColumns;
+ }
+
+ /**
+ * Gets the cell whose contents match the string passed in.
+ * If no match is found, then null is returned. The search is performed
+ * on a row by row basis, so the lower the row number, the more
+ * efficiently the algorithm will perform
+ *
+ * @param contents the string to match
+ * @return the Cell whose contents match the paramter, null if not found
+ */
+ public Cell findCell(String contents) {
+ CellFinder cellFinder = new CellFinder(this);
+ return cellFinder.findCell(contents);
+ }
+
+ /**
+ * Gets the cell whose contents match the string passed in.
+ * If no match is found, then null is returned. The search is performed
+ * on a row by row basis, so the lower the row number, the more
+ * efficiently the algorithm will perform
+ *
+ * @param contents the string to match
+ * @param firstCol the first column within the range
+ * @param firstRow the first row of the range
+ * @param lastCol the last column within the range
+ * @param lastRow the last row within the range
+ * @param reverse indicates whether to perform a reverse search or not
+ * @return the Cell whose contents match the parameter, null if not found
+ */
+ public Cell findCell(String contents,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow,
+ boolean reverse) {
+ CellFinder cellFinder = new CellFinder(this);
+ return cellFinder.findCell(contents,
+ firstCol,
+ firstRow,
+ lastCol,
+ lastRow,
+ reverse);
+ }
+
+ /**
+ * Gets the cell whose contents match the regular expressionstring passed in.
+ * If no match is found, then null is returned. The search is performed
+ * on a row by row basis, so the lower the row number, the more
+ * efficiently the algorithm will perform
+ *
+ * @param pattern the regular expression string to match
+ * @param firstCol the first column within the range
+ * @param firstRow the first row of the range
+ * @param lastRow the last row within the range
+ * @param lastCol the last column within the ranage
+ * @param reverse indicates whether to perform a reverse search or not
+ * @return the Cell whose contents match the parameter, null if not found
+ */
+ public Cell findCell(Pattern pattern,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow,
+ boolean reverse) {
+ CellFinder cellFinder = new CellFinder(this);
+ return cellFinder.findCell(pattern,
+ firstCol,
+ firstRow,
+ lastCol,
+ lastRow,
+ reverse);
+ }
+
+ /**
+ * Gets the cell whose contents match the string passed in.
+ * If no match is found, then null is returned. The search is performed
+ * on a row by row basis, so the lower the row number, the more
+ * efficiently the algorithm will perform. This method differs
+ * from the findCell methods in that only cells with labels are
+ * queried - all numerical cells are ignored. This should therefore
+ * improve performance.
+ *
+ * @param contents the string to match
+ * @return the Cell whose contents match the paramter, null if not found
+ */
+ public LabelCell findLabelCell(String contents) {
+ CellFinder cellFinder = new CellFinder(this);
+ return cellFinder.findLabelCell(contents);
+ }
+
+ /**
+ * Gets all the cells on the specified row
+ *
+ * @param row the rows whose cells are to be returned
+ * @return the cells on the given row
+ */
+ public Cell[] getRow(int row) {
+ // Find the last non-null cell
+ boolean found = false;
+ int col = numColumns - 1;
+ while (col >= 0 && !found) {
+ if (getCell(col, row).getType() != CellType.EMPTY) {
+ found = true;
+ } else {
+ col--;
+ }
+ }
+
+ // Only create entries for non-empty cells
+ Cell[] cells = new Cell[col + 1];
+
+ for (int i = 0; i <= col; i++) {
+ cells[i] = getCell(i, row);
+ }
+ return cells;
+ }
+
+ /**
+ * Gets all the cells on the specified column
+ *
+ * @param col the column whose cells are to be returned
+ * @return the cells on the specified column
+ */
+ public Cell[] getColumn(int col) {
+ // Find the last non-null cell
+ boolean found = false;
+ int row = numRows - 1;
+
+ while (row >= 0 && !found) {
+ if (getCell(col, row).getType() != CellType.EMPTY) {
+ found = true;
+ } else {
+ row--;
+ }
+ }
+
+ // Only create entries for non-empty cells
+ Cell[] cells = new Cell[row + 1];
+
+ for (int i = 0; i <= row; i++) {
+ cells[i] = getCell(col, i);
+ }
+ return cells;
+ }
+
+ /**
+ * Gets the name of this sheet
+ *
+ * @return the name of the sheet
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of this worksheet
+ *
+ * @param n the name of this sheet
+ */
+ public void setName(String n) {
+ name = n;
+ }
+
+ /**
+ * Inserts a blank row into this spreadsheet. If the row is out of range
+ * of the rows in the sheet, then no action is taken
+ *
+ * @param row the row to insert
+ */
+ public void insertRow(int row) {
+ if (row < 0 || row >= numRows) {
+ return;
+ }
+
+ // Create a new array to hold the new rows. Grow it if need be
+ RowRecord[] oldRows = rows;
+
+ if (numRows == rows.length) {
+ rows = new RowRecord[oldRows.length + rowGrowSize];
+ } else {
+ rows = new RowRecord[oldRows.length];
+ }
+
+ // Copy in everything up to the new row
+ System.arraycopy(oldRows, 0, rows, 0, row);
+
+ // Copy in the remaining rows
+ System.arraycopy(oldRows, row, rows, row + 1, numRows - row);
+
+ // Increment all the internal row number by one
+ for (int i = row + 1; i <= numRows; i++) {
+ if (rows[i] != null) {
+ rows[i].incrementRow();
+ }
+ }
+
+ // Adjust any hyperlinks
+ HyperlinkRecord hr = null;
+ Iterator i = hyperlinks.iterator();
+ while (i.hasNext()) {
+ hr = (HyperlinkRecord) i.next();
+ hr.insertRow(row);
+ }
+
+ // Adjust any data validations
+ if (dataValidation != null) {
+ dataValidation.insertRow(row);
+ }
+
+ if (validatedCells != null && validatedCells.size() > 0) {
+ for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) {
+ CellValue cv = (CellValue) vci.next();
+ CellFeatures cf = cv.getCellFeatures();
+ if (cf.getDVParser() != null) {
+ cf.getDVParser().insertRow(row);
+ }
+ }
+ }
+
+ // Adjust any merged cells
+ mergedCells.insertRow(row);
+
+ // Adjust any page breaks
+ ArrayList newRowBreaks = new ArrayList();
+ Iterator ri = rowBreaks.iterator();
+ while (ri.hasNext()) {
+ int val = ((Integer) ri.next()).intValue();
+ if (val >= row) {
+ val++;
+ }
+
+ newRowBreaks.add(new Integer(val));
+ }
+ rowBreaks = newRowBreaks;
+
+ // Adjust any conditional formats
+ for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) {
+ ConditionalFormat cf = (ConditionalFormat) cfit.next();
+ cf.insertRow(row);
+ }
+
+ // Handle interested cell references on the main workbook
+ if (workbookSettings.getFormulaAdjust()) {
+ workbook.rowInserted(this, row);
+ }
+
+ // Adjust the maximum row record
+ numRows++;
+ }
+
+ /**
+ * Inserts a blank column into this spreadsheet. If the column is out of
+ * range of the columns in the sheet, then no action is taken. If the
+ * max column on the sheet has been reached, then the last column entry
+ * gets dropped
+ *
+ * @param col the column to insert
+ */
+ public void insertColumn(int col) {
+ if (col < 0 || col >= numColumns) {
+ return;
+ }
+
+ // Iterate through all the row records adding in the column
+ for (int i = 0; i < numRows; i++) {
+ if (rows[i] != null) {
+ rows[i].insertColumn(col);
+ }
+ }
+
+ // Adjust any hyperlinks
+ HyperlinkRecord hr = null;
+ Iterator i = hyperlinks.iterator();
+ while (i.hasNext()) {
+ hr = (HyperlinkRecord) i.next();
+ hr.insertColumn(col);
+ }
+
+ // Iterate through the column views, incrementing the column number
+ i = columnFormats.iterator();
+ while (i.hasNext()) {
+ ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
+
+ if (cir.getColumn() >= col) {
+ cir.incrementColumn();
+ }
+ }
+
+ // Iterate through the autosized columns, incrementing the column number
+ if (autosizedColumns.size() > 0) {
+ TreeSet newAutosized = new TreeSet();
+ i = autosizedColumns.iterator();
+ while (i.hasNext()) {
+ Integer colnumber = (Integer) i.next();
+
+ if (colnumber.intValue() >= col) {
+ newAutosized.add(new Integer(colnumber.intValue() + 1));
+ } else {
+ newAutosized.add(colnumber);
+ }
+ }
+ autosizedColumns = newAutosized;
+ }
+
+ // Handle any data validations
+ if (dataValidation != null) {
+ dataValidation.insertColumn(col);
+ }
+
+ if (validatedCells != null && validatedCells.size() > 0) {
+ for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) {
+ CellValue cv = (CellValue) vci.next();
+ CellFeatures cf = cv.getCellFeatures();
+ if (cf.getDVParser() != null) {
+ cf.getDVParser().insertColumn(col);
+ }
+ }
+ }
+
+ // Adjust any merged cells
+ mergedCells.insertColumn(col);
+
+ // Adjust any page breaks
+ ArrayList newColumnBreaks = new ArrayList();
+ Iterator ri = columnBreaks.iterator();
+ while (ri.hasNext()) {
+ int val = ((Integer) ri.next()).intValue();
+ if (val >= col) {
+ val++;
+ }
+
+ newColumnBreaks.add(new Integer(val));
+ }
+ columnBreaks = newColumnBreaks;
+
+ // Adjust any conditional formats
+ for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) {
+ ConditionalFormat cf = (ConditionalFormat) cfit.next();
+ cf.insertColumn(col);
+ }
+
+ // Handle interested cell references on the main workbook
+ if (workbookSettings.getFormulaAdjust()) {
+ workbook.columnInserted(this, col);
+ }
+
+ numColumns++;
+ }
+
+ /**
+ * Removes a column from this spreadsheet. If the column is out of range
+ * of the columns in the sheet, then no action is taken
+ *
+ * @param col the column to remove
+ */
+ public void removeColumn(int col) {
+ if (col < 0 || col >= numColumns) {
+ return;
+ }
+
+ // Iterate through all the row records removing the column
+ for (int i = 0; i < numRows; i++) {
+ if (rows[i] != null) {
+ rows[i].removeColumn(col);
+ }
+ }
+
+ // Adjust any hyperlinks
+ HyperlinkRecord hr = null;
+ Iterator i = hyperlinks.iterator();
+ while (i.hasNext()) {
+ hr = (HyperlinkRecord) i.next();
+
+ if (hr.getColumn() == col &&
+ hr.getLastColumn() == col) {
+ // The row with the hyperlink on has been removed, so get
+ // rid of it from the list
+ i.remove();
+ } else {
+ hr.removeColumn(col);
+ }
+ }
+
+ // Adjust any data validations
+ if (dataValidation != null) {
+ dataValidation.removeColumn(col);
+ }
+
+ if (validatedCells != null && validatedCells.size() > 0) {
+ for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) {
+ CellValue cv = (CellValue) vci.next();
+ CellFeatures cf = cv.getCellFeatures();
+ if (cf.getDVParser() != null) {
+ cf.getDVParser().removeColumn(col);
+ }
+ }
+ }
+
+ // Adjust any merged cells
+ mergedCells.removeColumn(col);
+
+ // Adjust any page breaks
+ ArrayList newColumnBreaks = new ArrayList();
+ Iterator ri = columnBreaks.iterator();
+ while (ri.hasNext()) {
+ int val = ((Integer) ri.next()).intValue();
+
+ if (val != col) {
+ if (val > col) {
+ val--;
+ }
+
+ newColumnBreaks.add(new Integer(val));
+ }
+ }
+
+ columnBreaks = newColumnBreaks;
+
+
+ // Iterate through the column views, decrementing the column number
+ i = columnFormats.iterator();
+ ColumnInfoRecord removeColumn = null;
+ while (i.hasNext()) {
+ ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
+
+ if (cir.getColumn() == col) {
+ removeColumn = cir;
+ } else if (cir.getColumn() > col) {
+ cir.decrementColumn();
+ }
+ }
+
+ if (removeColumn != null) {
+ columnFormats.remove(removeColumn);
+ }
+
+ // Iterate through the autosized columns, decrementing the column number
+ if (autosizedColumns.size() > 0) {
+ TreeSet newAutosized = new TreeSet();
+ i = autosizedColumns.iterator();
+ while (i.hasNext()) {
+ Integer colnumber = (Integer) i.next();
+
+ if (colnumber.intValue() == col) {
+ // do nothing
+ } else if (colnumber.intValue() > col) {
+ newAutosized.add(new Integer(colnumber.intValue() - 1));
+ } else {
+ newAutosized.add(colnumber);
+ }
+ }
+ autosizedColumns = newAutosized;
+ }
+
+ // Adjust any conditional formats
+ for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) {
+ ConditionalFormat cf = (ConditionalFormat) cfit.next();
+ cf.removeColumn(col);
+ }
+
+ // Handle interested cell references on the main workbook
+ if (workbookSettings.getFormulaAdjust()) {
+ workbook.columnRemoved(this, col);
+ }
+
+ numColumns--;
+ }
+
+ /**
+ * Removes a row from this spreadsheet. If the row is out of
+ * range of the columns in the sheet, then no action is taken
+ *
+ * @param row the row to remove
+ */
+ public void removeRow(int row) {
+ if (row < 0 || row >= numRows) {
+ // Call rowRemoved anyway, to adjust the named cells
+ if (workbookSettings.getFormulaAdjust()) {
+ workbook.rowRemoved(this, row);
+ }
+
+ return;
+ }
+
+ // Create a new array to hold the new rows. Grow it if need be
+ RowRecord[] oldRows = rows;
+
+ rows = new RowRecord[oldRows.length];
+
+ // Copy in everything up to the row to be removed
+ System.arraycopy(oldRows, 0, rows, 0, row);
+
+ // Copy in the remaining rows
+ System.arraycopy(oldRows, row + 1, rows, row, numRows - (row + 1));
+
+ // Decrement all the internal row numbers by one
+ for (int i = row; i < numRows; i++) {
+ if (rows[i] != null) {
+ rows[i].decrementRow();
+ }
+ }
+
+ // Adjust any hyperlinks
+ HyperlinkRecord hr = null;
+ Iterator i = hyperlinks.iterator();
+ while (i.hasNext()) {
+ hr = (HyperlinkRecord) i.next();
+
+ if (hr.getRow() == row &&
+ hr.getLastRow() == row) {
+ // The row with the hyperlink on has been removed, so get
+ // rid of it from the list
+ i.remove();
+ } else {
+ hr.removeRow(row);
+ }
+ }
+
+ // Adjust any data validations
+ if (dataValidation != null) {
+ dataValidation.removeRow(row);
+ }
+
+ if (validatedCells != null && validatedCells.size() > 0) {
+ for (Iterator vci = validatedCells.iterator(); vci.hasNext(); ) {
+ CellValue cv = (CellValue) vci.next();
+ CellFeatures cf = cv.getCellFeatures();
+ if (cf.getDVParser() != null) {
+ cf.getDVParser().removeRow(row);
+ }
+ }
+ }
+
+ // Adjust any merged cells
+ mergedCells.removeRow(row);
+
+ // Adjust any page breaks
+ ArrayList newRowBreaks = new ArrayList();
+ Iterator ri = rowBreaks.iterator();
+ while (ri.hasNext()) {
+ int val = ((Integer) ri.next()).intValue();
+
+ if (val != row) {
+ if (val > row) {
+ val--;
+ }
+
+ newRowBreaks.add(new Integer(val));
+ }
+ }
+
+ rowBreaks = newRowBreaks;
+
+ // Adjust any conditional formats
+ for (Iterator cfit = conditionalFormats.iterator(); cfit.hasNext(); ) {
+ ConditionalFormat cf = (ConditionalFormat) cfit.next();
+ cf.removeRow(row);
+ }
+
+ // Handle interested cell references on the main workbook
+ if (workbookSettings.getFormulaAdjust()) {
+ workbook.rowRemoved(this, row);
+ }
+
+ // Adjust any drawings
+ /*
+ if (drawings != null)
+ {
+ for (Iterator drawingIt = drawings.iterator() ; drawingIt.hasNext() ; )
+ {
+ DrawingGroupObject dgo = (DrawingGroupObject) drawingIt.next();
+ dgo.removeRow(row);
+ }
+ }
+ */
+
+ // Adjust the maximum row record
+ numRows--;
+ }
+
+ /**
+ * Adds the cell to this sheet. If the cell has already been added to
+ * this sheet or another sheet, a WriteException is thrown. If the
+ * position to be occupied by this cell is already taken, the incumbent
+ * cell is replaced.
+ * The cell is then marked as referenced, and its formatting information
+ * registered with the list of formatting records updated if necessary
+ * The RowsExceededException may be caught if client code wishes to
+ * explicitly trap the case where too many rows have been written
+ * to the current sheet. If this behaviour is not desired, it is
+ * sufficient simply to handle the WriteException, since this is a base
+ * class of RowsExceededException
+ *
+ * @param cell the cell to add
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void addCell(WritableCell cell)
+ throws WriteException, RowsExceededException {
+ if (cell.getType() == CellType.EMPTY) {
+ if (cell != null && cell.getCellFormat() == null) {
+ // return if it's a blank cell with no particular cell formatting
+ // information
+ return;
+ }
+ }
+
+ CellValue cv = (CellValue) cell;
+
+ if (cv.isReferenced()) {
+ throw new JxlWriteException(JxlWriteException.cellReferenced);
+ }
+
+ int row = cell.getRow();
+ RowRecord rowrec = getRowRecord(row);
+
+ CellValue curcell = rowrec.getCell(cv.getColumn());
+ boolean curSharedValidation = (curcell != null &&
+ curcell.getCellFeatures() != null &&
+ curcell.getCellFeatures().getDVParser() != null &&
+ curcell.getCellFeatures().getDVParser().extendedCellsValidation());
+
+ // Check for shared data validations, but only if the cell being added
+ // has a data validation
+ if (cell.getCellFeatures() != null &&
+ cell.getCellFeatures().hasDataValidation() &&
+ curSharedValidation) {
+ DVParser dvp = curcell.getCellFeatures().getDVParser();
+ logger.warn("Cannot add cell at " +
+ CellReferenceHelper.getCellReference(cv) +
+ " because it is part of the shared cell validation group " +
+ CellReferenceHelper.getCellReference(dvp.getFirstColumn(),
+ dvp.getFirstRow()) +
+ "-" +
+ CellReferenceHelper.getCellReference(dvp.getLastColumn(),
+ dvp.getLastRow()));
+ return;
+ }
+
+ // Apply any shared validation from the current cell to this cell
+ if (curSharedValidation) {
+ WritableCellFeatures wcf = cell.getWritableCellFeatures();
+
+ if (wcf == null) {
+ wcf = new WritableCellFeatures();
+ cell.setCellFeatures(wcf);
+ }
+
+ wcf.shareDataValidation(curcell.getCellFeatures());
+ }
+
+ rowrec.addCell(cv);
+
+ // Adjust the max rows and max columns accordingly
+ numRows = Math.max(row + 1, numRows);
+ numColumns = Math.max(numColumns, rowrec.getMaxColumn());
+
+ // Indicate this cell is now part of a worksheet, so that it can't be
+ // added anywhere else
+ cv.setCellDetails(formatRecords, sharedStrings, this);
+ }
+
+ /**
+ * Gets the row record at the specified row number, growing the
+ * array as needs dictate
+ *
+ * @param row the row number we are interested in
+ * @return the row record at the specified row
+ * @throws RowsExceededException
+ */
+ RowRecord getRowRecord(int row) throws RowsExceededException {
+ if (row >= numRowsPerSheet) {
+ throw new RowsExceededException();
+ }
+
+ // Grow the array of rows if needs be
+ // Thanks to Brendan for spotting the flaw in merely adding on the
+ // grow size
+ if (row >= rows.length) {
+ RowRecord[] oldRows = rows;
+ rows = new RowRecord[Math.max(oldRows.length + rowGrowSize, row + 1)];
+ System.arraycopy(oldRows, 0, rows, 0, oldRows.length);
+ oldRows = null;
+ }
+
+ RowRecord rowrec = rows[row];
+
+ if (rowrec == null) {
+ rowrec = new RowRecord(row, this);
+ rows[row] = rowrec;
+ }
+
+ return rowrec;
+ }
+
+ /**
+ * Gets the row record for the specified row
+ *
+ * @param r the row
+ * @return the row record
+ */
+ RowRecord getRowInfo(int r) {
+ if (r < 0 || r > rows.length) {
+ return null;
+ }
+
+ return rows[r];
+ }
+
+ /**
+ * Gets the column info record for the specified column
+ *
+ * @param c the column
+ * @return the column record
+ */
+ ColumnInfoRecord getColumnInfo(int c) {
+ Iterator i = columnFormats.iterator();
+ ColumnInfoRecord cir = null;
+ boolean stop = false;
+
+ while (i.hasNext() && !stop) {
+ cir = (ColumnInfoRecord) i.next();
+
+ if (cir.getColumn() >= c) {
+ stop = true;
+ }
+ }
+
+ if (!stop) {
+ return null;
+ }
+
+ return cir.getColumn() == c ? cir : null;
+ }
+
+ /**
+ * Sets this sheet as selected
+ *
+ * @deprecated Use the settings bean
+ */
+ public void setSelected() {
+ settings.setSelected();
+ }
+
+ /**
+ * Retrieves the hidden status of this sheet
+ *
+ * @return TRUE if hidden, FALSE otherwise
+ * @deprecated Use the sheet settings bean instead
+ */
+ public boolean isHidden() {
+ return settings.isHidden();
+ }
+
+ /**
+ * Sets the hidden status of this sheet
+ *
+ * @param h the hiden flag
+ * @deprecated Use the settings bean instead
+ */
+ public void setHidden(boolean h) {
+ settings.setHidden(h);
+ }
+
+ /**
+ * Sets the width (in characters) for a particular column in this sheet
+ *
+ * @param col the column whose width to set
+ * @param width the width of the column in characters
+ */
+ public void setColumnView(int col, int width) {
+ CellView cv = new CellView();
+ cv.setSize(width * 256);
+ setColumnView(col, cv);
+ }
+
+ /**
+ * Sets the width (in characters) and format options for a
+ * particular column in this sheet
+ *
+ * @param col the column to set
+ * @param width the width in characters
+ * @param format the formt details for the column
+ */
+ public void setColumnView(int col, int width, CellFormat format) {
+ CellView cv = new CellView();
+ cv.setSize(width * 256);
+ cv.setFormat(format);
+ setColumnView(col, cv);
+ }
+
+ /**
+ * Sets the view for this column
+ *
+ * @param col the column on which to set the view
+ * @param view the view to set
+ */
+ public void setColumnView(int col, CellView view) {
+ XFRecord xfr = (XFRecord) view.getFormat();
+ if (xfr == null) {
+ Styles styles = getWorkbook().getStyles();
+ xfr = styles.getNormalStyle();
+ }
+
+ try {
+ if (!xfr.isInitialized()) {
+ formatRecords.addStyle(xfr);
+ }
+
+ int width = view.depUsed() ? view.getDimension() * 256 : view.getSize();
+
+ if (view.isAutosize()) {
+ autosizedColumns.add(new Integer(col));
+ }
+
+ ColumnInfoRecord cir = new ColumnInfoRecord(col,
+ width,
+ xfr);
+
+ if (view.isHidden()) {
+ cir.setHidden(true);
+ }
+
+ if (!columnFormats.contains(cir)) {
+ columnFormats.add(cir);
+ } else {
+ columnFormats.remove(cir);
+ columnFormats.add(cir);
+ }
+ } catch (NumFormatRecordsException e) {
+ logger.warn("Maximum number of format records exceeded. Using " +
+ "default format.");
+
+ ColumnInfoRecord cir = new ColumnInfoRecord
+ (col, view.getDimension() * 256, WritableWorkbook.NORMAL_STYLE);
+ columnFormats.add(cir);
+ }
+ }
+
+ /**
+ * Sets the height of the specified row, as well as its collapse status
+ *
+ * @param row the row to be formatted
+ * @param height the row height in 1/20ths of a point
+ * @throws RowsExceededException
+ * @deprecated use the override which takes a CellView object
+ */
+ public void setRowView(int row, int height) throws RowsExceededException {
+ CellView cv = new CellView();
+ cv.setSize(height);
+ cv.setHidden(false);
+ setRowView(row, cv);
+ }
+
+ /**
+ * Sets the height of the specified row, as well as its collapse status
+ *
+ * @param row the row to be formatted
+ * @param collapsed indicates whether the row is collapsed
+ * @throws RowsExceededException
+ * @deprecated use the override which takes a CellView object
+ */
+ public void setRowView(int row, boolean collapsed)
+ throws RowsExceededException {
+ CellView cv = new CellView();
+ cv.setHidden(collapsed);
+ setRowView(row, cv);
+ }
+
+ /**
+ * Sets the height of the specified row, as well as its collapse status
+ *
+ * @param row the row to be formatted
+ * @param height the row height in 1/20th of a point
+ * @param collapsed indicates whether the row is collapsed
+ * @param zeroHeight indicates that the row has zero height
+ * @throws RowsExceededException
+ * @deprecated use the override which takes a CellView object
+ */
+ public void setRowView(int row, int height,
+ boolean collapsed)
+ throws RowsExceededException {
+ CellView cv = new CellView();
+ cv.setSize(height);
+ cv.setHidden(collapsed);
+ setRowView(row, cv);
+ }
+
+ /**
+ * Sets the view for this column
+ *
+ * @param row the column on which to set the view
+ * @param view the view to set
+ * @throws RowsExceededException
+ */
+ public void setRowView(int row, CellView view) throws RowsExceededException {
+ RowRecord rowrec = getRowRecord(row);
+
+ XFRecord xfr = (XFRecord) view.getFormat();
+
+ try {
+ if (xfr != null) {
+ if (!xfr.isInitialized()) {
+ formatRecords.addStyle(xfr);
+ }
+ }
+ } catch (NumFormatRecordsException e) {
+ logger.warn("Maximum number of format records exceeded. Using " +
+ "default format.");
+
+ xfr = null;
+ }
+
+ rowrec.setRowDetails(view.getSize(),
+ false,
+ view.isHidden(),
+ 0,
+ false,
+ xfr);
+ numRows = Math.max(numRows, row + 1);
+ }
+
+ /**
+ * Writes out this sheet. This functionality is delegated off to the
+ * SheetWriter class in order to reduce the bloated nature of this source
+ * file
+ *
+ * @throws IOException
+ */
+ public void write() throws IOException {
+ boolean dmod = drawingsModified;
+ if (workbook.getDrawingGroup() != null) {
+ dmod |= workbook.getDrawingGroup().hasDrawingsOmitted();
+ }
+
+ if (autosizedColumns.size() > 0) {
+ autosizeColumns();
+ }
+
+ sheetWriter.setWriteData(rows,
+ rowBreaks,
+ columnBreaks,
+ hyperlinks,
+ mergedCells,
+ columnFormats,
+ maxRowOutlineLevel,
+ maxColumnOutlineLevel);
+ sheetWriter.setDimensions(getRows(), getColumns());
+ sheetWriter.setSettings(settings);
+ sheetWriter.setPLS(plsRecord);
+ sheetWriter.setDrawings(drawings, dmod);
+ sheetWriter.setButtonPropertySet(buttonPropertySet);
+ sheetWriter.setDataValidation(dataValidation, validatedCells);
+ sheetWriter.setConditionalFormats(conditionalFormats);
+ sheetWriter.setAutoFilter(autoFilter);
+
+ sheetWriter.write();
+ }
+
+ /**
+ * Copies the specified sheet, row by row and cell by cell
+ *
+ * @param s the sheet to copy
+ */
+ void copy(Sheet s) {
+ // Copy the settings
+ settings = new SheetSettings(s.getSettings(), this);
+
+ SheetCopier si = new SheetCopier(s, this);
+ si.setColumnFormats(columnFormats);
+ si.setFormatRecords(formatRecords);
+ si.setHyperlinks(hyperlinks);
+ si.setMergedCells(mergedCells);
+ si.setRowBreaks(rowBreaks);
+ si.setColumnBreaks(columnBreaks);
+ si.setSheetWriter(sheetWriter);
+ si.setDrawings(drawings);
+ si.setImages(images);
+ si.setConditionalFormats(conditionalFormats);
+ si.setValidatedCells(validatedCells);
+
+ si.copySheet();
+
+ dataValidation = si.getDataValidation();
+ comboBox = si.getComboBox();
+ plsRecord = si.getPLSRecord();
+ chartOnly = si.isChartOnly();
+ buttonPropertySet = si.getButtonPropertySet();
+ numRows = si.getRows();
+ autoFilter = si.getAutoFilter();
+ maxRowOutlineLevel = si.getMaxRowOutlineLevel();
+ maxColumnOutlineLevel = si.getMaxColumnOutlineLevel();
+ }
+
+ /**
+ * Copies the specified sheet, row by row and cell by cell
+ *
+ * @param s the sheet to copy
+ */
+ void copy(WritableSheet s) {
+ settings = new SheetSettings(s.getSettings(), this);
+ WritableSheetImpl si = (WritableSheetImpl) s;
+
+ WritableSheetCopier sc = new WritableSheetCopier(s, this);
+ sc.setColumnFormats(si.columnFormats, columnFormats);
+ sc.setMergedCells(si.mergedCells, mergedCells);
+ sc.setRows(si.rows);
+ sc.setRowBreaks(si.rowBreaks, rowBreaks);
+ sc.setColumnBreaks(si.columnBreaks, columnBreaks);
+ sc.setDataValidation(si.dataValidation);
+ sc.setSheetWriter(sheetWriter);
+ sc.setDrawings(si.drawings, drawings, images);
+ sc.setWorkspaceOptions(si.getWorkspaceOptions());
+ sc.setPLSRecord(si.plsRecord);
+ sc.setButtonPropertySetRecord(si.buttonPropertySet);
+ sc.setHyperlinks(si.hyperlinks, hyperlinks);
+ sc.setValidatedCells(validatedCells);
+
+ sc.copySheet();
+
+ dataValidation = sc.getDataValidation();
+ plsRecord = sc.getPLSRecord();
+ buttonPropertySet = sc.getButtonPropertySet();
+ }
+
+ /**
+ * Gets the header. Called when copying sheets
+ *
+ * @return the page header
+ */
+ final HeaderRecord getHeader() {
+ return sheetWriter.getHeader();
+ }
+
+ /**
+ * Gets the footer. Called when copying sheets
+ *
+ * @return the page footer
+ */
+ final FooterRecord getFooter() {
+ return sheetWriter.getFooter();
+ }
+
+ /**
+ * Determines whether the sheet is protected
+ *
+ * @return whether or not the sheet is protected
+ * @deprecated Use the SheetSettings bean instead
+ */
+ public boolean isProtected() {
+ return settings.isProtected();
+ }
+
+ /**
+ * Indicates whether or not this sheet is protected
+ *
+ * @param prot protected flag
+ * @deprecated Use the settings bean instead
+ */
+ public void setProtected(boolean prot) {
+ settings.setProtected(prot);
+ }
+
+ /**
+ * Gets the hyperlinks on this sheet
+ *
+ * @return an array of hyperlinks
+ */
+ public Hyperlink[] getHyperlinks() {
+ Hyperlink[] hl = new Hyperlink[hyperlinks.size()];
+
+ for (int i = 0; i < hyperlinks.size(); i++) {
+ hl[i] = (Hyperlink) hyperlinks.get(i);
+ }
+
+ return hl;
+ }
+
+ /**
+ * Gets the cells which have been merged on this sheet
+ *
+ * @return an array of range objects
+ */
+ public Range[] getMergedCells() {
+ return mergedCells.getMergedCells();
+ }
+
+ /**
+ * Gets the writable hyperlinks on this sheet
+ *
+ * @return an array of hyperlinks
+ */
+ public WritableHyperlink[] getWritableHyperlinks() {
+ WritableHyperlink[] hl = new WritableHyperlink[hyperlinks.size()];
+
+ for (int i = 0; i < hyperlinks.size(); i++) {
+ hl[i] = (WritableHyperlink) hyperlinks.get(i);
+ }
+
+ return hl;
+ }
+
+ /**
+ * Removes the specified hyperlink. Note that if you merely set the
+ * cell contents to be an Empty cell, then the cells containing the
+ * hyperlink will still be active. The contents of the cell which
+ * activate the hyperlink are removed.
+ * The hyperlink passed in must be a hyperlink retrieved using the
+ * getHyperlinks method
+ *
+ * @param h the hyperlink to remove.
+ * @param preserveLabel if TRUE preserves the label contents, if FALSE
+ * removes them
+ */
+ public void removeHyperlink(WritableHyperlink h) {
+ removeHyperlink(h, false);
+ }
+
+ /**
+ * Removes the specified hyperlink. Note that if you merely set the
+ * cell contents to be an Empty cell, then the cells containing the
+ * hyperlink will still be active.
+ * If the preserveLabel field is set, the cell contents of the
+ * hyperlink are preserved, although the hyperlink is deactivated. If
+ * this value is FALSE, the cell contents are removed
+ * The hyperlink passed in must be a hyperlink retrieved using the
+ * getHyperlinks method
+ *
+ * @param h the hyperlink to remove.
+ * @param preserveLabel if TRUE preserves the label contents, if FALSE
+ * removes them
+ */
+ public void removeHyperlink(WritableHyperlink h, boolean preserveLabel) {
+ // Remove the hyperlink
+ hyperlinks.remove(h);
+
+ if (!preserveLabel) {
+ // Set the cell contents for the hyperlink - including any formatting
+ // information - to be empty
+ Assert.verify(rows.length > h.getRow() && rows[h.getRow()] != null);
+ rows[h.getRow()].removeCell(h.getColumn());
+ }
+ }
+
+ /**
+ * Adds the specified hyperlink
+ *
+ * @param the hyperlink
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void addHyperlink(WritableHyperlink h)
+ throws WriteException, RowsExceededException {
+ // First set the label on the sheet
+ Cell c = getCell(h.getColumn(), h.getRow());
+
+ String contents = null;
+ if (h.isFile() || h.isUNC()) {
+ String cnts = ((HyperlinkRecord) h).getContents();
+ if (cnts == null) {
+ contents = h.getFile().getPath();
+ } else {
+ contents = cnts;
+ }
+ } else if (h.isURL()) {
+ String cnts = ((HyperlinkRecord) h).getContents();
+ if (cnts == null) {
+ contents = h.getURL().toString();
+ } else {
+ contents = cnts;
+ }
+ } else if (h.isLocation()) {
+ contents = ((HyperlinkRecord) h).getContents();
+ }
+
+ // If the cell type is a label, then preserve the cell contents
+ // and most of the format (apart from the font)
+ // otherwise overwrite the cell content and the format with the contents
+ // and the standard hyperlink format
+ if (c.getType() == CellType.LABEL) {
+ Label l = (Label) c;
+ l.setString(contents);
+ WritableCellFormat wcf = new WritableCellFormat(l.getCellFormat());
+ wcf.setFont(WritableWorkbook.HYPERLINK_FONT);
+ l.setCellFormat(wcf);
+ } else {
+ Label l = new Label(h.getColumn(), h.getRow(), contents,
+ WritableWorkbook.HYPERLINK_STYLE);
+ addCell(l);
+ }
+
+ // Set all other cells within range to be empty
+ for (int i = h.getRow(); i <= h.getLastRow(); i++) {
+ for (int j = h.getColumn(); j <= h.getLastColumn(); j++) {
+ if (i != h.getRow() && j != h.getColumn()) {
+ // Set the cell to be empty
+ if (rows.length < h.getLastColumn() && rows[i] != null) {
+ rows[i].removeCell(j);
+ }
+ }
+ }
+ }
+
+ ((HyperlinkRecord) h).initialize(this);
+ hyperlinks.add(h);
+ }
+
+ /**
+ * Merges the specified cells. Any clashes or intersections between
+ * merged cells are resolved when the spreadsheet is written out
+ *
+ * @param col1 the column number of the top left cell
+ * @param row1 the row number of the top left cell
+ * @param col2 the column number of the bottom right cell
+ * @param row2 the row number of the bottom right cell
+ * @return the Range object representing the merged cells
+ * @throws jxl.write..WriteException
+ * @throws RowsExceededException
+ */
+ public Range mergeCells(int col1, int row1, int col2, int row2)
+ throws WriteException, RowsExceededException {
+ // First check that the cells make sense
+ if (col2 < col1 || row2 < row1) {
+ logger.warn("Cannot merge cells - top left and bottom right " +
+ "incorrectly specified");
+ }
+
+ // Make sure the spreadsheet is up to size
+ if (col2 >= numColumns || row2 >= numRows) {
+ addCell(new Blank(col2, row2));
+ }
+
+ SheetRangeImpl range = new SheetRangeImpl(this, col1, row1, col2, row2);
+ mergedCells.add(range);
+
+ return range;
+ }
+
+ /**
+ * Sets a row grouping
+ *
+ * @param row1 the first row of the group
+ * @param row2 the last row of the group
+ * @param collapsed should the group be collapsed?
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void setRowGroup(int row1, int row2,
+ boolean collapsed)
+ throws WriteException, RowsExceededException {
+ if (row2 < row1) {
+ logger.warn("Cannot merge cells - top and bottom rows incorrectly " +
+ "specified");
+ }
+
+ for (int i = row1; i <= row2; i++) {
+ RowRecord row = getRowRecord(i);
+ numRows = Math.max(i + 1, numRows);
+ row.incrementOutlineLevel();
+ row.setCollapsed(collapsed);
+ maxRowOutlineLevel = Math.max(maxRowOutlineLevel,
+ row.getOutlineLevel());
+ }
+ }
+
+ /**
+ * Unsets a row grouping
+ *
+ * @param row1 the first row to unset
+ * @param row2 the last row to unset
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void unsetRowGroup(int row1, int row2)
+ throws WriteException, RowsExceededException {
+ if (row2 < row1) {
+ logger.warn("Cannot merge cells - top and bottom rows incorrectly " +
+ "specified");
+ }
+
+ // Make sure the spreadsheet is up to size
+ if (row2 >= numRows) {
+ logger.warn("" + row2 +
+ " is greater than the sheet bounds");
+ row2 = numRows - 1;
+ }
+
+ for (int i = row1; i <= row2; i++) {
+ rows[i].decrementOutlineLevel();
+ }
+
+ // Recalculate the max outline level
+ maxRowOutlineLevel = 0;
+ for (int i = rows.length; i-- > 0; ) {
+ maxRowOutlineLevel = Math.max(maxRowOutlineLevel,
+ rows[i].getOutlineLevel());
+ }
+ }
+
+ /**
+ * Sets a column grouping
+ *
+ * @param col1 the first column of the group
+ * @param col2 the last column of the group
+ * @param collapsed should the group be collapsed?
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void setColumnGroup(int col1, int col2, boolean collapsed)
+ throws WriteException, RowsExceededException {
+ if (col2 < col1) {
+ logger.warn("Cannot merge cells - top and bottom rows incorrectly " +
+ "specified");
+ }
+
+ for (int i = col1; i <= col2; i++) {
+ ColumnInfoRecord cir = getColumnInfo(i);
+
+ // Create the column info record if not present using a default
+ // cell view
+ if (cir == null) {
+ setColumnView(i, new CellView());
+ cir = getColumnInfo(i);
+ }
+
+ cir.incrementOutlineLevel();
+ cir.setCollapsed(collapsed);
+ maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel,
+ cir.getOutlineLevel());
+ }
+ }
+
+ /**
+ * Unsets a column grouping
+ *
+ * @param col1 the first column to unset
+ * @param col2 the last column to unset
+ * @throws WriteException
+ * @throws RowsExceededException
+ */
+ public void unsetColumnGroup(int col1, int col2)
+ throws WriteException, RowsExceededException {
+ if (col2 < col1) {
+ logger.warn("Cannot merge cells - top and bottom rows incorrectly " +
+ "specified");
+ }
+
+ for (int i = col1; i <= col2; i++) {
+ ColumnInfoRecord cir = getColumnInfo(i);
+ cir.decrementOutlineLevel();
+ }
+
+ // Recalculate the max outline level
+ maxColumnOutlineLevel = 0;
+ for (Iterator it = columnFormats.iterator(); it.hasNext(); ) {
+ ColumnInfoRecord cir = (ColumnInfoRecord) it.next();
+ maxColumnOutlineLevel = Math.max(maxColumnOutlineLevel,
+ cir.getOutlineLevel());
+ }
+ }
+
+ /**
+ * Unmerges the specified cells. The Range passed in should be one that
+ * has been previously returned as a result of the getMergedCells method
+ *
+ * @param r the range of cells to unmerge
+ */
+ public void unmergeCells(Range r) {
+ mergedCells.unmergeCells(r);
+ }
+
+ /**
+ * Sets the header for this page
+ *
+ * @param l the print header to print on the left side
+ * @param c the print header to print in the centre
+ * @param r the print header to print on the right hand side
+ * @deprecated Use the sheet settings bean
+ */
+ public void setHeader(String l, String c, String r) {
+ HeaderFooter header = new HeaderFooter();
+ header.getLeft().append(l);
+ header.getCentre().append(c);
+ header.getRight().append(r);
+ settings.setHeader(header);
+ }
+
+ /**
+ * Sets the footer for this page
+ *
+ * @param l the print header to print on the left side
+ * @param c the print header to print in the centre
+ * @param r the print header to print on the right hand side
+ * @deprecated Use the sheet settings bean
+ */
+ public void setFooter(String l, String c, String r) {
+ HeaderFooter footer = new HeaderFooter();
+ footer.getLeft().append(l);
+ footer.getCentre().append(c);
+ footer.getRight().append(r);
+ settings.setFooter(footer);
+ }
+
+ /**
+ * Sets the page setup details
+ *
+ * @param p the page orientation
+ * @deprecated Use the SheetSettings bean
+ */
+ public void setPageSetup(PageOrientation p) {
+ settings.setOrientation(p);
+ }
+
+ /**
+ * Sets the page setup details
+ *
+ * @param p the page orientation
+ * @param hm the header margin, in inches
+ * @param fm the footer margin, in inches
+ * @deprecated Use the SheetSettings bean
+ */
+ public void setPageSetup(PageOrientation p, double hm, double fm) {
+ settings.setOrientation(p);
+ settings.setHeaderMargin(hm);
+ settings.setFooterMargin(fm);
+ }
+
+ /**
+ * Sets the page setup details
+ *
+ * @param p the page orientation
+ * @param ps the paper size
+ * @param hm the header margin, in inches
+ * @param fm the footer margin, in inches
+ * @deprecated Use the SheetSettings bean
+ */
+ public void setPageSetup(PageOrientation p, PaperSize ps,
+ double hm, double fm) {
+ settings.setPaperSize(ps);
+ settings.setOrientation(p);
+ settings.setHeaderMargin(hm);
+ settings.setFooterMargin(fm);
+ }
+
+ /**
+ * Gets the settings for this sheet
+ *
+ * @return the page settings bean
+ */
+ public SheetSettings getSettings() {
+ return settings;
+ }
+
+ /**
+ * Gets the workbook settings
+ */
+ WorkbookSettings getWorkbookSettings() {
+ return workbookSettings;
+ }
+
+ /**
+ * Forces a page break at the specified row
+ *
+ * @param row the row to break at
+ */
+ public void addRowPageBreak(int row) {
+ // First check that the row is not already present
+ Iterator i = rowBreaks.iterator();
+ boolean found = false;
+
+ while (i.hasNext() && !found) {
+ if (((Integer) i.next()).intValue() == row) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ rowBreaks.add(new Integer(row));
+ }
+ }
+
+ /**
+ * Forces a page break at the specified column
+ *
+ * @param col the column to break at
+ */
+ public void addColumnPageBreak(int col) {
+ // First check that the row is not already present
+ Iterator i = columnBreaks.iterator();
+ boolean found = false;
+
+ while (i.hasNext() && !found) {
+ if (((Integer) i.next()).intValue() == col) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ columnBreaks.add(new Integer(col));
+ }
+ }
+
+ /**
+ * Accessor for the charts. Used when copying
+ *
+ * @return the charts on this sheet
+ */
+ Chart[] getCharts() {
+ return sheetWriter.getCharts();
+ }
+
+ /**
+ * Accessor for the drawings. Used when copying
+ *
+ * @return the drawings on this sheet
+ */
+ private DrawingGroupObject[] getDrawings() {
+ DrawingGroupObject[] dr = new DrawingGroupObject[drawings.size()];
+ dr = (DrawingGroupObject[]) drawings.toArray(dr);
+ return dr;
+ }
+
+ /**
+ * Check all the merged cells for borders. Although in an OO sense the
+ * logic should belong in this class, in order to reduce the bloated
+ * nature of the source code for this object this logic has been delegated
+ * to the SheetWriter
+ */
+ void checkMergedBorders() {
+ sheetWriter.setWriteData(rows,
+ rowBreaks,
+ columnBreaks,
+ hyperlinks,
+ mergedCells,
+ columnFormats,
+ maxRowOutlineLevel,
+ maxColumnOutlineLevel);
+ sheetWriter.setDimensions(getRows(), getColumns());
+ sheetWriter.checkMergedBorders();
+ }
+
+ /**
+ * Accessor for the workspace options
+ *
+ * @return the workspace options
+ */
+ private WorkspaceInformationRecord getWorkspaceOptions() {
+ return sheetWriter.getWorkspaceOptions();
+ }
+
+ /**
+ * Rationalizes the sheets xf index mapping
+ *
+ * @param xfMapping the index mapping for XFRecords
+ * @param fontMapping the index mapping for fonts
+ * @param formatMapping the index mapping for formats
+ */
+ void rationalize(IndexMapping xfMapping,
+ IndexMapping fontMapping,
+ IndexMapping formatMapping) {
+ // Rationalize the column formats
+ for (Iterator i = columnFormats.iterator(); i.hasNext(); ) {
+ ColumnInfoRecord cir = (ColumnInfoRecord) i.next();
+ cir.rationalize(xfMapping);
+ }
+
+ // Rationalize the row formats
+ for (int i = 0; i < rows.length; i++) {
+ if (rows[i] != null) {
+ rows[i].rationalize(xfMapping);
+ }
+ }
+
+ // Rationalize any data that appears on the charts
+ Chart[] charts = getCharts();
+ for (int c = 0; c < charts.length; c++) {
+ charts[c].rationalize(xfMapping, fontMapping, formatMapping);
+ }
+ }
+
+ /**
+ * Accessor for the workbook
+ *
+ * @return the workbook
+ */
+ WritableWorkbookImpl getWorkbook() {
+ return workbook;
+ }
+
+ /**
+ * Gets the column format for the specified column
+ *
+ * @param col the column number
+ * @return the column format, or NULL if the column has no specific format
+ * @deprecated Use getColumnView instead
+ */
+ public CellFormat getColumnFormat(int col) {
+ return getColumnView(col).getFormat();
+ }
+
+ /**
+ * Gets the column width for the specified column
+ *
+ * @param col the column number
+ * @return the column width, or the default width if the column has no
+ * specified format
+ * @deprecated Use getColumnView instead
+ */
+ public int getColumnWidth(int col) {
+ return getColumnView(col).getDimension();
+ }
+
+ /**
+ * Gets the column width for the specified column
+ *
+ * @param row the column number
+ * @return the row height, or the default height if the column has no
+ * specified format
+ * @deprecated Use getRowView instead
+ */
+ public int getRowHeight(int row) {
+ return getRowView(row).getDimension();
+ }
+
+ /**
+ * Accessor for the chart only method
+ *
+ * @return TRUE if this is a chart only, FALSE otherwise
+ */
+ boolean isChartOnly() {
+ return chartOnly;
+ }
+
+ /**
+ * Gets the row view for the specified row
+ *
+ * @param col the row number
+ * @return the row format, or the default format if no override is
+ * specified
+ */
+ public CellView getRowView(int row) {
+ CellView cv = new CellView();
+
+ try {
+ RowRecord rr = getRowRecord(row);
+
+ if (rr == null || rr.isDefaultHeight()) {
+ cv.setDimension(settings.getDefaultRowHeight());
+ cv.setSize(settings.getDefaultRowHeight());
+ } else if (rr.isCollapsed()) {
+ cv.setHidden(true);
+ } else {
+ cv.setDimension(rr.getRowHeight());
+ cv.setSize(rr.getRowHeight());
+ }
+ return cv;
+ } catch (RowsExceededException e) {
+ // Simple return the default
+ cv.setDimension(settings.getDefaultRowHeight());
+ cv.setSize(settings.getDefaultRowHeight());
+ return cv;
+ }
+ }
+
+ /**
+ * Gets the column width for the specified column
+ *
+ * @param col the column number
+ * @return the column format, or the default format if no override is
+ * specified
+ */
+ public CellView getColumnView(int col) {
+ ColumnInfoRecord cir = getColumnInfo(col);
+ CellView cv = new CellView();
+
+ if (cir != null) {
+ cv.setDimension(cir.getWidth() / 256);
+ cv.setSize(cir.getWidth());
+ cv.setHidden(cir.getHidden());
+ cv.setFormat(cir.getCellFormat());
+ } else {
+ cv.setDimension(settings.getDefaultColumnWidth() / 256);
+ cv.setSize(settings.getDefaultColumnWidth() * 256);
+ }
+
+ return cv;
+ }
+
+ /**
+ * Adds an image to this sheet
+ *
+ * @param image the image to add
+ */
+ public void addImage(WritableImage image) {
+ boolean supported = false;
+ java.io.File imageFile = image.getImageFile();
+ String fileType = "?";
+
+ if (imageFile != null) {
+ String fileName = imageFile.getName();
+ int fileTypeIndex = fileName.lastIndexOf('.');
+ fileType = fileTypeIndex != -1 ?
+ fileName.substring(fileTypeIndex + 1) : "";
+
+ for (int i = 0; i < imageTypes.length && !supported; i++) {
+ if (fileType.equalsIgnoreCase(imageTypes[i])) {
+ supported = true;
+ }
+ }
+ } else {
+ supported = true;
+ }
+
+ if (supported) {
+ workbook.addDrawing(image);
+ drawings.add(image);
+ images.add(image);
+ } else {
+ StringBuffer message = new StringBuffer("Image type ");
+ message.append(fileType);
+ message.append(" not supported. Supported types are ");
+ message.append(imageTypes[0]);
+ for (int i = 1; i < imageTypes.length; i++) {
+ message.append(", ");
+ message.append(imageTypes[i]);
+ }
+ logger.warn(message.toString());
+ }
+ }
+
+ /**
+ * Gets the number of images on this sheet
+ *
+ * @return the number of images on this sheet
+ */
+ public int getNumberOfImages() {
+ return images.size();
+ }
+
+ /**
+ * Accessor for a particular image on this sheet
+ *
+ * @param i the 0-based image index number
+ * @return the image with the specified index number
+ */
+ public WritableImage getImage(int i) {
+ return (WritableImage) images.get(i);
+ }
+
+ /**
+ * Accessor for a particular image on this sheet
+ *
+ * @param i the 0-based image index number
+ * @return the image with the specified index number
+ */
+ public Image getDrawing(int i) {
+ return (Image) images.get(i);
+ }
+
+ /**
+ * Removes the specified image from this sheet. The image passed in
+ * must be the same instance as that retrieved from a getImage call
+ *
+ * @param wi the image to remove
+ */
+ public void removeImage(WritableImage wi) {
+ drawings.remove(wi);
+ images.remove(wi);
+ drawingsModified = true;
+ workbook.removeDrawing(wi);
+ }
+
+ /**
+ * Validates the sheet name
+ */
+ private String validateName(String n) {
+ if (n.length() > maxSheetNameLength) {
+ logger.warn("Sheet name " + n + " too long - truncating");
+ n = n.substring(0, maxSheetNameLength);
+ }
+
+ if (n.charAt(0) == '\'') {
+ logger.warn("Sheet naming cannot start with ' - removing");
+ n = n.substring(1);
+ }
+
+ for (int i = 0; i < illegalSheetNameCharacters.length; i++) {
+ String newname = n.replace(illegalSheetNameCharacters[i], '@');
+ if (n != newname) {
+ logger.warn(illegalSheetNameCharacters[i] +
+ " is not a valid character within a sheet name - replacing");
+ }
+ n = newname;
+ }
+
+ return n;
+ }
+
+ /**
+ * Adds a drawing to the list - typically used for comments
+ *
+ * @param the drawing to add
+ */
+ void addDrawing(DrawingGroupObject o) {
+ drawings.add(o);
+ Assert.verify(!(o instanceof Drawing));
+ }
+
+ /**
+ * Removes a drawing to the list - typically used for comments
+ *
+ * @param the drawing to add
+ */
+ void removeDrawing(DrawingGroupObject o) {
+ int origSize = drawings.size();
+ drawings.remove(o);
+ int newSize = drawings.size();
+ drawingsModified = true;
+ Assert.verify(newSize == origSize - 1);
+ }
+
+ /**
+ * Removes the data validation for the specified cell. Called from
+ * CellValue in response to a cell being replaced
+ *
+ * @param cv the cell being removed
+ */
+ void removeDataValidation(CellValue cv) {
+ if (dataValidation != null) {
+ dataValidation.removeDataValidation(cv.getColumn(), cv.getRow());
+ }
+
+ if (validatedCells != null) {
+ boolean result = validatedCells.remove(cv);
+
+ if (!result) {
+ logger.warn("Could not remove validated cell " +
+ CellReferenceHelper.getCellReference(cv));
+ }
+ }
+ }
+
+ /**
+ * Accessor for the page breaks on this sheet
+ *
+ * @return the page breaks on this sheet
+ */
+ public int[] getRowPageBreaks() {
+ int[] rb = new int[rowBreaks.size()];
+ int pos = 0;
+ for (Iterator i = rowBreaks.iterator(); i.hasNext(); pos++) {
+ rb[pos] = ((Integer) i.next()).intValue();
+ }
+ return rb;
+ }
+
+ /**
+ * Accessor for the page breaks on this sheet
+ *
+ * @return the page breaks on this sheet
+ */
+ public int[] getColumnPageBreaks() {
+ int[] rb = new int[columnBreaks.size()];
+ int pos = 0;
+ for (Iterator i = columnBreaks.iterator(); i.hasNext(); pos++) {
+ rb[pos] = ((Integer) i.next()).intValue();
+ }
+ return rb;
+ }
+
+ /**
+ * Flags the added cell as having data validation
+ *
+ * @param cell the cell with data validation
+ */
+ void addValidationCell(CellValue cv) {
+ validatedCells.add(cv);
+ }
+
+ /**
+ * Accessor for the combo box object used for list data validations on this
+ * sheet
+ *
+ * @return the combo box
+ */
+ ComboBox getComboBox() {
+ return comboBox;
+ }
+
+ /**
+ * Sets the combo box object used for list validations on this sheet
+ *
+ * @param cb the combo box
+ */
+ void setComboBox(ComboBox cb) {
+ comboBox = cb;
+ }
+
+ /**
+ * Gets the data validation. Retrieved by CellValue when copying sheets
+ */
+ public DataValidation getDataValidation() {
+ return dataValidation;
+ }
+
+ /**
+ * Performs the column autosizing
+ */
+ private void autosizeColumns() {
+ Iterator i = autosizedColumns.iterator();
+ while (i.hasNext()) {
+ Integer col = (Integer) i.next();
+ autosizeColumn(col.intValue());
+ }
+ }
+
+ /**
+ * Autosizes the specified column
+ *
+ * @param col the column to autosize
+ */
+ private void autosizeColumn(int col) {
+ int maxWidth = 0;
+ ColumnInfoRecord cir = getColumnInfo(col);
+ Font columnFont = cir.getCellFormat().getFont();
+ Font defaultFont = WritableWorkbook.NORMAL_STYLE.getFont();
+
+ for (int i = 0; i < numRows; i++) {
+ Cell cell = null;
+ if (rows[i] != null) {
+ cell = rows[i].getCell(col);
+ }
+
+ if (cell != null) {
+ String contents = cell.getContents();
+ Font font = cell.getCellFormat().getFont();
+
+ Font activeFont = font.equals(defaultFont) ? columnFont : font;
+
+ int pointSize = activeFont.getPointSize();
+ int numChars = contents.length();
+
+ if (activeFont.isItalic() ||
+ activeFont.getBoldWeight() > 400) // magic value for normal bold
+ {
+ numChars += 2;
+ }
+
+ int points = numChars * pointSize;
+ maxWidth = Math.max(maxWidth, points * 256);
+ }
+ }
+ cir.setWidth(maxWidth / defaultFont.getPointSize());
+ }
+
+ /**
+ * Imports a sheet from a different workbook
+ *
+ * @param s the sheet to import
+ */
+ void importSheet(Sheet s) {
+ // Copy the settings
+ settings = new SheetSettings(s.getSettings(), this);
+
+ SheetCopier si = new SheetCopier(s, this);
+ si.setColumnFormats(columnFormats);
+ si.setFormatRecords(formatRecords);
+ si.setHyperlinks(hyperlinks);
+ si.setMergedCells(mergedCells);
+ si.setRowBreaks(rowBreaks);
+ si.setColumnBreaks(columnBreaks);
+ si.setSheetWriter(sheetWriter);
+ si.setDrawings(drawings);
+ si.setImages(images);
+ si.setValidatedCells(validatedCells);
+
+ si.importSheet();
+
+ dataValidation = si.getDataValidation();
+ comboBox = si.getComboBox();
+ plsRecord = si.getPLSRecord();
+ chartOnly = si.isChartOnly();
+ buttonPropertySet = si.getButtonPropertySet();
+ numRows = si.getRows();
+ maxRowOutlineLevel = si.getMaxRowOutlineLevel();
+ maxColumnOutlineLevel = si.getMaxColumnOutlineLevel();
+ }
+
+ /**
+ * Extend the data validation contained in the specified cell across and
+ * downwards
+ *
+ * @param c the number of cells accross to apply this data validation
+ * @param r the number of cells downwards to apply this data validation
+ */
+ public void applySharedDataValidation(WritableCell c,
+ int extraCols,
+ int extraRows)
+ throws WriteException {
+ // Check that the cell being applied has a data validation
+ if (c.getWritableCellFeatures() == null ||
+ !c.getWritableCellFeatures().hasDataValidation()) {
+ logger.warn("Cannot extend data validation for " +
+ CellReferenceHelper.getCellReference(c.getColumn(),
+ c.getRow()) +
+ " as it has no data validation");
+ return;
+ }
+
+ // Check that none of the other cells in the range have any
+ // cell validations
+ int startColumn = c.getColumn();
+ int startRow = c.getRow();
+ int endRow = Math.min(numRows - 1, startRow + extraRows);
+ for (int y = startRow; y <= endRow; y++) {
+ if (rows[y] != null) {
+ int endCol = Math.min(rows[y].getMaxColumn() - 1,
+ startColumn + extraCols);
+ for (int x = startColumn; x <= endCol; x++) {
+ // Ignore the first cell
+ if (x == startColumn && y == startRow) {
+ continue; // continue statements - they're no better than gotos
+ }
+
+ WritableCell c2 = rows[y].getCell(x);
+
+ // Check that the target cell does not have any data validation
+ if (c2 != null &&
+ c2.getWritableCellFeatures() != null &&
+ c2.getWritableCellFeatures().hasDataValidation()) {
+ logger.warn("Cannot apply data validation from " +
+ CellReferenceHelper.getCellReference(startColumn,
+ startRow) +
+ " to " +
+ CellReferenceHelper.getCellReference
+ (startColumn + extraCols,
+ startRow + extraRows) +
+ " as cell " +
+ CellReferenceHelper.getCellReference(x, y) +
+ " already has a data validation");
+ return;
+ }
+ }
+ }
+ }
+
+ // Extend the range on the source data validation
+ WritableCellFeatures sourceDataValidation = c.getWritableCellFeatures();
+ sourceDataValidation.getDVParser().extendCellValidation(extraCols,
+ extraRows);
+
+ // Go through all the additional cells and add the data validation cell
+ for (int y = startRow; y <= startRow + extraRows; y++) {
+ RowRecord rowrec = getRowRecord(y); // create the row if it doesn't exist
+ for (int x = startColumn; x <= startColumn + extraCols; x++) {
+ // Ignore the first cell
+ if (x == startColumn && y == startRow) {
+ continue; // continue statements - they're no better than gotos
+ }
+
+ WritableCell c2 = rowrec.getCell(x);
+
+ // Check that the target cell does not have any data validation
+ if (c2 == null) {
+ Blank b = new Blank(x, y);
+ WritableCellFeatures validation = new WritableCellFeatures();
+ validation.shareDataValidation(sourceDataValidation);
+ b.setCellFeatures(validation);
+ addCell(b);
+ } else {
+ // add the shared data validation to the existing cell
+ WritableCellFeatures validation = c2.getWritableCellFeatures();
+
+ if (validation != null) {
+ validation.shareDataValidation(sourceDataValidation);
+ } else {
+ validation = new WritableCellFeatures();
+ validation.shareDataValidation(sourceDataValidation);
+ c2.setCellFeatures(validation);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the shared data validation from multiple cells. The cell passed
+ * in is the top left cell. The data validation is removed from this
+ * cell and all cells which share the same validation.
+ *
+ * @param cell the top left cell containing the shared data validation
+ */
+ public void removeSharedDataValidation(WritableCell cell)
+ throws WriteException {
+ WritableCellFeatures wcf = cell.getWritableCellFeatures();
+ if (wcf == null ||
+ !wcf.hasDataValidation()) {
+ return;
+ }
+
+ DVParser dvp = wcf.getDVParser();
+
+ // If the cell is not part of an extended validation, then simply call
+ // the atomic remove validation from the cell features
+ if (!dvp.extendedCellsValidation()) {
+ wcf.removeDataValidation();
+ return;
+ }
+
+ // Check that the cell validation being removed is in the top left of the
+ // validated area
+ if (dvp.extendedCellsValidation()) {
+ if (cell.getColumn() != dvp.getFirstColumn() ||
+ cell.getRow() != dvp.getFirstRow()) {
+ logger.warn("Cannot remove data validation from " +
+ CellReferenceHelper.getCellReference(dvp.getFirstColumn(),
+ dvp.getFirstRow()) +
+ "-" +
+ CellReferenceHelper.getCellReference(dvp.getLastColumn(),
+ dvp.getLastRow()) +
+ " because the selected cell " +
+ CellReferenceHelper.getCellReference(cell) +
+ " is not the top left cell in the range");
+ return;
+ }
+ }
+
+ for (int y = dvp.getFirstRow(); y <= dvp.getLastRow(); y++) {
+ for (int x = dvp.getFirstColumn(); x <= dvp.getLastColumn(); x++) {
+ CellValue c2 = rows[y].getCell(x);
+
+ // It's possible that some cells in the shared data range might
+ // be null eg. in the event of an insertRow or insertColumn
+ if (c2 != null) {
+ c2.getWritableCellFeatures().removeSharedDataValidation();
+ c2.removeCellFeatures();
+ }
+ }
+ }
+
+ // Remove this shared validation from any data validations that were
+ // copied in
+ if (dataValidation != null) {
+ dataValidation.removeSharedDataValidation(dvp.getFirstColumn(),
+ dvp.getFirstRow(),
+ dvp.getLastColumn(),
+ dvp.getLastRow());
+ }
+ }
+
+ /**
+ * The comparator for column info record
+ */
+ private static class ColumnInfoComparator implements Comparator {
+ /**
+ * Equals method
+ *
+ * @param o the object to compare
+ * @return TRUE if equal, FALSE otherwise
+ */
+ public boolean equals(Object o) {
+ return o == this;
+ }
+
+ /**
+ * Comparison function for to ColumnInfoRecords
+ *
+ * @param o2 first object to compare
+ * @param o1 second object to compare
+ * @return the result of the comparison
+ */
+ public int compare(Object o1, Object o2) {
+ if (o1 == o2) {
+ return 0;
+ }
+
+ Assert.verify(o1 instanceof ColumnInfoRecord);
+ Assert.verify(o2 instanceof ColumnInfoRecord);
+
+ ColumnInfoRecord ci1 = (ColumnInfoRecord) o1;
+ ColumnInfoRecord ci2 = (ColumnInfoRecord) o2;
+
+ return ci1.getColumn() - ci2.getColumn();
+ }
+ }
+}
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java b/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java
new file mode 100755
index 0000000..63d789c
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WritableWorkbookImpl.java
@@ -0,0 +1,1703 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import jxl.Range;
+import jxl.Sheet;
+import jxl.Workbook;
+import jxl.WorkbookSettings;
+import jxl.biff.BuiltInName;
+import jxl.biff.CellReferenceHelper;
+import jxl.biff.CountryCode;
+import jxl.biff.Fonts;
+import jxl.biff.FormattingRecords;
+import jxl.biff.IndexMapping;
+import jxl.biff.IntegerHelper;
+import jxl.biff.RangeImpl;
+import jxl.biff.WorkbookMethods;
+import jxl.biff.XCTRecord;
+import jxl.biff.drawing.Drawing;
+import jxl.biff.drawing.DrawingGroup;
+import jxl.biff.drawing.DrawingGroupObject;
+import jxl.biff.drawing.Origin;
+import jxl.biff.formula.ExternalSheet;
+import jxl.common.Assert;
+import jxl.common.Logger;
+import jxl.format.Colour;
+import jxl.format.RGB;
+import jxl.read.biff.WorkbookParser;
+import jxl.write.WritableCell;
+import jxl.write.WritableSheet;
+import jxl.write.WritableWorkbook;
+
+
+/**
+ * A writable workbook
+ */
+public class WritableWorkbookImpl extends WritableWorkbook
+ implements ExternalSheet, WorkbookMethods {
+ /**
+ * The logger
+ */
+ private static final Logger logger = Logger.getLogger(WritableWorkbookImpl.class);
+ // synchronizer object for static unitiatialization
+ private static final Object SYNCHRONIZER = new Object();
+ /**
+ * The list of formats available within this workbook
+ */
+ private final FormattingRecords formatRecords;
+ /**
+ * The output file to write the workbook to
+ */
+ private final File outputFile;
+ /**
+ * The list of sheets within this workbook
+ */
+ private final ArrayList sheets;
+ /**
+ * The list of fonts available within this workbook
+ */
+ private final Fonts fonts;
+ /**
+ * The list of external sheets, used by cell references in formulas
+ */
+ private ExternalSheetRecord externSheet;
+ /**
+ * The supbook records
+ */
+ private ArrayList supbooks;
+ /**
+ * The name records
+ */
+ private ArrayList names;
+ /**
+ * A lookup hash map of the name records
+ */
+ private final HashMap nameRecords;
+ /**
+ * The shared strings used by this workbook
+ */
+ private final SharedStrings sharedStrings;
+ /**
+ * Indicates whether or not the output stream should be closed. This
+ * depends on whether this Workbook was created with an output stream,
+ * or a flat file (flat file closes the stream
+ */
+ private final boolean closeStream;
+ /**
+ * The workbook protection flag
+ */
+ private boolean wbProtected;
+ /**
+ * The settings for the workbook
+ */
+ private final WorkbookSettings settings;
+ /**
+ * The list of cells for the entire workbook which need to be updated
+ * following a row/column insert or remove
+ */
+ private final ArrayList rcirCells;
+ /**
+ * The drawing group
+ */
+ private DrawingGroup drawingGroup;
+ /**
+ * The jxl.common.workbook styles
+ */
+ private final Styles styles;
+ /**
+ * Contains macros flag
+ */
+ private boolean containsMacros;
+ /**
+ * The buttons property set
+ */
+ private ButtonPropertySetRecord buttonPropertySet;
+ /**
+ * The country record, initialised when copying a workbook
+ */
+ private CountryRecord countryRecord;
+ /**
+ * The names of any add in functions
+ */
+ private String[] addInFunctionNames;
+
+ /**
+ * The XCT records
+ */
+ private XCTRecord[] xctRecords;
+
+ /**
+ * Constructor. Writes the workbook direct to the existing output stream
+ *
+ * @param os the output stream
+ * @param cs TRUE if the workbook should close the output stream, FALSE
+ * @param ws the configuration for this workbook
+ * otherwise
+ * @throws IOException
+ */
+ public WritableWorkbookImpl(OutputStream os, boolean cs, WorkbookSettings ws)
+ throws IOException {
+ super();
+ outputFile = new File(os, ws, null);
+ sheets = new ArrayList();
+ sharedStrings = new SharedStrings();
+ nameRecords = new HashMap();
+ closeStream = cs;
+ wbProtected = false;
+ containsMacros = false;
+ settings = ws;
+ rcirCells = new ArrayList();
+ styles = new Styles();
+
+ // Reset the statically declared styles. These are no longer needed
+ // because the Styles class will intercept all calls within
+ // CellValue.setCellDetails and if it detects a standard format, then it
+ // will return a clone. In short, the static cell values will
+ // never get initialized anyway. Still, just to be extra sure...
+ synchronized (SYNCHRONIZER) {
+ WritableWorkbook.ARIAL_10_PT.uninitialize();
+ WritableWorkbook.HYPERLINK_FONT.uninitialize();
+ WritableWorkbook.NORMAL_STYLE.uninitialize();
+ WritableWorkbook.HYPERLINK_STYLE.uninitialize();
+ WritableWorkbook.HIDDEN_STYLE.uninitialize();
+ DateRecord.defaultDateFormat.uninitialize();
+ }
+
+ WritableFonts wf = new WritableFonts(this);
+ fonts = wf;
+
+ WritableFormattingRecords wfr = new WritableFormattingRecords(fonts,
+ styles);
+ formatRecords = wfr;
+ }
+
+ /**
+ * A pseudo copy constructor. Takes the handles to the font and formatting
+ * records
+ *
+ * @param w the workbook to copy
+ * @param os the output stream to write the data to
+ * @param cs TRUE if the workbook should close the output stream, FALSE
+ * @param ws the configuration for this workbook
+ * @throws IOException
+ */
+ public WritableWorkbookImpl(OutputStream os,
+ Workbook w,
+ boolean cs,
+ WorkbookSettings ws) throws IOException {
+ super();
+ WorkbookParser wp = (WorkbookParser) w;
+
+ // Reset the statically declared styles. These are no longer needed
+ // because the Styles class will intercept all calls within
+ // CellValue.setCellDetails and if it detects a standard format, then it
+ // will return a clone. In short, the static cell values will
+ // never get initialized anyway. Still, just to be extra sure...
+ synchronized (SYNCHRONIZER) {
+ WritableWorkbook.ARIAL_10_PT.uninitialize();
+ WritableWorkbook.HYPERLINK_FONT.uninitialize();
+ WritableWorkbook.NORMAL_STYLE.uninitialize();
+ WritableWorkbook.HYPERLINK_STYLE.uninitialize();
+ WritableWorkbook.HIDDEN_STYLE.uninitialize();
+ DateRecord.defaultDateFormat.uninitialize();
+ }
+
+ closeStream = cs;
+ sheets = new ArrayList();
+ sharedStrings = new SharedStrings();
+ nameRecords = new HashMap();
+ fonts = wp.getFonts();
+ formatRecords = wp.getFormattingRecords();
+ wbProtected = false;
+ settings = ws;
+ rcirCells = new ArrayList();
+ styles = new Styles();
+ outputFile = new File(os, ws, wp.getCompoundFile());
+
+ containsMacros = false;
+ if (!ws.getPropertySetsDisabled()) {
+ containsMacros = wp.containsMacros();
+ }
+
+ // Copy the country settings
+ if (wp.getCountryRecord() != null) {
+ countryRecord = new CountryRecord(wp.getCountryRecord());
+ }
+
+ // Copy any add in functions
+ addInFunctionNames = wp.getAddInFunctionNames();
+
+ // Copy XCT records
+ xctRecords = wp.getXCTRecords();
+
+ // Copy any external sheets
+ if (wp.getExternalSheetRecord() != null) {
+ externSheet = new ExternalSheetRecord(wp.getExternalSheetRecord());
+
+ // Get the associated supbooks
+ jxl.read.biff.SupbookRecord[] readsr = wp.getSupbookRecords();
+ supbooks = new ArrayList(readsr.length);
+
+ for (int i = 0; i < readsr.length; i++) {
+ jxl.read.biff.SupbookRecord readSupbook = readsr[i];
+ if (readSupbook.getType() == jxl.read.biff.SupbookRecord.INTERNAL ||
+ readSupbook.getType() == jxl.read.biff.SupbookRecord.EXTERNAL) {
+ supbooks.add(new SupbookRecord(readSupbook, settings));
+ } else {
+ if (readSupbook.getType() != jxl.read.biff.SupbookRecord.ADDIN) {
+ logger.warn("unsupported supbook type - ignoring");
+ }
+ }
+ }
+ }
+
+ // Copy any drawings. These must be present before we try and copy
+ // the images from the read workbook
+ if (wp.getDrawingGroup() != null) {
+ drawingGroup = new DrawingGroup(wp.getDrawingGroup());
+ }
+
+ // Copy the property set references
+ if (containsMacros && wp.getButtonPropertySet() != null) {
+ buttonPropertySet = new ButtonPropertySetRecord
+ (wp.getButtonPropertySet());
+ }
+
+ // Copy any names
+ if (!settings.getNamesDisabled()) {
+ jxl.read.biff.NameRecord[] na = wp.getNameRecords();
+ names = new ArrayList(na.length);
+
+ for (int i = 0; i < na.length; i++) {
+ if (na[i].isBiff8()) {
+ NameRecord n = new NameRecord(na[i], i);
+ names.add(n);
+ String name = n.getName();
+ nameRecords.put(name, n);
+ } else {
+ logger.warn("Cannot copy Biff7 name records - ignoring");
+ }
+ }
+ }
+
+ copyWorkbook(w);
+
+ // The copy process may have caused some critical fields in the
+ // read drawing group to change. Make sure these updates are reflected
+ // in the writable drawing group
+ if (drawingGroup != null) {
+ drawingGroup.updateData(wp.getDrawingGroup());
+ }
+ }
+
+ /**
+ * Gets the sheets within this workbook. Use of this method for
+ * large worksheets can cause performance problems.
+ *
+ * @return an array of the individual sheets
+ */
+ public WritableSheet[] getSheets() {
+ WritableSheet[] sheetArray = new WritableSheet[getNumberOfSheets()];
+
+ for (int i = 0; i < getNumberOfSheets(); i++) {
+ sheetArray[i] = getSheet(i);
+ }
+ return sheetArray;
+ }
+
+ /**
+ * Gets the sheet names
+ *
+ * @return an array of strings containing the sheet names
+ */
+ public String[] getSheetNames() {
+ String[] sheetNames = new String[getNumberOfSheets()];
+
+ for (int i = 0; i < sheetNames.length; i++) {
+ sheetNames[i] = getSheet(i).getName();
+ }
+
+ return sheetNames;
+ }
+
+ /**
+ * Interface method from WorkbookMethods - gets the specified
+ * sheet within this workbook
+ *
+ * @param index the zero based index of the required sheet
+ * @return The sheet specified by the index
+ */
+ public Sheet getReadSheet(int index) {
+ return getSheet(index);
+ }
+
+ /**
+ * Gets the specified sheet within this workbook
+ *
+ * @param index the zero based index of the reQuired sheet
+ * @return The sheet specified by the index
+ */
+ public WritableSheet getSheet(int index) {
+ return (WritableSheet) sheets.get(index);
+ }
+
+ /**
+ * Gets the sheet with the specified name from within this workbook
+ *
+ * @param name the sheet name
+ * @return The sheet with the specified name, or null if it is not found
+ */
+ public WritableSheet getSheet(String name) {
+ // Iterate through the boundsheet records
+ boolean found = false;
+ Iterator i = sheets.iterator();
+ WritableSheet s = null;
+
+ while (i.hasNext() && !found) {
+ s = (WritableSheet) i.next();
+
+ if (s.getName().equals(name)) {
+ found = true;
+ }
+ }
+
+ return found ? s : null;
+ }
+
+ /**
+ * Returns the number of sheets in this workbook
+ *
+ * @return the number of sheets in this workbook
+ */
+ public int getNumberOfSheets() {
+ return sheets.size();
+ }
+
+ /**
+ * Closes this workbook, and frees makes any memory allocated available
+ * for garbage collection
+ *
+ * @throws IOException
+ * @throws JxlWriteException
+ */
+ public void close() throws IOException, JxlWriteException {
+ outputFile.close(closeStream);
+ }
+
+ /**
+ * Sets a new output file. This allows the smae workbook to be
+ * written to various different output files without having to
+ * read in any templates again
+ *
+ * @param fileName the file name
+ * @throws IOException
+ */
+ public void setOutputFile(java.io.File fileName) throws IOException {
+ FileOutputStream fos = new FileOutputStream(fileName);
+ outputFile.setOutputFile(fos);
+ }
+
+
+ /**
+ * The internal method implementation for creating new sheets
+ *
+ * @param name
+ * @param index
+ * @param handleRefs flag indicating whether or not to handle external
+ * sheet references
+ * @return
+ */
+ private WritableSheet createSheet(String name, int index,
+ boolean handleRefs) {
+ WritableSheet w = new WritableSheetImpl(name,
+ outputFile,
+ formatRecords,
+ sharedStrings,
+ settings,
+ this);
+
+ int pos = index;
+
+ if (index <= 0) {
+ pos = 0;
+ sheets.add(0, w);
+ } else if (index > sheets.size()) {
+ pos = sheets.size();
+ sheets.add(w);
+ } else {
+ sheets.add(index, w);
+ }
+
+ if (handleRefs && externSheet != null) {
+ externSheet.sheetInserted(pos);
+ }
+
+ if (supbooks != null && supbooks.size() > 0) {
+ SupbookRecord supbook = (SupbookRecord) supbooks.get(0);
+ if (supbook.getType() == SupbookRecord.INTERNAL) {
+ supbook.adjustInternal(sheets.size());
+ }
+ }
+
+ return w;
+ }
+
+ /**
+ * Creates a new sheet within the workbook, at the specified position.
+ * The new sheet is inserted at the specified position, or prepended/appended
+ * to the list of sheets if the index specified is somehow inappropriate
+ *
+ * @param name the name of the new sheet
+ * @param index the index at which to add the sheet
+ * @return the created sheet
+ */
+ public WritableSheet createSheet(String name, int index) {
+ return createSheet(name, index, true);
+ }
+
+ /**
+ * Removes a sheet from this workbook, the other sheets indices being
+ * altered accordingly. If the sheet referenced by the index
+ * does not exist, then no action is taken.
+ *
+ * @param index the index of the sheet to remove
+ */
+ public void removeSheet(int index) {
+ int pos = index;
+ if (index <= 0) {
+ pos = 0;
+ sheets.remove(0);
+ } else if (index >= sheets.size()) {
+ pos = sheets.size() - 1;
+ sheets.remove(sheets.size() - 1);
+ } else {
+ sheets.remove(index);
+ }
+
+ if (externSheet != null) {
+ externSheet.sheetRemoved(pos);
+ }
+
+ if (supbooks != null && supbooks.size() > 0) {
+ SupbookRecord supbook = (SupbookRecord) supbooks.get(0);
+ if (supbook.getType() == SupbookRecord.INTERNAL) {
+ supbook.adjustInternal(sheets.size());
+ }
+ }
+
+ if (names != null && names.size() > 0) {
+ for (int i = 0; i < names.size(); i++) {
+ NameRecord n = (NameRecord) names.get(i);
+ int oldRef = n.getSheetRef();
+ if (oldRef == (pos + 1)) {
+ n.setSheetRef(0); // make a global name reference
+ } else if (oldRef > (pos + 1)) {
+ if (oldRef < 1) {
+ oldRef = 1;
+ }
+ n.setSheetRef(oldRef - 1); // move one sheet
+ }
+ }
+ }
+ }
+
+ /**
+ * Moves the specified sheet within this workbook to another index
+ * position.
+ *
+ * @param fromIndex the zero based index of the reQuired sheet
+ * @param toIndex the zero based index of the reQuired sheet
+ * @return the sheet that has been moved
+ */
+ public WritableSheet moveSheet(int fromIndex, int toIndex) {
+ // Handle dodgy index
+ fromIndex = Math.max(fromIndex, 0);
+ fromIndex = Math.min(fromIndex, sheets.size() - 1);
+ toIndex = Math.max(toIndex, 0);
+ toIndex = Math.min(toIndex, sheets.size() - 1);
+
+ WritableSheet sheet = (WritableSheet) sheets.remove(fromIndex);
+ sheets.add(toIndex, sheet);
+
+ return sheet;
+ }
+
+ /**
+ * Writes out this sheet to the output file. First it writes out
+ * the standard workbook information required by excel, before calling
+ * the write method on each sheet individually
+ *
+ * @throws IOException
+ */
+ public void write() throws IOException {
+ // Perform some preliminary sheet check before we start writing out
+ // the workbook
+ WritableSheetImpl wsi = null;
+ for (int i = 0; i < getNumberOfSheets(); i++) {
+ wsi = (WritableSheetImpl) getSheet(i);
+
+ // Check the merged records. This has to be done before the
+ // globals are written out because some more XF formats might be created
+ wsi.checkMergedBorders();
+
+ // Check to see if there are any predefined names
+ Range range = wsi.getSettings().getPrintArea();
+ if (range != null) {
+ addNameArea(BuiltInName.PRINT_AREA,
+ wsi,
+ range.getTopLeft().getColumn(),
+ range.getTopLeft().getRow(),
+ range.getBottomRight().getColumn(),
+ range.getBottomRight().getRow(),
+ false);
+ }
+
+ // Check to see if print titles by row were set
+ Range rangeR = wsi.getSettings().getPrintTitlesRow();
+ Range rangeC = wsi.getSettings().getPrintTitlesCol();
+ if (rangeR != null && rangeC != null) {
+ addNameArea(BuiltInName.PRINT_TITLES,
+ wsi,
+ rangeR.getTopLeft().getColumn(),
+ rangeR.getTopLeft().getRow(),
+ rangeR.getBottomRight().getColumn(),
+ rangeR.getBottomRight().getRow(),
+ rangeC.getTopLeft().getColumn(),
+ rangeC.getTopLeft().getRow(),
+ rangeC.getBottomRight().getColumn(),
+ rangeC.getBottomRight().getRow(),
+ false);
+ }
+ // Check to see if print titles by row were set
+ else if (rangeR != null) {
+ addNameArea(BuiltInName.PRINT_TITLES,
+ wsi,
+ rangeR.getTopLeft().getColumn(),
+ rangeR.getTopLeft().getRow(),
+ rangeR.getBottomRight().getColumn(),
+ rangeR.getBottomRight().getRow(),
+ false);
+ }
+ // Check to see if print titles by column were set
+ else if (rangeC != null) {
+ addNameArea(BuiltInName.PRINT_TITLES,
+ wsi,
+ rangeC.getTopLeft().getColumn(),
+ rangeC.getTopLeft().getRow(),
+ rangeC.getBottomRight().getColumn(),
+ rangeC.getBottomRight().getRow(),
+ false);
+ }
+ }
+
+ // Rationalize all the XF and number formats
+ if (!settings.getRationalizationDisabled()) {
+ rationalize();
+ }
+
+ // Write the workbook globals
+ BOFRecord bof = new BOFRecord(BOFRecord.workbookGlobals);
+ outputFile.write(bof);
+
+ // Must immediatly follow the BOF record
+ if (settings.getTemplate()) {
+ // Only write record if we are a template
+ TemplateRecord trec = new TemplateRecord();
+ outputFile.write(trec);
+ }
+
+
+ InterfaceHeaderRecord ihr = new InterfaceHeaderRecord();
+ outputFile.write(ihr);
+
+ MMSRecord mms = new MMSRecord(0, 0);
+ outputFile.write(mms);
+
+ InterfaceEndRecord ier = new InterfaceEndRecord();
+ outputFile.write(ier);
+
+ WriteAccessRecord wr = new WriteAccessRecord(settings.getWriteAccess());
+ outputFile.write(wr);
+
+ CodepageRecord cp = new CodepageRecord();
+ outputFile.write(cp);
+
+ DSFRecord dsf = new DSFRecord();
+ outputFile.write(dsf);
+
+ if (settings.getExcel9File()) {
+ // Only write record if we are a template
+ // We are not excel 2000, should we still set the flag
+ Excel9FileRecord e9rec = new Excel9FileRecord();
+ outputFile.write(e9rec);
+ }
+
+ TabIdRecord tabid = new TabIdRecord(getNumberOfSheets());
+ outputFile.write(tabid);
+
+ if (containsMacros) {
+ ObjProjRecord objproj = new ObjProjRecord();
+ outputFile.write(objproj);
+ }
+
+ if (buttonPropertySet != null) {
+ outputFile.write(buttonPropertySet);
+ }
+
+ FunctionGroupCountRecord fgcr = new FunctionGroupCountRecord();
+ outputFile.write(fgcr);
+
+ // do not support password protected workbooks
+ WindowProtectRecord wpr = new WindowProtectRecord
+ (settings.getWindowProtected());
+ outputFile.write(wpr);
+
+ ProtectRecord pr = new ProtectRecord(wbProtected);
+ outputFile.write(pr);
+
+ PasswordRecord pw = new PasswordRecord(null);
+ outputFile.write(pw);
+
+ Prot4RevRecord p4r = new Prot4RevRecord(false);
+ outputFile.write(p4r);
+
+ Prot4RevPassRecord p4rp = new Prot4RevPassRecord();
+ outputFile.write(p4rp);
+
+ // If no sheet is identified as being selected, then select
+ // the first one
+ boolean sheetSelected = false;
+ WritableSheetImpl wsheet = null;
+ int selectedSheetIndex = 0;
+ for (int i = 0; i < getNumberOfSheets() && !sheetSelected; i++) {
+ wsheet = (WritableSheetImpl) getSheet(i);
+ if (wsheet.getSettings().isSelected()) {
+ sheetSelected = true;
+ selectedSheetIndex = i;
+ }
+ }
+
+ if (!sheetSelected) {
+ wsheet = (WritableSheetImpl) getSheet(0);
+ wsheet.getSettings().setSelected(true);
+ selectedSheetIndex = 0;
+ }
+
+ Window1Record w1r = new Window1Record(selectedSheetIndex);
+ outputFile.write(w1r);
+
+ BackupRecord bkr = new BackupRecord(false);
+ outputFile.write(bkr);
+
+ HideobjRecord ho = new HideobjRecord(settings.getHideobj());
+ outputFile.write(ho);
+
+ NineteenFourRecord nf = new NineteenFourRecord(false);
+ outputFile.write(nf);
+
+ PrecisionRecord pc = new PrecisionRecord(false);
+ outputFile.write(pc);
+
+ RefreshAllRecord rar = new RefreshAllRecord(settings.getRefreshAll());
+ outputFile.write(rar);
+
+ BookboolRecord bb = new BookboolRecord(true);
+ outputFile.write(bb);
+
+ // Write out all the fonts used
+ fonts.write(outputFile);
+
+ // Write out the cell formats used within this workbook
+ formatRecords.write(outputFile);
+
+ // Write out the palette, if it exists
+ if (formatRecords.getPalette() != null) {
+ outputFile.write(formatRecords.getPalette());
+ }
+
+ // Write out the uses elfs record
+ UsesElfsRecord uer = new UsesElfsRecord();
+ outputFile.write(uer);
+
+ // Write out the boundsheet records. Keep a handle to each one's
+ // position so we can write in the stream offset later
+ int[] boundsheetPos = new int[getNumberOfSheets()];
+ Sheet sheet = null;
+
+ for (int i = 0; i < getNumberOfSheets(); i++) {
+ boundsheetPos[i] = outputFile.getPos();
+ sheet = getSheet(i);
+ BoundsheetRecord br = new BoundsheetRecord(sheet.getName());
+ if (sheet.getSettings().isHidden()) {
+ br.setHidden();
+ }
+
+ if (((WritableSheetImpl) sheets.get(i)).isChartOnly()) {
+ br.setChartOnly();
+ }
+
+ outputFile.write(br);
+ }
+
+ if (countryRecord == null) {
+ CountryCode lang =
+ CountryCode.getCountryCode(settings.getExcelDisplayLanguage());
+ if (lang == CountryCode.UNKNOWN) {
+ logger.warn("Unknown country code " +
+ settings.getExcelDisplayLanguage() +
+ " using " + CountryCode.USA.getCode());
+ lang = CountryCode.USA;
+ }
+ CountryCode region =
+ CountryCode.getCountryCode(settings.getExcelRegionalSettings());
+ countryRecord = new CountryRecord(lang, region);
+ if (region == CountryCode.UNKNOWN) {
+ logger.warn("Unknown country code " +
+ settings.getExcelDisplayLanguage() +
+ " using " + CountryCode.UK.getCode());
+ region = CountryCode.UK;
+ }
+ }
+
+ outputFile.write(countryRecord);
+
+ // Write out the names of any add in functions
+ if (addInFunctionNames != null && addInFunctionNames.length > 0) {
+ // Write out the supbook record
+ // SupbookRecord supbook = new SupbookRecord();
+ // outputFile.write(supbook);
+
+ for (int i = 0; i < addInFunctionNames.length; i++) {
+ ExternalNameRecord enr = new ExternalNameRecord(addInFunctionNames[i]);
+ outputFile.write(enr);
+ }
+ }
+
+ if (xctRecords != null) {
+ for (int i = 0; i < xctRecords.length; i++) {
+ outputFile.write(xctRecords[i]);
+ }
+ }
+
+ // Write out the external sheet record, if it exists
+ if (externSheet != null) {
+ //Write out all the supbook records
+ for (int i = 0; i < supbooks.size(); i++) {
+ SupbookRecord supbook = (SupbookRecord) supbooks.get(i);
+ outputFile.write(supbook);
+ }
+ outputFile.write(externSheet);
+ }
+
+ // Write out the names, if any exists
+ if (names != null) {
+ for (int i = 0; i < names.size(); i++) {
+ NameRecord n = (NameRecord) names.get(i);
+ outputFile.write(n);
+ }
+ }
+
+ // Write out the mso drawing group, if it exists
+ if (drawingGroup != null) {
+ drawingGroup.write(outputFile);
+ }
+
+ sharedStrings.write(outputFile);
+
+ EOFRecord eof = new EOFRecord();
+ outputFile.write(eof);
+
+
+ // Write out the sheets
+ for (int i = 0; i < getNumberOfSheets(); i++) {
+ // first go back and modify the offset we wrote out for the
+ // boundsheet record
+ outputFile.setData
+ (IntegerHelper.getFourBytes(outputFile.getPos()),
+ boundsheetPos[i] + 4);
+
+ wsheet = (WritableSheetImpl) getSheet(i);
+ wsheet.write();
+ }
+ }
+
+ /**
+ * Produces a writable copy of the workbook passed in by
+ * creating copies of each sheet in the specified workbook and adding
+ * them to its own record
+ *
+ * @param w the workbook to copy
+ */
+ private void copyWorkbook(Workbook w) {
+ int numSheets = w.getNumberOfSheets();
+ wbProtected = w.isProtected();
+ Sheet s = null;
+ WritableSheetImpl ws = null;
+ for (int i = 0; i < numSheets; i++) {
+ s = w.getSheet(i);
+ ws = (WritableSheetImpl) createSheet(s.getName(), i, false);
+ ws.copy(s);
+ }
+ }
+
+ /**
+ * Copies the specified sheet and places it at the index
+ * specified by the parameter
+ *
+ * @param s the index of the sheet to copy
+ * @param name the name of the new sheet
+ * @param index the position of the new sheet
+ */
+ public void copySheet(int s, String name, int index) {
+ WritableSheet sheet = getSheet(s);
+ WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index);
+ ws.copy(sheet);
+ }
+
+ /**
+ * Copies the specified sheet and places it at the index
+ * specified by the parameter
+ *
+ * @param s the name of the sheet to copy
+ * @param name the name of the new sheet
+ * @param index the position of the new sheet
+ */
+ public void copySheet(String s, String name, int index) {
+ WritableSheet sheet = getSheet(s);
+ WritableSheetImpl ws = (WritableSheetImpl) createSheet(name, index);
+ ws.copy(sheet);
+ }
+
+ /**
+ * Indicates whether or not this workbook is protected
+ *
+ * @param prot protected flag
+ */
+ public void setProtected(boolean prot) {
+ wbProtected = prot;
+ }
+
+ /**
+ * Rationalizes the cell formats, and then passes the resultant XF index
+ * mappings to each sheet in turn
+ */
+ private void rationalize() {
+ IndexMapping fontMapping = formatRecords.rationalizeFonts();
+ IndexMapping formatMapping = formatRecords.rationalizeDisplayFormats();
+ IndexMapping xfMapping = formatRecords.rationalize(fontMapping,
+ formatMapping);
+
+ WritableSheetImpl wsi = null;
+ for (int i = 0; i < sheets.size(); i++) {
+ wsi = (WritableSheetImpl) sheets.get(i);
+ wsi.rationalize(xfMapping, fontMapping, formatMapping);
+ }
+ }
+
+ /**
+ * Gets the internal sheet index for a sheet name
+ *
+ * @param name the sheet name
+ * @return the internal sheet index
+ */
+ private int getInternalSheetIndex(String name) {
+ int index = -1;
+ String[] names = getSheetNames();
+ for (int i = 0; i < names.length; i++) {
+ if (name.equals(names[i])) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Gets the name of the external sheet specified by the index
+ *
+ * @param index the external sheet index
+ * @return the name of the external sheet
+ */
+ public String getExternalSheetName(int index) {
+ int supbookIndex = externSheet.getSupbookIndex(index);
+ SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex);
+
+ int firstTab = externSheet.getFirstTabIndex(index);
+
+ if (sr.getType() == SupbookRecord.INTERNAL) {
+ // It's an internal reference - get the name from the sheets list
+ WritableSheet ws = getSheet(firstTab);
+
+ return ws.getName();
+ } else if (sr.getType() == SupbookRecord.EXTERNAL) {
+ String name = sr.getFileName() + sr.getSheetName(firstTab);
+ return name;
+ }
+
+ // An unknown supbook - return unkown
+ logger.warn("Unknown Supbook 1");
+ return "[UNKNOWN]";
+ }
+
+ /**
+ * Gets the name of the last external sheet specified by the index
+ *
+ * @param index the external sheet index
+ * @return the name of the external sheet
+ */
+ public String getLastExternalSheetName(int index) {
+ int supbookIndex = externSheet.getSupbookIndex(index);
+ SupbookRecord sr = (SupbookRecord) supbooks.get(supbookIndex);
+
+ int lastTab = externSheet.getLastTabIndex(index);
+
+ if (sr.getType() == SupbookRecord.INTERNAL) {
+ // It's an internal reference - get the name from the sheets list
+ WritableSheet ws = getSheet(lastTab);
+
+ return ws.getName();
+ } else if (sr.getType() == SupbookRecord.EXTERNAL) {
+ Assert.verify(false);
+ }
+
+ // An unknown supbook - return unkown
+ logger.warn("Unknown Supbook 2");
+ return "[UNKNOWN]";
+ }
+
+ /**
+ * Parsing of formulas is only supported for a subset of the available
+ * biff version, so we need to test to see if this version is acceptable
+ *
+ * @return the BOF record, which
+ */
+ public jxl.read.biff.BOFRecord getWorkbookBof() {
+ return null;
+ }
+
+
+ /**
+ * Gets the index of the external sheet for the name
+ *
+ * @param sheetName
+ * @return the sheet index of the external sheet index
+ */
+ public int getExternalSheetIndex(int index) {
+ if (externSheet == null) {
+ return index;
+ }
+
+ Assert.verify(externSheet != null);
+
+ int firstTab = externSheet.getFirstTabIndex(index);
+
+ return firstTab;
+ }
+
+ /**
+ * Gets the index of the external sheet for the name
+ *
+ * @param sheetName
+ * @return the sheet index of the external sheet index
+ */
+ public int getLastExternalSheetIndex(int index) {
+ if (externSheet == null) {
+ return index;
+ }
+
+ Assert.verify(externSheet != null);
+
+ int lastTab = externSheet.getLastTabIndex(index);
+
+ return lastTab;
+ }
+
+ /**
+ * Gets the external sheet index for the sheet name
+ *
+ * @param sheetName
+ * @return the sheet index or -1 if the sheet could not be found
+ */
+ public int getExternalSheetIndex(String sheetName) {
+ if (externSheet == null) {
+ externSheet = new ExternalSheetRecord();
+ supbooks = new ArrayList();
+ supbooks.add(new SupbookRecord(getNumberOfSheets(), settings));
+ }
+
+ // Iterate through the sheets records
+ boolean found = false;
+ Iterator i = sheets.iterator();
+ int sheetpos = 0;
+ WritableSheetImpl s = null;
+
+ while (i.hasNext() && !found) {
+ s = (WritableSheetImpl) i.next();
+
+ if (s.getName().equals(sheetName)) {
+ found = true;
+ } else {
+ sheetpos++;
+ }
+ }
+
+ if (found) {
+ // Check that the supbook record at position zero is internal and
+ // contains all the sheets
+ SupbookRecord supbook = (SupbookRecord) supbooks.get(0);
+ if (supbook.getType() != SupbookRecord.INTERNAL ||
+ supbook.getNumberOfSheets() != getNumberOfSheets()) {
+ logger.warn("Cannot find sheet " + sheetName + " in supbook record");
+ }
+
+ return externSheet.getIndex(0, sheetpos);
+ }
+
+ // Check for square brackets
+ int closeSquareBracketsIndex = sheetName.lastIndexOf(']');
+ int openSquareBracketsIndex = sheetName.lastIndexOf('[');
+
+ if (closeSquareBracketsIndex == -1 ||
+ openSquareBracketsIndex == -1) {
+ logger.warn("Square brackets");
+ return -1;
+ }
+
+ String worksheetName = sheetName.substring(closeSquareBracketsIndex + 1);
+ String workbookName = sheetName.substring(openSquareBracketsIndex + 1,
+ closeSquareBracketsIndex);
+ String path = sheetName.substring(0, openSquareBracketsIndex);
+ String fileName = path + workbookName;
+
+ boolean supbookFound = false;
+ SupbookRecord externalSupbook = null;
+ int supbookIndex = -1;
+ for (int ind = 0; ind < supbooks.size() && !supbookFound; ind++) {
+ externalSupbook = (SupbookRecord) supbooks.get(ind);
+ if (externalSupbook.getType() == SupbookRecord.EXTERNAL &&
+ externalSupbook.getFileName().equals(fileName)) {
+ supbookFound = true;
+ supbookIndex = ind;
+ }
+ }
+
+ if (!supbookFound) {
+ externalSupbook = new SupbookRecord(fileName, settings);
+ supbookIndex = supbooks.size();
+ supbooks.add(externalSupbook);
+ }
+
+ int sheetIndex = externalSupbook.getSheetIndex(worksheetName);
+
+ return externSheet.getIndex(supbookIndex, sheetIndex);
+ }
+
+ /**
+ * Gets the last external sheet index for the sheet name
+ *
+ * @param sheetName
+ * @return the sheet index or -1 if the sheet could not be found
+ */
+ public int getLastExternalSheetIndex(String sheetName) {
+ if (externSheet == null) {
+ externSheet = new ExternalSheetRecord();
+ supbooks = new ArrayList();
+ supbooks.add(new SupbookRecord(getNumberOfSheets(), settings));
+ }
+
+ // Iterate through the sheets records
+ boolean found = false;
+ Iterator i = sheets.iterator();
+ int sheetpos = 0;
+ WritableSheetImpl s = null;
+
+ while (i.hasNext() && !found) {
+ s = (WritableSheetImpl) i.next();
+
+ if (s.getName().equals(sheetName)) {
+ found = true;
+ } else {
+ sheetpos++;
+ }
+ }
+
+ if (!found) {
+ return -1;
+ }
+
+ // Check that the supbook record at position zero is internal and contains
+ // all the sheets
+ SupbookRecord supbook = (SupbookRecord) supbooks.get(0);
+ Assert.verify(supbook.getType() == SupbookRecord.INTERNAL &&
+ supbook.getNumberOfSheets() == getNumberOfSheets());
+
+ return externSheet.getIndex(0, sheetpos);
+ }
+
+ /**
+ * Sets the RGB value for the specified colour for this workbook
+ *
+ * @param c the colour whose RGB value is to be overwritten
+ * @param r the red portion to set (0-255)
+ * @param g the green portion to set (0-255)
+ * @param b the blue portion to set (0-255)
+ */
+ public void setColourRGB(Colour c, int r, int g, int b) {
+ formatRecords.setColourRGB(c, r, g, b);
+ }
+
+ /**
+ * Accessor for the RGB value for the specified colour
+ *
+ * @return the RGB for the specified colour
+ */
+ public RGB getColourRGB(Colour c) {
+ return formatRecords.getColourRGB(c);
+ }
+
+ /**
+ * Gets the name at the specified index
+ *
+ * @param index the index into the name table
+ * @return the name of the cell
+ */
+ public String getName(int index) {
+ Assert.verify(index >= 0 && index < names.size());
+ NameRecord n = (NameRecord) names.get(index);
+ return n.getName();
+ }
+
+ /**
+ * Gets the index of the name record for the name
+ *
+ * @param name
+ * @return the index in the name table
+ */
+ public int getNameIndex(String name) {
+ NameRecord nr = (NameRecord) nameRecords.get(name);
+ return nr != null ? nr.getIndex() : -1;
+ }
+
+ /**
+ * Adds a cell to workbook wide range of cells which need adjustment
+ * following a row/column insert or remove
+ *
+ * @param f the cell to add to the list
+ */
+ void addRCIRCell(CellValue cv) {
+ rcirCells.add(cv);
+ }
+
+ /**
+ * Called when a column is inserted on the specified sheet. Notifies all
+ * RCIR cells of this change
+ *
+ * @param s the sheet on which the column was inserted
+ * @param col the column number which was inserted
+ */
+ void columnInserted(WritableSheetImpl s, int col) {
+ int externalSheetIndex = getExternalSheetIndex(s.getName());
+ for (Iterator i = rcirCells.iterator(); i.hasNext(); ) {
+ CellValue cv = (CellValue) i.next();
+ cv.columnInserted(s, externalSheetIndex, col);
+ }
+
+ // Adjust any named cells
+ if (names != null) {
+ for (Iterator i = names.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ nameRecord.columnInserted(externalSheetIndex, col);
+ }
+ }
+ }
+
+ /**
+ * Called when a column is removed on the specified sheet. Notifies all
+ * RCIR cells of this change
+ *
+ * @param s the sheet on which the column was removed
+ * @param col the column number which was removed
+ */
+ void columnRemoved(WritableSheetImpl s, int col) {
+ int externalSheetIndex = getExternalSheetIndex(s.getName());
+ for (Iterator i = rcirCells.iterator(); i.hasNext(); ) {
+ CellValue cv = (CellValue) i.next();
+ cv.columnRemoved(s, externalSheetIndex, col);
+ }
+
+ // Adjust any named cells
+ ArrayList removedNames = new ArrayList();
+ if (names != null) {
+ for (Iterator i = names.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ boolean removeName = nameRecord.columnRemoved(externalSheetIndex,
+ col);
+
+ if (removeName) {
+ removedNames.add(nameRecord);
+ }
+ }
+
+ // Remove any names which have been deleted
+ for (Iterator i = removedNames.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ boolean removed = names.remove(nameRecord);
+ Assert.verify(removed, "Could not remove name " +
+ nameRecord.getName());
+ }
+ }
+ }
+
+ /**
+ * Called when a row is inserted on the specified sheet. Notifies all
+ * RCIR cells of this change
+ *
+ * @param s the sheet on which the row was inserted
+ * @param row the row number which was inserted
+ */
+ void rowInserted(WritableSheetImpl s, int row) {
+ int externalSheetIndex = getExternalSheetIndex(s.getName());
+
+ // Adjust the row infos
+ for (Iterator i = rcirCells.iterator(); i.hasNext(); ) {
+ CellValue cv = (CellValue) i.next();
+ cv.rowInserted(s, externalSheetIndex, row);
+ }
+
+ // Adjust any named cells
+ if (names != null) {
+ for (Iterator i = names.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ nameRecord.rowInserted(externalSheetIndex, row);
+ }
+ }
+ }
+
+ /**
+ * Called when a row is removed on the specified sheet. Notifies all
+ * RCIR cells of this change
+ *
+ * @param s the sheet on which the row was removed
+ * @param row the row number which was removed
+ */
+ void rowRemoved(WritableSheetImpl s, int row) {
+ int externalSheetIndex = getExternalSheetIndex(s.getName());
+ for (Iterator i = rcirCells.iterator(); i.hasNext(); ) {
+ CellValue cv = (CellValue) i.next();
+ cv.rowRemoved(s, externalSheetIndex, row);
+ }
+
+ // Adjust any named cells
+ ArrayList removedNames = new ArrayList();
+ if (names != null) {
+ for (Iterator i = names.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ boolean removeName = nameRecord.rowRemoved(externalSheetIndex, row);
+
+ if (removeName) {
+ removedNames.add(nameRecord);
+ }
+ }
+
+ // Remove any names which have been deleted
+ for (Iterator i = removedNames.iterator(); i.hasNext(); ) {
+ NameRecord nameRecord = (NameRecord) i.next();
+ boolean removed = names.remove(nameRecord);
+ Assert.verify(removed, "Could not remove name " +
+ nameRecord.getName());
+ }
+ }
+ }
+
+ /**
+ * Gets the named cell from this workbook. If the name refers to a
+ * range of cells, then the cell on the top left is returned. If
+ * the name cannot be found, null is returned
+ *
+ * @param the name of the cell/range to search for
+ * @return the cell in the top left of the range if found, NULL
+ * otherwise
+ */
+ public WritableCell findCellByName(String name) {
+ NameRecord nr = (NameRecord) nameRecords.get(name);
+
+ if (nr == null) {
+ return null;
+ }
+
+ NameRecord.NameRange[] ranges = nr.getRanges();
+
+ // Go and retrieve the first cell in the first range
+ int sheetIndex = getExternalSheetIndex(ranges[0].getExternalSheet());
+ WritableSheet s = getSheet(sheetIndex);
+ WritableCell cell = s.getWritableCell(ranges[0].getFirstColumn(),
+ ranges[0].getFirstRow());
+
+ return cell;
+ }
+
+ /**
+ * Gets the named range from this workbook. The Range object returns
+ * contains all the cells from the top left to the bottom right
+ * of the range.
+ * If the named range comprises an adjacent range,
+ * the Range[] will contain one object; for non-adjacent
+ * ranges, it is necessary to return an array of length greater than
+ * one.
+ * If the named range contains a single cell, the top left and
+ * bottom right cell will be the same cell
+ *
+ * @param the name of the cell/range to search for
+ * @return the range of cells
+ */
+ public Range[] findByName(String name) {
+ NameRecord nr = (NameRecord) nameRecords.get(name);
+
+ if (nr == null) {
+ return null;
+ }
+
+ NameRecord.NameRange[] ranges = nr.getRanges();
+
+ Range[] cellRanges = new Range[ranges.length];
+
+ for (int i = 0; i < ranges.length; i++) {
+ cellRanges[i] = new RangeImpl
+ (this,
+ getExternalSheetIndex(ranges[i].getExternalSheet()),
+ ranges[i].getFirstColumn(),
+ ranges[i].getFirstRow(),
+ getLastExternalSheetIndex(ranges[i].getExternalSheet()),
+ ranges[i].getLastColumn(),
+ ranges[i].getLastRow());
+ }
+
+ return cellRanges;
+ }
+
+ /**
+ * Adds a drawing to this workbook
+ *
+ * @param d the drawing to add
+ */
+ void addDrawing(DrawingGroupObject d) {
+ if (drawingGroup == null) {
+ drawingGroup = new DrawingGroup(Origin.WRITE);
+ }
+
+ drawingGroup.add(d);
+ }
+
+ /**
+ * Removes a drawing from this workbook
+ *
+ * @param d the drawing to remove
+ */
+ void removeDrawing(Drawing d) {
+ Assert.verify(drawingGroup != null);
+
+ drawingGroup.remove(d);
+ }
+
+ /**
+ * Accessor for the drawing group
+ *
+ * @return the drawing group
+ */
+ DrawingGroup getDrawingGroup() {
+ return drawingGroup;
+ }
+
+ /**
+ * Create a drawing group for this workbook - used when importing sheets
+ * which contain drawings, but this workbook doesn't.
+ * We can't subsume this into the getDrawingGroup() method because the
+ * null-ness of the return value is used elsewhere to determine the
+ * origin of the workbook
+ */
+ DrawingGroup createDrawingGroup() {
+ if (drawingGroup == null) {
+ drawingGroup = new DrawingGroup(Origin.WRITE);
+ }
+
+ return drawingGroup;
+ }
+
+ /**
+ * Gets the named ranges
+ *
+ * @return the list of named cells within the workbook
+ */
+ public String[] getRangeNames() {
+ if (names == null) {
+ return new String[0];
+ }
+
+ String[] n = new String[names.size()];
+ for (int i = 0; i < names.size(); i++) {
+ NameRecord nr = (NameRecord) names.get(i);
+ n[i] = nr.getName();
+ }
+
+ return n;
+ }
+
+ /**
+ * Removes the specified named range from the workbook
+ *
+ * @param name the name to remove
+ */
+ public void removeRangeName(String name) {
+ int pos = 0;
+ boolean found = false;
+ for (Iterator i = names.iterator(); i.hasNext() && !found; ) {
+ NameRecord nr = (NameRecord) i.next();
+ if (nr.getName().equals(name)) {
+ found = true;
+ } else {
+ pos++;
+ }
+ }
+
+ // Remove the name from the list of names and the associated hashmap
+ // of names (used to retrieve the name index). If the name cannot
+ // be found, a warning is displayed
+ if (found) {
+ names.remove(pos);
+ if (nameRecords.remove(name) == null) {
+ logger.warn("Could not remove " + name + " from index lookups");
+ }
+ }
+ }
+
+ /**
+ * Accessor for the jxl.common.styles
+ *
+ * @return the standard styles for this workbook
+ */
+ Styles getStyles() {
+ return styles;
+ }
+
+ /**
+ * Add new named area to this workbook with the given information.
+ *
+ * @param name name to be created.
+ * @param sheet sheet containing the name
+ * @param firstCol first column this name refers to.
+ * @param firstRow first row this name refers to.
+ * @param lastCol last column this name refers to.
+ * @param lastRow last row this name refers to.
+ */
+ public void addNameArea(String name,
+ WritableSheet sheet,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow) {
+ addNameArea(name, sheet, firstCol, firstRow, lastCol, lastRow, true);
+ }
+
+ /**
+ * Add new named area to this workbook with the given information.
+ *
+ * @param name name to be created.
+ * @param sheet sheet containing the name
+ * @param firstCol first column this name refers to.
+ * @param firstRow first row this name refers to.
+ * @param lastCol last column this name refers to.
+ * @param lastRow last row this name refers to.
+ * @param global TRUE if this is a global name, FALSE if this is tied to
+ * the sheet
+ */
+ void addNameArea(String name,
+ WritableSheet sheet,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow,
+ boolean global) {
+ if (names == null) {
+ names = new ArrayList();
+ }
+
+ int externalSheetIndex = getExternalSheetIndex(sheet.getName());
+
+ // Create a new name record.
+ NameRecord nr =
+ new NameRecord(name,
+ names.size(),
+ externalSheetIndex,
+ firstRow, lastRow,
+ firstCol, lastCol,
+ global);
+
+ // Add new name to name array.
+ names.add(nr);
+
+ // Add new name to name hash table.
+ nameRecords.put(name, nr);
+ }
+
+ /**
+ * Add new named area to this workbook with the given information.
+ *
+ * @param name name to be created.
+ * @param sheet sheet containing the name
+ * @param firstCol first column this name refers to.
+ * @param firstRow first row this name refers to.
+ * @param lastCol last column this name refers to.
+ * @param lastRow last row this name refers to.
+ * @param global TRUE if this is a global name, FALSE if this is tied to
+ * the sheet
+ */
+ void addNameArea(BuiltInName name,
+ WritableSheet sheet,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow,
+ boolean global) {
+ if (names == null) {
+ names = new ArrayList();
+ }
+
+ int index = getInternalSheetIndex(sheet.getName());
+ int externalSheetIndex = getExternalSheetIndex(sheet.getName());
+
+ // Create a new name record.
+ NameRecord nr =
+ new NameRecord(name,
+ index,
+ externalSheetIndex,
+ firstRow, lastRow,
+ firstCol, lastCol,
+ global);
+
+ // Add new name to name array.
+ names.add(nr);
+
+ // Add new name to name hash table.
+ nameRecords.put(name, nr);
+ }
+
+ /**
+ * Add new named area to this workbook with the given information.
+ *
+ * @param name name to be created.
+ * @param sheet sheet containing the name
+ * @param firstCol first column this name refers to.
+ * @param firstRow first row this name refers to.
+ * @param lastCol last column this name refers to.
+ * @param lastRow last row this name refers to.
+ * @param firstCol2 first column this name refers to.
+ * @param firstRow2 first row this name refers to.
+ * @param lastCol2 last column this name refers to.
+ * @param lastRow2 last row this name refers to.
+ * @param global TRUE if this is a global name, FALSE if this is tied to
+ * the sheet
+ */
+ void addNameArea(BuiltInName name,
+ WritableSheet sheet,
+ int firstCol,
+ int firstRow,
+ int lastCol,
+ int lastRow,
+ int firstCol2,
+ int firstRow2,
+ int lastCol2,
+ int lastRow2,
+ boolean global) {
+ if (names == null) {
+ names = new ArrayList();
+ }
+
+ int index = getInternalSheetIndex(sheet.getName());
+ int externalSheetIndex = getExternalSheetIndex(sheet.getName());
+
+ // Create a new name record.
+ NameRecord nr =
+ new NameRecord(name,
+ index,
+ externalSheetIndex,
+ firstRow2, lastRow2,
+ firstCol2, lastCol2,
+ firstRow, lastRow,
+ firstCol, lastCol,
+ global);
+
+ // Add new name to name array.
+ names.add(nr);
+
+ // Add new name to name hash table.
+ nameRecords.put(name, nr);
+ }
+
+ /**
+ * Accessor for the workbook settings
+ */
+ WorkbookSettings getSettings() {
+ return settings;
+ }
+
+ /**
+ * Returns the cell for the specified location eg. "Sheet1!A4".
+ * This is identical to using the CellReferenceHelper with its
+ * associated performance overheads, consequently it should
+ * be use sparingly
+ *
+ * @param loc the cell to retrieve
+ * @return the cell at the specified location
+ */
+ public WritableCell getWritableCell(String loc) {
+ WritableSheet s = getSheet(CellReferenceHelper.getSheet(loc));
+ return s.getWritableCell(loc);
+ }
+
+ /**
+ * Imports a sheet from a different workbook. Does a deep copy on all
+ * elements within that sheet
+ *
+ * @param name the name of the new sheet
+ * @param index the position for the new sheet within this workbook
+ * @param sheet the sheet (from another workbook) to merge into this one
+ * @return the new sheet
+ */
+ public WritableSheet importSheet(String name, int index, Sheet sheet) {
+ WritableSheet ws = createSheet(name, index);
+ ((WritableSheetImpl) ws).importSheet(sheet);
+
+ return ws;
+ }
+
+}
+
+
+
+
+
diff --git a/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java b/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java
new file mode 100755
index 0000000..9d0c840
--- /dev/null
+++ b/datastructures-xslx/src/main/java/jxl/write/biff/WriteAccessRecord.java
@@ -0,0 +1,73 @@
+/*********************************************************************
+ *
+ * Copyright (C) 2002 Andrew Khan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ***************************************************************************/
+
+package jxl.write.biff;
+
+import jxl.Workbook;
+import jxl.biff.StringHelper;
+import jxl.biff.Type;
+import jxl.biff.WritableRecordData;
+
+/**
+ * The name used when Excel was installed.
+ * When writing worksheets, it uses the value from the WorkbookSettings object,
+ * if this is not set (null) this is hard coded as
+ * Java Excel API + Version number
+ */
+class WriteAccessRecord extends WritableRecordData {
+ /**
+ * The author of this workbook (ie. the Java Excel API)
+ */
+ private final static String authorString = "Java Excel API";
+
+ // String of length 112 characters
+ /**
+ * The data to output to file
+ */
+ private final byte[] data;
+ private String userName;
+
+ /**
+ * Constructor
+ */
+ public WriteAccessRecord(String userName) {
+ super(Type.WRITEACCESS);
+
+ data = new byte[112];
+ String astring = userName != null ?
+ userName :
+ authorString + " v" + Workbook.getVersion();
+
+ StringHelper.getBytes(astring, data, 0);
+
+ // Pad out the record with space characters
+ for (int i = astring.length(); i < data.length; i++) {
+ data[i] = 0x20;
+ }
+ }
+
+ /**
+ * Gets the data for output to file
+ *
+ * @return the binary data
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java b/datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java
similarity index 94%
rename from datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java
rename to datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java
index 9acc9d1..95f7f24 100644
--- a/datastructures-xslx/src/main/java/com/incesoft/tools/excel/xlsx/TestSJXLSX.java
+++ b/datastructures-xslx/src/test/java/org/xbib/datastructures/xslx/TestSJXLSX.java
@@ -1,5 +1,12 @@
-package com.incesoft.tools.excel.xlsx;
-
+package org.xbib.datastructures.xslx;
+
+import com.incesoft.tools.excel.xlsx.Cell;
+import com.incesoft.tools.excel.xlsx.CellStyle;
+import com.incesoft.tools.excel.xlsx.Fill;
+import com.incesoft.tools.excel.xlsx.Font;
+import com.incesoft.tools.excel.xlsx.RichText;
+import com.incesoft.tools.excel.xlsx.Sheet;
+import com.incesoft.tools.excel.xlsx.SimpleXLSXWorkbook;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
diff --git a/gradle.properties b/gradle.properties
index 4ae4df0..2ac09bf 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
group = org.xbib
name = datastructures
-version = 1.0.0
+version = 1.0.1
org.gradle.warning.mode = ALL
diff --git a/settings.gradle b/settings.gradle
index 11eb78a..72b2a75 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,7 +2,7 @@ dependencyResolutionManagement {
versionCatalogs {
libs {
version('gradle', '7.5')
- version('junit', '5.8.2')
+ version('junit', '5.9.0')
version('jackson', '2.12.7')
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
@@ -24,7 +24,6 @@ dependencyResolutionManagement {
library('okio', 'com.squareup.okio', 'okio').version('1.13.0')
library('assertj', 'org.assertj', 'assertj-core').version('3.22.0')
library('compile-testing', 'com.google.testing.compile', 'compile-testing').version('0.19')
- library('jxl', 'net.sourceforge.jexcelapi', 'jxl').version('2.6.12')
}
}
}