poi/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java

670 lines
20 KiB
Java
Raw Normal View History

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.poifs.filesystem;
import java.io.*;
import java.util.*;
import org.apache.poi.poifs.common.PoiFSConstants;
import org.apache.poi.poifs.dev.POIFSViewable;
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.property.Property;
import org.apache.poi.poifs.storage.BlockWritable;
import org.apache.poi.poifs.storage.ListManagedBlock;
import org.apache.poi.poifs.storage.DocumentBlock;
import org.apache.poi.poifs.storage.RawDataBlock;
import org.apache.poi.poifs.storage.SmallDocumentBlock;
import org.apache.poi.util.HexDump;
/**
* This class manages a document in the POIFS filesystem.
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
public class POIFSDocument
implements BATManaged, BlockWritable, POIFSViewable
{
private DocumentProperty _property;
private int _size;
// one of these stores will be valid
private SmallBlockStore _small_store;
private BigBlockStore _big_store;
/**
* Constructor from large blocks
*
* @param name the name of the POIFSDocument
* @param blocks the big blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*
* @exception IOException
*/
public POIFSDocument(final String name, final RawDataBlock [] blocks,
final int length)
throws IOException
{
_size = length;
_big_store = new BigBlockStore(blocks);
_property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
_property.setDocument(this);
}
/**
* Constructor from small blocks
*
* @param name the name of the POIFSDocument
* @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*/
public POIFSDocument(final String name,
final SmallDocumentBlock [] blocks, final int length)
{
_size = length;
try
{
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
}
catch (IOException ignored)
{
// can't happen with that constructor
}
_property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(blocks);
_property.setDocument(this);
}
/**
* Constructor from small blocks
*
* @param name the name of the POIFSDocument
* @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*
* @exception IOException
*/
public POIFSDocument(final String name, final ListManagedBlock [] blocks,
final int length)
throws IOException
{
_size = length;
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (Property.isSmall(_size))
{
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
_small_store = new SmallBlockStore(blocks);
}
else
{
_big_store = new BigBlockStore(blocks);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
}
}
/**
* Constructor
*
* @param name the name of the POIFSDocument
* @param stream the InputStream we read data from
*
* @exception IOException thrown on read errors
*/
public POIFSDocument(final String name, final InputStream stream)
throws IOException
{
List blocks = new ArrayList();
_size = 0;
while (true)
{
DocumentBlock block = new DocumentBlock(stream);
int blockSize = block.size();
if (blockSize > 0)
{
blocks.add(block);
_size += blockSize;
}
if (block.partiallyRead())
{
break;
}
}
DocumentBlock[] bigBlocks =
( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]);
_big_store = new BigBlockStore(bigBlocks);
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (_property.shouldUseSmallBlocks())
{
_small_store =
new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks,
_size));
_big_store = new BigBlockStore(new DocumentBlock[ 0 ]);
}
else
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
}
}
/**
* Constructor
*
* @param name the name of the POIFSDocument
* @param size the length of the POIFSDocument
* @param path the path of the POIFSDocument
* @param writer the writer who will eventually write the document
* contents
*
* @exception IOException thrown on read errors
*/
public POIFSDocument(final String name, final int size,
final POIFSDocumentPath path,
final POIFSWriterListener writer)
throws IOException
{
_size = size;
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (_property.shouldUseSmallBlocks())
{
_small_store = new SmallBlockStore(path, name, size, writer);
_big_store = new BigBlockStore(new Object[ 0 ]);
}
else
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
_big_store = new BigBlockStore(path, name, size, writer);
}
}
/**
* return the array of SmallDocumentBlocks used
*
* @return array of SmallDocumentBlocks; may be empty, cannot be null
*/
public BlockWritable [] getSmallBlocks()
{
return _small_store.getBlocks();
}
/**
* @return size of the document
*/
public int getSize()
{
return _size;
}
/**
* read data from the internal stores
*
* @param buffer the buffer to write to
* @param offset the offset into our storage to read from
*/
void read(final byte [] buffer, final int offset)
{
if (_property.shouldUseSmallBlocks())
{
SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset);
}
else
{
DocumentBlock.read(_big_store.getBlocks(), buffer, offset);
}
}
/**
* Get the DocumentProperty
*
* @return the instance's DocumentProperty
*/
DocumentProperty getDocumentProperty()
{
return _property;
}
/* ********** START implementation of BlockWritable ********** */
/**
* Write the storage to an OutputStream
*
* @param stream the OutputStream to which the stored data should
* be written
*
* @exception IOException on problems writing to the specified
* stream
*/
public void writeBlocks(final OutputStream stream)
throws IOException
{
_big_store.writeBlocks(stream);
}
/* ********** END implementation of BlockWritable ********** */
/* ********** START implementation of BATManaged ********** */
/**
* Return the number of BigBlock's this instance uses
*
* @return count of BigBlock instances
*/
public int countBlocks()
{
return _big_store.countBlocks();
}
/**
* Set the start block for this instance
*
* @param index index into the array of blocks making up the
* filesystem
*/
public void setStartBlock(final int index)
{
_property.setStartBlock(index);
}
/* ********** END implementation of BATManaged ********** */
/* ********** START begin implementation of POIFSViewable ********** */
/**
* Get an array of objects, some of which may implement
* POIFSViewable
*
* @return an array of Object; may not be null, but may be empty
*/
public Object [] getViewableArray()
{
Object[] results = new Object[ 1 ];
String result;
try
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
BlockWritable[] blocks = null;
if (_big_store.isValid())
{
blocks = _big_store.getBlocks();
}
else if (_small_store.isValid())
{
blocks = _small_store.getBlocks();
}
if (blocks != null)
{
for (int k = 0; k < blocks.length; k++)
{
blocks[ k ].writeBlocks(output);
}
byte[] data = output.toByteArray();
if (data.length > _property.getSize())
{
byte[] tmp = new byte[ _property.getSize() ];
System.arraycopy(data, 0, tmp, 0, tmp.length);
data = tmp;
}
output = new ByteArrayOutputStream();
HexDump.dump(data, 0, output, 0);
result = output.toString();
}
else
{
result = "<NO DATA>";
}
}
catch (IOException e)
{
result = e.getMessage();
}
results[ 0 ] = result;
return results;
}
/**
* Get an Iterator of objects, some of which may implement
* POIFSViewable
*
* @return an Iterator; may not be null, but may have an empty
* back end store
*/
public Iterator getViewableIterator()
{
return Collections.EMPTY_LIST.iterator();
}
/**
* Give viewers a hint as to whether to call getViewableArray or
* getViewableIterator
*
* @return true if a viewer should call getViewableArray, false if
* a viewer should call getViewableIterator
*/
public boolean preferArray()
{
return true;
}
/**
* Provides a short description of the object, to be used when a
* POIFSViewable object has not provided its contents.
*
* @return short description
*/
public String getShortDescription()
{
StringBuffer buffer = new StringBuffer();
buffer.append("Document: \"").append(_property.getName())
.append("\"");
buffer.append(" size = ").append(getSize());
return buffer.toString();
}
/* ********** END begin implementation of POIFSViewable ********** */
private class SmallBlockStore
{
private SmallDocumentBlock[] smallBlocks;
private POIFSDocumentPath path;
private String name;
private int size;
private POIFSWriterListener writer;
/**
* Constructor
*
* @param blocks blocks to construct the store from
*/
SmallBlockStore(final Object [] blocks)
{
smallBlocks = new SmallDocumentBlock[ blocks.length ];
for (int j = 0; j < blocks.length; j++)
{
smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ];
}
this.path = null;
this.name = null;
this.size = -1;
this.writer = null;
}
/**
* Constructor for a small block store that will be written
* later
*
* @param path path of the document
* @param name name of the document
* @param size length of the document
* @param writer the object that will eventually write the document
*/
SmallBlockStore(final POIFSDocumentPath path, final String name,
final int size, final POIFSWriterListener writer)
{
smallBlocks = new SmallDocumentBlock[ 0 ];
this.path = path;
this.name = name;
this.size = size;
this.writer = writer;
}
/**
* @return true if this store is a valid source of data
*/
boolean isValid()
{
return ((smallBlocks.length > 0) || (writer != null));
}
/**
* @return the SmallDocumentBlocks
*/
BlockWritable [] getBlocks()
{
if (isValid() && (writer != null))
{
ByteArrayOutputStream stream =
new ByteArrayOutputStream(size);
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
path, name, size));
smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(),
size);
}
return smallBlocks;
}
} // end private class SmallBlockStore
private class BigBlockStore
{
private DocumentBlock[] bigBlocks;
private POIFSDocumentPath path;
private String name;
private int size;
private POIFSWriterListener writer;
/**
* Constructor
*
* @param blocks the blocks making up the store
*
* @exception IOException on I/O error
*/
BigBlockStore(final Object [] blocks)
throws IOException
{
bigBlocks = new DocumentBlock[ blocks.length ];
for (int j = 0; j < blocks.length; j++)
{
if (blocks[ j ] instanceof DocumentBlock)
{
bigBlocks[ j ] = ( DocumentBlock ) blocks[ j ];
}
else
{
bigBlocks[ j ] =
new DocumentBlock(( RawDataBlock ) blocks[ j ]);
}
}
this.path = null;
this.name = null;
this.size = -1;
this.writer = null;
}
/**
* Constructor for a big block store that will be written
* later
*
* @param path path of the document
* @param name name of the document
* @param size length of the document
* @param writer the object that will eventually write the
* document
*/
BigBlockStore(final POIFSDocumentPath path, final String name,
final int size, final POIFSWriterListener writer)
{
bigBlocks = new DocumentBlock[ 0 ];
this.path = path;
this.name = name;
this.size = size;
this.writer = writer;
}
/**
* @return true if this store is a valid source of data
*/
boolean isValid()
{
return ((bigBlocks.length > 0) || (writer != null));
}
/**
* @return the DocumentBlocks
*/
DocumentBlock [] getBlocks()
{
if (isValid() && (writer != null))
{
ByteArrayOutputStream stream =
new ByteArrayOutputStream(size);
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
path, name, size));
bigBlocks = DocumentBlock.convert(stream.toByteArray(), size);
}
return bigBlocks;
}
/**
* write the blocks to a stream
*
* @param stream the stream to which the data is to be written
*
* @exception IOException on error
*/
void writeBlocks(OutputStream stream)
throws IOException
{
if (isValid())
{
if (writer != null)
{
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(
new POIFSWriterEvent(dstream, path, name, size));
dstream.writeFiller(countBlocks()
* PoiFSConstants
.BIG_BLOCK_SIZE, DocumentBlock
.getFillByte());
}
else
{
for (int k = 0; k < bigBlocks.length; k++)
{
bigBlocks[ k ].writeBlocks(stream);
}
}
}
}
/**
* @return number of big blocks making up this document
*/
int countBlocks()
{
int rval = 0;
if (isValid())
{
if (writer != null)
{
rval = (size + PoiFSConstants.BIG_BLOCK_SIZE - 1)
/ PoiFSConstants.BIG_BLOCK_SIZE;
}
else
{
rval = bigBlocks.length;
}
}
return rval;
}
} // end private class BigBlockStore
} // end class POIFSDocument