/********************************************************************* * * Copyright (C) 2002 Andrew Khan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along 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.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import common.Logger; import jxl.WorkbookSettings; import jxl.biff.BaseCompoundFile; import jxl.biff.IntegerHelper; import jxl.biff.Type; /** * File containing the data from the binary stream */ public class File { /** * The logger */ private static 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 * @exception IOException * @exception 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; } }