Example #1
0
/**
 * 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 DefaultSheet implements Sheet {
  /** The logger */
  private static Logger logger = LoggerFactory.getLog(DefaultSheet.class);

  /** The excel file */
  private File excelFile;
  /** A handle to the shared string table */
  private SSTRecord sharedStrings;

  /** A handle to the sheet BOF record, which indicates the stream type */
  private BOFRecord sheetBof;

  /** A handle to the workbook BOF record, which indicates the stream type */
  private BOFRecord workbookBof;

  /** A handle to the formatting records */
  private 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 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 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 WorkbookParser workbook;

  /** A handle to the workbook settings */
  private 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
   */
  DefaultSheet(
      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;
  }

  /**
   * 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;
  }

  /**
   * Sets the visibility of this sheet
   *
   * @param h hidden flag
   */
  final void setHidden(boolean h) {
    hidden = h;
  }

  /**
   * 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;
  }
}
Example #2
0
/** An options record in the escher stream */
class Opt extends EscherAtom {
  /** The logger */
  private static Logger logger = LoggerFactory.getLog(Opt.class);

  /** The binary data */
  private byte[] data;

  /** The number of properties */
  private int numProperties;

  /** The list of properties */
  private ArrayList properties;

  /** Properties enumeration inner class */
  static final 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;
    }
  }

  /**
   * Constructor
   *
   * @param erd the escher record data
   */
  public Opt(EscherRecordData erd) {
    super(erd);
    numProperties = getInstance();
    readProperties();
  }

  /** 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;
      }
    }
  }

  /** Constructor */
  public Opt() {
    super(EscherRecordType.OPT);
    properties = new ArrayList();
    setVersion(3);
  }

  /**
   * 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;
  }
}
Example #3
0
/** A record which merely holds the OBJ data. Used when copying files which contain images */
public class ObjRecord extends WritableRecordData {
  /** The logger */
  private static final Logger logger = LoggerFactory.getLog(ObjRecord.class);

  /** The object type */
  private ObjType type;

  /** Indicates whether this record was read in */
  private boolean read;

  /** The object id */
  private int objectId;

  /** Object type enumeration */
  private static final class ObjType {
    public int value;
    public String desc;

    private static ObjType[] types = new ObjType[0];

    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 String toString() {
      return desc;
    }

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

  // 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");

  // 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;

  /**
   * 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 DefaultSheet 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 org.areasy.common.parser.documents.excel.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 org.areasy.common.parser.documents.excel.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 org.areasy.common.parser.documents.excel.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 DefaultSheet 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;
  }
}
Example #4
0
/** Abstract class for all records which actually contain cell values */
public abstract class CellValue extends RecordData implements Cell, CellFeaturesAccessor {
  /** The logger */
  private static Logger logger = LoggerFactory.getLog(CellValue.class);

  /** The row number of this cell record */
  private int row;

  /** The column number of this cell record */
  private int column;

  /** The XF index */
  private int xfIndex;

  /** A handle to the formatting records, so that we can retrieve the formatting information */
  private 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 DefaultSheet 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, DefaultSheet 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);

    if (rr != null && (rr.getRowHeight() == 0 || rr.isCollapsed())) {
      return true;
    }

    return false;
  }

  /**
   * Accessor for the sheet
   *
   * @return the sheet
   */
  protected DefaultSheet 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;
  }
}
Example #5
0
/** A Note (TXO) record which contains the information for comments */
public class NoteRecord extends WritableRecordData {
  /** The logger */
  private static Logger logger = LoggerFactory.getLog(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 DefaultSheet 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;
  }
}