Compare commits
7 Commits
master
...
NIO_32_BRA
Author | SHA1 | Date | |
---|---|---|---|
|
8571f9e70c | ||
|
c1e2c16412 | ||
|
58a6793fdd | ||
|
fc34d3e1b1 | ||
|
19cfe88647 | ||
|
2a2ae2097f | ||
|
c49ceaba31 |
@ -119,11 +119,11 @@ under the License.
|
|||||||
<property name="mavendist.poi.dir" location="build/maven-dist/poi"/>
|
<property name="mavendist.poi.dir" location="build/maven-dist/poi"/>
|
||||||
<property name="mavendist.oap.dir" location="build/maven-dist/org.apache.poi"/>
|
<property name="mavendist.oap.dir" location="build/maven-dist/org.apache.poi"/>
|
||||||
<property name="jar.name" value="poi"/>
|
<property name="jar.name" value="poi"/>
|
||||||
<property name="version.id" value="3.2-alpha1"/>
|
<property name="version.id" value="3.2-NIObackport"/>
|
||||||
<property name="halt.on.test.failure" value="true"/>
|
<property name="halt.on.test.failure" value="true"/>
|
||||||
<property name="jdk.version.source" value="1.3"
|
<property name="jdk.version.source" value="1.5"
|
||||||
description="JDK version of source code"/>
|
description="JDK version of source code"/>
|
||||||
<property name="jdk.version.class" value="1.3"
|
<property name="jdk.version.class" value="1.5"
|
||||||
description="JDK version of generated class files"/>
|
description="JDK version of generated class files"/>
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ public class TreeReaderListener implements POIFSReaderListener
|
|||||||
{
|
{
|
||||||
is.close();
|
is.close();
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.err.println
|
System.err.println
|
||||||
("Unexpected exception while closing " +
|
("Unexpected exception while closing " +
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
</devs>
|
</devs>
|
||||||
|
|
||||||
<!-- Don't forget to update status.xml too! -->
|
<!-- Don't forget to update status.xml too! -->
|
||||||
<release version="3.2-alpha1" date="2008-??-??">
|
<release version="3.2-FINAL" date="2008-10-19">
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
|
<action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
|
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
|
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
|
||||||
|
@ -207,7 +207,7 @@ if (cell!=null) {
|
|||||||
FileInputStream fis = new FileInputStream("/somepath/test.xls");
|
FileInputStream fis = new FileInputStream("/somepath/test.xls");
|
||||||
HSSFWorkbook wb = new HSSFWorkbook(fis);
|
HSSFWorkbook wb = new HSSFWorkbook(fis);
|
||||||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
|
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
|
||||||
for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
|
for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
|
||||||
HSSFSheet sheet = wb.getSheetAt(sheetNum);
|
HSSFSheet sheet = wb.getSheetAt(sheetNum);
|
||||||
|
|
||||||
for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<!-- Don't forget to update changes.xml too! -->
|
<!-- Don't forget to update changes.xml too! -->
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.2-alpha1" date="2008-??-??">
|
<release version="3.2-FINAL" date="2008-10-19">
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
|
<action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
|
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
|
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
|
||||||
|
@ -20,6 +20,7 @@ package org.apache.poi;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -47,27 +48,28 @@ import org.apache.poi.util.POILogger;
|
|||||||
*/
|
*/
|
||||||
public abstract class POIDocument {
|
public abstract class POIDocument {
|
||||||
/** Holds metadata on our document */
|
/** Holds metadata on our document */
|
||||||
protected SummaryInformation sInf;
|
private SummaryInformation sInf;
|
||||||
/** Holds further metadata on our document */
|
/** Holds further metadata on our document */
|
||||||
protected DocumentSummaryInformation dsInf;
|
private DocumentSummaryInformation dsInf;
|
||||||
/** The open POIFS FileSystem that contains our document */
|
|
||||||
protected POIFSFileSystem filesystem;
|
|
||||||
/** The directory that our document lives in */
|
/** The directory that our document lives in */
|
||||||
protected DirectoryNode directory;
|
protected DirectoryNode directory;
|
||||||
|
|
||||||
/** For our own logging use */
|
/** For our own logging use */
|
||||||
protected POILogger logger = POILogFactory.getLogger(this.getClass());
|
private final static POILogger logger = POILogFactory.getLogger(POIDocument.class);
|
||||||
|
|
||||||
/* Have the property streams been read yet? (Only done on-demand) */
|
/* Have the property streams been read yet? (Only done on-demand) */
|
||||||
protected boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
|
||||||
|
|
||||||
protected POIDocument(DirectoryNode dir, POIFSFileSystem fs) {
|
protected POIDocument(DirectoryNode dir) {
|
||||||
this.filesystem = fs;
|
|
||||||
this.directory = dir;
|
this.directory = dir;
|
||||||
}
|
}
|
||||||
|
@Deprecated
|
||||||
|
protected POIDocument(DirectoryNode dir, POIFSFileSystem fs) {
|
||||||
|
this.directory = dir;
|
||||||
|
}
|
||||||
protected POIDocument(POIFSFileSystem fs) {
|
protected POIDocument(POIFSFileSystem fs) {
|
||||||
this(fs.getRoot(), fs);
|
this(fs.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +87,25 @@ public abstract class POIDocument {
|
|||||||
if(!initialized) readProperties();
|
if(!initialized) readProperties();
|
||||||
return sInf;
|
return sInf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will create whichever of SummaryInformation
|
||||||
|
* and DocumentSummaryInformation (HPSF) properties
|
||||||
|
* are not already part of your document.
|
||||||
|
* This is normally useful when creating a new
|
||||||
|
* document from scratch.
|
||||||
|
* If the information properties are already there,
|
||||||
|
* then nothing will happen.
|
||||||
|
*/
|
||||||
|
public void createInformationProperties() {
|
||||||
|
if(!initialized) readProperties();
|
||||||
|
if(sInf == null) {
|
||||||
|
sInf = PropertySetFactory.newSummaryInformation();
|
||||||
|
}
|
||||||
|
if(dsInf == null) {
|
||||||
|
dsInf = PropertySetFactory.newDocumentSummaryInformation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find, and create objects for, the standard
|
* Find, and create objects for, the standard
|
||||||
@ -120,28 +141,31 @@ public abstract class POIDocument {
|
|||||||
* if it wasn't found
|
* if it wasn't found
|
||||||
*/
|
*/
|
||||||
protected PropertySet getPropertySet(String setName) {
|
protected PropertySet getPropertySet(String setName) {
|
||||||
DocumentInputStream dis;
|
//directory can be null when creating new documents
|
||||||
try {
|
if(directory == null) return null;
|
||||||
// Find the entry, and get an input stream for it
|
|
||||||
dis = directory.createDocumentInputStream(setName);
|
|
||||||
} catch(IOException ie) {
|
|
||||||
// Oh well, doesn't exist
|
|
||||||
logger.log(POILogger.WARN, "Error getting property set with name " + setName + "\n" + ie);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
DocumentInputStream dis;
|
||||||
// Create the Property Set
|
try {
|
||||||
PropertySet set = PropertySetFactory.create(dis);
|
// Find the entry, and get an input stream for it
|
||||||
return set;
|
dis = directory.createDocumentInputStream( directory.getEntry(setName) );
|
||||||
} catch(IOException ie) {
|
} catch(IOException ie) {
|
||||||
// Must be corrupt or something like that
|
// Oh well, doesn't exist
|
||||||
logger.log(POILogger.WARN, "Error creating property set with name " + setName + "\n" + ie);
|
logger.log(POILogger.WARN, "Error getting property set with name " + setName + "\n" + ie);
|
||||||
} catch(org.apache.poi.hpsf.HPSFException he) {
|
return null;
|
||||||
// Oh well, doesn't exist
|
}
|
||||||
logger.log(POILogger.WARN, "Error creating property set with name " + setName + "\n" + he);
|
|
||||||
}
|
try {
|
||||||
return null;
|
// Create the Property Set
|
||||||
|
PropertySet set = PropertySetFactory.create(dis);
|
||||||
|
return set;
|
||||||
|
} catch(IOException ie) {
|
||||||
|
// Must be corrupt or something like that
|
||||||
|
logger.log(POILogger.WARN, "Error creating property set with name " + setName + "\n" + ie);
|
||||||
|
} catch(org.apache.poi.hpsf.HPSFException he) {
|
||||||
|
// Oh well, doesn't exist
|
||||||
|
logger.log(POILogger.WARN, "Error creating property set with name " + setName + "\n" + he);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,14 +181,16 @@ public abstract class POIDocument {
|
|||||||
* @param writtenEntries a list of POIFS entries to add the property names too
|
* @param writtenEntries a list of POIFS entries to add the property names too
|
||||||
*/
|
*/
|
||||||
protected void writeProperties(POIFSFileSystem outFS, List writtenEntries) throws IOException {
|
protected void writeProperties(POIFSFileSystem outFS, List writtenEntries) throws IOException {
|
||||||
if(sInf != null) {
|
SummaryInformation si = getSummaryInformation();
|
||||||
writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME,sInf,outFS);
|
if(si != null) {
|
||||||
|
writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, outFS);
|
||||||
if(writtenEntries != null) {
|
if(writtenEntries != null) {
|
||||||
writtenEntries.add(SummaryInformation.DEFAULT_STREAM_NAME);
|
writtenEntries.add(SummaryInformation.DEFAULT_STREAM_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(dsInf != null) {
|
DocumentSummaryInformation dsi = getDocumentSummaryInformation();
|
||||||
writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME,dsInf,outFS);
|
if(dsi != null) {
|
||||||
|
writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, dsi, outFS);
|
||||||
if(writtenEntries != null) {
|
if(writtenEntries != null) {
|
||||||
writtenEntries.add(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
|
writtenEntries.add(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
@ -27,8 +27,6 @@ import org.apache.poi.util.HexDump;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class ClassID
|
public class ClassID
|
||||||
{
|
{
|
||||||
|
@ -22,8 +22,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2004-06-20
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
|
@ -33,7 +33,7 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
|||||||
* name is the key that maps to a typed value. This implementation hides
|
* name is the key that maps to a typed value. This implementation hides
|
||||||
* property IDs from the developer and regards the property names as keys to
|
* property IDs from the developer and regards the property names as keys to
|
||||||
* typed values.</p>
|
* typed values.</p>
|
||||||
*
|
*
|
||||||
* <p>While this class provides a simple API to custom properties, it ignores
|
* <p>While this class provides a simple API to custom properties, it ignores
|
||||||
* the fact that not names, but IDs are the real keys to properties. Under the
|
* the fact that not names, but IDs are the real keys to properties. Under the
|
||||||
* hood this class maintains a 1:1 relationship between IDs and names. Therefore
|
* hood this class maintains a 1:1 relationship between IDs and names. Therefore
|
||||||
@ -41,71 +41,68 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
|||||||
* mapping to the same name or with properties without a name: the result will
|
* mapping to the same name or with properties without a name: the result will
|
||||||
* contain only a subset of the original properties. If you really need to deal
|
* contain only a subset of the original properties. If you really need to deal
|
||||||
* such property sets, use HPSF's low-level access methods.</p>
|
* such property sets, use HPSF's low-level access methods.</p>
|
||||||
*
|
*
|
||||||
* <p>An application can call the {@link #isPure} method to check whether a
|
* <p>An application can call the {@link #isPure} method to check whether a
|
||||||
* property set parsed by {@link CustomProperties} is still pure (i.e.
|
* property set parsed by {@link CustomProperties} is still pure (i.e.
|
||||||
* unmodified) or whether one or more properties have been dropped.</p>
|
* unmodified) or whether one or more properties have been dropped.</p>
|
||||||
*
|
*
|
||||||
* <p>This class is not thread-safe; concurrent access to instances of this
|
* <p>This class is not thread-safe; concurrent access to instances of this
|
||||||
* class must be syncronized.</p>
|
* class must be synchronized.</p>
|
||||||
*
|
*
|
||||||
|
* <p>While this class is roughly HashMap<Long,CustomProperty>, that's the
|
||||||
|
* internal representation. To external calls, it should appear as
|
||||||
|
* HashMap<String,Object> mapping between Names and Custom Property Values.</p>
|
||||||
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2006-02-09
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class CustomProperties extends HashMap
|
@SuppressWarnings("serial")
|
||||||
|
public class CustomProperties extends HashMap<Object,CustomProperty>
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Maps property IDs to property names.</p>
|
* <p>Maps property IDs to property names.</p>
|
||||||
*/
|
*/
|
||||||
private Map dictionaryIDToName = new HashMap();
|
private Map<Long,String> dictionaryIDToName = new HashMap<Long,String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Maps property names to property IDs.</p>
|
* <p>Maps property names to property IDs.</p>
|
||||||
*/
|
*/
|
||||||
private Map dictionaryNameToID = new HashMap();
|
private Map<String,Long> dictionaryNameToID = new HashMap<String,Long>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Tells whether this object is pure or not.</p>
|
* <p>Tells whether this object is pure or not.</p>
|
||||||
*/
|
*/
|
||||||
private boolean isPure = true;
|
private boolean isPure = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Puts a {@link CustomProperty} into this map. It is assumed that the
|
* <p>Puts a {@link CustomProperty} into this map. It is assumed that the
|
||||||
* {@link CustomProperty} already has a valid ID. Otherwise use
|
* {@link CustomProperty} already has a valid ID. Otherwise use
|
||||||
* {@link #put(CustomProperty)}.</p>
|
* {@link #put(CustomProperty)}.</p>
|
||||||
*/
|
*/
|
||||||
public Object put(final Object name, final Object customProperty) throws ClassCastException
|
public CustomProperty put(final String name, final CustomProperty cp)
|
||||||
{
|
{
|
||||||
final CustomProperty cp = (CustomProperty) customProperty;
|
|
||||||
if (name == null)
|
if (name == null)
|
||||||
{
|
{
|
||||||
/* Ignoring a property without a name. */
|
/* Ignoring a property without a name. */
|
||||||
isPure = false;
|
isPure = false;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!(name instanceof String))
|
|
||||||
throw new ClassCastException("The name of a custom property must " +
|
|
||||||
"be a java.lang.String, but it is a " +
|
|
||||||
name.getClass().getName());
|
|
||||||
if (!(name.equals(cp.getName())))
|
if (!(name.equals(cp.getName())))
|
||||||
throw new IllegalArgumentException("Parameter \"name\" (" + name +
|
throw new IllegalArgumentException("Parameter \"name\" (" + name +
|
||||||
") and custom property's name (" + cp.getName() +
|
") and custom property's name (" + cp.getName() +
|
||||||
") do not match.");
|
") do not match.");
|
||||||
|
|
||||||
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
|
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
|
||||||
final Long idKey = new Long(cp.getID());
|
final Long idKey = Long.valueOf(cp.getID());
|
||||||
final Object oldID = dictionaryNameToID.get(name);
|
final Long oldID = dictionaryNameToID.get(name);
|
||||||
dictionaryIDToName.remove(oldID);
|
dictionaryIDToName.remove(oldID);
|
||||||
dictionaryNameToID.put(name, idKey);
|
dictionaryNameToID.put(name, idKey);
|
||||||
dictionaryIDToName.put(idKey, name);
|
dictionaryIDToName.put(idKey, name);
|
||||||
|
|
||||||
/* Put the custom property into this map. */
|
/* Put the custom property into this map. */
|
||||||
final Object oldCp = super.remove(oldID);
|
final CustomProperty oldCp = super.remove(oldID);
|
||||||
super.put(idKey, cp);
|
super.put(idKey, cp);
|
||||||
return oldCp;
|
return oldCp;
|
||||||
}
|
}
|
||||||
@ -115,16 +112,16 @@ public class CustomProperties extends HashMap
|
|||||||
/**
|
/**
|
||||||
* <p>Puts a {@link CustomProperty} that has not yet a valid ID into this
|
* <p>Puts a {@link CustomProperty} that has not yet a valid ID into this
|
||||||
* map. The method will allocate a suitable ID for the custom property:</p>
|
* map. The method will allocate a suitable ID for the custom property:</p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><p>If there is already a property with the same name, take the ID
|
* <li><p>If there is already a property with the same name, take the ID
|
||||||
* of that property.</p></li>
|
* of that property.</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
|
* <li><p>Otherwise find the highest ID and use its value plus one.</p></li>
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param customProperty
|
* @param customProperty
|
||||||
* @return If the was already a property with the same name, the
|
* @return If the was already a property with the same name, the
|
||||||
* @throws ClassCastException
|
* @throws ClassCastException
|
||||||
@ -140,9 +137,9 @@ public class CustomProperties extends HashMap
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
long max = 1;
|
long max = 1;
|
||||||
for (final Iterator i = dictionaryIDToName.keySet().iterator(); i.hasNext();)
|
for (final Iterator<Long> i = dictionaryIDToName.keySet().iterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
final long id = ((Long) i.next()).longValue();
|
final long id = i.next().longValue();
|
||||||
if (id > max)
|
if (id > max)
|
||||||
max = id;
|
max = id;
|
||||||
}
|
}
|
||||||
@ -155,7 +152,7 @@ public class CustomProperties extends HashMap
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes a custom property.</p>
|
* <p>Removes a custom property.</p>
|
||||||
* @param name The name of the custom property to remove
|
* @param name The name of the custom property to remove
|
||||||
* @return The removed property or <code>null</code> if the specified property was not found.
|
* @return The removed property or <code>null</code> if the specified property was not found.
|
||||||
*
|
*
|
||||||
* @see java.util.HashSet#remove(java.lang.Object)
|
* @see java.util.HashSet#remove(java.lang.Object)
|
||||||
@ -172,7 +169,7 @@ public class CustomProperties extends HashMap
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds a named string property.</p>
|
* <p>Adds a named string property.</p>
|
||||||
*
|
*
|
||||||
* @param name The property's name.
|
* @param name The property's name.
|
||||||
* @param value The property's value.
|
* @param value The property's value.
|
||||||
* @return the property that was stored under the specified name before, or
|
* @return the property that was stored under the specified name before, or
|
||||||
@ -260,10 +257,10 @@ public class CustomProperties extends HashMap
|
|||||||
return put(cp);
|
return put(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Gets a named value from the custom properties.</p>
|
* <p>Gets a named value from the custom properties.</p>
|
||||||
*
|
*
|
||||||
* @param name the name of the value to get
|
* @param name the name of the value to get
|
||||||
* @return the value or <code>null</code> if a value with the specified
|
* @return the value or <code>null</code> if a value with the specified
|
||||||
* name is not found in the custom properties.
|
* name is not found in the custom properties.
|
||||||
@ -294,18 +291,34 @@ public class CustomProperties extends HashMap
|
|||||||
final CustomProperty cp = new CustomProperty(p, name);
|
final CustomProperty cp = new CustomProperty(p, name);
|
||||||
return put(cp);
|
return put(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set of all the names of our
|
||||||
|
* custom properties. Equivalent to
|
||||||
|
* {@link #nameSet()}
|
||||||
|
*/
|
||||||
|
public Set keySet() {
|
||||||
|
return dictionaryNameToID.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set of all the names of our
|
* Returns a set of all the names of our
|
||||||
* custom properties
|
* custom properties
|
||||||
*/
|
*/
|
||||||
public Set keySet() {
|
public Set<String> nameSet() {
|
||||||
return dictionaryNameToID.keySet();
|
return dictionaryNameToID.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set of all the IDs of our
|
||||||
|
* custom properties
|
||||||
|
*/
|
||||||
|
public Set<String> idSet() {
|
||||||
|
return dictionaryNameToID.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
|
||||||
* <p>Sets the codepage.</p>
|
* <p>Sets the codepage.</p>
|
||||||
*
|
*
|
||||||
* @param codepage the codepage
|
* @param codepage the codepage
|
||||||
@ -315,7 +328,7 @@ public class CustomProperties extends HashMap
|
|||||||
final MutableProperty p = new MutableProperty();
|
final MutableProperty p = new MutableProperty();
|
||||||
p.setID(PropertyIDMap.PID_CODEPAGE);
|
p.setID(PropertyIDMap.PID_CODEPAGE);
|
||||||
p.setType(Variant.VT_I2);
|
p.setType(Variant.VT_I2);
|
||||||
p.setValue(new Integer(codepage));
|
p.setValue(Integer.valueOf(codepage));
|
||||||
put(new CustomProperty(p));
|
put(new CustomProperty(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,17 +337,47 @@ public class CustomProperties extends HashMap
|
|||||||
/**
|
/**
|
||||||
* <p>Gets the dictionary which contains IDs and names of the named custom
|
* <p>Gets the dictionary which contains IDs and names of the named custom
|
||||||
* properties.
|
* properties.
|
||||||
*
|
*
|
||||||
* @return the dictionary.
|
* @return the dictionary.
|
||||||
*/
|
*/
|
||||||
Map getDictionary()
|
Map<Long,String> getDictionary()
|
||||||
{
|
{
|
||||||
return dictionaryIDToName;
|
return dictionaryIDToName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Checks against both String Name and Long ID
|
||||||
|
*/
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
if(key instanceof Long) {
|
||||||
|
return super.containsKey((Long)key);
|
||||||
|
}
|
||||||
|
if(key instanceof String) {
|
||||||
|
return super.containsKey((Long)dictionaryNameToID.get(key));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks against both the property, and its values.
|
||||||
|
*/
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
if(value instanceof CustomProperty) {
|
||||||
|
return super.containsValue((CustomProperty)value);
|
||||||
|
} else {
|
||||||
|
for(CustomProperty cp : super.values()) {
|
||||||
|
if(cp.getValue() == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
* <p>Gets the codepage.</p>
|
* <p>Gets the codepage.</p>
|
||||||
*
|
*
|
||||||
* @return the codepage or -1 if the codepage is undefined.
|
* @return the codepage or -1 if the codepage is undefined.
|
||||||
@ -342,9 +385,9 @@ public class CustomProperties extends HashMap
|
|||||||
public int getCodepage()
|
public int getCodepage()
|
||||||
{
|
{
|
||||||
int codepage = -1;
|
int codepage = -1;
|
||||||
for (final Iterator i = this.values().iterator(); codepage == -1 && i.hasNext();)
|
for (final Iterator<CustomProperty> i = this.values().iterator(); codepage == -1 && i.hasNext();)
|
||||||
{
|
{
|
||||||
final CustomProperty cp = (CustomProperty) i.next();
|
final CustomProperty cp = i.next();
|
||||||
if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
|
if (cp.getID() == PropertyIDMap.PID_CODEPAGE)
|
||||||
codepage = ((Integer) cp.getValue()).intValue();
|
codepage = ((Integer) cp.getValue()).intValue();
|
||||||
}
|
}
|
||||||
@ -357,7 +400,7 @@ public class CustomProperties extends HashMap
|
|||||||
* <p>Tells whether this {@link CustomProperties} instance is pure or one or
|
* <p>Tells whether this {@link CustomProperties} instance is pure or one or
|
||||||
* more properties of the underlying low-level property set has been
|
* more properties of the underlying low-level property set has been
|
||||||
* dropped.</p>
|
* dropped.</p>
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if the {@link CustomProperties} is pure, else
|
* @return <code>true</code> if the {@link CustomProperties} is pure, else
|
||||||
* <code>false</code>.
|
* <code>false</code>.
|
||||||
*/
|
*/
|
||||||
@ -375,5 +418,4 @@ public class CustomProperties extends HashMap
|
|||||||
{
|
{
|
||||||
this.isPure = isPure;
|
this.isPure = isPure;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,13 @@
|
|||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This class represents custum properties in the document summary
|
* <p>This class represents custom properties in the document summary
|
||||||
* information stream. The difference to normal properties is that custom
|
* information stream. The difference to normal properties is that custom
|
||||||
* properties have an optional name. If the name is not <code>null</code> it
|
* properties have an optional name. If the name is not <code>null</code> it
|
||||||
* will be maintained in the section's dictionary.</p>
|
* will be maintained in the section's dictionary.</p>
|
||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2006-02-09
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class CustomProperty extends MutableProperty
|
public class CustomProperty extends MutableProperty
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -32,8 +32,6 @@ import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
|||||||
* @author Drew Varner (Drew.Varner closeTo sc.edu)
|
* @author Drew Varner (Drew.Varner closeTo sc.edu)
|
||||||
* @author robert_flaherty@hyperion.com
|
* @author robert_flaherty@hyperion.com
|
||||||
* @see SummaryInformation
|
* @see SummaryInformation
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class DocumentSummaryInformation extends SpecialPropertySet
|
public class DocumentSummaryInformation extends SpecialPropertySet
|
||||||
{
|
{
|
||||||
@ -569,9 +567,8 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Gets the custom properties.</p>
|
* <p>Gets the custom properties.</p>
|
||||||
*
|
*
|
||||||
* @return The custom properties.
|
* @return The custom properties.
|
||||||
* @since 2006-02-09
|
|
||||||
*/
|
*/
|
||||||
public CustomProperties getCustomProperties()
|
public CustomProperties getCustomProperties()
|
||||||
{
|
{
|
||||||
@ -580,7 +577,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
{
|
{
|
||||||
cps = new CustomProperties();
|
cps = new CustomProperties();
|
||||||
final Section section = (Section) getSections().get(1);
|
final Section section = (Section) getSections().get(1);
|
||||||
final Map dictionary = section.getDictionary();
|
final Map<Long,String> dictionary = section.getDictionary();
|
||||||
final Property[] properties = section.getProperties();
|
final Property[] properties = section.getProperties();
|
||||||
int propertyCount = 0;
|
int propertyCount = 0;
|
||||||
for (int i = 0; i < properties.length; i++)
|
for (int i = 0; i < properties.length; i++)
|
||||||
@ -591,7 +588,7 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
{
|
{
|
||||||
propertyCount++;
|
propertyCount++;
|
||||||
final CustomProperty cp = new CustomProperty(p,
|
final CustomProperty cp = new CustomProperty(p,
|
||||||
(String) dictionary.get(new Long(id)));
|
dictionary.get(Long.valueOf(id)));
|
||||||
cps.put(cp.getName(), cp);
|
cps.put(cp.getName(), cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -603,15 +600,14 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the custom properties.</p>
|
* <p>Sets the custom properties.</p>
|
||||||
*
|
*
|
||||||
* @param customProperties The custom properties
|
* @param customProperties The custom properties
|
||||||
* @since 2006-02-07
|
|
||||||
*/
|
*/
|
||||||
public void setCustomProperties(final CustomProperties customProperties)
|
public void setCustomProperties(final CustomProperties customProperties)
|
||||||
{
|
{
|
||||||
ensureSection2();
|
ensureSection2();
|
||||||
final MutableSection section = (MutableSection) getSections().get(1);
|
final MutableSection section = (MutableSection) getSections().get(1);
|
||||||
final Map dictionary = customProperties.getDictionary();
|
final Map<Long,String> dictionary = customProperties.getDictionary();
|
||||||
section.clear();
|
section.clear();
|
||||||
|
|
||||||
/* Set the codepage. If both custom properties and section have a
|
/* Set the codepage. If both custom properties and section have a
|
||||||
@ -625,9 +621,9 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
customProperties.setCodepage(cpCodepage);
|
customProperties.setCodepage(cpCodepage);
|
||||||
section.setCodepage(cpCodepage);
|
section.setCodepage(cpCodepage);
|
||||||
section.setDictionary(dictionary);
|
section.setDictionary(dictionary);
|
||||||
for (final Iterator i = customProperties.values().iterator(); i.hasNext();)
|
for (final Iterator<CustomProperty> i = customProperties.values().iterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
final Property p = (Property) i.next();
|
final Property p = i.next();
|
||||||
section.setProperty(p);
|
section.setProperty(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -652,8 +648,6 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Removes the custom properties.</p>
|
* <p>Removes the custom properties.</p>
|
||||||
*
|
|
||||||
* @since 2006-02-08
|
|
||||||
*/
|
*/
|
||||||
public void removeCustomProperties()
|
public void removeCustomProperties()
|
||||||
{
|
{
|
||||||
@ -676,5 +670,4 @@ public class DocumentSummaryInformation extends SpecialPropertySet
|
|||||||
{
|
{
|
||||||
throw new UnsupportedOperationException(msg + " is not yet implemented.");
|
throw new UnsupportedOperationException(msg + " is not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,8 +24,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class HPSFException extends Exception
|
public class HPSFException extends Exception
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
@ -27,8 +27,6 @@ import java.io.PrintWriter;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class HPSFRuntimeException extends RuntimeException
|
public class HPSFRuntimeException extends RuntimeException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,8 +26,6 @@ package org.apache.poi.hpsf;
|
|||||||
* thrown.</p>
|
* thrown.</p>
|
||||||
*
|
*
|
||||||
* @author Drew Varner(Drew.Varner atDomain sc.edu)
|
* @author Drew Varner(Drew.Varner atDomain sc.edu)
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-05-26
|
|
||||||
*/
|
*/
|
||||||
public class IllegalPropertySetDataException extends HPSFRuntimeException
|
public class IllegalPropertySetDataException extends HPSFRuntimeException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
@ -25,8 +25,6 @@ import org.apache.poi.util.HexDump;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2004-06-21
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class IllegalVariantTypeException extends VariantTypeException
|
public class IllegalVariantTypeException extends VariantTypeException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,8 +23,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class MarkUnsupportedException extends HPSFException
|
public class MarkUnsupportedException extends HPSFException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,8 +26,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id: NoSingleSectionException.java 353545 2004-04-09 13:05:39Z glens $
|
|
||||||
* @since 2006-02-08
|
|
||||||
*/
|
*/
|
||||||
public class MissingSectionException extends HPSFRuntimeException
|
public class MissingSectionException extends HPSFRuntimeException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -28,8 +28,6 @@ import java.io.OutputStream;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2003-08-03
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class MutableProperty extends Property
|
public class MutableProperty extends Property
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@ -33,8 +33,6 @@ import org.apache.poi.poifs.filesystem.Entry;
|
|||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Adds writing support to the {@link PropertySet} class.</p>
|
* <p>Adds writing support to the {@link PropertySet} class.</p>
|
||||||
*
|
*
|
||||||
@ -43,8 +41,6 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2003-02-19
|
|
||||||
*/
|
*/
|
||||||
public class MutablePropertySet extends PropertySet
|
public class MutablePropertySet extends PropertySet
|
||||||
{
|
{
|
||||||
@ -79,10 +75,10 @@ public class MutablePropertySet extends PropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructs a <code>MutablePropertySet</code> by doing a deep copy of
|
* <p>Constructs a <code>MutablePropertySet</code> by doing a deep copy of
|
||||||
* an existing <code>PropertySet</code>. All nested elements, i.e.
|
* an existing <code>PropertySet</code>. All nested elements, i.e.
|
||||||
* <code>Section</code>s and <code>Property</code> instances, will be their
|
* <code>Section</code>s and <code>Property</code> instances, will be their
|
||||||
* mutable counterparts in the new <code>MutablePropertySet</code>.</p>
|
* mutable counterparts in the new <code>MutablePropertySet</code>.</p>
|
||||||
*
|
*
|
||||||
* @param ps The property set to copy
|
* @param ps The property set to copy
|
||||||
*/
|
*/
|
||||||
public MutablePropertySet(final PropertySet ps)
|
public MutablePropertySet(final PropertySet ps)
|
||||||
@ -194,7 +190,7 @@ public class MutablePropertySet extends PropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Writes the property set to an output stream.</p>
|
* <p>Writes the property set to an output stream.</p>
|
||||||
*
|
*
|
||||||
* @param out the output stream to write the section to
|
* @param out the output stream to write the section to
|
||||||
* @exception IOException if an error when writing to the output stream
|
* @exception IOException if an error when writing to the output stream
|
||||||
* occurs
|
* occurs
|
||||||
@ -236,10 +232,10 @@ public class MutablePropertySet extends PropertySet
|
|||||||
catch (HPSFRuntimeException ex)
|
catch (HPSFRuntimeException ex)
|
||||||
{
|
{
|
||||||
final Throwable cause = ex.getReason();
|
final Throwable cause = ex.getReason();
|
||||||
if (cause instanceof UnsupportedEncodingException)
|
if (cause instanceof UnsupportedEncodingException) {
|
||||||
throw new IllegalPropertySetDataException(cause);
|
throw new IllegalPropertySetDataException(cause);
|
||||||
else
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +259,7 @@ public class MutablePropertySet extends PropertySet
|
|||||||
* the {@link MutablePropertySet} only.</p>
|
* the {@link MutablePropertySet} only.</p>
|
||||||
*
|
*
|
||||||
* @return the contents of this property set stream
|
* @return the contents of this property set stream
|
||||||
*
|
*
|
||||||
* @throws WritingNotSupportedException if HPSF does not yet support writing
|
* @throws WritingNotSupportedException if HPSF does not yet support writing
|
||||||
* of a property's variant type.
|
* of a property's variant type.
|
||||||
* @throws IOException if an I/O exception occurs.
|
* @throws IOException if an I/O exception occurs.
|
||||||
@ -284,7 +280,7 @@ public class MutablePropertySet extends PropertySet
|
|||||||
* @param dir The directory in the POI filesystem to write the document to.
|
* @param dir The directory in the POI filesystem to write the document to.
|
||||||
* @param name The document's name. If there is already a document with the
|
* @param name The document's name. If there is already a document with the
|
||||||
* same name in the directory the latter will be overwritten.
|
* same name in the directory the latter will be overwritten.
|
||||||
*
|
*
|
||||||
* @throws WritingNotSupportedException
|
* @throws WritingNotSupportedException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -37,9 +37,6 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
*
|
*
|
||||||
* <p>Please be aware that this class' functionality will be merged into the
|
* <p>Please be aware that this class' functionality will be merged into the
|
||||||
* {@link Section} class at a later time, so the API will change.</p>
|
* {@link Section} class at a later time, so the API will change.</p>
|
||||||
*
|
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-20
|
|
||||||
*/
|
*/
|
||||||
public class MutableSection extends Section
|
public class MutableSection extends Section
|
||||||
{
|
{
|
||||||
@ -56,7 +53,7 @@ public class MutableSection extends Section
|
|||||||
* decision has been taken when specifying the "properties" field
|
* decision has been taken when specifying the "properties" field
|
||||||
* as an Property[]. It should have been a {@link java.util.List}.</p>
|
* as an Property[]. It should have been a {@link java.util.List}.</p>
|
||||||
*/
|
*/
|
||||||
private List preprops;
|
private List<Property> preprops;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -77,17 +74,17 @@ public class MutableSection extends Section
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
formatID = null;
|
formatID = null;
|
||||||
offset = -1;
|
offset = -1;
|
||||||
preprops = new LinkedList();
|
preprops = new LinkedList<Property>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Constructs a <code>MutableSection</code> by doing a deep copy of an
|
* <p>Constructs a <code>MutableSection</code> by doing a deep copy of an
|
||||||
* existing <code>Section</code>. All nested <code>Property</code>
|
* existing <code>Section</code>. All nested <code>Property</code>
|
||||||
* instances, will be their mutable counterparts in the new
|
* instances, will be their mutable counterparts in the new
|
||||||
* <code>MutableSection</code>.</p>
|
* <code>MutableSection</code>.</p>
|
||||||
*
|
*
|
||||||
* @param s The section set to copy
|
* @param s The section set to copy
|
||||||
*/
|
*/
|
||||||
public MutableSection(final Section s)
|
public MutableSection(final Section s)
|
||||||
@ -148,7 +145,7 @@ public class MutableSection extends Section
|
|||||||
public void setProperties(final Property[] properties)
|
public void setProperties(final Property[] properties)
|
||||||
{
|
{
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
preprops = new LinkedList();
|
preprops = new LinkedList<Property>();
|
||||||
for (int i = 0; i < properties.length; i++)
|
for (int i = 0; i < properties.length; i++)
|
||||||
preprops.add(properties[i]);
|
preprops.add(properties[i]);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
@ -185,7 +182,7 @@ public class MutableSection extends Section
|
|||||||
*/
|
*/
|
||||||
public void setProperty(final int id, final int value)
|
public void setProperty(final int id, final int value)
|
||||||
{
|
{
|
||||||
setProperty(id, Variant.VT_I4, new Integer(value));
|
setProperty(id, Variant.VT_I4, Integer.valueOf(value));
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +199,7 @@ public class MutableSection extends Section
|
|||||||
*/
|
*/
|
||||||
public void setProperty(final int id, final long value)
|
public void setProperty(final int id, final long value)
|
||||||
{
|
{
|
||||||
setProperty(id, Variant.VT_I8, new Long(value));
|
setProperty(id, Variant.VT_I8, Long.valueOf(value));
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +216,7 @@ public class MutableSection extends Section
|
|||||||
*/
|
*/
|
||||||
public void setProperty(final int id, final boolean value)
|
public void setProperty(final int id, final boolean value)
|
||||||
{
|
{
|
||||||
setProperty(id, Variant.VT_BOOL, new Boolean(value));
|
setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,8 +276,8 @@ public class MutableSection extends Section
|
|||||||
*/
|
*/
|
||||||
public void removeProperty(final long id)
|
public void removeProperty(final long id)
|
||||||
{
|
{
|
||||||
for (final Iterator i = preprops.iterator(); i.hasNext();)
|
for (final Iterator<Property> i = preprops.iterator(); i.hasNext();)
|
||||||
if (((Property) i.next()).getID() == id)
|
if (i.next().getID() == id)
|
||||||
{
|
{
|
||||||
i.remove();
|
i.remove();
|
||||||
break;
|
break;
|
||||||
@ -303,7 +300,7 @@ public class MutableSection extends Section
|
|||||||
*/
|
*/
|
||||||
protected void setPropertyBooleanValue(final int id, final boolean value)
|
protected void setPropertyBooleanValue(final int id, final boolean value)
|
||||||
{
|
{
|
||||||
setProperty(id, Variant.VT_BOOL, new Boolean(value));
|
setProperty(id, Variant.VT_BOOL, Boolean.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -342,10 +339,10 @@ public class MutableSection extends Section
|
|||||||
* properties) and the properties themselves.</p>
|
* properties) and the properties themselves.</p>
|
||||||
*
|
*
|
||||||
* @return the section's length in bytes.
|
* @return the section's length in bytes.
|
||||||
* @throws WritingNotSupportedException
|
* @throws WritingNotSupportedException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private int calcSize() throws WritingNotSupportedException, IOException
|
private int calcSize() throws WritingNotSupportedException, IOException
|
||||||
{
|
{
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
write(out);
|
write(out);
|
||||||
@ -360,7 +357,7 @@ public class MutableSection extends Section
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Writes this section into an output stream.</p>
|
* <p>Writes this section into an output stream.</p>
|
||||||
*
|
*
|
||||||
* <p>Internally this is done by writing into three byte array output
|
* <p>Internally this is done by writing into three byte array output
|
||||||
* streams: one for the properties, one for the property list and one for
|
* streams: one for the properties, one for the property list and one for
|
||||||
* the section as such. The two former are appended to the latter when they
|
* the section as such. The two former are appended to the latter when they
|
||||||
@ -393,7 +390,7 @@ public class MutableSection extends Section
|
|||||||
* "propertyListStream". */
|
* "propertyListStream". */
|
||||||
final ByteArrayOutputStream propertyListStream =
|
final ByteArrayOutputStream propertyListStream =
|
||||||
new ByteArrayOutputStream();
|
new ByteArrayOutputStream();
|
||||||
|
|
||||||
/* Maintain the current position in the list. */
|
/* Maintain the current position in the list. */
|
||||||
int position = 0;
|
int position = 0;
|
||||||
|
|
||||||
@ -421,17 +418,15 @@ public class MutableSection extends Section
|
|||||||
* dictionary is present. In order to cope with this problem we
|
* dictionary is present. In order to cope with this problem we
|
||||||
* add the codepage property and set it to Unicode. */
|
* add the codepage property and set it to Unicode. */
|
||||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||||
new Integer(Constants.CP_UNICODE));
|
Integer.valueOf(Constants.CP_UNICODE));
|
||||||
codepage = getCodepage();
|
codepage = getCodepage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort the property list by their property IDs: */
|
/* Sort the property list by their property IDs: */
|
||||||
Collections.sort(preprops, new Comparator()
|
Collections.sort(preprops, new Comparator<Property>()
|
||||||
{
|
{
|
||||||
public int compare(final Object o1, final Object o2)
|
public int compare(final Property p1, final Property p2)
|
||||||
{
|
{
|
||||||
final Property p1 = (Property) o1;
|
|
||||||
final Property p2 = (Property) o2;
|
|
||||||
if (p1.getID() < p2.getID())
|
if (p1.getID() < p2.getID())
|
||||||
return -1;
|
return -1;
|
||||||
else if (p1.getID() == p2.getID())
|
else if (p1.getID() == p2.getID())
|
||||||
@ -443,11 +438,11 @@ public class MutableSection extends Section
|
|||||||
|
|
||||||
/* Write the properties and the property list into their respective
|
/* Write the properties and the property list into their respective
|
||||||
* streams: */
|
* streams: */
|
||||||
for (final ListIterator i = preprops.listIterator(); i.hasNext();)
|
for (final ListIterator<Property> i = preprops.listIterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
final MutableProperty p = (MutableProperty) i.next();
|
final MutableProperty p = (MutableProperty) i.next();
|
||||||
final long id = p.getID();
|
final long id = p.getID();
|
||||||
|
|
||||||
/* Write the property list entry. */
|
/* Write the property list entry. */
|
||||||
TypeWriter.writeUIntToStream(propertyListStream, p.getID());
|
TypeWriter.writeUIntToStream(propertyListStream, p.getID());
|
||||||
TypeWriter.writeUIntToStream(propertyListStream, position);
|
TypeWriter.writeUIntToStream(propertyListStream, position);
|
||||||
@ -475,17 +470,17 @@ public class MutableSection extends Section
|
|||||||
/* Write the section: */
|
/* Write the section: */
|
||||||
byte[] pb1 = propertyListStream.toByteArray();
|
byte[] pb1 = propertyListStream.toByteArray();
|
||||||
byte[] pb2 = propertyStream.toByteArray();
|
byte[] pb2 = propertyStream.toByteArray();
|
||||||
|
|
||||||
/* Write the section's length: */
|
/* Write the section's length: */
|
||||||
TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
|
TypeWriter.writeToStream(out, LittleEndian.INT_SIZE * 2 +
|
||||||
pb1.length + pb2.length);
|
pb1.length + pb2.length);
|
||||||
|
|
||||||
/* Write the section's number of properties: */
|
/* Write the section's number of properties: */
|
||||||
TypeWriter.writeToStream(out, getPropertyCount());
|
TypeWriter.writeToStream(out, getPropertyCount());
|
||||||
|
|
||||||
/* Write the property list: */
|
/* Write the property list: */
|
||||||
out.write(pb1);
|
out.write(pb1);
|
||||||
|
|
||||||
/* Write the properties: */
|
/* Write the properties: */
|
||||||
out.write(pb2);
|
out.write(pb2);
|
||||||
|
|
||||||
@ -505,14 +500,14 @@ public class MutableSection extends Section
|
|||||||
* @exception IOException if an I/O exception occurs.
|
* @exception IOException if an I/O exception occurs.
|
||||||
*/
|
*/
|
||||||
private static int writeDictionary(final OutputStream out,
|
private static int writeDictionary(final OutputStream out,
|
||||||
final Map dictionary, final int codepage)
|
final Map<Long,String> dictionary, final int codepage)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
int length = TypeWriter.writeUIntToStream(out, dictionary.size());
|
int length = TypeWriter.writeUIntToStream(out, dictionary.size());
|
||||||
for (final Iterator i = dictionary.keySet().iterator(); i.hasNext();)
|
for (final Iterator<Long> i = dictionary.keySet().iterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
final Long key = (Long) i.next();
|
final Long key = i.next();
|
||||||
final String value = (String) dictionary.get(key);
|
final String value = dictionary.get(key);
|
||||||
|
|
||||||
if (codepage == Constants.CP_UNICODE)
|
if (codepage == Constants.CP_UNICODE)
|
||||||
{
|
{
|
||||||
@ -565,7 +560,7 @@ public class MutableSection extends Section
|
|||||||
* <p>Overwrites the super class' method to cope with a redundancy:
|
* <p>Overwrites the super class' method to cope with a redundancy:
|
||||||
* the property count is maintained in a separate member variable, but
|
* the property count is maintained in a separate member variable, but
|
||||||
* shouldn't.</p>
|
* shouldn't.</p>
|
||||||
*
|
*
|
||||||
* @return The number of properties in this section
|
* @return The number of properties in this section
|
||||||
*/
|
*/
|
||||||
public int getPropertyCount()
|
public int getPropertyCount()
|
||||||
@ -577,7 +572,7 @@ public class MutableSection extends Section
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Gets this section's properties.</p>
|
* <p>Gets this section's properties.</p>
|
||||||
*
|
*
|
||||||
* @return this section's properties.
|
* @return this section's properties.
|
||||||
*/
|
*/
|
||||||
public Property[] getProperties()
|
public Property[] getProperties()
|
||||||
@ -590,7 +585,7 @@ public class MutableSection extends Section
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Gets a property.</p>
|
* <p>Gets a property.</p>
|
||||||
*
|
*
|
||||||
* @param id The ID of the property to get
|
* @param id The ID of the property to get
|
||||||
* @return The property or <code>null</code> if there is no such property
|
* @return The property or <code>null</code> if there is no such property
|
||||||
*/
|
*/
|
||||||
@ -614,27 +609,17 @@ public class MutableSection extends Section
|
|||||||
* method.</p>
|
* method.</p>
|
||||||
*
|
*
|
||||||
* @param dictionary The dictionary
|
* @param dictionary The dictionary
|
||||||
*
|
*
|
||||||
* @exception IllegalPropertySetDataException if the dictionary's key and
|
* @exception IllegalPropertySetDataException if the dictionary's key and
|
||||||
* value types are not correct.
|
* value types are not correct.
|
||||||
*
|
*
|
||||||
* @see Section#getDictionary()
|
* @see Section#getDictionary()
|
||||||
*/
|
*/
|
||||||
public void setDictionary(final Map dictionary)
|
public void setDictionary(final Map<Long,String> dictionary)
|
||||||
throws IllegalPropertySetDataException
|
throws IllegalPropertySetDataException
|
||||||
{
|
{
|
||||||
if (dictionary != null)
|
if (dictionary != null)
|
||||||
{
|
{
|
||||||
for (final Iterator i = dictionary.keySet().iterator();
|
|
||||||
i.hasNext();)
|
|
||||||
if (!(i.next() instanceof Long))
|
|
||||||
throw new IllegalPropertySetDataException
|
|
||||||
("Dictionary keys must be of type Long.");
|
|
||||||
for (final Iterator i = dictionary.values().iterator();
|
|
||||||
i.hasNext();)
|
|
||||||
if (!(i.next() instanceof String))
|
|
||||||
throw new IllegalPropertySetDataException
|
|
||||||
("Dictionary values must be of type String.");
|
|
||||||
this.dictionary = dictionary;
|
this.dictionary = dictionary;
|
||||||
|
|
||||||
/* Set the dictionary property (ID 0). Please note that the second
|
/* Set the dictionary property (ID 0). Please note that the second
|
||||||
@ -649,7 +634,7 @@ public class MutableSection extends Section
|
|||||||
(Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
|
(Integer) getProperty(PropertyIDMap.PID_CODEPAGE);
|
||||||
if (codepage == null)
|
if (codepage == null)
|
||||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||||
new Integer(Constants.CP_UNICODE));
|
Integer.valueOf(Constants.CP_UNICODE));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* Setting the dictionary to null means to remove property 0.
|
/* Setting the dictionary to null means to remove property 0.
|
||||||
@ -661,7 +646,7 @@ public class MutableSection extends Section
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets a property.</p>
|
* <p>Sets a property.</p>
|
||||||
*
|
*
|
||||||
* @param id The property ID.
|
* @param id The property ID.
|
||||||
* @param value The property's value. The value's class must be one of those
|
* @param value The property's value. The value's class must be one of those
|
||||||
* supported by HPSF.
|
* supported by HPSF.
|
||||||
@ -710,7 +695,6 @@ public class MutableSection extends Section
|
|||||||
public void setCodepage(final int codepage)
|
public void setCodepage(final int codepage)
|
||||||
{
|
{
|
||||||
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2,
|
||||||
new Integer(codepage));
|
Integer.valueOf(codepage));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,8 +25,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-09-03
|
|
||||||
*/
|
*/
|
||||||
public class NoFormatIDException extends HPSFRuntimeException
|
public class NoFormatIDException extends HPSFRuntimeException
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,8 +26,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class NoPropertySetStreamException extends HPSFException
|
public class NoPropertySetStreamException extends HPSFException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,8 +28,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class NoSingleSectionException extends HPSFRuntimeException
|
public class NoSingleSectionException extends HPSFRuntimeException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@ -46,7 +46,7 @@ import org.apache.poi.util.POILogger;
|
|||||||
* over time but largely depends on your feedback so that the POI team knows
|
* over time but largely depends on your feedback so that the POI team knows
|
||||||
* which variant types are really needed. So please feel free to submit error
|
* which variant types are really needed. So please feel free to submit error
|
||||||
* reports or patches for the types you need.</p>
|
* reports or patches for the types you need.</p>
|
||||||
*
|
*
|
||||||
* <p>Microsoft documentation: <a
|
* <p>Microsoft documentation: <a
|
||||||
* href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
|
* href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
|
||||||
* Property Set Display Name Dictionary</a>.
|
* Property Set Display Name Dictionary</a>.
|
||||||
@ -56,8 +56,6 @@ import org.apache.poi.util.POILogger;
|
|||||||
* @author Drew Varner (Drew.Varner InAndAround sc.edu)
|
* @author Drew Varner (Drew.Varner InAndAround sc.edu)
|
||||||
* @see Section
|
* @see Section
|
||||||
* @see Variant
|
* @see Variant
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class Property
|
public class Property
|
||||||
{
|
{
|
||||||
@ -112,7 +110,7 @@ public class Property
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a property.</p>
|
* <p>Creates a property.</p>
|
||||||
*
|
*
|
||||||
* @param id the property's ID.
|
* @param id the property's ID.
|
||||||
* @param type the property's type, see {@link Variant}.
|
* @param type the property's type, see {@link Variant}.
|
||||||
* @param value the property's value. Only certain types are allowed, see
|
* @param value the property's value. Only certain types are allowed, see
|
||||||
@ -185,7 +183,7 @@ public class Property
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Reads a dictionary.</p>
|
* <p>Reads a dictionary.</p>
|
||||||
*
|
*
|
||||||
* @param src The byte array containing the bytes making out the dictionary.
|
* @param src The byte array containing the bytes making out the dictionary.
|
||||||
* @param offset At this offset within <var>src </var> the dictionary
|
* @param offset At this offset within <var>src </var> the dictionary
|
||||||
* starts.
|
* starts.
|
||||||
@ -219,7 +217,7 @@ public class Property
|
|||||||
for (int i = 0; i < nrEntries; i++)
|
for (int i = 0; i < nrEntries; i++)
|
||||||
{
|
{
|
||||||
/* The key. */
|
/* The key. */
|
||||||
final Long id = new Long(LittleEndian.getUInt(src, o));
|
final Long id = Long.valueOf(LittleEndian.getUInt(src, o));
|
||||||
o += LittleEndian.INT_SIZE;
|
o += LittleEndian.INT_SIZE;
|
||||||
|
|
||||||
/* The value (a string). The length is the either the
|
/* The value (a string). The length is the either the
|
||||||
@ -298,7 +296,7 @@ public class Property
|
|||||||
* 4.</p>
|
* 4.</p>
|
||||||
*
|
*
|
||||||
* @return the property's size in bytes
|
* @return the property's size in bytes
|
||||||
*
|
*
|
||||||
* @exception WritingNotSupportedException if HPSF does not yet support the
|
* @exception WritingNotSupportedException if HPSF does not yet support the
|
||||||
* property's variant type.
|
* property's variant type.
|
||||||
*/
|
*/
|
||||||
@ -339,13 +337,14 @@ public class Property
|
|||||||
* ID == 0 is a special case: It does not have a type, and its value is the
|
* ID == 0 is a special case: It does not have a type, and its value is the
|
||||||
* section's dictionary. Another special case are strings: Two properties
|
* section's dictionary. Another special case are strings: Two properties
|
||||||
* may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
|
* may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
|
||||||
*
|
*
|
||||||
* @see Object#equals(java.lang.Object)
|
* @see Object#equals(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public boolean equals(final Object o)
|
public boolean equals(final Object o)
|
||||||
{
|
{
|
||||||
if (!(o instanceof Property))
|
if (!(o instanceof Property)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
final Property p = (Property) o;
|
final Property p = (Property) o;
|
||||||
final Object pValue = p.getValue();
|
final Object pValue = p.getValue();
|
||||||
final long pId = p.getID();
|
final long pId = p.getID();
|
||||||
@ -357,8 +356,8 @@ public class Property
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* It's clear now that both values are non-null. */
|
/* It's clear now that both values are non-null. */
|
||||||
final Class valueClass = value.getClass();
|
final Class<?> valueClass = value.getClass();
|
||||||
final Class pValueClass = pValue.getClass();
|
final Class<?> pValueClass = pValue.getClass();
|
||||||
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
if (!(valueClass.isAssignableFrom(pValueClass)) &&
|
||||||
!(pValueClass.isAssignableFrom(valueClass)))
|
!(pValueClass.isAssignableFrom(valueClass)))
|
||||||
return false;
|
return false;
|
||||||
@ -375,10 +374,10 @@ public class Property
|
|||||||
{
|
{
|
||||||
if (t1 == t2 ||
|
if (t1 == t2 ||
|
||||||
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
|
(t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
|
||||||
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR))
|
(t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR)) {
|
||||||
return true;
|
return true;
|
||||||
else
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -57,8 +57,6 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @author Drew Varner (Drew.Varner hanginIn sc.edu)
|
* @author Drew Varner (Drew.Varner hanginIn sc.edu)
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class PropertySet
|
public class PropertySet
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -30,8 +30,6 @@ import org.apache.poi.hpsf.wellknown.SectionIDMap;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class PropertySetFactory
|
public class PropertySetFactory
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,8 +26,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2003-08-08
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class ReadingNotSupportedException
|
public class ReadingNotSupportedException
|
||||||
extends UnsupportedVariantTypeException
|
extends UnsupportedVariantTypeException
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@ -34,8 +34,6 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @author Drew Varner (Drew.Varner allUpIn sc.edu)
|
* @author Drew Varner (Drew.Varner allUpIn sc.edu)
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class Section
|
public class Section
|
||||||
{
|
{
|
||||||
@ -44,7 +42,7 @@ public class Section
|
|||||||
* <p>Maps property IDs to section-private PID strings. These
|
* <p>Maps property IDs to section-private PID strings. These
|
||||||
* strings can be found in the property with ID 0.</p>
|
* strings can be found in the property with ID 0.</p>
|
||||||
*/
|
*/
|
||||||
protected Map dictionary;
|
protected Map<Long,String> dictionary;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The section's format ID, {@link #getFormatID}.</p>
|
* <p>The section's format ID, {@link #getFormatID}.</p>
|
||||||
@ -148,7 +146,7 @@ public class Section
|
|||||||
* @param src Contains the complete property set stream.
|
* @param src Contains the complete property set stream.
|
||||||
* @param offset The position in the stream that points to the
|
* @param offset The position in the stream that points to the
|
||||||
* section's format ID.
|
* section's format ID.
|
||||||
*
|
*
|
||||||
* @exception UnsupportedEncodingException if the section's codepage is not
|
* @exception UnsupportedEncodingException if the section's codepage is not
|
||||||
* supported.
|
* supported.
|
||||||
*/
|
*/
|
||||||
@ -185,7 +183,7 @@ public class Section
|
|||||||
/*
|
/*
|
||||||
* Read the properties. The offset is positioned at the first
|
* Read the properties. The offset is positioned at the first
|
||||||
* entry of the property list. There are two problems:
|
* entry of the property list. There are two problems:
|
||||||
*
|
*
|
||||||
* 1. For each property we have to find out its length. In the
|
* 1. For each property we have to find out its length. In the
|
||||||
* property list we find each property's ID and its offset relative
|
* property list we find each property's ID and its offset relative
|
||||||
* to the section's beginning. Unfortunately the properties in the
|
* to the section's beginning. Unfortunately the properties in the
|
||||||
@ -193,8 +191,8 @@ public class Section
|
|||||||
* possible to calculate the length as
|
* possible to calculate the length as
|
||||||
* (offset of property(i+1) - offset of property(i)). Before we can
|
* (offset of property(i+1) - offset of property(i)). Before we can
|
||||||
* that we first have to sort the property list by ascending offsets.
|
* that we first have to sort the property list by ascending offsets.
|
||||||
*
|
*
|
||||||
* 2. We have to read the property with ID 1 before we read other
|
* 2. We have to read the property with ID 1 before we read other
|
||||||
* properties, at least before other properties containing strings.
|
* properties, at least before other properties containing strings.
|
||||||
* The reason is that property 1 specifies the codepage. If it is
|
* The reason is that property 1 specifies the codepage. If it is
|
||||||
* 1200, all strings are in Unicode. In other words: Before we can
|
* 1200, all strings are in Unicode. In other words: Before we can
|
||||||
@ -207,10 +205,10 @@ public class Section
|
|||||||
* seconds pass reads the other properties.
|
* seconds pass reads the other properties.
|
||||||
*/
|
*/
|
||||||
properties = new Property[propertyCount];
|
properties = new Property[propertyCount];
|
||||||
|
|
||||||
/* Pass 1: Read the property list. */
|
/* Pass 1: Read the property list. */
|
||||||
int pass1Offset = o1;
|
int pass1Offset = o1;
|
||||||
final List propertyList = new ArrayList(propertyCount);
|
final List<PropertyListEntry> propertyList = new ArrayList<PropertyListEntry>(propertyCount);
|
||||||
PropertyListEntry ple;
|
PropertyListEntry ple;
|
||||||
for (int i = 0; i < properties.length; i++)
|
for (int i = 0; i < properties.length; i++)
|
||||||
{
|
{
|
||||||
@ -234,34 +232,22 @@ public class Section
|
|||||||
/* Calculate the properties' lengths. */
|
/* Calculate the properties' lengths. */
|
||||||
for (int i = 0; i < propertyCount - 1; i++)
|
for (int i = 0; i < propertyCount - 1; i++)
|
||||||
{
|
{
|
||||||
final PropertyListEntry ple1 =
|
PropertyListEntry ple1 = propertyList.get(i);
|
||||||
(PropertyListEntry) propertyList.get(i);
|
PropertyListEntry ple2 = propertyList.get(i + 1);
|
||||||
final PropertyListEntry ple2 =
|
|
||||||
(PropertyListEntry) propertyList.get(i + 1);
|
|
||||||
ple1.length = ple2.offset - ple1.offset;
|
ple1.length = ple2.offset - ple1.offset;
|
||||||
}
|
}
|
||||||
if (propertyCount > 0)
|
if (propertyCount > 0)
|
||||||
{
|
{
|
||||||
ple = (PropertyListEntry) propertyList.get(propertyCount - 1);
|
ple = propertyList.get(propertyCount - 1);
|
||||||
ple.length = size - ple.offset;
|
ple.length = size - ple.offset;
|
||||||
if (ple.length <= 0)
|
|
||||||
{
|
|
||||||
final StringBuffer b = new StringBuffer();
|
|
||||||
b.append("The property set claims to have a size of ");
|
|
||||||
b.append(size);
|
|
||||||
b.append(" bytes. However, it exceeds ");
|
|
||||||
b.append(ple.offset);
|
|
||||||
b.append(" bytes.");
|
|
||||||
throw new IllegalPropertySetDataException(b.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look for the codepage. */
|
/* Look for the codepage. */
|
||||||
int codepage = -1;
|
int codepage = -1;
|
||||||
for (final Iterator i = propertyList.iterator();
|
for (final Iterator<PropertyListEntry> i = propertyList.iterator();
|
||||||
codepage == -1 && i.hasNext();)
|
codepage == -1 && i.hasNext();)
|
||||||
{
|
{
|
||||||
ple = (PropertyListEntry) i.next();
|
ple = i.next();
|
||||||
|
|
||||||
/* Read the codepage if the property ID is 1. */
|
/* Read the codepage if the property ID is 1. */
|
||||||
if (ple.id == PropertyIDMap.PID_CODEPAGE)
|
if (ple.id == PropertyIDMap.PID_CODEPAGE)
|
||||||
@ -285,14 +271,14 @@ public class Section
|
|||||||
/* Pass 2: Read all properties - including the codepage property,
|
/* Pass 2: Read all properties - including the codepage property,
|
||||||
* if available. */
|
* if available. */
|
||||||
int i1 = 0;
|
int i1 = 0;
|
||||||
for (final Iterator i = propertyList.iterator(); i.hasNext();)
|
for (final Iterator<PropertyListEntry> i = propertyList.iterator(); i.hasNext();)
|
||||||
{
|
{
|
||||||
ple = (PropertyListEntry) i.next();
|
ple = i.next();
|
||||||
Property p = new Property(ple.id, src,
|
Property p = new Property(ple.id, src,
|
||||||
this.offset + ple.offset,
|
this.offset + ple.offset,
|
||||||
ple.length, codepage);
|
ple.length, codepage);
|
||||||
if (p.getID() == PropertyIDMap.PID_CODEPAGE)
|
if (p.getID() == PropertyIDMap.PID_CODEPAGE)
|
||||||
p = new Property(p.getID(), p.getType(), new Integer(codepage));
|
p = new Property(p.getID(), p.getType(), Integer.valueOf(codepage));
|
||||||
properties[i1++] = p;
|
properties[i1++] = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +294,7 @@ public class Section
|
|||||||
* <p>Represents an entry in the property list and holds a property's ID and
|
* <p>Represents an entry in the property list and holds a property's ID and
|
||||||
* its offset from the section's beginning.</p>
|
* its offset from the section's beginning.</p>
|
||||||
*/
|
*/
|
||||||
class PropertyListEntry implements Comparable
|
class PropertyListEntry implements Comparable<PropertyListEntry>
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
int offset;
|
int offset;
|
||||||
@ -321,11 +307,9 @@ public class Section
|
|||||||
*
|
*
|
||||||
* @see Comparable#compareTo(java.lang.Object)
|
* @see Comparable#compareTo(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public int compareTo(final Object o)
|
public int compareTo(final PropertyListEntry o)
|
||||||
{
|
{
|
||||||
if (!(o instanceof PropertyListEntry))
|
final int otherOffset = o.offset;
|
||||||
throw new ClassCastException(o.toString());
|
|
||||||
final int otherOffset = ((PropertyListEntry) o).offset;
|
|
||||||
if (offset < otherOffset)
|
if (offset < otherOffset)
|
||||||
return -1;
|
return -1;
|
||||||
else if (offset == otherOffset)
|
else if (offset == otherOffset)
|
||||||
@ -414,11 +398,11 @@ public class Section
|
|||||||
protected boolean getPropertyBooleanValue(final int id)
|
protected boolean getPropertyBooleanValue(final int id)
|
||||||
{
|
{
|
||||||
final Boolean b = (Boolean) getProperty(id);
|
final Boolean b = (Boolean) getProperty(id);
|
||||||
if (b != null)
|
if (b == null) {
|
||||||
return b.booleanValue();
|
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return b.booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -464,7 +448,7 @@ public class Section
|
|||||||
{
|
{
|
||||||
String s = null;
|
String s = null;
|
||||||
if (dictionary != null)
|
if (dictionary != null)
|
||||||
s = (String) dictionary.get(new Long(pid));
|
s = (String) dictionary.get(Long.valueOf(pid));
|
||||||
if (s == null)
|
if (s == null)
|
||||||
s = SectionIDMap.getPIDString(getFormatID().getBytes(), pid);
|
s = SectionIDMap.getPIDString(getFormatID().getBytes(), pid);
|
||||||
if (s == null)
|
if (s == null)
|
||||||
@ -477,23 +461,23 @@ public class Section
|
|||||||
/**
|
/**
|
||||||
* <p>Checks whether this section is equal to another object. The result is
|
* <p>Checks whether this section is equal to another object. The result is
|
||||||
* <code>false</code> if one of the the following conditions holds:</p>
|
* <code>false</code> if one of the the following conditions holds:</p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><p>The other object is not a {@link Section}.</p></li>
|
* <li><p>The other object is not a {@link Section}.</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>The format IDs of the two sections are not equal.</p></li>
|
* <li><p>The format IDs of the two sections are not equal.</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>The sections have a different number of properties. However,
|
* <li><p>The sections have a different number of properties. However,
|
||||||
* properties with ID 1 (codepage) are not counted.</p></li>
|
* properties with ID 1 (codepage) are not counted.</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>The other object is not a {@link Section}.</p></li>
|
* <li><p>The other object is not a {@link Section}.</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>The properties have different values. The order of the properties
|
* <li><p>The properties have different values. The order of the properties
|
||||||
* is irrelevant.</p></li>
|
* is irrelevant.</p></li>
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param o The object to compare this section with
|
* @param o The object to compare this section with
|
||||||
* @return <code>true</code> if the objects are equal, <code>false</code> if
|
* @return <code>true</code> if the objects are equal, <code>false</code> if
|
||||||
* not
|
* not
|
||||||
@ -506,7 +490,7 @@ public class Section
|
|||||||
if (!s.getFormatID().equals(getFormatID()))
|
if (!s.getFormatID().equals(getFormatID()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Compare all properties except 0 and 1 as they must be handled
|
/* Compare all properties except 0 and 1 as they must be handled
|
||||||
* specially. */
|
* specially. */
|
||||||
Property[] pa1 = new Property[getProperties().length];
|
Property[] pa1 = new Property[getProperties().length];
|
||||||
Property[] pa2 = new Property[s.getProperties().length];
|
Property[] pa2 = new Property[s.getProperties().length];
|
||||||
@ -561,10 +545,10 @@ public class Section
|
|||||||
dictionaryEqual = p10.getValue().equals(p20.getValue());
|
dictionaryEqual = p10.getValue().equals(p20.getValue());
|
||||||
else if (p10 != null || p20 != null)
|
else if (p10 != null || p20 != null)
|
||||||
dictionaryEqual = false;
|
dictionaryEqual = false;
|
||||||
if (!dictionaryEqual)
|
if (dictionaryEqual) {
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return Util.equals(pa1, pa2);
|
return Util.equals(pa1, pa2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -573,7 +557,7 @@ public class Section
|
|||||||
* <p>Removes a field from a property array. The resulting array is
|
* <p>Removes a field from a property array. The resulting array is
|
||||||
* compactified and returned.</p>
|
* compactified and returned.</p>
|
||||||
*
|
*
|
||||||
* @param pa The property array.
|
* @param pa The property array.
|
||||||
* @param i The index of the field to be removed.
|
* @param i The index of the field to be removed.
|
||||||
* @return the compactified array.
|
* @return the compactified array.
|
||||||
*/
|
*/
|
||||||
@ -644,7 +628,7 @@ public class Section
|
|||||||
* @return the dictionary or <code>null</code> if the section does not have
|
* @return the dictionary or <code>null</code> if the section does not have
|
||||||
* a dictionary.
|
* a dictionary.
|
||||||
*/
|
*/
|
||||||
public Map getDictionary()
|
public Map<Long,String> getDictionary()
|
||||||
{
|
{
|
||||||
return dictionary;
|
return dictionary;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -53,8 +53,6 @@ import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public abstract class SpecialPropertySet extends MutablePropertySet
|
public abstract class SpecialPropertySet extends MutablePropertySet
|
||||||
{
|
{
|
||||||
|
@ -24,15 +24,12 @@ import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
|||||||
/**
|
/**
|
||||||
* <p>Convenience class representing a Summary Information stream in a
|
* <p>Convenience class representing a Summary Information stream in a
|
||||||
* Microsoft Office document.</p>
|
* Microsoft Office document.</p>
|
||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @see DocumentSummaryInformation
|
* @see DocumentSummaryInformation
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class SummaryInformation extends SpecialPropertySet
|
public final class SummaryInformation extends SpecialPropertySet {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>The document name a summary information stream usually has in a POIFS
|
* <p>The document name a summary information stream usually has in a POIFS
|
||||||
@ -48,7 +45,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
/**
|
/**
|
||||||
* <p>Creates a {@link SummaryInformation} from a given {@link
|
* <p>Creates a {@link SummaryInformation} from a given {@link
|
||||||
* PropertySet}.</p>
|
* PropertySet}.</p>
|
||||||
*
|
*
|
||||||
* @param ps A property set which should be created from a summary
|
* @param ps A property set which should be created from a summary
|
||||||
* information stream.
|
* information stream.
|
||||||
* @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
|
* @throws UnexpectedPropertySetTypeException if <var>ps</var> does not
|
||||||
@ -67,7 +64,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the title (or <code>null</code>).</p>
|
* <p>Returns the title (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The title or <code>null</code>
|
* @return The title or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getTitle()
|
public String getTitle()
|
||||||
@ -79,7 +76,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the title.</p>
|
* <p>Sets the title.</p>
|
||||||
*
|
*
|
||||||
* @param title The title to set.
|
* @param title The title to set.
|
||||||
*/
|
*/
|
||||||
public void setTitle(final String title)
|
public void setTitle(final String title)
|
||||||
@ -103,7 +100,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the subject (or <code>null</code>).</p>
|
* <p>Returns the subject (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The subject or <code>null</code>
|
* @return The subject or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getSubject()
|
public String getSubject()
|
||||||
@ -115,7 +112,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the subject.</p>
|
* <p>Sets the subject.</p>
|
||||||
*
|
*
|
||||||
* @param subject The subject to set.
|
* @param subject The subject to set.
|
||||||
*/
|
*/
|
||||||
public void setSubject(final String subject)
|
public void setSubject(final String subject)
|
||||||
@ -139,7 +136,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the author (or <code>null</code>).</p>
|
* <p>Returns the author (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The author or <code>null</code>
|
* @return The author or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getAuthor()
|
public String getAuthor()
|
||||||
@ -151,7 +148,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the author.</p>
|
* <p>Sets the author.</p>
|
||||||
*
|
*
|
||||||
* @param author The author to set.
|
* @param author The author to set.
|
||||||
*/
|
*/
|
||||||
public void setAuthor(final String author)
|
public void setAuthor(final String author)
|
||||||
@ -175,7 +172,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the keywords (or <code>null</code>).</p>
|
* <p>Returns the keywords (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The keywords or <code>null</code>
|
* @return The keywords or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getKeywords()
|
public String getKeywords()
|
||||||
@ -187,7 +184,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the keywords.</p>
|
* <p>Sets the keywords.</p>
|
||||||
*
|
*
|
||||||
* @param keywords The keywords to set.
|
* @param keywords The keywords to set.
|
||||||
*/
|
*/
|
||||||
public void setKeywords(final String keywords)
|
public void setKeywords(final String keywords)
|
||||||
@ -211,7 +208,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the comments (or <code>null</code>).</p>
|
* <p>Returns the comments (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The comments or <code>null</code>
|
* @return The comments or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getComments()
|
public String getComments()
|
||||||
@ -223,7 +220,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the comments.</p>
|
* <p>Sets the comments.</p>
|
||||||
*
|
*
|
||||||
* @param comments The comments to set.
|
* @param comments The comments to set.
|
||||||
*/
|
*/
|
||||||
public void setComments(final String comments)
|
public void setComments(final String comments)
|
||||||
@ -247,7 +244,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the template (or <code>null</code>).</p>
|
* <p>Returns the template (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The template or <code>null</code>
|
* @return The template or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getTemplate()
|
public String getTemplate()
|
||||||
@ -259,7 +256,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the template.</p>
|
* <p>Sets the template.</p>
|
||||||
*
|
*
|
||||||
* @param template The template to set.
|
* @param template The template to set.
|
||||||
*/
|
*/
|
||||||
public void setTemplate(final String template)
|
public void setTemplate(final String template)
|
||||||
@ -283,7 +280,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the last author (or <code>null</code>).</p>
|
* <p>Returns the last author (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The last author or <code>null</code>
|
* @return The last author or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getLastAuthor()
|
public String getLastAuthor()
|
||||||
@ -295,7 +292,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the last author.</p>
|
* <p>Sets the last author.</p>
|
||||||
*
|
*
|
||||||
* @param lastAuthor The last author to set.
|
* @param lastAuthor The last author to set.
|
||||||
*/
|
*/
|
||||||
public void setLastAuthor(final String lastAuthor)
|
public void setLastAuthor(final String lastAuthor)
|
||||||
@ -319,7 +316,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the revision number (or <code>null</code>). </p>
|
* <p>Returns the revision number (or <code>null</code>). </p>
|
||||||
*
|
*
|
||||||
* @return The revision number or <code>null</code>
|
* @return The revision number or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getRevNumber()
|
public String getRevNumber()
|
||||||
@ -331,7 +328,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the revision number.</p>
|
* <p>Sets the revision number.</p>
|
||||||
*
|
*
|
||||||
* @param revNumber The revision number to set.
|
* @param revNumber The revision number to set.
|
||||||
*/
|
*/
|
||||||
public void setRevNumber(final String revNumber)
|
public void setRevNumber(final String revNumber)
|
||||||
@ -356,24 +353,24 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
/**
|
/**
|
||||||
* <p>Returns the total time spent in editing the document (or
|
* <p>Returns the total time spent in editing the document (or
|
||||||
* <code>0</code>).</p>
|
* <code>0</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The total time spent in editing the document or 0 if the {@link
|
* @return The total time spent in editing the document or 0 if the {@link
|
||||||
* SummaryInformation} does not contain this information.
|
* SummaryInformation} does not contain this information.
|
||||||
*/
|
*/
|
||||||
public long getEditTime()
|
public long getEditTime()
|
||||||
{
|
{
|
||||||
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
|
final Date d = (Date) getProperty(PropertyIDMap.PID_EDITTIME);
|
||||||
if (d == null)
|
if (d == null) {
|
||||||
return 0;
|
return 0;
|
||||||
else
|
}
|
||||||
return Util.dateToFileTime(d);
|
return Util.dateToFileTime(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the total time spent in editing the document.</p>
|
* <p>Sets the total time spent in editing the document.</p>
|
||||||
*
|
*
|
||||||
* @param time The time to set.
|
* @param time The time to set.
|
||||||
*/
|
*/
|
||||||
public void setEditTime(final long time)
|
public void setEditTime(final long time)
|
||||||
@ -398,7 +395,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the last printed time (or <code>null</code>).</p>
|
* <p>Returns the last printed time (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The last printed time or <code>null</code>
|
* @return The last printed time or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public Date getLastPrinted()
|
public Date getLastPrinted()
|
||||||
@ -410,7 +407,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the lastPrinted.</p>
|
* <p>Sets the lastPrinted.</p>
|
||||||
*
|
*
|
||||||
* @param lastPrinted The lastPrinted to set.
|
* @param lastPrinted The lastPrinted to set.
|
||||||
*/
|
*/
|
||||||
public void setLastPrinted(final Date lastPrinted)
|
public void setLastPrinted(final Date lastPrinted)
|
||||||
@ -435,7 +432,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the creation time (or <code>null</code>).</p>
|
* <p>Returns the creation time (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The creation time or <code>null</code>
|
* @return The creation time or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public Date getCreateDateTime()
|
public Date getCreateDateTime()
|
||||||
@ -447,7 +444,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the creation time.</p>
|
* <p>Sets the creation time.</p>
|
||||||
*
|
*
|
||||||
* @param createDateTime The creation time to set.
|
* @param createDateTime The creation time to set.
|
||||||
*/
|
*/
|
||||||
public void setCreateDateTime(final Date createDateTime)
|
public void setCreateDateTime(final Date createDateTime)
|
||||||
@ -472,7 +469,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the last save time (or <code>null</code>).</p>
|
* <p>Returns the last save time (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The last save time or <code>null</code>
|
* @return The last save time or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public Date getLastSaveDateTime()
|
public Date getLastSaveDateTime()
|
||||||
@ -484,7 +481,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the total time spent in editing the document.</p>
|
* <p>Sets the total time spent in editing the document.</p>
|
||||||
*
|
*
|
||||||
* @param time The time to set.
|
* @param time The time to set.
|
||||||
*/
|
*/
|
||||||
public void setLastSaveDateTime(final Date time)
|
public void setLastSaveDateTime(final Date time)
|
||||||
@ -511,7 +508,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
/**
|
/**
|
||||||
* <p>Returns the page count or 0 if the {@link SummaryInformation} does
|
* <p>Returns the page count or 0 if the {@link SummaryInformation} does
|
||||||
* not contain a page count.</p>
|
* not contain a page count.</p>
|
||||||
*
|
*
|
||||||
* @return The page count or 0 if the {@link SummaryInformation} does not
|
* @return The page count or 0 if the {@link SummaryInformation} does not
|
||||||
* contain a page count.
|
* contain a page count.
|
||||||
*/
|
*/
|
||||||
@ -524,7 +521,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the page count.</p>
|
* <p>Sets the page count.</p>
|
||||||
*
|
*
|
||||||
* @param pageCount The page count to set.
|
* @param pageCount The page count to set.
|
||||||
*/
|
*/
|
||||||
public void setPageCount(final int pageCount)
|
public void setPageCount(final int pageCount)
|
||||||
@ -549,7 +546,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
/**
|
/**
|
||||||
* <p>Returns the word count or 0 if the {@link SummaryInformation} does
|
* <p>Returns the word count or 0 if the {@link SummaryInformation} does
|
||||||
* not contain a word count.</p>
|
* not contain a word count.</p>
|
||||||
*
|
*
|
||||||
* @return The word count or <code>null</code>
|
* @return The word count or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public int getWordCount()
|
public int getWordCount()
|
||||||
@ -561,7 +558,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the word count.</p>
|
* <p>Sets the word count.</p>
|
||||||
*
|
*
|
||||||
* @param wordCount The word count to set.
|
* @param wordCount The word count to set.
|
||||||
*/
|
*/
|
||||||
public void setWordCount(final int wordCount)
|
public void setWordCount(final int wordCount)
|
||||||
@ -586,7 +583,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
/**
|
/**
|
||||||
* <p>Returns the character count or 0 if the {@link SummaryInformation}
|
* <p>Returns the character count or 0 if the {@link SummaryInformation}
|
||||||
* does not contain a char count.</p>
|
* does not contain a char count.</p>
|
||||||
*
|
*
|
||||||
* @return The character count or <code>null</code>
|
* @return The character count or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public int getCharCount()
|
public int getCharCount()
|
||||||
@ -598,7 +595,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the character count.</p>
|
* <p>Sets the character count.</p>
|
||||||
*
|
*
|
||||||
* @param charCount The character count to set.
|
* @param charCount The character count to set.
|
||||||
*/
|
*/
|
||||||
public void setCharCount(final int charCount)
|
public void setCharCount(final int charCount)
|
||||||
@ -624,12 +621,12 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
* <p>Returns the thumbnail (or <code>null</code>) <strong>when this
|
* <p>Returns the thumbnail (or <code>null</code>) <strong>when this
|
||||||
* method is implemented. Please note that the return type is likely to
|
* method is implemented. Please note that the return type is likely to
|
||||||
* change!</strong></p>
|
* change!</strong></p>
|
||||||
*
|
*
|
||||||
* <p><strong>Hint to developers:</strong> Drew Varner <Drew.Varner
|
* <p><strong>Hint to developers:</strong> Drew Varner <Drew.Varner
|
||||||
* -at- sc.edu> said that this is an image in WMF or Clipboard (BMP?)
|
* -at- sc.edu> said that this is an image in WMF or Clipboard (BMP?)
|
||||||
* format. However, we won't do any conversion into any image type but
|
* format. However, we won't do any conversion into any image type but
|
||||||
* instead just return a byte array.</p>
|
* instead just return a byte array.</p>
|
||||||
*
|
*
|
||||||
* @return The thumbnail or <code>null</code>
|
* @return The thumbnail or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public byte[] getThumbnail()
|
public byte[] getThumbnail()
|
||||||
@ -641,7 +638,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the thumbnail.</p>
|
* <p>Sets the thumbnail.</p>
|
||||||
*
|
*
|
||||||
* @param thumbnail The thumbnail to set.
|
* @param thumbnail The thumbnail to set.
|
||||||
*/
|
*/
|
||||||
public void setThumbnail(final byte[] thumbnail)
|
public void setThumbnail(final byte[] thumbnail)
|
||||||
@ -666,7 +663,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns the application name (or <code>null</code>).</p>
|
* <p>Returns the application name (or <code>null</code>).</p>
|
||||||
*
|
*
|
||||||
* @return The application name or <code>null</code>
|
* @return The application name or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public String getApplicationName()
|
public String getApplicationName()
|
||||||
@ -678,7 +675,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the application name.</p>
|
* <p>Sets the application name.</p>
|
||||||
*
|
*
|
||||||
* @param applicationName The application name to set.
|
* @param applicationName The application name to set.
|
||||||
*/
|
*/
|
||||||
public void setApplicationName(final String applicationName)
|
public void setApplicationName(final String applicationName)
|
||||||
@ -702,24 +699,24 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns a security code which is one of the following values:</p>
|
* <p>Returns a security code which is one of the following values:</p>
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><p>0 if the {@link SummaryInformation} does not contain a
|
* <li><p>0 if the {@link SummaryInformation} does not contain a
|
||||||
* security field or if there is no security on the document. Use
|
* security field or if there is no security on the document. Use
|
||||||
* {@link PropertySet#wasNull()} to distinguish between the two
|
* {@link PropertySet#wasNull()} to distinguish between the two
|
||||||
* cases!</p></li>
|
* cases!</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>1 if the document is password protected</p></li>
|
* <li><p>1 if the document is password protected</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>2 if the document is read-only recommended</p></li>
|
* <li><p>2 if the document is read-only recommended</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>4 if the document is read-only enforced</p></li>
|
* <li><p>4 if the document is read-only enforced</p></li>
|
||||||
*
|
*
|
||||||
* <li><p>8 if the document is locked for annotations</p></li>
|
* <li><p>8 if the document is locked for annotations</p></li>
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @return The security code or <code>null</code>
|
* @return The security code or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public int getSecurity()
|
public int getSecurity()
|
||||||
@ -731,7 +728,7 @@ public class SummaryInformation extends SpecialPropertySet
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sets the security code.</p>
|
* <p>Sets the security code.</p>
|
||||||
*
|
*
|
||||||
* @param security The security code to set.
|
* @param security The security code to set.
|
||||||
*/
|
*/
|
||||||
public void setSecurity(final int security)
|
public void setSecurity(final int security)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
@ -24,11 +24,8 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
*
|
*
|
||||||
* @author Drew Varner (Drew.Varner inOrAround sc.edu)
|
* @author Drew Varner (Drew.Varner inOrAround sc.edu)
|
||||||
* @see SummaryInformation#getThumbnail()
|
* @see SummaryInformation#getThumbnail()
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-04-29
|
|
||||||
*/
|
*/
|
||||||
public class Thumbnail
|
public final class Thumbnail {
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Offset in bytes where the Clipboard Format Tag starts in the
|
* <p>Offset in bytes where the Clipboard Format Tag starts in the
|
||||||
@ -130,7 +127,7 @@ public class Thumbnail
|
|||||||
* <p>A <code>byte[]</code> to hold a thumbnail image in ({@link
|
* <p>A <code>byte[]</code> to hold a thumbnail image in ({@link
|
||||||
* Variant#VT_CF VT_CF}) format.</p>
|
* Variant#VT_CF VT_CF}) format.</p>
|
||||||
*/
|
*/
|
||||||
private byte[] thumbnailData = null;
|
private byte[] _thumbnailData = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +153,7 @@ public class Thumbnail
|
|||||||
*/
|
*/
|
||||||
public Thumbnail(final byte[] thumbnailData)
|
public Thumbnail(final byte[] thumbnailData)
|
||||||
{
|
{
|
||||||
this.thumbnailData = thumbnailData;
|
this._thumbnailData = thumbnailData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -170,7 +167,7 @@ public class Thumbnail
|
|||||||
*/
|
*/
|
||||||
public byte[] getThumbnail()
|
public byte[] getThumbnail()
|
||||||
{
|
{
|
||||||
return thumbnailData;
|
return _thumbnailData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -184,7 +181,7 @@ public class Thumbnail
|
|||||||
*/
|
*/
|
||||||
public void setThumbnail(final byte[] thumbnail)
|
public void setThumbnail(final byte[] thumbnail)
|
||||||
{
|
{
|
||||||
this.thumbnailData = thumbnail;
|
this._thumbnailData = thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -263,21 +260,18 @@ public class Thumbnail
|
|||||||
if (!(getClipboardFormatTag() == CFTAG_WINDOWS))
|
if (!(getClipboardFormatTag() == CFTAG_WINDOWS))
|
||||||
throw new HPSFException("Clipboard Format Tag of Thumbnail must " +
|
throw new HPSFException("Clipboard Format Tag of Thumbnail must " +
|
||||||
"be CFTAG_WINDOWS.");
|
"be CFTAG_WINDOWS.");
|
||||||
if (!(getClipboardFormat() == CF_METAFILEPICT))
|
if (!(getClipboardFormat() == CF_METAFILEPICT)) {
|
||||||
throw new HPSFException("Clipboard Format of Thumbnail must " +
|
throw new HPSFException("Clipboard Format of Thumbnail must " +
|
||||||
"be CF_METAFILEPICT.");
|
"be CF_METAFILEPICT.");
|
||||||
else
|
|
||||||
{
|
|
||||||
byte[] thumbnail = getThumbnail();
|
|
||||||
int wmfImageLength = thumbnail.length - OFFSET_WMFDATA;
|
|
||||||
byte[] wmfImage = new byte[wmfImageLength];
|
|
||||||
System.arraycopy(thumbnail,
|
|
||||||
OFFSET_WMFDATA,
|
|
||||||
wmfImage,
|
|
||||||
0,
|
|
||||||
wmfImageLength);
|
|
||||||
return wmfImage;
|
|
||||||
}
|
}
|
||||||
|
byte[] thumbnail = getThumbnail();
|
||||||
|
int wmfImageLength = thumbnail.length - OFFSET_WMFDATA;
|
||||||
|
byte[] wmfImage = new byte[wmfImageLength];
|
||||||
|
System.arraycopy(thumbnail,
|
||||||
|
OFFSET_WMFDATA,
|
||||||
|
wmfImage,
|
||||||
|
0,
|
||||||
|
wmfImageLength);
|
||||||
|
return wmfImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -27,8 +27,6 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2003-02-20
|
|
||||||
*/
|
*/
|
||||||
public class TypeWriter
|
public class TypeWriter
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,8 +27,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class UnexpectedPropertySetTypeException extends HPSFException
|
public class UnexpectedPropertySetTypeException extends HPSFException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import org.apache.poi.util.HexDump;
|
import org.apache.poi.util.HexDump;
|
||||||
@ -28,8 +28,6 @@ import org.apache.poi.util.HexDump;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2003-08-05
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public abstract class UnsupportedVariantTypeException
|
public abstract class UnsupportedVariantTypeException
|
||||||
extends VariantTypeException
|
extends VariantTypeException
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -27,8 +27,6 @@ import java.util.Date;
|
|||||||
* <p>Provides various static utility methods.</p>
|
* <p>Provides various static utility methods.</p>
|
||||||
*
|
*
|
||||||
* @author Rainer Klute (klute@rainer-klute.de)
|
* @author Rainer Klute (klute@rainer-klute.de)
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class Util
|
public class Util
|
||||||
{
|
{
|
||||||
@ -179,7 +177,7 @@ public class Util
|
|||||||
*
|
*
|
||||||
* @param date The date to be converted
|
* @param date The date to be converted
|
||||||
* @return The filetime
|
* @return The filetime
|
||||||
*
|
*
|
||||||
* @see #filetimeToDate(long)
|
* @see #filetimeToDate(long)
|
||||||
* @see #filetimeToDate(int, int)
|
* @see #filetimeToDate(int, int)
|
||||||
*/
|
*/
|
||||||
@ -213,10 +211,10 @@ public class Util
|
|||||||
* @return <code>true</code> if the collections are equal, else
|
* @return <code>true</code> if the collections are equal, else
|
||||||
* <code>false</code>.
|
* <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public static boolean equals(final Collection c1, final Collection c2)
|
public static boolean equals(Collection<?> c1, Collection<?> c2)
|
||||||
{
|
{
|
||||||
final Object[] o1 = c1.toArray();
|
Object[] o1 = c1.toArray();
|
||||||
final Object[] o2 = c2.toArray();
|
Object[] o2 = c2.toArray();
|
||||||
return internalEquals(o1, o2);
|
return internalEquals(o1, o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,14 +229,14 @@ public class Util
|
|||||||
* @return <code>true</code> if the object arrays are equal,
|
* @return <code>true</code> if the object arrays are equal,
|
||||||
* <code>false</code> if they are not.
|
* <code>false</code> if they are not.
|
||||||
*/
|
*/
|
||||||
public static boolean equals(final Object[] c1, final Object[] c2)
|
public static boolean equals(Object[] c1, Object[] c2)
|
||||||
{
|
{
|
||||||
final Object[] o1 = (Object[]) c1.clone();
|
final Object[] o1 = c1.clone();
|
||||||
final Object[] o2 = (Object[]) c2.clone();
|
final Object[] o2 = c2.clone();
|
||||||
return internalEquals(o1, o2);
|
return internalEquals(o1, o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean internalEquals(final Object[] o1, final Object[] o2)
|
private static boolean internalEquals(Object[] o1, Object[] o2)
|
||||||
{
|
{
|
||||||
for (int i1 = 0; i1 < o1.length; i1++)
|
for (int i1 = 0; i1 < o1.length; i1++)
|
||||||
{
|
{
|
||||||
@ -328,9 +326,9 @@ public class Util
|
|||||||
/**
|
/**
|
||||||
* <p>Returns a textual representation of a {@link Throwable}, including a
|
* <p>Returns a textual representation of a {@link Throwable}, including a
|
||||||
* stacktrace.</p>
|
* stacktrace.</p>
|
||||||
*
|
*
|
||||||
* @param t The {@link Throwable}
|
* @param t The {@link Throwable}
|
||||||
*
|
*
|
||||||
* @return a string containing the output of a call to
|
* @return a string containing the output of a call to
|
||||||
* <code>t.printStacktrace()</code>.
|
* <code>t.printStacktrace()</code>.
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -34,8 +34,6 @@ import java.util.Map;
|
|||||||
* <strong>[S]</strong> - may appear in a Safe Array.</p>
|
* <strong>[S]</strong> - may appear in a Safe Array.</p>
|
||||||
*
|
*
|
||||||
* @author Rainer Klute (klute@rainer-klute.de)
|
* @author Rainer Klute (klute@rainer-klute.de)
|
||||||
* @version $Id$
|
|
||||||
* @since 2002-02-09
|
|
||||||
*/
|
*/
|
||||||
public class Variant
|
public class Variant
|
||||||
{
|
{
|
||||||
@ -353,32 +351,32 @@ public class Variant
|
|||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a length that is unknown to HPSF yet.</p>
|
* <p>Denotes a variant type with a length that is unknown to HPSF yet.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_UNKNOWN = new Integer(-2);
|
public static final Integer LENGTH_UNKNOWN = Integer.valueOf(-2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a variable length.</p>
|
* <p>Denotes a variant type with a variable length.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_VARIABLE = new Integer(-1);
|
public static final Integer LENGTH_VARIABLE = Integer.valueOf(-1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a length of 0 bytes.</p>
|
* <p>Denotes a variant type with a length of 0 bytes.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_0 = new Integer(0);
|
public static final Integer LENGTH_0 = Integer.valueOf(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a length of 2 bytes.</p>
|
* <p>Denotes a variant type with a length of 2 bytes.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_2 = new Integer(2);
|
public static final Integer LENGTH_2 = Integer.valueOf(2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a length of 4 bytes.</p>
|
* <p>Denotes a variant type with a length of 4 bytes.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_4 = new Integer(4);
|
public static final Integer LENGTH_4 = Integer.valueOf(4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Denotes a variant type with a length of 8 bytes.</p>
|
* <p>Denotes a variant type with a length of 8 bytes.</p>
|
||||||
*/
|
*/
|
||||||
public static final Integer LENGTH_8 = new Integer(8);
|
public static final Integer LENGTH_8 = Integer.valueOf(8);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -386,92 +384,92 @@ public class Variant
|
|||||||
{
|
{
|
||||||
/* Initialize the number-to-name map: */
|
/* Initialize the number-to-name map: */
|
||||||
Map tm1 = new HashMap();
|
Map tm1 = new HashMap();
|
||||||
tm1.put(new Long(0), "VT_EMPTY");
|
tm1.put(Long.valueOf(0), "VT_EMPTY");
|
||||||
tm1.put(new Long(1), "VT_NULL");
|
tm1.put(Long.valueOf(1), "VT_NULL");
|
||||||
tm1.put(new Long(2), "VT_I2");
|
tm1.put(Long.valueOf(2), "VT_I2");
|
||||||
tm1.put(new Long(3), "VT_I4");
|
tm1.put(Long.valueOf(3), "VT_I4");
|
||||||
tm1.put(new Long(4), "VT_R4");
|
tm1.put(Long.valueOf(4), "VT_R4");
|
||||||
tm1.put(new Long(5), "VT_R8");
|
tm1.put(Long.valueOf(5), "VT_R8");
|
||||||
tm1.put(new Long(6), "VT_CY");
|
tm1.put(Long.valueOf(6), "VT_CY");
|
||||||
tm1.put(new Long(7), "VT_DATE");
|
tm1.put(Long.valueOf(7), "VT_DATE");
|
||||||
tm1.put(new Long(8), "VT_BSTR");
|
tm1.put(Long.valueOf(8), "VT_BSTR");
|
||||||
tm1.put(new Long(9), "VT_DISPATCH");
|
tm1.put(Long.valueOf(9), "VT_DISPATCH");
|
||||||
tm1.put(new Long(10), "VT_ERROR");
|
tm1.put(Long.valueOf(10), "VT_ERROR");
|
||||||
tm1.put(new Long(11), "VT_BOOL");
|
tm1.put(Long.valueOf(11), "VT_BOOL");
|
||||||
tm1.put(new Long(12), "VT_VARIANT");
|
tm1.put(Long.valueOf(12), "VT_VARIANT");
|
||||||
tm1.put(new Long(13), "VT_UNKNOWN");
|
tm1.put(Long.valueOf(13), "VT_UNKNOWN");
|
||||||
tm1.put(new Long(14), "VT_DECIMAL");
|
tm1.put(Long.valueOf(14), "VT_DECIMAL");
|
||||||
tm1.put(new Long(16), "VT_I1");
|
tm1.put(Long.valueOf(16), "VT_I1");
|
||||||
tm1.put(new Long(17), "VT_UI1");
|
tm1.put(Long.valueOf(17), "VT_UI1");
|
||||||
tm1.put(new Long(18), "VT_UI2");
|
tm1.put(Long.valueOf(18), "VT_UI2");
|
||||||
tm1.put(new Long(19), "VT_UI4");
|
tm1.put(Long.valueOf(19), "VT_UI4");
|
||||||
tm1.put(new Long(20), "VT_I8");
|
tm1.put(Long.valueOf(20), "VT_I8");
|
||||||
tm1.put(new Long(21), "VT_UI8");
|
tm1.put(Long.valueOf(21), "VT_UI8");
|
||||||
tm1.put(new Long(22), "VT_INT");
|
tm1.put(Long.valueOf(22), "VT_INT");
|
||||||
tm1.put(new Long(23), "VT_UINT");
|
tm1.put(Long.valueOf(23), "VT_UINT");
|
||||||
tm1.put(new Long(24), "VT_VOID");
|
tm1.put(Long.valueOf(24), "VT_VOID");
|
||||||
tm1.put(new Long(25), "VT_HRESULT");
|
tm1.put(Long.valueOf(25), "VT_HRESULT");
|
||||||
tm1.put(new Long(26), "VT_PTR");
|
tm1.put(Long.valueOf(26), "VT_PTR");
|
||||||
tm1.put(new Long(27), "VT_SAFEARRAY");
|
tm1.put(Long.valueOf(27), "VT_SAFEARRAY");
|
||||||
tm1.put(new Long(28), "VT_CARRAY");
|
tm1.put(Long.valueOf(28), "VT_CARRAY");
|
||||||
tm1.put(new Long(29), "VT_USERDEFINED");
|
tm1.put(Long.valueOf(29), "VT_USERDEFINED");
|
||||||
tm1.put(new Long(30), "VT_LPSTR");
|
tm1.put(Long.valueOf(30), "VT_LPSTR");
|
||||||
tm1.put(new Long(31), "VT_LPWSTR");
|
tm1.put(Long.valueOf(31), "VT_LPWSTR");
|
||||||
tm1.put(new Long(64), "VT_FILETIME");
|
tm1.put(Long.valueOf(64), "VT_FILETIME");
|
||||||
tm1.put(new Long(65), "VT_BLOB");
|
tm1.put(Long.valueOf(65), "VT_BLOB");
|
||||||
tm1.put(new Long(66), "VT_STREAM");
|
tm1.put(Long.valueOf(66), "VT_STREAM");
|
||||||
tm1.put(new Long(67), "VT_STORAGE");
|
tm1.put(Long.valueOf(67), "VT_STORAGE");
|
||||||
tm1.put(new Long(68), "VT_STREAMED_OBJECT");
|
tm1.put(Long.valueOf(68), "VT_STREAMED_OBJECT");
|
||||||
tm1.put(new Long(69), "VT_STORED_OBJECT");
|
tm1.put(Long.valueOf(69), "VT_STORED_OBJECT");
|
||||||
tm1.put(new Long(70), "VT_BLOB_OBJECT");
|
tm1.put(Long.valueOf(70), "VT_BLOB_OBJECT");
|
||||||
tm1.put(new Long(71), "VT_CF");
|
tm1.put(Long.valueOf(71), "VT_CF");
|
||||||
tm1.put(new Long(72), "VT_CLSID");
|
tm1.put(Long.valueOf(72), "VT_CLSID");
|
||||||
Map tm2 = new HashMap(tm1.size(), 1.0F);
|
Map tm2 = new HashMap(tm1.size(), 1.0F);
|
||||||
tm2.putAll(tm1);
|
tm2.putAll(tm1);
|
||||||
numberToName = Collections.unmodifiableMap(tm2);
|
numberToName = Collections.unmodifiableMap(tm2);
|
||||||
|
|
||||||
/* Initialize the number-to-length map: */
|
/* Initialize the number-to-length map: */
|
||||||
tm1.clear();
|
tm1.clear();
|
||||||
tm1.put(new Long(0), LENGTH_0);
|
tm1.put(Long.valueOf(0), LENGTH_0);
|
||||||
tm1.put(new Long(1), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(1), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(2), LENGTH_2);
|
tm1.put(Long.valueOf(2), LENGTH_2);
|
||||||
tm1.put(new Long(3), LENGTH_4);
|
tm1.put(Long.valueOf(3), LENGTH_4);
|
||||||
tm1.put(new Long(4), LENGTH_4);
|
tm1.put(Long.valueOf(4), LENGTH_4);
|
||||||
tm1.put(new Long(5), LENGTH_8);
|
tm1.put(Long.valueOf(5), LENGTH_8);
|
||||||
tm1.put(new Long(6), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(6), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(7), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(7), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(8), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(8), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(9), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(9), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(10), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(10), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(11), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(11), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(12), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(12), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(13), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(13), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(14), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(14), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(16), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(16), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(17), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(17), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(18), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(18), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(19), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(19), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(20), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(20), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(21), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(21), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(22), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(22), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(23), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(23), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(24), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(24), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(25), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(25), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(26), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(26), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(27), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(27), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(28), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(28), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(29), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(29), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(30), LENGTH_VARIABLE);
|
tm1.put(Long.valueOf(30), LENGTH_VARIABLE);
|
||||||
tm1.put(new Long(31), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(31), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(64), LENGTH_8);
|
tm1.put(Long.valueOf(64), LENGTH_8);
|
||||||
tm1.put(new Long(65), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(65), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(66), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(66), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(67), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(67), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(68), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(68), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(69), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(69), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(70), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(70), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(71), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(71), LENGTH_UNKNOWN);
|
||||||
tm1.put(new Long(72), LENGTH_UNKNOWN);
|
tm1.put(Long.valueOf(72), LENGTH_UNKNOWN);
|
||||||
tm2 = new HashMap(tm1.size(), 1.0F);
|
tm2 = new HashMap(tm1.size(), 1.0F);
|
||||||
tm2.putAll(tm1);
|
tm2.putAll(tm1);
|
||||||
numberToLength = Collections.unmodifiableMap(tm2);
|
numberToLength = Collections.unmodifiableMap(tm2);
|
||||||
@ -488,7 +486,7 @@ public class Variant
|
|||||||
*/
|
*/
|
||||||
public static String getVariantName(final long variantType)
|
public static String getVariantName(final long variantType)
|
||||||
{
|
{
|
||||||
final String name = (String) numberToName.get(new Long(variantType));
|
final String name = (String) numberToName.get(Long.valueOf(variantType));
|
||||||
return name != null ? name : "unknown variant type";
|
return name != null ? name : "unknown variant type";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +501,7 @@ public class Variant
|
|||||||
*/
|
*/
|
||||||
public static int getVariantLength(final long variantType)
|
public static int getVariantLength(final long variantType)
|
||||||
{
|
{
|
||||||
final Long key = new Long((int) variantType);
|
final Long key = Long.valueOf((int) variantType);
|
||||||
final Long length = (Long) numberToLength.get(key);
|
final Long length = (Long) numberToLength.get(key);
|
||||||
if (length == null)
|
if (length == null)
|
||||||
return -2;
|
return -2;
|
||||||
|
@ -29,10 +29,10 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Supports reading and writing of variant data.</p>
|
* <p>Supports reading and writing of variant data.</p>
|
||||||
*
|
*
|
||||||
* <p><strong>FIXME (3):</strong> Reading and writing should be made more
|
* <p><strong>FIXME (3):</strong> Reading and writing should be made more
|
||||||
* uniform than it is now. The following items should be resolved:
|
* uniform than it is now. The following items should be resolved:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li><p>Reading requires a length parameter that is 4 byte greater than the
|
* <li><p>Reading requires a length parameter that is 4 byte greater than the
|
||||||
@ -45,8 +45,6 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2003-08-08
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class VariantSupport extends Variant
|
public class VariantSupport extends Variant
|
||||||
{
|
{
|
||||||
@ -70,7 +68,7 @@ public class VariantSupport extends Variant
|
|||||||
* on or off.</p>
|
* on or off.</p>
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if logging is turned on, else
|
* @return <code>true</code> if logging is turned on, else
|
||||||
* <code>false</code>.
|
* <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public static boolean isLogUnsupportedTypes()
|
public static boolean isLogUnsupportedTypes()
|
||||||
{
|
{
|
||||||
@ -99,7 +97,7 @@ public class VariantSupport extends Variant
|
|||||||
{
|
{
|
||||||
if (unsupportedMessage == null)
|
if (unsupportedMessage == null)
|
||||||
unsupportedMessage = new LinkedList();
|
unsupportedMessage = new LinkedList();
|
||||||
Long vt = new Long(ex.getVariantType());
|
Long vt = Long.valueOf(ex.getVariantType());
|
||||||
if (!unsupportedMessage.contains(vt))
|
if (!unsupportedMessage.contains(vt))
|
||||||
{
|
{
|
||||||
System.err.println(ex.getMessage());
|
System.err.println(ex.getMessage());
|
||||||
@ -123,7 +121,7 @@ public class VariantSupport extends Variant
|
|||||||
* <p>Checks whether HPSF supports the specified variant type. Unsupported
|
* <p>Checks whether HPSF supports the specified variant type. Unsupported
|
||||||
* types should be implemented included in the {@link #SUPPORTED_TYPES}
|
* types should be implemented included in the {@link #SUPPORTED_TYPES}
|
||||||
* array.</p>
|
* array.</p>
|
||||||
*
|
*
|
||||||
* @see Variant
|
* @see Variant
|
||||||
* @param variantType the variant type to check
|
* @param variantType the variant type to check
|
||||||
* @return <code>true</code> if HPFS supports this type, else
|
* @return <code>true</code> if HPFS supports this type, else
|
||||||
@ -141,7 +139,7 @@ public class VariantSupport extends Variant
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Reads a variant type from a byte array.</p>
|
* <p>Reads a variant type from a byte array.</p>
|
||||||
*
|
*
|
||||||
* @param src The byte array
|
* @param src The byte array
|
||||||
* @param offset The offset in the byte array where the variant starts
|
* @param offset The offset in the byte array where the variant starts
|
||||||
* @param length The length of the variant including the variant type field
|
* @param length The length of the variant including the variant type field
|
||||||
@ -184,7 +182,7 @@ public class VariantSupport extends Variant
|
|||||||
* Read a short. In Java it is represented as an
|
* Read a short. In Java it is represented as an
|
||||||
* Integer object.
|
* Integer object.
|
||||||
*/
|
*/
|
||||||
value = new Integer(LittleEndian.getShort(src, o1));
|
value = Integer.valueOf(LittleEndian.getShort(src, o1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Variant.VT_I4:
|
case Variant.VT_I4:
|
||||||
@ -193,7 +191,7 @@ public class VariantSupport extends Variant
|
|||||||
* Read a word. In Java it is represented as an
|
* Read a word. In Java it is represented as an
|
||||||
* Integer object.
|
* Integer object.
|
||||||
*/
|
*/
|
||||||
value = new Integer(LittleEndian.getInt(src, o1));
|
value = Integer.valueOf(LittleEndian.getInt(src, o1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Variant.VT_I8:
|
case Variant.VT_I8:
|
||||||
@ -202,7 +200,7 @@ public class VariantSupport extends Variant
|
|||||||
* Read a double word. In Java it is represented as a
|
* Read a double word. In Java it is represented as a
|
||||||
* Long object.
|
* Long object.
|
||||||
*/
|
*/
|
||||||
value = new Long(LittleEndian.getLong(src, o1));
|
value = Long.valueOf(LittleEndian.getLong(src, o1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Variant.VT_R8:
|
case Variant.VT_R8:
|
||||||
@ -274,9 +272,20 @@ public class VariantSupport extends Variant
|
|||||||
}
|
}
|
||||||
case Variant.VT_CF:
|
case Variant.VT_CF:
|
||||||
{
|
{
|
||||||
|
if(l1 < 0) {
|
||||||
|
/**
|
||||||
|
* YK: reading the ClipboardData packet (VT_CF) is not quite correct.
|
||||||
|
* The size of the data is determined by the first four bytes of the packet
|
||||||
|
* while the current implementation calculates it in the Section constructor.
|
||||||
|
* Test files in Bugzilla 42726 and 45583 clearly show that this approach does not always work.
|
||||||
|
* The workaround below attempts to gracefully handle such cases instead of throwing exceptions.
|
||||||
|
*
|
||||||
|
* August 20, 2009
|
||||||
|
*/
|
||||||
|
l1 = LittleEndian.getInt(src, o1); o1 += LittleEndian.INT_SIZE;
|
||||||
|
}
|
||||||
final byte[] v = new byte[l1];
|
final byte[] v = new byte[l1];
|
||||||
for (int i = 0; i < l1; i++)
|
System.arraycopy(src, o1, v, 0, v.length);
|
||||||
v[i] = src[(o1 + i)];
|
|
||||||
value = v;
|
value = v;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -309,16 +318,16 @@ public class VariantSupport extends Variant
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Turns a codepage number into the equivalent character encoding's
|
* <p>Turns a codepage number into the equivalent character encoding's
|
||||||
* name.</p>
|
* name.</p>
|
||||||
*
|
*
|
||||||
* @param codepage The codepage number
|
* @param codepage The codepage number
|
||||||
*
|
*
|
||||||
* @return The character encoding's name. If the codepage number is 65001,
|
* @return The character encoding's name. If the codepage number is 65001,
|
||||||
* the encoding name is "UTF-8". All other positive numbers are mapped to
|
* the encoding name is "UTF-8". All other positive numbers are mapped to
|
||||||
* "cp" followed by the number, e.g. if the codepage number is 1252 the
|
* "cp" followed by the number, e.g. if the codepage number is 1252 the
|
||||||
* returned character encoding name will be "cp1252".
|
* returned character encoding name will be "cp1252".
|
||||||
*
|
*
|
||||||
* @exception UnsupportedEncodingException if the specified codepage is
|
* @exception UnsupportedEncodingException if the specified codepage is
|
||||||
* less than zero.
|
* less than zero.
|
||||||
*/
|
*/
|
||||||
@ -491,7 +500,7 @@ public class VariantSupport extends Variant
|
|||||||
}
|
}
|
||||||
case Variant.VT_LPWSTR:
|
case Variant.VT_LPWSTR:
|
||||||
{
|
{
|
||||||
final int nrOfChars = ((String) value).length() + 1;
|
final int nrOfChars = ((String) value).length() + 1;
|
||||||
length += TypeWriter.writeUIntToStream(out, nrOfChars);
|
length += TypeWriter.writeUIntToStream(out, nrOfChars);
|
||||||
char[] s = Util.pad4((String) value);
|
char[] s = Util.pad4((String) value);
|
||||||
for (int i = 0; i < s.length; i++)
|
for (int i = 0; i < s.length; i++)
|
||||||
@ -511,7 +520,7 @@ public class VariantSupport extends Variant
|
|||||||
}
|
}
|
||||||
case Variant.VT_CF:
|
case Variant.VT_CF:
|
||||||
{
|
{
|
||||||
final byte[] b = (byte[]) value;
|
final byte[] b = (byte[]) value;
|
||||||
out.write(b);
|
out.write(b);
|
||||||
length = b.length;
|
length = b.length;
|
||||||
break;
|
break;
|
||||||
@ -537,7 +546,7 @@ public class VariantSupport extends Variant
|
|||||||
+ value.getClass().toString() + ", "
|
+ value.getClass().toString() + ", "
|
||||||
+ value.toString());
|
+ value.toString());
|
||||||
}
|
}
|
||||||
length += TypeWriter.writeToStream(out,
|
length += TypeWriter.writeToStream(out,
|
||||||
((Integer) value).intValue());
|
((Integer) value).intValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -549,7 +558,7 @@ public class VariantSupport extends Variant
|
|||||||
}
|
}
|
||||||
case Variant.VT_R8:
|
case Variant.VT_R8:
|
||||||
{
|
{
|
||||||
length += TypeWriter.writeToStream(out,
|
length += TypeWriter.writeToStream(out,
|
||||||
((Double) value).doubleValue());
|
((Double) value).doubleValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -570,7 +579,7 @@ public class VariantSupport extends Variant
|
|||||||
* is a byte array we can write it nevertheless. */
|
* is a byte array we can write it nevertheless. */
|
||||||
if (value instanceof byte[])
|
if (value instanceof byte[])
|
||||||
{
|
{
|
||||||
final byte[] b = (byte[]) value;
|
final byte[] b = (byte[]) value;
|
||||||
out.write(b);
|
out.write(b);
|
||||||
length = b.length;
|
length = b.length;
|
||||||
writeUnsupportedTypeMessage
|
writeUnsupportedTypeMessage
|
||||||
@ -584,5 +593,4 @@ public class VariantSupport extends Variant
|
|||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,8 +23,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2004-06-21
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public abstract class VariantTypeException extends HPSFException
|
public abstract class VariantTypeException extends HPSFException
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf;
|
package org.apache.poi.hpsf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,8 +26,6 @@ package org.apache.poi.hpsf;
|
|||||||
*
|
*
|
||||||
* @author Rainer Klute <a
|
* @author Rainer Klute <a
|
||||||
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
* href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
|
||||||
* @since 2003-08-08
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class WritingNotSupportedException
|
public class WritingNotSupportedException
|
||||||
extends UnsupportedVariantTypeException
|
extends UnsupportedVariantTypeException
|
||||||
|
@ -14,8 +14,11 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.hpsf.extractor;
|
package org.apache.poi.hpsf.extractor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -28,12 +31,13 @@ import org.apache.poi.hpsf.Property;
|
|||||||
import org.apache.poi.hpsf.SpecialPropertySet;
|
import org.apache.poi.hpsf.SpecialPropertySet;
|
||||||
import org.apache.poi.hpsf.SummaryInformation;
|
import org.apache.poi.hpsf.SummaryInformation;
|
||||||
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
import org.apache.poi.hpsf.wellknown.PropertyIDMap;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts all of the HPSF properties, both
|
* Extracts all of the HPSF properties, both
|
||||||
* build in and custom, returning them in
|
* build in and custom, returning them in
|
||||||
* textual form.
|
* textual form.
|
||||||
*/
|
*/
|
||||||
public class HPSFPropertiesExtractor extends POITextExtractor {
|
public class HPSFPropertiesExtractor extends POITextExtractor {
|
||||||
@ -46,54 +50,59 @@ public class HPSFPropertiesExtractor extends POITextExtractor {
|
|||||||
public HPSFPropertiesExtractor(POIFSFileSystem fs) {
|
public HPSFPropertiesExtractor(POIFSFileSystem fs) {
|
||||||
super(new PropertiesOnlyDocument(fs));
|
super(new PropertiesOnlyDocument(fs));
|
||||||
}
|
}
|
||||||
|
public HPSFPropertiesExtractor(NPOIFSFileSystem fs) {
|
||||||
|
super(new PropertiesOnlyDocument(fs));
|
||||||
|
}
|
||||||
|
|
||||||
public String getDocumentSummaryInformationText() {
|
public String getDocumentSummaryInformationText() {
|
||||||
DocumentSummaryInformation dsi = document.getDocumentSummaryInformation();
|
DocumentSummaryInformation dsi = document.getDocumentSummaryInformation();
|
||||||
StringBuffer text = new StringBuffer();
|
StringBuffer text = new StringBuffer();
|
||||||
|
|
||||||
// Normal properties
|
// Normal properties
|
||||||
text.append( getPropertiesText(dsi) );
|
text.append( getPropertiesText(dsi) );
|
||||||
|
|
||||||
// Now custom ones
|
// Now custom ones
|
||||||
CustomProperties cps = dsi.getCustomProperties();
|
CustomProperties cps = dsi == null ? null : dsi.getCustomProperties();
|
||||||
Iterator keys = cps.keySet().iterator();
|
if(cps != null) {
|
||||||
while(keys.hasNext()) {
|
Iterator<String> keys = cps.nameSet().iterator();
|
||||||
String key = (String)keys.next();
|
while(keys.hasNext()) {
|
||||||
String val = getPropertyValueText( cps.get(key) );
|
String key = keys.next();
|
||||||
text.append(key + " = " + val + "\n");
|
String val = getPropertyValueText( cps.get(key) );
|
||||||
|
text.append(key + " = " + val + "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All done
|
// All done
|
||||||
return text.toString();
|
return text.toString();
|
||||||
}
|
}
|
||||||
public String getSummaryInformationText() {
|
public String getSummaryInformationText() {
|
||||||
SummaryInformation si = document.getSummaryInformation();
|
SummaryInformation si = document.getSummaryInformation();
|
||||||
|
|
||||||
// Just normal properties
|
// Just normal properties
|
||||||
return getPropertiesText(si);
|
return getPropertiesText(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getPropertiesText(SpecialPropertySet ps) {
|
private static String getPropertiesText(SpecialPropertySet ps) {
|
||||||
if(ps == null) {
|
if(ps == null) {
|
||||||
// Not defined, oh well
|
// Not defined, oh well
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer text = new StringBuffer();
|
StringBuffer text = new StringBuffer();
|
||||||
|
|
||||||
PropertyIDMap idMap = ps.getPropertySetIDMap();
|
PropertyIDMap idMap = ps.getPropertySetIDMap();
|
||||||
Property[] props = ps.getProperties();
|
Property[] props = ps.getProperties();
|
||||||
for(int i=0; i<props.length; i++) {
|
for(int i=0; i<props.length; i++) {
|
||||||
String type = Long.toString( props[i].getID() );
|
String type = Long.toString( props[i].getID() );
|
||||||
Object typeObj = idMap.get(props[i].getID());
|
Object typeObj = idMap.get(props[i].getID());
|
||||||
if(typeObj != null) {
|
if(typeObj != null) {
|
||||||
type = typeObj.toString();
|
type = typeObj.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String val = getPropertyValueText( props[i].getValue() );
|
String val = getPropertyValueText( props[i].getValue() );
|
||||||
text.append(type + " = " + val + "\n");
|
text.append(type + " = " + val + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return text.toString();
|
return text.toString();
|
||||||
}
|
}
|
||||||
private static String getPropertyValueText(Object val) {
|
private static String getPropertyValueText(Object val) {
|
||||||
@ -121,13 +130,13 @@ public class HPSFPropertiesExtractor extends POITextExtractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the text of all the properties defined in
|
* @return the text of all the properties defined in
|
||||||
* the document.
|
* the document.
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return getSummaryInformationText() + getDocumentSummaryInformationText();
|
return getSummaryInformationText() + getDocumentSummaryInformationText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent recursion!
|
* Prevent recursion!
|
||||||
*/
|
*/
|
||||||
@ -136,16 +145,28 @@ public class HPSFPropertiesExtractor extends POITextExtractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* So we can get at the properties of any
|
* So we can get at the properties of any
|
||||||
* random OLE2 document.
|
* random OLE2 document.
|
||||||
*/
|
*/
|
||||||
private static class PropertiesOnlyDocument extends POIDocument {
|
private static final class PropertiesOnlyDocument extends POIDocument {
|
||||||
private PropertiesOnlyDocument(POIFSFileSystem fs) {
|
public PropertiesOnlyDocument(NPOIFSFileSystem fs) {
|
||||||
|
super(fs.getRoot());
|
||||||
|
}
|
||||||
|
public PropertiesOnlyDocument(POIFSFileSystem fs) {
|
||||||
super(fs);
|
super(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(OutputStream out) throws IOException {
|
public void write(OutputStream out) {
|
||||||
throw new IllegalStateException("Unable to write, only for properties!");
|
throw new IllegalStateException("Unable to write, only for properties!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
for(String file : args) {
|
||||||
|
HPSFPropertiesExtractor ext = new HPSFPropertiesExtractor(
|
||||||
|
new NPOIFSFileSystem(new File(file))
|
||||||
|
);
|
||||||
|
System.out.println(ext.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ public final class StyleRecord extends Record {
|
|||||||
|
|
||||||
byte[] string = in.readRemainder();
|
byte[] string = in.readRemainder();
|
||||||
if (fHighByte.isSet(field_3_string_options)) {
|
if (fHighByte.isSet(field_3_string_options)) {
|
||||||
field_4_name= StringUtil.getFromUnicodeBE(string, 0, field_2_name_length);
|
field_4_name= StringUtil.getFromUnicodeLE(string, 0, field_2_name_length);
|
||||||
} else {
|
} else {
|
||||||
field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length);
|
field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length);
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,25 @@ public class HSSFWorkbook extends POIDocument
|
|||||||
public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes)
|
public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
super(directory, fs);
|
this(directory, preserveNodes);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* given a POI POIFSFileSystem object, and a specific directory
|
||||||
|
* within it, read in its Workbook and populate the high and
|
||||||
|
* low level models. If you're reading in a workbook...start here.
|
||||||
|
*
|
||||||
|
* @param directory the POI filesystem directory to process from
|
||||||
|
* @param preserveNodes whether to preseve other nodes, such as
|
||||||
|
* macros. This takes more memory, so only say yes if you
|
||||||
|
* need to. If set, will store all of the POIFSFileSystem
|
||||||
|
* in memory
|
||||||
|
* @see org.apache.poi.poifs.filesystem.POIFSFileSystem
|
||||||
|
* @exception IOException if the stream cannot be read
|
||||||
|
*/
|
||||||
|
public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super(directory);
|
||||||
String workbookName = getWorkbookDirEntryName(directory);
|
String workbookName = getWorkbookDirEntryName(directory);
|
||||||
|
|
||||||
this.preserveNodes = preserveNodes;
|
this.preserveNodes = preserveNodes;
|
||||||
@ -257,7 +275,6 @@ public class HSSFWorkbook extends POIDocument
|
|||||||
// If we're not preserving nodes, don't track the
|
// If we're not preserving nodes, don't track the
|
||||||
// POIFS any more
|
// POIFS any more
|
||||||
if(! preserveNodes) {
|
if(! preserveNodes) {
|
||||||
this.filesystem = null;
|
|
||||||
this.directory = null;
|
this.directory = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1163,7 +1180,7 @@ public class HSSFWorkbook extends POIDocument
|
|||||||
excepts.add("WORKBOOK");
|
excepts.add("WORKBOOK");
|
||||||
|
|
||||||
// Copy over all the other nodes to our new poifs
|
// Copy over all the other nodes to our new poifs
|
||||||
copyNodes(this.filesystem,fs,excepts);
|
copyNodes(this.directory.getFileSystem(),fs,excepts);
|
||||||
}
|
}
|
||||||
fs.writeFilesystem(stream);
|
fs.writeFilesystem(stream);
|
||||||
//poifs.writeFilesystem(stream);
|
//poifs.writeFilesystem(stream);
|
||||||
@ -1664,7 +1681,7 @@ public class HSSFWorkbook extends POIDocument
|
|||||||
Object sub = subRecordIter.next();
|
Object sub = subRecordIter.next();
|
||||||
if (sub instanceof EmbeddedObjectRefSubRecord)
|
if (sub instanceof EmbeddedObjectRefSubRecord)
|
||||||
{
|
{
|
||||||
objects.add(new HSSFObjectData((ObjRecord) obj, filesystem));
|
objects.add(new HSSFObjectData((ObjRecord) obj, this.directory.getFileSystem()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
src/java/org/apache/poi/poifs/common/POIFSBigBlockSize.java
Normal file
64
src/java/org/apache/poi/poifs/common/POIFSBigBlockSize.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.common;
|
||||||
|
|
||||||
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A class describing attributes of the Big Block Size</p>
|
||||||
|
*/
|
||||||
|
public final class POIFSBigBlockSize
|
||||||
|
{
|
||||||
|
private int bigBlockSize;
|
||||||
|
private short headerValue;
|
||||||
|
|
||||||
|
protected POIFSBigBlockSize(int bigBlockSize, short headerValue) {
|
||||||
|
this.bigBlockSize = bigBlockSize;
|
||||||
|
this.headerValue = headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBigBlockSize() {
|
||||||
|
return bigBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value that gets written into the
|
||||||
|
* header.
|
||||||
|
* Is the power of two that corresponds to the
|
||||||
|
* size of the block, eg 512 => 9
|
||||||
|
*/
|
||||||
|
public short getHeaderValue() {
|
||||||
|
return headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPropertiesPerBlock() {
|
||||||
|
return bigBlockSize / POIFSConstants.PROPERTY_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBATEntriesPerBlock() {
|
||||||
|
return bigBlockSize / LittleEndianConsts.INT_SIZE;
|
||||||
|
}
|
||||||
|
public int getXBATEntriesPerBlock() {
|
||||||
|
return getBATEntriesPerBlock() - 1;
|
||||||
|
}
|
||||||
|
public int getNextXBATChainOffset() {
|
||||||
|
return getXBATEntriesPerBlock() * LittleEndianConsts.INT_SIZE;
|
||||||
|
}
|
||||||
|
}
|
@ -21,21 +21,48 @@ package org.apache.poi.poifs.common;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A repository for constants shared by POI classes.</p>
|
* <p>A repository for constants shared by POI classes.</p>
|
||||||
*
|
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface POIFSConstants
|
public interface POIFSConstants
|
||||||
{
|
{
|
||||||
/** Most files use 512 bytes as their big block size */
|
/** Most files use 512 bytes as their big block size */
|
||||||
public static final int BIG_BLOCK_SIZE = 0x0200;
|
public static final int SMALLER_BIG_BLOCK_SIZE = 0x0200;
|
||||||
|
public static final POIFSBigBlockSize SMALLER_BIG_BLOCK_SIZE_DETAILS =
|
||||||
|
new POIFSBigBlockSize(SMALLER_BIG_BLOCK_SIZE, (short)9);
|
||||||
/** Some use 4096 bytes */
|
/** Some use 4096 bytes */
|
||||||
public static final int LARGER_BIG_BLOCK_SIZE = 0x1000;
|
public static final int LARGER_BIG_BLOCK_SIZE = 0x1000;
|
||||||
|
public static final POIFSBigBlockSize LARGER_BIG_BLOCK_SIZE_DETAILS =
|
||||||
|
new POIFSBigBlockSize(LARGER_BIG_BLOCK_SIZE, (short)12);
|
||||||
|
|
||||||
public static final int END_OF_CHAIN = -2;
|
/** How big a block in the small block stream is. Fixed size */
|
||||||
|
public static final int SMALL_BLOCK_SIZE = 0x0040;
|
||||||
|
|
||||||
|
/** How big a single property is */
|
||||||
public static final int PROPERTY_SIZE = 0x0080;
|
public static final int PROPERTY_SIZE = 0x0080;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum size of a document before it's stored using
|
||||||
|
* Big Blocks (normal streams). Smaller documents go in the
|
||||||
|
* Mini Stream (SBAT / Small Blocks)
|
||||||
|
*/
|
||||||
|
public static final int BIG_BLOCK_MINIMUM_DOCUMENT_SIZE = 0x1000;
|
||||||
|
|
||||||
|
/** The highest sector number you're allowed, 0xFFFFFFFA */
|
||||||
|
public static final int LARGEST_REGULAR_SECTOR_NUMBER = -5;
|
||||||
|
|
||||||
|
/** Indicates the sector holds a DIFAT block (0xFFFFFFFC) */
|
||||||
|
public static final int DIFAT_SECTOR_BLOCK = -4;
|
||||||
|
/** Indicates the sector holds a FAT block (0xFFFFFFFD) */
|
||||||
|
public static final int FAT_SECTOR_BLOCK = -3;
|
||||||
|
/** Indicates the sector is the end of a chain (0xFFFFFFFE) */
|
||||||
|
public static final int END_OF_CHAIN = -2;
|
||||||
|
/** Indicates the sector is not used (0xFFFFFFFF) */
|
||||||
public static final int UNUSED_BLOCK = -1;
|
public static final int UNUSED_BLOCK = -1;
|
||||||
|
|
||||||
|
/** The first 4 bytes of an OOXML file, used in detection */
|
||||||
public static final byte[] OOXML_FILE_HEADER =
|
public static final byte[] OOXML_FILE_HEADER =
|
||||||
new byte[] { 0x50, 0x4b, 0x03, 0x04 };
|
new byte[] { 0x50, 0x4b, 0x03, 0x04 };
|
||||||
|
|
||||||
|
/** HACKY: For backwards compatibility on 3.2 */
|
||||||
|
public static final int BIG_BLOCK_SIZE = SMALLER_BIG_BLOCK_SIZE;
|
||||||
|
|
||||||
} // end public interface POIFSConstants;
|
} // end public interface POIFSConstants;
|
||||||
|
@ -1,74 +1,74 @@
|
|||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
this work for additional information regarding copyright ownership.
|
this work for additional information regarding copyright ownership.
|
||||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
(the "License"); you may not use this file except in compliance with
|
(the "License"); you may not use this file except in compliance with
|
||||||
the License. You may obtain a copy of the License at
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
package org.apache.poi.poifs.dev;
|
package org.apache.poi.poifs.dev;
|
||||||
|
|
||||||
import org.apache.poi.poifs.filesystem.*;
|
import org.apache.poi.poifs.filesystem.*;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Dump internal structure of a OLE2 file into file system
|
* Dump internal structure of a OLE2 file into file system
|
||||||
*
|
*
|
||||||
* @author Yegor Kozlov
|
* @author Yegor Kozlov
|
||||||
*/
|
*/
|
||||||
public class POIFSDump {
|
public class POIFSDump {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
System.out.println("Dumping " + args[i]);
|
System.out.println("Dumping " + args[i]);
|
||||||
FileInputStream is = new FileInputStream(args[i]);
|
FileInputStream is = new FileInputStream(args[i]);
|
||||||
POIFSFileSystem fs = new POIFSFileSystem(is);
|
POIFSFileSystem fs = new POIFSFileSystem(is);
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
DirectoryEntry root = fs.getRoot();
|
DirectoryEntry root = fs.getRoot();
|
||||||
File file = new File(root.getName());
|
File file = new File(root.getName());
|
||||||
file.mkdir();
|
file.mkdir();
|
||||||
|
|
||||||
dump(root, file);
|
dump(root, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void dump(DirectoryEntry root, File parent) throws IOException {
|
public static void dump(DirectoryEntry root, File parent) throws IOException {
|
||||||
for(Iterator it = root.getEntries(); it.hasNext();){
|
for(Iterator it = root.getEntries(); it.hasNext();){
|
||||||
Entry entry = (Entry)it.next();
|
Entry entry = (Entry)it.next();
|
||||||
if(entry instanceof DocumentNode){
|
if(entry instanceof DocumentNode){
|
||||||
DocumentNode node = (DocumentNode)entry;
|
DocumentNode node = (DocumentNode)entry;
|
||||||
DocumentInputStream is = new DocumentInputStream(node);
|
DocumentInputStream is = new DocumentInputStream(node);
|
||||||
byte[] bytes = new byte[node.getSize()];
|
byte[] bytes = new byte[node.getSize()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
FileOutputStream out = new FileOutputStream(new File(parent, node.getName().trim()));
|
FileOutputStream out = new FileOutputStream(new File(parent, node.getName().trim()));
|
||||||
out.write(bytes);
|
out.write(bytes);
|
||||||
out.close();
|
out.close();
|
||||||
} else if (entry instanceof DirectoryEntry){
|
} else if (entry instanceof DirectoryEntry){
|
||||||
DirectoryEntry dir = (DirectoryEntry)entry;
|
DirectoryEntry dir = (DirectoryEntry)entry;
|
||||||
File file = new File(parent, entry.getName());
|
File file = new File(parent, entry.getName());
|
||||||
file.mkdir();
|
file.mkdir();
|
||||||
dump(dir, file);
|
dump(dir, file);
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Skipping unsupported POIFS entry: " + entry);
|
System.err.println("Skipping unsupported POIFS entry: " + entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
152
src/java/org/apache/poi/poifs/dev/POIFSHeaderDumper.java
Normal file
152
src/java/org/apache/poi/poifs/dev/POIFSHeaderDumper.java
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.dev;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.property.PropertyTable;
|
||||||
|
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
|
||||||
|
import org.apache.poi.poifs.storage.BlockList;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
import org.apache.poi.poifs.storage.ListManagedBlock;
|
||||||
|
import org.apache.poi.poifs.storage.RawDataBlockList;
|
||||||
|
import org.apache.poi.poifs.storage.SmallBlockTableReader;
|
||||||
|
import org.apache.poi.util.HexDump;
|
||||||
|
import org.apache.poi.util.IntList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A very low level debugging tool, for printing out core
|
||||||
|
* information on the headers and FAT blocks.
|
||||||
|
* You probably only want to use this if you're trying
|
||||||
|
* to understand POIFS, or if you're trying to track
|
||||||
|
* down the source of corruption in a file.
|
||||||
|
*/
|
||||||
|
public class POIFSHeaderDumper {
|
||||||
|
/**
|
||||||
|
* Display the entries of multiple POIFS files
|
||||||
|
*
|
||||||
|
* @param args the names of the files to be displayed
|
||||||
|
*/
|
||||||
|
public static void main(final String args[]) throws Exception {
|
||||||
|
if (args.length == 0) {
|
||||||
|
System.err.println("Must specify at least one file to view");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < args.length; j++) {
|
||||||
|
viewFile(args[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void viewFile(final String filename) throws Exception {
|
||||||
|
InputStream inp = new FileInputStream(filename);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
HeaderBlock header_block = new HeaderBlock(inp);
|
||||||
|
displayHeader(header_block);
|
||||||
|
|
||||||
|
// Raw blocks
|
||||||
|
POIFSBigBlockSize bigBlockSize = header_block.getBigBlockSize();
|
||||||
|
RawDataBlockList data_blocks = new RawDataBlockList(inp, bigBlockSize);
|
||||||
|
displayRawBlocksSummary(data_blocks);
|
||||||
|
|
||||||
|
// Main FAT Table
|
||||||
|
BlockAllocationTableReader batReader =
|
||||||
|
new BlockAllocationTableReader(
|
||||||
|
header_block.getBigBlockSize(),
|
||||||
|
header_block.getBATCount(),
|
||||||
|
header_block.getBATArray(),
|
||||||
|
header_block.getXBATCount(),
|
||||||
|
header_block.getXBATIndex(),
|
||||||
|
data_blocks);
|
||||||
|
displayBATReader(batReader);
|
||||||
|
|
||||||
|
// Properties Table
|
||||||
|
PropertyTable properties =
|
||||||
|
new PropertyTable(header_block, data_blocks);
|
||||||
|
|
||||||
|
// Mini Fat
|
||||||
|
BlockList sbat =
|
||||||
|
SmallBlockTableReader.getSmallDocumentBlocks(
|
||||||
|
bigBlockSize, data_blocks, properties.getRoot(),
|
||||||
|
header_block.getSBATStart()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void displayHeader(HeaderBlock header_block) throws Exception {
|
||||||
|
System.out.println("Header Details:");
|
||||||
|
System.out.println(" Block size: " + header_block.getBigBlockSize().getBigBlockSize());
|
||||||
|
System.out.println(" BAT (FAT) header blocks: " + header_block.getBATArray().length);
|
||||||
|
System.out.println(" BAT (FAT) block count: " + header_block.getBATCount());
|
||||||
|
System.out.println(" XBAT (FAT) block count: " + header_block.getXBATCount());
|
||||||
|
System.out.println(" XBAT (FAT) block 1 at: " + header_block.getXBATIndex());
|
||||||
|
System.out.println(" SBAT (MiniFAT) block count: " + header_block.getSBATCount());
|
||||||
|
System.out.println(" SBAT (MiniFAT) block 1 at: " + header_block.getSBATStart());
|
||||||
|
System.out.println(" Property table at: " + header_block.getPropertyStart());
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void displayRawBlocksSummary(RawDataBlockList data_blocks) throws Exception {
|
||||||
|
System.out.println("Raw Blocks Details:");
|
||||||
|
System.out.println(" Number of blocks: " + data_blocks.blockCount());
|
||||||
|
|
||||||
|
Method gbm = data_blocks.getClass().getSuperclass().getDeclaredMethod("get", int.class);
|
||||||
|
gbm.setAccessible(true);
|
||||||
|
|
||||||
|
for(int i=0; i<Math.min(16, data_blocks.blockCount()); i++) {
|
||||||
|
ListManagedBlock block = (ListManagedBlock)gbm.invoke(data_blocks, Integer.valueOf(i));
|
||||||
|
byte[] data = new byte[Math.min(48, block.getData().length)];
|
||||||
|
System.arraycopy(block.getData(), 0, data, 0, data.length);
|
||||||
|
|
||||||
|
System.out.println(" Block #" + i + ":");
|
||||||
|
System.out.println(HexDump.dump(data, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void displayBATReader(BlockAllocationTableReader batReader) throws Exception {
|
||||||
|
System.out.println("Sectors, as referenced from the FAT:");
|
||||||
|
Field entriesF = batReader.getClass().getDeclaredField("_entries");
|
||||||
|
entriesF.setAccessible(true);
|
||||||
|
IntList entries = (IntList)entriesF.get(batReader);
|
||||||
|
|
||||||
|
for(int i=0; i<entries.size(); i++) {
|
||||||
|
int bn = entries.get(i);
|
||||||
|
String bnS = Integer.toString(bn);
|
||||||
|
if(bn == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
bnS = "End Of Chain";
|
||||||
|
} else if(bn == POIFSConstants.DIFAT_SECTOR_BLOCK) {
|
||||||
|
bnS = "DI Fat Block";
|
||||||
|
} else if(bn == POIFSConstants.FAT_SECTOR_BLOCK) {
|
||||||
|
bnS = "Normal Fat Block";
|
||||||
|
} else if(bn == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
bnS = "Block Not Used (Free)";
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(" Block # " + i + " -> " + bnS);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
}
|
@ -17,82 +17,88 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.dev;
|
package org.apache.poi.poifs.dev;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryEntry;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
||||||
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
|
||||||
import org.apache.poi.poifs.filesystem.DocumentNode;
|
import org.apache.poi.poifs.filesystem.DocumentNode;
|
||||||
|
import org.apache.poi.poifs.filesystem.Entry;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lister of the entries in POIFS files.
|
* A lister of the entries in POIFS files.
|
||||||
*
|
*
|
||||||
* Much simpler than {@link POIFSViewer}
|
* Much simpler than {@link POIFSViewer}
|
||||||
*/
|
*/
|
||||||
public class POIFSLister {
|
public class POIFSLister {
|
||||||
/**
|
/**
|
||||||
* Display the entries of multiple POIFS files
|
* Display the entries of multiple POIFS files
|
||||||
*
|
*
|
||||||
* @param args the names of the files to be displayed
|
* @param args the names of the files to be displayed
|
||||||
*/
|
*/
|
||||||
public static void main(final String args[]) throws IOException {
|
public static void main(final String args[]) throws IOException {
|
||||||
if (args.length == 0)
|
if (args.length == 0) {
|
||||||
{
|
System.err.println("Must specify at least one file to view");
|
||||||
System.err.println("Must specify at least one file to view");
|
System.exit(1);
|
||||||
System.exit(1);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
boolean withSizes = false;
|
boolean withSizes = false;
|
||||||
for (int j = 0; j < args.length; j++) {
|
boolean newPOIFS = true;
|
||||||
if(args[j].equalsIgnoreCase("-size") ||
|
for (int j = 0; j < args.length; j++) {
|
||||||
args[j].equalsIgnoreCase("-sizes")) {
|
if (args[j].equalsIgnoreCase("-size") || args[j].equalsIgnoreCase("-sizes")) {
|
||||||
withSizes = true;
|
withSizes = true;
|
||||||
} else {
|
} else if (args[j].equalsIgnoreCase("-old") || args[j].equalsIgnoreCase("-old-poifs")) {
|
||||||
viewFile(args[j], withSizes);
|
newPOIFS = false;
|
||||||
}
|
} else {
|
||||||
}
|
if(newPOIFS) {
|
||||||
}
|
viewFile(args[j], withSizes);
|
||||||
|
} else {
|
||||||
|
viewFileOld(args[j], withSizes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void viewFile(final String filename, boolean withSizes) throws IOException
|
public static void viewFile(final String filename, boolean withSizes) throws IOException {
|
||||||
{
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File(filename));
|
||||||
POIFSFileSystem fs = new POIFSFileSystem(
|
displayDirectory(fs.getRoot(), "", withSizes);
|
||||||
new FileInputStream(filename)
|
}
|
||||||
);
|
|
||||||
displayDirectory(fs.getRoot(), "", withSizes);
|
public static void viewFileOld(final String filename, boolean withSizes) throws IOException {
|
||||||
}
|
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(filename));
|
||||||
|
displayDirectory(fs.getRoot(), "", withSizes);
|
||||||
public static void displayDirectory(DirectoryNode dir, String indent, boolean withSizes) {
|
}
|
||||||
System.out.println(indent + dir.getName() + " -");
|
|
||||||
String newIndent = indent + " ";
|
public static void displayDirectory(DirectoryNode dir, String indent, boolean withSizes) {
|
||||||
|
System.out.println(indent + dir.getName() + " -");
|
||||||
boolean hadChildren = false;
|
String newIndent = indent + " ";
|
||||||
for(Iterator it = dir.getEntries(); it.hasNext(); ) {
|
|
||||||
hadChildren = true;
|
boolean hadChildren = false;
|
||||||
Object entry = it.next();
|
for(Iterator<Entry> it = dir.getEntries(); it.hasNext();) {
|
||||||
if(entry instanceof DirectoryNode) {
|
hadChildren = true;
|
||||||
displayDirectory((DirectoryNode)entry, newIndent, withSizes);
|
Entry entry = it.next();
|
||||||
} else {
|
if (entry instanceof DirectoryNode) {
|
||||||
DocumentNode doc = (DocumentNode)entry;
|
displayDirectory((DirectoryNode) entry, newIndent, withSizes);
|
||||||
String name = doc.getName();
|
} else {
|
||||||
String size = "";
|
DocumentNode doc = (DocumentNode) entry;
|
||||||
if(name.charAt(0) < 10) {
|
String name = doc.getName();
|
||||||
String altname = "(0x0" + (int)name.charAt(0) + ")" + name.substring(1);
|
String size = "";
|
||||||
name = name.substring(1) + " <" + altname + ">";
|
if (name.charAt(0) < 10) {
|
||||||
}
|
String altname = "(0x0" + (int) name.charAt(0) + ")" + name.substring(1);
|
||||||
if(withSizes) {
|
name = name.substring(1) + " <" + altname + ">";
|
||||||
size = " [" +
|
}
|
||||||
doc.getSize() + " / 0x" +
|
if (withSizes) {
|
||||||
Integer.toHexString(doc.getSize()) +
|
size = " [" + doc.getSize() + " / 0x" +
|
||||||
"]";
|
Integer.toHexString(doc.getSize()) + "]";
|
||||||
}
|
}
|
||||||
System.out.println(newIndent + name + size);
|
System.out.println(newIndent + name + size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!hadChildren) {
|
if (!hadChildren) {
|
||||||
System.out.println(newIndent + "(no children)");
|
System.out.println(newIndent + "(no children)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ public interface POIFSViewable
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public Object [] getViewableArray();
|
public Object [] getViewableArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an Iterator of objects, some of which may implement
|
* Get an Iterator of objects, some of which may implement
|
||||||
* POIFSViewable
|
* POIFSViewable
|
||||||
@ -54,7 +54,7 @@ public interface POIFSViewable
|
|||||||
* @return an Iterator; may not be null, but may have an empty
|
* @return an Iterator; may not be null, but may have an empty
|
||||||
* back end store
|
* back end store
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Iterator getViewableIterator();
|
public Iterator getViewableIterator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ import org.apache.poi.poifs.property.Property;
|
|||||||
import org.apache.poi.poifs.property.PropertyTable;
|
import org.apache.poi.poifs.property.PropertyTable;
|
||||||
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
|
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
|
||||||
import org.apache.poi.poifs.storage.BlockList;
|
import org.apache.poi.poifs.storage.BlockList;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlockReader;
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
import org.apache.poi.poifs.storage.RawDataBlockList;
|
import org.apache.poi.poifs.storage.RawDataBlockList;
|
||||||
import org.apache.poi.poifs.storage.SmallBlockTableReader;
|
import org.apache.poi.poifs.storage.SmallBlockTableReader;
|
||||||
|
|
||||||
@ -75,29 +75,31 @@ public class POIFSReader
|
|||||||
registryClosed = true;
|
registryClosed = true;
|
||||||
|
|
||||||
// read the header block from the stream
|
// read the header block from the stream
|
||||||
HeaderBlockReader header_block_reader = new HeaderBlockReader(stream);
|
HeaderBlock header_block = new HeaderBlock(stream);
|
||||||
|
|
||||||
// read the rest of the stream into blocks
|
// read the rest of the stream into blocks
|
||||||
RawDataBlockList data_blocks = new RawDataBlockList(stream, header_block_reader.getBigBlockSize());
|
RawDataBlockList data_blocks = new RawDataBlockList(stream, header_block.getBigBlockSize());
|
||||||
|
|
||||||
// set up the block allocation table (necessary for the
|
// set up the block allocation table (necessary for the
|
||||||
// data_blocks to be manageable
|
// data_blocks to be manageable
|
||||||
new BlockAllocationTableReader(header_block_reader.getBATCount(),
|
new BlockAllocationTableReader(header_block.getBigBlockSize(),
|
||||||
header_block_reader.getBATArray(),
|
header_block.getBATCount(),
|
||||||
header_block_reader.getXBATCount(),
|
header_block.getBATArray(),
|
||||||
header_block_reader.getXBATIndex(),
|
header_block.getXBATCount(),
|
||||||
|
header_block.getXBATIndex(),
|
||||||
data_blocks);
|
data_blocks);
|
||||||
|
|
||||||
// get property table from the document
|
// get property table from the document
|
||||||
PropertyTable properties =
|
PropertyTable properties =
|
||||||
new PropertyTable(header_block_reader.getPropertyStart(),
|
new PropertyTable(header_block, data_blocks);
|
||||||
data_blocks);
|
|
||||||
|
|
||||||
// process documents
|
// process documents
|
||||||
processProperties(SmallBlockTableReader
|
processProperties(SmallBlockTableReader
|
||||||
.getSmallDocumentBlocks(data_blocks, properties
|
.getSmallDocumentBlocks(
|
||||||
.getRoot(), header_block_reader
|
header_block.getBigBlockSize(),
|
||||||
.getSBATStart()), data_blocks, properties.getRoot()
|
data_blocks, properties.getRoot(),
|
||||||
|
header_block.getSBATStart()),
|
||||||
|
data_blocks, properties.getRoot()
|
||||||
.getChildren(), new POIFSDocumentPath());
|
.getChildren(), new POIFSDocumentPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,13 +247,13 @@ public class POIFSReader
|
|||||||
{
|
{
|
||||||
document =
|
document =
|
||||||
new POIFSDocument(name, small_blocks
|
new POIFSDocument(name, small_blocks
|
||||||
.fetchBlocks(startBlock), size);
|
.fetchBlocks(startBlock, -1), size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
document =
|
document =
|
||||||
new POIFSDocument(name, big_blocks
|
new POIFSDocument(name, big_blocks
|
||||||
.fetchBlocks(startBlock), size);
|
.fetchBlocks(startBlock, -1), size);
|
||||||
}
|
}
|
||||||
while (listeners.hasNext())
|
while (listeners.hasNext())
|
||||||
{
|
{
|
||||||
@ -270,11 +272,11 @@ public class POIFSReader
|
|||||||
// consume the document's data and discard it
|
// consume the document's data and discard it
|
||||||
if (property.shouldUseSmallBlocks())
|
if (property.shouldUseSmallBlocks())
|
||||||
{
|
{
|
||||||
small_blocks.fetchBlocks(startBlock);
|
small_blocks.fetchBlocks(startBlock, -1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
big_blocks.fetchBlocks(startBlock);
|
big_blocks.fetchBlocks(startBlock, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
105
src/java/org/apache/poi/poifs/filesystem/BlockStore.java
Normal file
105
src/java/org/apache/poi/poifs/filesystem/BlockStore.java
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.storage.BATBlock.BATBlockAndIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This abstract class describes a way to read, store, chain
|
||||||
|
* and free a series of blocks (be they Big or Small ones)
|
||||||
|
*/
|
||||||
|
public abstract class BlockStore {
|
||||||
|
/**
|
||||||
|
* Returns the size of the blocks managed through the block store.
|
||||||
|
*/
|
||||||
|
protected abstract int getBlockStoreBlockSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the block at the given offset.
|
||||||
|
*/
|
||||||
|
protected abstract ByteBuffer getBlockAt(final int offset) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the file if required to hold blocks up to
|
||||||
|
* the specified offset, and return the block from there.
|
||||||
|
*/
|
||||||
|
protected abstract ByteBuffer createBlockIfNeeded(final int offset) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the BATBlock that handles the specified offset,
|
||||||
|
* and the relative index within it
|
||||||
|
*/
|
||||||
|
protected abstract BATBlockAndIndex getBATBlockAndIndex(final int offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Works out what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected abstract int getNextBlock(final int offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the record of what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected abstract void setNextBlock(final int offset, final int nextBlock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a free block, and returns its offset.
|
||||||
|
* This method will extend the file/stream if needed, and if doing
|
||||||
|
* so, allocate new FAT blocks to address the extra space.
|
||||||
|
*/
|
||||||
|
protected abstract int getFreeBlock() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Detector for loops in the chain
|
||||||
|
*/
|
||||||
|
protected abstract ChainLoopDetector getChainLoopDetector() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to detect if a chain has a loop in it, so
|
||||||
|
* we can bail out with an error rather than
|
||||||
|
* spinning away for ever...
|
||||||
|
*/
|
||||||
|
protected class ChainLoopDetector {
|
||||||
|
private boolean[] used_blocks;
|
||||||
|
protected ChainLoopDetector(long rawSize) {
|
||||||
|
int numBlocks = (int)Math.ceil( rawSize / getBlockStoreBlockSize() );
|
||||||
|
used_blocks = new boolean[numBlocks];
|
||||||
|
}
|
||||||
|
protected void claim(int offset) {
|
||||||
|
if(offset >= used_blocks.length) {
|
||||||
|
// They're writing, and have had new blocks requested
|
||||||
|
// for the write to proceed. That means they're into
|
||||||
|
// blocks we've allocated for them, so are safe
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claiming an existing block, ensure there's no loop
|
||||||
|
if(used_blocks[offset]) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Potential loop detected - Block " + offset +
|
||||||
|
" was already claimed but was just requested again"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
used_blocks[offset] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import org.apache.poi.hpsf.ClassID;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public interface DirectoryEntry
|
public interface DirectoryEntry
|
||||||
extends Entry
|
extends Entry, Iterable<Entry>
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +47,7 @@ public interface DirectoryEntry
|
|||||||
* implementations of Entry.
|
* implementations of Entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Iterator getEntries();
|
public Iterator<Entry> getEntries();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is this DirectoryEntry empty?
|
* is this DirectoryEntry empty?
|
||||||
|
@ -19,9 +19,14 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.hpsf.ClassID;
|
import org.apache.poi.hpsf.ClassID;
|
||||||
import org.apache.poi.poifs.dev.POIFSViewable;
|
import org.apache.poi.poifs.dev.POIFSViewable;
|
||||||
@ -34,17 +39,21 @@ import org.apache.poi.poifs.property.Property;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class DirectoryNode
|
public class DirectoryNode
|
||||||
extends EntryNode
|
extends EntryNode
|
||||||
implements DirectoryEntry, POIFSViewable
|
implements DirectoryEntry, POIFSViewable, Iterable<Entry>
|
||||||
{
|
{
|
||||||
|
|
||||||
// Map of Entry instances, keyed by their names
|
// Map of Entry instances, keyed by their names
|
||||||
private Map _entries;
|
private Map<String,Entry> _byname;
|
||||||
|
// Our list of entries, kept sorted to preserve order
|
||||||
|
private ArrayList<Entry> _entries;
|
||||||
|
|
||||||
|
// Only one of these two will exist
|
||||||
// the POIFSFileSystem we belong to
|
// the POIFSFileSystem we belong to
|
||||||
private POIFSFileSystem _filesystem;
|
private POIFSFileSystem _ofilesystem;
|
||||||
|
// the NPOIFSFileSytem we belong to
|
||||||
|
private NPOIFSFileSystem _nfilesystem;
|
||||||
|
|
||||||
// the path described by this document
|
// the path described by this document
|
||||||
private POIFSDocumentPath _path;
|
private POIFSDocumentPath _path;
|
||||||
@ -57,10 +66,32 @@ public class DirectoryNode
|
|||||||
* @param filesystem the POIFSFileSystem we belong to
|
* @param filesystem the POIFSFileSystem we belong to
|
||||||
* @param parent the parent of this entry
|
* @param parent the parent of this entry
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DirectoryNode(final DirectoryProperty property,
|
DirectoryNode(final DirectoryProperty property,
|
||||||
final POIFSFileSystem filesystem,
|
final POIFSFileSystem filesystem,
|
||||||
final DirectoryNode parent)
|
final DirectoryNode parent)
|
||||||
|
{
|
||||||
|
this(property, parent);
|
||||||
|
_ofilesystem = filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a DirectoryNode. This method is not public by design; it
|
||||||
|
* is intended strictly for the internal use of this package
|
||||||
|
*
|
||||||
|
* @param property the DirectoryProperty for this DirectoryEntry
|
||||||
|
* @param nfilesystem the NPOIFSFileSystem we belong to
|
||||||
|
* @param parent the parent of this entry
|
||||||
|
*/
|
||||||
|
DirectoryNode(final DirectoryProperty property,
|
||||||
|
final NPOIFSFileSystem nfilesystem,
|
||||||
|
final DirectoryNode parent)
|
||||||
|
{
|
||||||
|
this(property, parent);
|
||||||
|
_nfilesystem = nfilesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DirectoryNode(final DirectoryProperty property,
|
||||||
|
final DirectoryNode parent)
|
||||||
{
|
{
|
||||||
super(property, parent);
|
super(property, parent);
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
@ -74,26 +105,30 @@ public class DirectoryNode
|
|||||||
property.getName()
|
property.getName()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_filesystem = filesystem;
|
_byname = new HashMap<String, Entry>();
|
||||||
_entries = new HashMap();
|
_entries = new ArrayList<Entry>();
|
||||||
Iterator iter = property.getChildren();
|
Iterator<Property> iter = property.getChildren();
|
||||||
|
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
Property child = ( Property ) iter.next();
|
Property child = iter.next();
|
||||||
Entry childNode = null;
|
Entry childNode = null;
|
||||||
|
|
||||||
if (child.isDirectory())
|
if (child.isDirectory())
|
||||||
{
|
{
|
||||||
childNode = new DirectoryNode(( DirectoryProperty ) child,
|
DirectoryProperty childDir = (DirectoryProperty) child;
|
||||||
_filesystem, this);
|
if(_ofilesystem != null) {
|
||||||
|
childNode = new DirectoryNode(childDir, _ofilesystem, this);
|
||||||
|
} else {
|
||||||
|
childNode = new DirectoryNode(childDir, _nfilesystem, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
childNode = new DocumentNode(( DocumentProperty ) child,
|
childNode = new DocumentNode((DocumentProperty) child, this);
|
||||||
this);
|
|
||||||
}
|
}
|
||||||
_entries.put(childNode.getName(), childNode);
|
_entries.add(childNode);
|
||||||
|
_byname.put(childNode.getName(), childNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +141,22 @@ public class DirectoryNode
|
|||||||
return _path;
|
return _path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the filesystem that this belongs to
|
||||||
|
*/
|
||||||
|
public POIFSFileSystem getFileSystem()
|
||||||
|
{
|
||||||
|
return _ofilesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the filesystem that this belongs to
|
||||||
|
*/
|
||||||
|
public NPOIFSFileSystem getNFileSystem()
|
||||||
|
{
|
||||||
|
return _nfilesystem;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* open a document in the directory's entry's list of entries
|
* open a document in the directory's entry's list of entries
|
||||||
*
|
*
|
||||||
@ -116,19 +167,34 @@ public class DirectoryNode
|
|||||||
* @exception IOException if the document does not exist or the
|
* @exception IOException if the document does not exist or the
|
||||||
* name is that of a DirectoryEntry
|
* name is that of a DirectoryEntry
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public DocumentInputStream createDocumentInputStream(
|
public DocumentInputStream createDocumentInputStream(
|
||||||
final String documentName)
|
final String documentName)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Entry document = getEntry(documentName);
|
return createDocumentInputStream(getEntry(documentName));
|
||||||
|
}
|
||||||
|
|
||||||
if (!document.isDocumentEntry())
|
/**
|
||||||
{
|
* open a document in the directory's entry's list of entries
|
||||||
throw new IOException("Entry '" + documentName
|
*
|
||||||
|
* @param documentEntry the document to be opened
|
||||||
|
*
|
||||||
|
* @return a newly opened DocumentInputStream or NDocumentInputStream
|
||||||
|
*
|
||||||
|
* @exception IOException if the document does not exist or the
|
||||||
|
* name is that of a DirectoryEntry
|
||||||
|
*/
|
||||||
|
public DocumentInputStream createDocumentInputStream(
|
||||||
|
final Entry document)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (!document.isDocumentEntry()) {
|
||||||
|
throw new IOException("Entry '" + document.getName()
|
||||||
+ "' is not a DocumentEntry");
|
+ "' is not a DocumentEntry");
|
||||||
}
|
}
|
||||||
return new DocumentInputStream(( DocumentEntry ) document);
|
|
||||||
|
DocumentEntry entry = (DocumentEntry)document;
|
||||||
|
return new DocumentInputStream(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,7 +206,6 @@ public class DirectoryNode
|
|||||||
*
|
*
|
||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DocumentEntry createDocument(final POIFSDocument document)
|
DocumentEntry createDocument(final POIFSDocument document)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -148,8 +213,33 @@ public class DirectoryNode
|
|||||||
DocumentNode rval = new DocumentNode(property, this);
|
DocumentNode rval = new DocumentNode(property, this);
|
||||||
|
|
||||||
(( DirectoryProperty ) getProperty()).addChild(property);
|
(( DirectoryProperty ) getProperty()).addChild(property);
|
||||||
_filesystem.addDocument(document);
|
_ofilesystem.addDocument(document);
|
||||||
_entries.put(property.getName(), rval);
|
|
||||||
|
_entries.add(rval);
|
||||||
|
_byname.put(property.getName(), rval);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new DocumentEntry
|
||||||
|
*
|
||||||
|
* @param document the new document
|
||||||
|
*
|
||||||
|
* @return the new DocumentEntry
|
||||||
|
*
|
||||||
|
* @exception IOException
|
||||||
|
*/
|
||||||
|
DocumentEntry createDocument(final NPOIFSDocument document)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
DocumentProperty property = document.getDocumentProperty();
|
||||||
|
DocumentNode rval = new DocumentNode(property, this);
|
||||||
|
|
||||||
|
(( DirectoryProperty ) getProperty()).addChild(property);
|
||||||
|
_nfilesystem.addDocument(document);
|
||||||
|
|
||||||
|
_entries.add(rval);
|
||||||
|
_byname.put(property.getName(), rval);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,11 +251,10 @@ public class DirectoryNode
|
|||||||
*
|
*
|
||||||
* @return true if the operation succeeded, else false
|
* @return true if the operation succeeded, else false
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boolean changeName(final String oldName, final String newName)
|
boolean changeName(final String oldName, final String newName)
|
||||||
{
|
{
|
||||||
boolean rval = false;
|
boolean rval = false;
|
||||||
EntryNode child = ( EntryNode ) _entries.get(oldName);
|
EntryNode child = ( EntryNode ) _byname.get(oldName);
|
||||||
|
|
||||||
if (child != null)
|
if (child != null)
|
||||||
{
|
{
|
||||||
@ -173,8 +262,8 @@ public class DirectoryNode
|
|||||||
.changeName(child.getProperty(), newName);
|
.changeName(child.getProperty(), newName);
|
||||||
if (rval)
|
if (rval)
|
||||||
{
|
{
|
||||||
_entries.remove(oldName);
|
_byname.remove(oldName);
|
||||||
_entries.put(child.getProperty().getName(), child);
|
_byname.put(child.getProperty().getName(), child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
@ -196,8 +285,14 @@ public class DirectoryNode
|
|||||||
|
|
||||||
if (rval)
|
if (rval)
|
||||||
{
|
{
|
||||||
_entries.remove(entry.getName());
|
_entries.remove(entry);
|
||||||
_filesystem.remove(entry);
|
_byname.remove(entry.getName());
|
||||||
|
|
||||||
|
if(_ofilesystem != null) {
|
||||||
|
_ofilesystem.remove(entry);
|
||||||
|
} else {
|
||||||
|
_nfilesystem.remove(entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -215,9 +310,9 @@ public class DirectoryNode
|
|||||||
* implementations of Entry.
|
* implementations of Entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Iterator getEntries()
|
public Iterator<Entry> getEntries()
|
||||||
{
|
{
|
||||||
return _entries.values().iterator();
|
return _entries.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,7 +358,7 @@ public class DirectoryNode
|
|||||||
|
|
||||||
if (name != null)
|
if (name != null)
|
||||||
{
|
{
|
||||||
rval = ( Entry ) _entries.get(name);
|
rval = _byname.get(name);
|
||||||
}
|
}
|
||||||
if (rval == null)
|
if (rval == null)
|
||||||
{
|
{
|
||||||
@ -326,13 +421,20 @@ public class DirectoryNode
|
|||||||
public DirectoryEntry createDirectory(final String name)
|
public DirectoryEntry createDirectory(final String name)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
DirectoryNode rval;
|
||||||
DirectoryProperty property = new DirectoryProperty(name);
|
DirectoryProperty property = new DirectoryProperty(name);
|
||||||
DirectoryNode rval = new DirectoryNode(property, _filesystem,
|
|
||||||
this);
|
if(_ofilesystem != null) {
|
||||||
|
rval = new DirectoryNode(property, _ofilesystem, this);
|
||||||
|
_ofilesystem.addDirectory(property);
|
||||||
|
} else {
|
||||||
|
rval = new DirectoryNode(property, _nfilesystem, this);
|
||||||
|
_nfilesystem.addDirectory(property);
|
||||||
|
}
|
||||||
|
|
||||||
(( DirectoryProperty ) getProperty()).addChild(property);
|
(( DirectoryProperty ) getProperty()).addChild(property);
|
||||||
_filesystem.addDirectory(property);
|
_entries.add(rval);
|
||||||
_entries.put(name, rval);
|
_byname.put(name, rval);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,15 +512,13 @@ public class DirectoryNode
|
|||||||
* @return an Iterator; may not be null, but may have an empty
|
* @return an Iterator; may not be null, but may have an empty
|
||||||
* back end store
|
* back end store
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Iterator getViewableIterator()
|
public Iterator getViewableIterator()
|
||||||
{
|
{
|
||||||
List components = new ArrayList();
|
List components = new ArrayList();
|
||||||
|
|
||||||
components.add(getProperty());
|
components.add(getProperty());
|
||||||
SortedMap sortedEntries = new TreeMap(_entries);
|
Iterator<Entry> iter = _entries.iterator();
|
||||||
Iterator iter = sortedEntries.values().iterator();
|
|
||||||
|
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
components.add(iter.next());
|
components.add(iter.next());
|
||||||
@ -451,6 +551,13 @@ public class DirectoryNode
|
|||||||
return getName();
|
return getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator over all the entries
|
||||||
|
*/
|
||||||
|
public Iterator<Entry> iterator() {
|
||||||
|
return getEntries();
|
||||||
|
}
|
||||||
|
|
||||||
/* ********** END begin implementation of POIFSViewable ********** */
|
/* ********** END begin implementation of POIFSViewable ********** */
|
||||||
} // end public class DirectoryNode
|
} // end public class DirectoryNode
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,437 +14,156 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.util.LittleEndianInput;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides methods to read a DocumentEntry managed by a
|
* This class provides methods to read a DocumentEntry managed by a
|
||||||
* Filesystem instance.
|
* {@link POIFSFileSystem} or {@link NPOIFSFileSystem} instance.
|
||||||
*
|
* It creates the appropriate one, and delegates, allowing us to
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* work transparently with the two.
|
||||||
*/
|
*/
|
||||||
|
public class DocumentInputStream extends InputStream implements LittleEndianInput {
|
||||||
|
/** returned by read operations if we're at end of document */
|
||||||
|
protected static final int EOF = -1;
|
||||||
|
|
||||||
public class DocumentInputStream
|
protected static final int SIZE_SHORT = 2;
|
||||||
extends InputStream
|
protected static final int SIZE_INT = 4;
|
||||||
{
|
protected static final int SIZE_LONG = 8;
|
||||||
|
|
||||||
|
private DocumentInputStream delegate;
|
||||||
|
|
||||||
|
/** For use by downstream implementations */
|
||||||
|
protected DocumentInputStream() {}
|
||||||
|
|
||||||
// current offset into the Document
|
/**
|
||||||
private int _current_offset;
|
* Create an InputStream from the specified DocumentEntry
|
||||||
|
*
|
||||||
|
* @param document the DocumentEntry to be read
|
||||||
|
*
|
||||||
|
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
|
||||||
|
* been deleted?)
|
||||||
|
*/
|
||||||
|
public DocumentInputStream(DocumentEntry document) throws IOException {
|
||||||
|
if (!(document instanceof DocumentNode)) {
|
||||||
|
throw new IOException("Cannot open internal document storage");
|
||||||
|
}
|
||||||
|
DocumentNode documentNode = (DocumentNode)document;
|
||||||
|
DirectoryNode parentNode = (DirectoryNode)document.getParent();
|
||||||
|
|
||||||
// current marked offset into the Document (used by mark and
|
if(documentNode.getDocument() != null) {
|
||||||
// reset)
|
delegate = new ODocumentInputStream(document);
|
||||||
private int _marked_offset;
|
} else if(parentNode.getFileSystem() != null) {
|
||||||
|
delegate = new ODocumentInputStream(document);
|
||||||
|
} else if(parentNode.getNFileSystem() != null) {
|
||||||
|
delegate = new NDocumentInputStream(document);
|
||||||
|
} else {
|
||||||
|
throw new IOException("No FileSystem bound on the parent, can't read contents");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the Document's size
|
/**
|
||||||
private int _document_size;
|
* Create an InputStream from the specified Document
|
||||||
|
*
|
||||||
|
* @param document the Document to be read
|
||||||
|
*/
|
||||||
|
public DocumentInputStream(POIFSDocument document) {
|
||||||
|
delegate = new ODocumentInputStream(document);
|
||||||
|
}
|
||||||
|
|
||||||
// have we been closed?
|
/**
|
||||||
private boolean _closed;
|
* Create an InputStream from the specified Document
|
||||||
|
*
|
||||||
|
* @param document the Document to be read
|
||||||
|
*/
|
||||||
|
public DocumentInputStream(NPOIFSDocument document) {
|
||||||
|
delegate = new NDocumentInputStream(document);
|
||||||
|
}
|
||||||
|
|
||||||
// the actual Document
|
public int available() {
|
||||||
private POIFSDocument _document;
|
return delegate.available();
|
||||||
|
}
|
||||||
|
|
||||||
// buffer used to read one byte at a time
|
public void close() {
|
||||||
private byte[] _tiny_buffer;
|
delegate.close();
|
||||||
|
}
|
||||||
|
|
||||||
// returned by read operations if we're at end of document
|
public void mark(int ignoredReadlimit) {
|
||||||
static private final int EOD = -1;
|
delegate.mark(ignoredReadlimit);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an InputStream from the specified DocumentEntry
|
* Tests if this input stream supports the mark and reset methods.
|
||||||
*
|
*
|
||||||
* @param document the DocumentEntry to be read
|
* @return <code>true</code> always
|
||||||
*
|
*/
|
||||||
* @exception IOException if the DocumentEntry cannot be opened
|
public boolean markSupported() {
|
||||||
* (like, maybe it has been deleted?)
|
return true;
|
||||||
*/
|
}
|
||||||
|
|
||||||
public DocumentInputStream(final DocumentEntry document)
|
public int read() throws IOException {
|
||||||
throws IOException
|
return delegate.read();
|
||||||
{
|
}
|
||||||
_current_offset = 0;
|
|
||||||
_marked_offset = 0;
|
|
||||||
_document_size = document.getSize();
|
|
||||||
_closed = false;
|
|
||||||
_tiny_buffer = null;
|
|
||||||
if (document instanceof DocumentNode)
|
|
||||||
{
|
|
||||||
_document = (( DocumentNode ) document).getDocument();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new IOException("Cannot open internal document storage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public int read(byte[] b) throws IOException {
|
||||||
* Create an InputStream from the specified Document
|
return read(b, 0, b.length);
|
||||||
*
|
}
|
||||||
* @param document the Document to be read
|
|
||||||
*
|
|
||||||
* @exception IOException if the DocumentEntry cannot be opened
|
|
||||||
* (like, maybe it has been deleted?)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public DocumentInputStream(final POIFSDocument document)
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
throws IOException
|
return delegate.read(b, off, len);
|
||||||
{
|
}
|
||||||
_current_offset = 0;
|
|
||||||
_marked_offset = 0;
|
|
||||||
_document_size = document.getSize();
|
|
||||||
_closed = false;
|
|
||||||
_tiny_buffer = null;
|
|
||||||
_document = document;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of bytes that can be read (or skipped over)
|
* Repositions this stream to the position at the time the mark() method was
|
||||||
* from this input stream without blocking by the next caller of a
|
* last called on this input stream. If mark() has not been called this
|
||||||
* method for this input stream. The next caller might be the same
|
* method repositions the stream to its beginning.
|
||||||
* thread or or another thread.
|
*/
|
||||||
*
|
public void reset() {
|
||||||
* @return the number of bytes that can be read from this input
|
delegate.reset();
|
||||||
* stream without blocking.
|
}
|
||||||
*
|
|
||||||
* @exception IOException on error (such as the stream has been
|
|
||||||
* closed)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int available()
|
public long skip(long n) throws IOException {
|
||||||
throws IOException
|
return delegate.skip(n);
|
||||||
{
|
}
|
||||||
dieIfClosed();
|
|
||||||
return _document_size - _current_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public byte readByte() {
|
||||||
* Closes this input stream and releases any system resources
|
return delegate.readByte();
|
||||||
* associated with the stream.
|
}
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void close()
|
public double readDouble() {
|
||||||
throws IOException
|
return delegate.readDouble();
|
||||||
{
|
}
|
||||||
_closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public short readShort() {
|
||||||
* Marks the current position in this input stream. A subsequent
|
return (short) readUShort();
|
||||||
* call to the reset method repositions this stream at the last
|
}
|
||||||
* marked position so that subsequent reads re-read the same
|
|
||||||
* bytes.
|
|
||||||
* <p>
|
|
||||||
* The readlimit arguments tells this input stream to allow that
|
|
||||||
* many bytes to be read before the mark position gets
|
|
||||||
* invalidated. This implementation, however, does not care.
|
|
||||||
* <p>
|
|
||||||
* The general contract of mark is that, if the method
|
|
||||||
* markSupported returns true, the stream somehow remembers all
|
|
||||||
* the bytes read after the call to mark and stands ready to
|
|
||||||
* supply those same bytes again if and whenever the method reset
|
|
||||||
* is called. However, the stream is not required to remember any
|
|
||||||
* data at all if more than readlimit bytes are read from the
|
|
||||||
* stream before reset is called. But this stream will.
|
|
||||||
*
|
|
||||||
* @param ignoredReadlimit the maximum limit of bytes that can be
|
|
||||||
* read before the mark position becomes
|
|
||||||
* invalid. Ignored by this
|
|
||||||
* implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void mark(int ignoredReadlimit)
|
public void readFully(byte[] buf) {
|
||||||
{
|
readFully(buf, 0, buf.length);
|
||||||
_marked_offset = _current_offset;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
* Tests if this input stream supports the mark and reset methods.
|
delegate.readFully(buf, off, len);
|
||||||
*
|
}
|
||||||
* @return true
|
|
||||||
*/
|
|
||||||
|
|
||||||
public boolean markSupported()
|
public long readLong() {
|
||||||
{
|
return delegate.readLong();
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public int readInt() {
|
||||||
* Reads the next byte of data from the input stream. The value
|
return delegate.readInt();
|
||||||
* byte is returned as an int in the range 0 to 255. If no byte is
|
}
|
||||||
* available because the end of the stream has been reached, the
|
|
||||||
* value -1 is returned. The definition of this method in
|
|
||||||
* java.io.InputStream allows this method to block, but it won't.
|
|
||||||
*
|
|
||||||
* @return the next byte of data, or -1 if the end of the stream
|
|
||||||
* is reached.
|
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int read()
|
public int readUShort() {
|
||||||
throws IOException
|
return delegate.readUShort();
|
||||||
{
|
}
|
||||||
dieIfClosed();
|
|
||||||
if (atEOD())
|
|
||||||
{
|
|
||||||
return EOD;
|
|
||||||
}
|
|
||||||
if (_tiny_buffer == null)
|
|
||||||
{
|
|
||||||
_tiny_buffer = new byte[ 1 ];
|
|
||||||
}
|
|
||||||
_document.read(_tiny_buffer, _current_offset++);
|
|
||||||
return ((int)_tiny_buffer[ 0 ]) & 0x000000FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads some number of bytes from the input stream and stores
|
|
||||||
* them into the buffer array b. The number of bytes actually read
|
|
||||||
* is returned as an integer. The definition of this method in
|
|
||||||
* java.io.InputStream allows this method to block, but it won't.
|
|
||||||
* <p>
|
|
||||||
* If b is null, a NullPointerException is thrown. If the length
|
|
||||||
* of b is zero, then no bytes are read and 0 is returned;
|
|
||||||
* otherwise, there is an attempt to read at least one byte. If no
|
|
||||||
* byte is available because the stream is at end of file, the
|
|
||||||
* value -1 is returned; otherwise, at least one byte is read and
|
|
||||||
* stored into b.
|
|
||||||
* <p>
|
|
||||||
* The first byte read is stored into element b[0], the next one
|
|
||||||
* into b[1], and so on. The number of bytes read is, at most,
|
|
||||||
* equal to the length of b. Let k be the number of bytes actually
|
|
||||||
* read; these bytes will be stored in elements b[0] through
|
|
||||||
* b[k-1], leaving elements b[k] through b[b.length-1] unaffected.
|
|
||||||
* <p>
|
|
||||||
* If the first byte cannot be read for any reason other than end
|
|
||||||
* of file, then an IOException is thrown. In particular, an
|
|
||||||
* IOException is thrown if the input stream has been closed.
|
|
||||||
* <p>
|
|
||||||
* The read(b) method for class InputStream has the same effect as:
|
|
||||||
* <p>
|
|
||||||
* <code>read(b, 0, b.length)</code>
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
*
|
|
||||||
* @return the total number of bytes read into the buffer, or -1
|
|
||||||
* if there is no more data because the end of the stream
|
|
||||||
* has been reached.
|
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
* @exception NullPointerException
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int read(final byte [] b)
|
|
||||||
throws IOException, NullPointerException
|
|
||||||
{
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads up to len bytes of data from the input stream into an
|
|
||||||
* array of bytes. An attempt is made to read as many as len
|
|
||||||
* bytes, but a smaller number may be read, possibly zero. The
|
|
||||||
* number of bytes actually read is returned as an integer.
|
|
||||||
* <p>
|
|
||||||
* The definition of this method in java.io.InputStream allows it
|
|
||||||
* to block, but it won't.
|
|
||||||
* <p>
|
|
||||||
* If b is null, a NullPointerException is thrown.
|
|
||||||
* <p>
|
|
||||||
* If off is negative, or len is negative, or off+len is greater
|
|
||||||
* than the length of the array b, then an
|
|
||||||
* IndexOutOfBoundsException is thrown.
|
|
||||||
* <p>
|
|
||||||
* If len is zero, then no bytes are read and 0 is returned;
|
|
||||||
* otherwise, there is an attempt to read at least one byte. If no
|
|
||||||
* byte is available because the stream is at end of file, the
|
|
||||||
* value -1 is returned; otherwise, at least one byte is read and
|
|
||||||
* stored into b.
|
|
||||||
* <p>
|
|
||||||
* The first byte read is stored into element b[off], the next one
|
|
||||||
* into b[off+1], and so on. The number of bytes read is, at most,
|
|
||||||
* equal to len. Let k be the number of bytes actually read; these
|
|
||||||
* bytes will be stored in elements b[off] through b[off+k-1],
|
|
||||||
* leaving elements b[off+k] through b[off+len-1] unaffected.
|
|
||||||
* <p>
|
|
||||||
* In every case, elements b[0] through b[off] and elements
|
|
||||||
* b[off+len] through b[b.length-1] are unaffected.
|
|
||||||
* <p>
|
|
||||||
* If the first byte cannot be read for any reason other than end
|
|
||||||
* of file, then an IOException is thrown. In particular, an
|
|
||||||
* IOException is thrown if the input stream has been closed.
|
|
||||||
*
|
|
||||||
* @param b the buffer into which the data is read.
|
|
||||||
* @param off the start offset in array b at which the data is
|
|
||||||
* written.
|
|
||||||
* @param len the maximum number of bytes to read.
|
|
||||||
*
|
|
||||||
* @return the total number of bytes read into the buffer, or -1
|
|
||||||
* if there is no more data because the end of the stream
|
|
||||||
* has been reached.
|
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
* @exception NullPointerException
|
|
||||||
* @exception IndexOutOfBoundsException
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int read(final byte [] b, final int off, final int len)
|
|
||||||
throws IOException, NullPointerException, IndexOutOfBoundsException
|
|
||||||
{
|
|
||||||
dieIfClosed();
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
throw new NullPointerException("buffer is null");
|
|
||||||
}
|
|
||||||
if ((off < 0) || (len < 0) || (b.length < (off + len)))
|
|
||||||
{
|
|
||||||
throw new IndexOutOfBoundsException(
|
|
||||||
"can't read past buffer boundaries");
|
|
||||||
}
|
|
||||||
if (len == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (atEOD())
|
|
||||||
{
|
|
||||||
return EOD;
|
|
||||||
}
|
|
||||||
int limit = Math.min(available(), len);
|
|
||||||
|
|
||||||
if ((off == 0) && (limit == b.length))
|
|
||||||
{
|
|
||||||
_document.read(b, _current_offset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[ limit ];
|
|
||||||
|
|
||||||
_document.read(buffer, _current_offset);
|
|
||||||
System.arraycopy(buffer, 0, b, off, limit);
|
|
||||||
}
|
|
||||||
_current_offset += limit;
|
|
||||||
return limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repositions this stream to the position at the time the mark
|
|
||||||
* method was last called on this input stream.
|
|
||||||
* <p>
|
|
||||||
* The general contract of reset is:
|
|
||||||
* <p>
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* If the method markSupported returns true, then:
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* If the method mark has not been called since the
|
|
||||||
* stream was created, or the number of bytes read
|
|
||||||
* from the stream since mark was last called is
|
|
||||||
* larger than the argument to mark at that last
|
|
||||||
* call, then an IOException might be thrown.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* If such an IOException is not thrown, then the
|
|
||||||
* stream is reset to a state such that all the
|
|
||||||
* bytes read since the most recent call to mark
|
|
||||||
* (or since the start of the file, if mark has not
|
|
||||||
* been called) will be resupplied to subsequent
|
|
||||||
* callers of the read method, followed by any
|
|
||||||
* bytes that otherwise would have been the next
|
|
||||||
* input data as of the time of the call to reset.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* If the method markSupported returns false, then:
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* The call to reset may throw an IOException.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* If an IOException is not thrown, then the
|
|
||||||
* stream is reset to a fixed state that depends
|
|
||||||
* on the particular type of the input and how it
|
|
||||||
* was created. The bytes that will be supplied to
|
|
||||||
* subsequent callers of the read method depend on
|
|
||||||
* the particular type of the input stream.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
* <p>
|
|
||||||
* All well and good ... this class's markSupported method returns
|
|
||||||
* true and this method does not care whether you've called mark
|
|
||||||
* at all, or whether you've exceeded the number of bytes
|
|
||||||
* specified in the last call to mark. We're basically walking a
|
|
||||||
* byte array ... mark and reset to your heart's content.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
_current_offset = _marked_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips over and discards n bytes of data from this input
|
|
||||||
* stream. The skip method may, for a variety of reasons, end up
|
|
||||||
* skipping over some smaller number of bytes, possibly 0. This
|
|
||||||
* may result from any of a number of conditions; reaching end of
|
|
||||||
* file before n bytes have been skipped is only one
|
|
||||||
* possibility. The actual number of bytes skipped is returned. If
|
|
||||||
* n is negative, no bytes are skipped.
|
|
||||||
*
|
|
||||||
* @param n the number of bytes to be skipped.
|
|
||||||
*
|
|
||||||
* @return the actual number of bytes skipped.
|
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
|
||||||
|
|
||||||
public long skip(final long n)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
dieIfClosed();
|
|
||||||
if (n < 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int new_offset = _current_offset + ( int ) n;
|
|
||||||
|
|
||||||
if (new_offset < _current_offset)
|
|
||||||
{
|
|
||||||
|
|
||||||
// wrap around in converting a VERY large long to an int
|
|
||||||
new_offset = _document_size;
|
|
||||||
}
|
|
||||||
else if (new_offset > _document_size)
|
|
||||||
{
|
|
||||||
new_offset = _document_size;
|
|
||||||
}
|
|
||||||
long rval = new_offset - _current_offset;
|
|
||||||
|
|
||||||
_current_offset = new_offset;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dieIfClosed()
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (_closed)
|
|
||||||
{
|
|
||||||
throw new IOException(
|
|
||||||
"cannot perform requested operation on a closed stream");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean atEOD()
|
|
||||||
{
|
|
||||||
return _current_offset == _document_size;
|
|
||||||
}
|
|
||||||
} // end public class DocumentInputStream
|
|
||||||
|
|
||||||
|
public int readUByte() {
|
||||||
|
return delegate.readUByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,7 +14,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
@ -30,12 +28,10 @@ import java.util.*;
|
|||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class DocumentOutputStream
|
public final class DocumentOutputStream extends OutputStream {
|
||||||
extends OutputStream
|
private final OutputStream _stream;
|
||||||
{
|
private final int _limit;
|
||||||
private OutputStream stream;
|
private int _written;
|
||||||
private int limit;
|
|
||||||
private int written;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a DocumentOutputStream
|
* Create a DocumentOutputStream
|
||||||
@ -44,12 +40,10 @@ public class DocumentOutputStream
|
|||||||
* read
|
* read
|
||||||
* @param limit the maximum number of bytes that can be written
|
* @param limit the maximum number of bytes that can be written
|
||||||
*/
|
*/
|
||||||
|
DocumentOutputStream(OutputStream stream, int limit) {
|
||||||
DocumentOutputStream(final OutputStream stream, final int limit)
|
_stream = stream;
|
||||||
{
|
_limit = limit;
|
||||||
this.stream = stream;
|
_written = 0;
|
||||||
this.limit = limit;
|
|
||||||
this.written = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,12 +58,11 @@ public class DocumentOutputStream
|
|||||||
* output stream has been closed, or if the
|
* output stream has been closed, or if the
|
||||||
* writer tries to write too much data.
|
* writer tries to write too much data.
|
||||||
*/
|
*/
|
||||||
|
public void write(int b)
|
||||||
public void write(final int b)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
limitCheck(1);
|
limitCheck(1);
|
||||||
stream.write(b);
|
_stream.write(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,8 +72,7 @@ public class DocumentOutputStream
|
|||||||
* @param b the data.
|
* @param b the data.
|
||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
|
public void write(byte b[])
|
||||||
public void write(final byte b[])
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
write(b, 0, b.length);
|
write(b, 0, b.length);
|
||||||
@ -106,12 +98,11 @@ public class DocumentOutputStream
|
|||||||
* output stream is closed or if the writer
|
* output stream is closed or if the writer
|
||||||
* tries to write too many bytes.
|
* tries to write too many bytes.
|
||||||
*/
|
*/
|
||||||
|
public void write(byte b[], int off, int len)
|
||||||
public void write(final byte b[], final int off, final int len)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
limitCheck(len);
|
limitCheck(len);
|
||||||
stream.write(b, off, len);
|
_stream.write(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,11 +111,10 @@ public class DocumentOutputStream
|
|||||||
*
|
*
|
||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void flush()
|
public void flush()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
stream.flush();
|
_stream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,10 +125,7 @@ public class DocumentOutputStream
|
|||||||
*
|
*
|
||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
|
public void close() {
|
||||||
public void close()
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
|
|
||||||
// ignore this call
|
// ignore this call
|
||||||
}
|
}
|
||||||
@ -152,27 +139,25 @@ public class DocumentOutputStream
|
|||||||
*
|
*
|
||||||
* @exception IOException on I/O error
|
* @exception IOException on I/O error
|
||||||
*/
|
*/
|
||||||
|
void writeFiller(int totalLimit, byte fill)
|
||||||
void writeFiller(final int totalLimit, final byte fill)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if (totalLimit > written)
|
if (totalLimit > _written)
|
||||||
{
|
{
|
||||||
byte[] filler = new byte[ totalLimit - written ];
|
byte[] filler = new byte[ totalLimit - _written ];
|
||||||
|
|
||||||
Arrays.fill(filler, fill);
|
Arrays.fill(filler, fill);
|
||||||
stream.write(filler);
|
_stream.write(filler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void limitCheck(final int toBeWritten)
|
private void limitCheck(int toBeWritten)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if ((written + toBeWritten) > limit)
|
if ((_written + toBeWritten) > _limit)
|
||||||
{
|
{
|
||||||
throw new IOException("tried to write too much data");
|
throw new IOException("tried to write too much data");
|
||||||
}
|
}
|
||||||
written += toBeWritten;
|
_written += toBeWritten;
|
||||||
}
|
}
|
||||||
} // end public class DocumentOutputStream
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,303 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.property.DocumentProperty;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides methods to read a DocumentEntry managed by a
|
||||||
|
* {@link NPOIFSFileSystem} instance.
|
||||||
|
*/
|
||||||
|
public final class NDocumentInputStream extends DocumentInputStream {
|
||||||
|
/** current offset into the Document */
|
||||||
|
private int _current_offset;
|
||||||
|
/** current block count */
|
||||||
|
private int _current_block_count;
|
||||||
|
|
||||||
|
/** current marked offset into the Document (used by mark and reset) */
|
||||||
|
private int _marked_offset;
|
||||||
|
/** and the block count for it */
|
||||||
|
private int _marked_offset_count;
|
||||||
|
|
||||||
|
/** the Document's size */
|
||||||
|
private int _document_size;
|
||||||
|
|
||||||
|
/** have we been closed? */
|
||||||
|
private boolean _closed;
|
||||||
|
|
||||||
|
/** the actual Document */
|
||||||
|
private NPOIFSDocument _document;
|
||||||
|
|
||||||
|
private Iterator<ByteBuffer> _data;
|
||||||
|
private ByteBuffer _buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an InputStream from the specified DocumentEntry
|
||||||
|
*
|
||||||
|
* @param document the DocumentEntry to be read
|
||||||
|
*
|
||||||
|
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
|
||||||
|
* been deleted?)
|
||||||
|
*/
|
||||||
|
public NDocumentInputStream(DocumentEntry document) throws IOException {
|
||||||
|
if (!(document instanceof DocumentNode)) {
|
||||||
|
throw new IOException("Cannot open internal document storage");
|
||||||
|
}
|
||||||
|
_current_offset = 0;
|
||||||
|
_current_block_count = 0;
|
||||||
|
_marked_offset = 0;
|
||||||
|
_marked_offset_count = 0;
|
||||||
|
_document_size = document.getSize();
|
||||||
|
_closed = false;
|
||||||
|
|
||||||
|
DocumentNode doc = (DocumentNode)document;
|
||||||
|
DocumentProperty property = (DocumentProperty)doc.getProperty();
|
||||||
|
_document = new NPOIFSDocument(
|
||||||
|
property,
|
||||||
|
((DirectoryNode)doc.getParent()).getNFileSystem()
|
||||||
|
);
|
||||||
|
_data = _document.getBlockIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an InputStream from the specified Document
|
||||||
|
*
|
||||||
|
* @param document the Document to be read
|
||||||
|
*/
|
||||||
|
public NDocumentInputStream(NPOIFSDocument document) {
|
||||||
|
_current_offset = 0;
|
||||||
|
_current_block_count = 0;
|
||||||
|
_marked_offset = 0;
|
||||||
|
_marked_offset_count = 0;
|
||||||
|
_document_size = document.getSize();
|
||||||
|
_closed = false;
|
||||||
|
_document = document;
|
||||||
|
_data = _document.getBlockIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
return _document_size - _current_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
_closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark(int ignoredReadlimit) {
|
||||||
|
_marked_offset = _current_offset;
|
||||||
|
_marked_offset_count = _current_block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (atEOD()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
byte[] b = new byte[1];
|
||||||
|
int result = read(b, 0, 1);
|
||||||
|
if(result >= 0) {
|
||||||
|
if(b[0] < 0) {
|
||||||
|
return b[0]+256;
|
||||||
|
}
|
||||||
|
return b[0];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (b == null) {
|
||||||
|
throw new IllegalArgumentException("buffer must not be null");
|
||||||
|
}
|
||||||
|
if (off < 0 || len < 0 || b.length < off + len) {
|
||||||
|
throw new IndexOutOfBoundsException("can't read past buffer boundaries");
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (atEOD()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
int limit = Math.min(available(), len);
|
||||||
|
readFully(b, off, limit);
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repositions this stream to the position at the time the mark() method was
|
||||||
|
* last called on this input stream. If mark() has not been called this
|
||||||
|
* method repositions the stream to its beginning.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
// Special case for reset to the start
|
||||||
|
if(_marked_offset == 0 && _marked_offset_count == 0) {
|
||||||
|
_current_block_count = _marked_offset_count;
|
||||||
|
_current_offset = _marked_offset;
|
||||||
|
_data = _document.getBlockIterator();
|
||||||
|
_buffer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start again, then wind on to the required block
|
||||||
|
_data = _document.getBlockIterator();
|
||||||
|
_current_offset = 0;
|
||||||
|
for(int i=0; i<_marked_offset_count; i++) {
|
||||||
|
_buffer = _data.next();
|
||||||
|
_current_offset += _buffer.remaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
_current_block_count = _marked_offset_count;
|
||||||
|
|
||||||
|
// Do we need to position within it?
|
||||||
|
if(_current_offset != _marked_offset) {
|
||||||
|
// Grab the right block
|
||||||
|
_buffer = _data.next();
|
||||||
|
_current_block_count++;
|
||||||
|
|
||||||
|
// Skip to the right place in it
|
||||||
|
_buffer.position(_marked_offset - _current_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All done
|
||||||
|
_current_offset = _marked_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (n < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int new_offset = _current_offset + (int) n;
|
||||||
|
|
||||||
|
if (new_offset < _current_offset) {
|
||||||
|
// wrap around in converting a VERY large long to an int
|
||||||
|
new_offset = _document_size;
|
||||||
|
} else if (new_offset > _document_size) {
|
||||||
|
new_offset = _document_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
long rval = new_offset - _current_offset;
|
||||||
|
|
||||||
|
// TODO Do this better
|
||||||
|
byte[] skip = new byte[(int)rval];
|
||||||
|
readFully(skip);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dieIfClosed() throws IOException {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IOException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean atEOD() {
|
||||||
|
return _current_offset == _document_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAvaliable(int requestedSize) {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
if (requestedSize > _document_size - _current_offset) {
|
||||||
|
throw new RuntimeException("Buffer underrun - requested " + requestedSize
|
||||||
|
+ " bytes but " + (_document_size - _current_offset) + " was available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
|
checkAvaliable(len);
|
||||||
|
|
||||||
|
int read = 0;
|
||||||
|
while(read < len) {
|
||||||
|
if(_buffer == null || _buffer.remaining() == 0) {
|
||||||
|
_current_block_count++;
|
||||||
|
_buffer = _data.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = Math.min(len-read, _buffer.remaining());
|
||||||
|
_buffer.get(buf, off+read, limit);
|
||||||
|
_current_offset += limit;
|
||||||
|
read += limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte readByte() {
|
||||||
|
return (byte) readUByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double readDouble() {
|
||||||
|
return Double.longBitsToDouble(readLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long readLong() {
|
||||||
|
checkAvaliable(SIZE_LONG);
|
||||||
|
byte[] data = new byte[SIZE_LONG];
|
||||||
|
readFully(data, 0, SIZE_LONG);
|
||||||
|
return LittleEndian.getLong(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short readShort() {
|
||||||
|
return (short) readUShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readInt() {
|
||||||
|
checkAvaliable(SIZE_INT);
|
||||||
|
byte[] data = new byte[SIZE_INT];
|
||||||
|
readFully(data, 0, SIZE_INT);
|
||||||
|
return LittleEndian.getInt(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUShort() {
|
||||||
|
checkAvaliable(SIZE_SHORT);
|
||||||
|
byte[] data = new byte[SIZE_SHORT];
|
||||||
|
readFully(data, 0, SIZE_SHORT);
|
||||||
|
return LittleEndian.getShort(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUByte() {
|
||||||
|
checkAvaliable(1);
|
||||||
|
byte[] data = new byte[1];
|
||||||
|
readFully(data, 0, 1);
|
||||||
|
if(data[0] >= 0)
|
||||||
|
return data[0];
|
||||||
|
return data[0] + 256;
|
||||||
|
}
|
||||||
|
}
|
193
src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java
Normal file
193
src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
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.util.HexDump;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class manages a document in the NIO POIFS filesystem.
|
||||||
|
* This is the {@link NPOIFSFileSystem} version.
|
||||||
|
*/
|
||||||
|
public final class NPOIFSDocument implements POIFSViewable {
|
||||||
|
private DocumentProperty _property;
|
||||||
|
|
||||||
|
private NPOIFSFileSystem _filesystem;
|
||||||
|
private NPOIFSStream _stream;
|
||||||
|
private int _block_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for an existing Document
|
||||||
|
*/
|
||||||
|
public NPOIFSDocument(DocumentProperty property, NPOIFSFileSystem filesystem)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this._property = property;
|
||||||
|
this._filesystem = filesystem;
|
||||||
|
|
||||||
|
if(property.getSize() < POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) {
|
||||||
|
_stream = new NPOIFSStream(_filesystem.getMiniStore(), property.getStartBlock());
|
||||||
|
_block_size = _filesystem.getMiniStore().getBlockStoreBlockSize();
|
||||||
|
} else {
|
||||||
|
_stream = new NPOIFSStream(_filesystem, property.getStartBlock());
|
||||||
|
_block_size = _filesystem.getBlockStoreBlockSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a new Document
|
||||||
|
*
|
||||||
|
* @param name the name of the POIFSDocument
|
||||||
|
* @param stream the InputStream we read data from
|
||||||
|
*/
|
||||||
|
public NPOIFSDocument(String name, NPOIFSFileSystem filesystem, InputStream stream)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this._filesystem = filesystem;
|
||||||
|
|
||||||
|
// Buffer the contents into memory. This is a bit icky...
|
||||||
|
// TODO Replace with a buffer up to the mini stream size, then streaming write
|
||||||
|
byte[] contents;
|
||||||
|
if(stream instanceof ByteArrayInputStream) {
|
||||||
|
ByteArrayInputStream bais = (ByteArrayInputStream)stream;
|
||||||
|
contents = new byte[bais.available()];
|
||||||
|
bais.read(contents);
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
IOUtils.copy(stream, baos);
|
||||||
|
contents = baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to store as a mini stream or a full one?
|
||||||
|
if(contents.length <= POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) {
|
||||||
|
_stream = new NPOIFSStream(filesystem.getMiniStore());
|
||||||
|
_block_size = _filesystem.getMiniStore().getBlockStoreBlockSize();
|
||||||
|
} else {
|
||||||
|
_stream = new NPOIFSStream(filesystem);
|
||||||
|
_block_size = _filesystem.getBlockStoreBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
_stream.updateContents(contents);
|
||||||
|
|
||||||
|
// And build the property for it
|
||||||
|
this._property = new DocumentProperty(name, contents.length);
|
||||||
|
_property.setStartBlock(_stream.getStartBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDocumentBlockSize() {
|
||||||
|
return _block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<ByteBuffer> getBlockIterator() {
|
||||||
|
return _stream.getBlockIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return size of the document
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
return _property.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instance's DocumentProperty
|
||||||
|
*/
|
||||||
|
DocumentProperty getDocumentProperty() {
|
||||||
|
return _property;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
if(getSize() > 0) {
|
||||||
|
// Get all the data into a single array
|
||||||
|
byte[] data = new byte[getSize()];
|
||||||
|
int offset = 0;
|
||||||
|
for(ByteBuffer buffer : _stream) {
|
||||||
|
int length = Math.min(_block_size, data.length-offset);
|
||||||
|
buffer.get(data, offset, length);
|
||||||
|
offset += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream 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 <code>true</code> if a viewer should call getViewableArray,
|
||||||
|
* <code>false</code> 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();
|
||||||
|
}
|
||||||
|
}
|
845
src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java
Normal file
845
src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java
Normal file
@ -0,0 +1,845 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PushbackInputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.dev.POIFSViewable;
|
||||||
|
import org.apache.poi.poifs.nio.ByteArrayBackedDataSource;
|
||||||
|
import org.apache.poi.poifs.nio.DataSource;
|
||||||
|
import org.apache.poi.poifs.nio.FileBackedDataSource;
|
||||||
|
import org.apache.poi.poifs.property.DirectoryProperty;
|
||||||
|
import org.apache.poi.poifs.property.NPropertyTable;
|
||||||
|
import org.apache.poi.poifs.storage.BATBlock;
|
||||||
|
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
|
||||||
|
import org.apache.poi.poifs.storage.BlockAllocationTableWriter;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlockConstants;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlockWriter;
|
||||||
|
import org.apache.poi.poifs.storage.BATBlock.BATBlockAndIndex;
|
||||||
|
import org.apache.poi.util.CloseIgnoringInputStream;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.util.LongField;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main class of the POIFS system; it manages the entire
|
||||||
|
* life cycle of the filesystem.
|
||||||
|
* This is the new NIO version
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class NPOIFSFileSystem extends BlockStore
|
||||||
|
implements POIFSViewable
|
||||||
|
{
|
||||||
|
private static final POILogger _logger =
|
||||||
|
POILogFactory.getLogger(NPOIFSFileSystem.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for clients that want to avoid the auto-close behaviour of the constructor.
|
||||||
|
*/
|
||||||
|
public static InputStream createNonClosingInputStream(InputStream is) {
|
||||||
|
return new CloseIgnoringInputStream(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NPOIFSMiniStore _mini_store;
|
||||||
|
private NPropertyTable _property_table;
|
||||||
|
private List<BATBlock> _xbat_blocks;
|
||||||
|
private List<BATBlock> _bat_blocks;
|
||||||
|
private HeaderBlock _header;
|
||||||
|
private DirectoryNode _root;
|
||||||
|
|
||||||
|
private DataSource _data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What big block size the file uses. Most files
|
||||||
|
* use 512 bytes, but a few use 4096
|
||||||
|
*/
|
||||||
|
private POIFSBigBlockSize bigBlockSize =
|
||||||
|
POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, intended for writing
|
||||||
|
*/
|
||||||
|
public NPOIFSFileSystem()
|
||||||
|
{
|
||||||
|
_header = new HeaderBlock(bigBlockSize);
|
||||||
|
_property_table = new NPropertyTable(_header);
|
||||||
|
_mini_store = new NPOIFSMiniStore(this, _property_table.getRoot(), new ArrayList<BATBlock>(), _header);
|
||||||
|
_xbat_blocks = new ArrayList<BATBlock>();
|
||||||
|
_bat_blocks = new ArrayList<BATBlock>();
|
||||||
|
_root = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a POIFSFileSystem from a <tt>File</tt>. This uses less memory than
|
||||||
|
* creating from an <tt>InputStream</tt>. The File will be opened read-only
|
||||||
|
*
|
||||||
|
* Note that with this constructor, you will need to call {@link #close()}
|
||||||
|
* when you're done to have the underlying file closed, as the file is
|
||||||
|
* kept open during normal operation to read the data out.
|
||||||
|
*
|
||||||
|
* @param file the File from which to read the data
|
||||||
|
*
|
||||||
|
* @exception IOException on errors reading, or on invalid data
|
||||||
|
*/
|
||||||
|
public NPOIFSFileSystem(File file)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this(file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a POIFSFileSystem from a <tt>File</tt>. This uses less memory than
|
||||||
|
* creating from an <tt>InputStream</tt>.
|
||||||
|
*
|
||||||
|
* Note that with this constructor, you will need to call {@link #close()}
|
||||||
|
* when you're done to have the underlying file closed, as the file is
|
||||||
|
* kept open during normal operation to read the data out.
|
||||||
|
*
|
||||||
|
* @param file the File from which to read the data
|
||||||
|
*
|
||||||
|
* @exception IOException on errors reading, or on invalid data
|
||||||
|
*/
|
||||||
|
public NPOIFSFileSystem(File file, boolean readOnly)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this(
|
||||||
|
(new RandomAccessFile(file, readOnly? "r" : "rw")).getChannel(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a POIFSFileSystem from an open <tt>FileChannel</tt>. This uses
|
||||||
|
* less memory than creating from an <tt>InputStream</tt>.
|
||||||
|
*
|
||||||
|
* Note that with this constructor, you will need to call {@link #close()}
|
||||||
|
* when you're done to have the underlying Channel closed, as the channel is
|
||||||
|
* kept open during normal operation to read the data out.
|
||||||
|
*
|
||||||
|
* @param channel the FileChannel from which to read the data
|
||||||
|
*
|
||||||
|
* @exception IOException on errors reading, or on invalid data
|
||||||
|
*/
|
||||||
|
public NPOIFSFileSystem(FileChannel channel)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this(channel, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NPOIFSFileSystem(FileChannel channel, boolean closeChannelOnError)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the header
|
||||||
|
ByteBuffer headerBuffer = ByteBuffer.allocate(POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
|
||||||
|
IOUtils.readFully(channel, headerBuffer);
|
||||||
|
|
||||||
|
// Have the header processed
|
||||||
|
_header = new HeaderBlock(headerBuffer);
|
||||||
|
|
||||||
|
// Now process the various entries
|
||||||
|
_data = new FileBackedDataSource(channel);
|
||||||
|
readCoreContents();
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(closeChannelOnError) {
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
} catch(RuntimeException e) {
|
||||||
|
// Comes from Iterators etc.
|
||||||
|
// TODO Decide if we can handle these better whilst
|
||||||
|
// still sticking to the iterator contract
|
||||||
|
if(closeChannelOnError) {
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a POIFSFileSystem from an <tt>InputStream</tt>. Normally the stream is read until
|
||||||
|
* EOF. The stream is always closed.<p/>
|
||||||
|
*
|
||||||
|
* Some streams are usable after reaching EOF (typically those that return <code>true</code>
|
||||||
|
* for <tt>markSupported()</tt>). In the unlikely case that the caller has such a stream
|
||||||
|
* <i>and</i> needs to use it after this constructor completes, a work around is to wrap the
|
||||||
|
* stream in order to trap the <tt>close()</tt> call. A convenience method (
|
||||||
|
* <tt>createNonClosingInputStream()</tt>) has been provided for this purpose:
|
||||||
|
* <pre>
|
||||||
|
* InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(is);
|
||||||
|
* HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
|
||||||
|
* is.reset();
|
||||||
|
* doSomethingElse(is);
|
||||||
|
* </pre>
|
||||||
|
* Note also the special case of <tt>ByteArrayInputStream</tt> for which the <tt>close()</tt>
|
||||||
|
* method does nothing.
|
||||||
|
* <pre>
|
||||||
|
* ByteArrayInputStream bais = ...
|
||||||
|
* HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.close() !
|
||||||
|
* bais.reset(); // no problem
|
||||||
|
* doSomethingElse(bais);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param stream the InputStream from which to read the data
|
||||||
|
*
|
||||||
|
* @exception IOException on errors reading, or on invalid data
|
||||||
|
*/
|
||||||
|
|
||||||
|
public NPOIFSFileSystem(InputStream stream)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
|
||||||
|
ReadableByteChannel channel = null;
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Turn our InputStream into something NIO based
|
||||||
|
channel = Channels.newChannel(stream);
|
||||||
|
|
||||||
|
// Get the header
|
||||||
|
ByteBuffer headerBuffer = ByteBuffer.allocate(POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
|
||||||
|
IOUtils.readFully(channel, headerBuffer);
|
||||||
|
|
||||||
|
// Have the header processed
|
||||||
|
_header = new HeaderBlock(headerBuffer);
|
||||||
|
|
||||||
|
// Sanity check the block count
|
||||||
|
BlockAllocationTableReader.sanityCheckBlockCount(_header.getBATCount());
|
||||||
|
|
||||||
|
// We need to buffer the whole file into memory when
|
||||||
|
// working with an InputStream.
|
||||||
|
// The max possible size is when each BAT block entry is used
|
||||||
|
int maxSize = BATBlock.calculateMaximumSize(_header);
|
||||||
|
ByteBuffer data = ByteBuffer.allocate(maxSize);
|
||||||
|
// Copy in the header
|
||||||
|
headerBuffer.position(0);
|
||||||
|
data.put(headerBuffer);
|
||||||
|
data.position(headerBuffer.capacity());
|
||||||
|
// Now read the rest of the stream
|
||||||
|
IOUtils.readFully(channel, data);
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Turn it into a DataSource
|
||||||
|
_data = new ByteArrayBackedDataSource(data.array(), data.position());
|
||||||
|
} finally {
|
||||||
|
// As per the constructor contract, always close the stream
|
||||||
|
if(channel != null)
|
||||||
|
channel.close();
|
||||||
|
closeInputStream(stream, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now process the various entries
|
||||||
|
readCoreContents();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param stream the stream to be closed
|
||||||
|
* @param success <code>false</code> if an exception is currently being thrown in the calling method
|
||||||
|
*/
|
||||||
|
private void closeInputStream(InputStream stream, boolean success) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if(success) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
// else not success? Try block did not complete normally
|
||||||
|
// just print stack trace and leave original ex to be thrown
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the supplied InputStream (which MUST
|
||||||
|
* support mark and reset, or be a PushbackInputStream)
|
||||||
|
* has a POIFS (OLE2) header at the start of it.
|
||||||
|
* If your InputStream does not support mark / reset,
|
||||||
|
* then wrap it in a PushBackInputStream, then be
|
||||||
|
* sure to always use that, and not the original!
|
||||||
|
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream
|
||||||
|
*/
|
||||||
|
public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
|
||||||
|
// We want to peek at the first 8 bytes
|
||||||
|
inp.mark(8);
|
||||||
|
|
||||||
|
byte[] header = new byte[8];
|
||||||
|
IOUtils.readFully(inp, header);
|
||||||
|
LongField signature = new LongField(HeaderBlockConstants._signature_offset, header);
|
||||||
|
|
||||||
|
// Wind back those 8 bytes
|
||||||
|
if(inp instanceof PushbackInputStream) {
|
||||||
|
PushbackInputStream pin = (PushbackInputStream)inp;
|
||||||
|
pin.unread(header);
|
||||||
|
} else {
|
||||||
|
inp.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did it match the signature?
|
||||||
|
return (signature.get() == HeaderBlockConstants._signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and process the PropertiesTable and the
|
||||||
|
* FAT / XFAT blocks, so that we're ready to
|
||||||
|
* work with the file
|
||||||
|
*/
|
||||||
|
private void readCoreContents() throws IOException {
|
||||||
|
// Grab the block size
|
||||||
|
bigBlockSize = _header.getBigBlockSize();
|
||||||
|
|
||||||
|
// Each block should only ever be used by one of the
|
||||||
|
// FAT, XFAT or Property Table. Ensure it does
|
||||||
|
ChainLoopDetector loopDetector = getChainLoopDetector();
|
||||||
|
|
||||||
|
// Read the FAT blocks
|
||||||
|
for(int fatAt : _header.getBATArray()) {
|
||||||
|
readBAT(fatAt, loopDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now read the XFAT blocks, and the FATs within them
|
||||||
|
BATBlock xfat;
|
||||||
|
int nextAt = _header.getXBATIndex();
|
||||||
|
for(int i=0; i<_header.getXBATCount(); i++) {
|
||||||
|
loopDetector.claim(nextAt);
|
||||||
|
ByteBuffer fatData = getBlockAt(nextAt);
|
||||||
|
xfat = BATBlock.createBATBlock(bigBlockSize, fatData);
|
||||||
|
xfat.setOurBlockIndex(nextAt);
|
||||||
|
nextAt = xfat.getValueAt(bigBlockSize.getXBATEntriesPerBlock());
|
||||||
|
_xbat_blocks.add(xfat);
|
||||||
|
|
||||||
|
for(int j=0; j<bigBlockSize.getXBATEntriesPerBlock(); j++) {
|
||||||
|
int fatAt = xfat.getValueAt(j);
|
||||||
|
if(fatAt == POIFSConstants.UNUSED_BLOCK) break;
|
||||||
|
readBAT(fatAt, loopDetector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're now able to load steams
|
||||||
|
// Use this to read in the properties
|
||||||
|
_property_table = new NPropertyTable(_header, this);
|
||||||
|
|
||||||
|
// Finally read the Small Stream FAT (SBAT) blocks
|
||||||
|
BATBlock sfat;
|
||||||
|
List<BATBlock> sbats = new ArrayList<BATBlock>();
|
||||||
|
_mini_store = new NPOIFSMiniStore(this, _property_table.getRoot(), sbats, _header);
|
||||||
|
nextAt = _header.getSBATStart();
|
||||||
|
for(int i=0; i<_header.getSBATCount(); i++) {
|
||||||
|
loopDetector.claim(nextAt);
|
||||||
|
ByteBuffer fatData = getBlockAt(nextAt);
|
||||||
|
sfat = BATBlock.createBATBlock(bigBlockSize, fatData);
|
||||||
|
sfat.setOurBlockIndex(nextAt);
|
||||||
|
sbats.add(sfat);
|
||||||
|
nextAt = getNextBlock(nextAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void readBAT(int batAt, ChainLoopDetector loopDetector) throws IOException {
|
||||||
|
loopDetector.claim(batAt);
|
||||||
|
ByteBuffer fatData = getBlockAt(batAt);
|
||||||
|
BATBlock bat = BATBlock.createBATBlock(bigBlockSize, fatData);
|
||||||
|
bat.setOurBlockIndex(batAt);
|
||||||
|
_bat_blocks.add(bat);
|
||||||
|
}
|
||||||
|
private BATBlock createBAT(int offset, boolean isBAT) throws IOException {
|
||||||
|
// Create a new BATBlock
|
||||||
|
BATBlock newBAT = BATBlock.createEmptyBATBlock(bigBlockSize, !isBAT);
|
||||||
|
newBAT.setOurBlockIndex(offset);
|
||||||
|
// Ensure there's a spot in the file for it
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(bigBlockSize.getBigBlockSize());
|
||||||
|
int writeTo = (1+offset) * bigBlockSize.getBigBlockSize(); // Header isn't in BATs
|
||||||
|
_data.write(buffer, writeTo);
|
||||||
|
// All done
|
||||||
|
return newBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the block at the given offset.
|
||||||
|
*/
|
||||||
|
protected ByteBuffer getBlockAt(final int offset) throws IOException {
|
||||||
|
// The header block doesn't count, so add one
|
||||||
|
long startAt = (offset+1) * bigBlockSize.getBigBlockSize();
|
||||||
|
return _data.read(bigBlockSize.getBigBlockSize(), startAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the block at the given offset,
|
||||||
|
* extending the file if needed
|
||||||
|
*/
|
||||||
|
protected ByteBuffer createBlockIfNeeded(final int offset) throws IOException {
|
||||||
|
try {
|
||||||
|
return getBlockAt(offset);
|
||||||
|
} catch(IndexOutOfBoundsException e) {
|
||||||
|
// The header block doesn't count, so add one
|
||||||
|
long startAt = (offset+1) * bigBlockSize.getBigBlockSize();
|
||||||
|
// Allocate and write
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(getBigBlockSize());
|
||||||
|
_data.write(buffer, startAt);
|
||||||
|
// Retrieve the properly backed block
|
||||||
|
return getBlockAt(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the BATBlock that handles the specified offset,
|
||||||
|
* and the relative index within it
|
||||||
|
*/
|
||||||
|
protected BATBlockAndIndex getBATBlockAndIndex(final int offset) {
|
||||||
|
return BATBlock.getBATBlockAndIndex(
|
||||||
|
offset, _header, _bat_blocks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Works out what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected int getNextBlock(final int offset) {
|
||||||
|
BATBlockAndIndex bai = getBATBlockAndIndex(offset);
|
||||||
|
return bai.getBlock().getValueAt( bai.getIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the record of what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected void setNextBlock(final int offset, final int nextBlock) {
|
||||||
|
BATBlockAndIndex bai = getBATBlockAndIndex(offset);
|
||||||
|
bai.getBlock().setValueAt(
|
||||||
|
bai.getIndex(), nextBlock
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a free block, and returns its offset.
|
||||||
|
* This method will extend the file if needed, and if doing
|
||||||
|
* so, allocate new FAT blocks to address the extra space.
|
||||||
|
*/
|
||||||
|
protected int getFreeBlock() throws IOException {
|
||||||
|
// First up, do we have any spare ones?
|
||||||
|
int offset = 0;
|
||||||
|
for(int i=0; i<_bat_blocks.size(); i++) {
|
||||||
|
int numSectors = bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
|
||||||
|
// Check this one
|
||||||
|
BATBlock bat = _bat_blocks.get(i);
|
||||||
|
if(bat.hasFreeSectors()) {
|
||||||
|
// Claim one of them and return it
|
||||||
|
for(int j=0; j<numSectors; j++) {
|
||||||
|
int batValue = bat.getValueAt(j);
|
||||||
|
if(batValue == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
// Bingo
|
||||||
|
return offset + j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move onto the next BAT
|
||||||
|
offset += numSectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, then there aren't any free sectors
|
||||||
|
// in any of the BATs, so we need another BAT
|
||||||
|
BATBlock bat = createBAT(offset, true);
|
||||||
|
bat.setValueAt(0, POIFSConstants.FAT_SECTOR_BLOCK);
|
||||||
|
_bat_blocks.add(bat);
|
||||||
|
|
||||||
|
// Now store a reference to the BAT in the required place
|
||||||
|
if(_header.getBATCount() >= 109) {
|
||||||
|
// Needs to come from an XBAT
|
||||||
|
BATBlock xbat = null;
|
||||||
|
for(BATBlock x : _xbat_blocks) {
|
||||||
|
if(x.hasFreeSectors()) {
|
||||||
|
xbat = x;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(xbat == null) {
|
||||||
|
// Oh joy, we need a new XBAT too...
|
||||||
|
xbat = createBAT(offset+1, false);
|
||||||
|
xbat.setValueAt(0, offset);
|
||||||
|
bat.setValueAt(1, POIFSConstants.DIFAT_SECTOR_BLOCK);
|
||||||
|
|
||||||
|
// Will go one place higher as XBAT added in
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
// Chain it
|
||||||
|
if(_xbat_blocks.size() == 0) {
|
||||||
|
_header.setXBATStart(offset);
|
||||||
|
} else {
|
||||||
|
_xbat_blocks.get(_xbat_blocks.size()-1).setValueAt(
|
||||||
|
bigBlockSize.getXBATEntriesPerBlock(), offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_xbat_blocks.add(xbat);
|
||||||
|
_header.setXBATCount(_xbat_blocks.size());
|
||||||
|
}
|
||||||
|
// Allocate us in the XBAT
|
||||||
|
for(int i=0; i<bigBlockSize.getXBATEntriesPerBlock(); i++) {
|
||||||
|
if(xbat.getValueAt(i) == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
xbat.setValueAt(i, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Store us in the header
|
||||||
|
int[] newBATs = new int[_header.getBATCount()+1];
|
||||||
|
System.arraycopy(_header.getBATArray(), 0, newBATs, 0, newBATs.length-1);
|
||||||
|
newBATs[newBATs.length-1] = offset;
|
||||||
|
_header.setBATArray(newBATs);
|
||||||
|
}
|
||||||
|
_header.setBATCount(_bat_blocks.size());
|
||||||
|
|
||||||
|
// The current offset stores us, but the next one is free
|
||||||
|
return offset+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChainLoopDetector getChainLoopDetector() throws IOException {
|
||||||
|
return new ChainLoopDetector(_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For unit testing only! Returns the underlying
|
||||||
|
* properties table
|
||||||
|
*/
|
||||||
|
NPropertyTable _get_property_table() {
|
||||||
|
return _property_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MiniStore, which performs a similar low
|
||||||
|
* level function to this, except for the small blocks.
|
||||||
|
*/
|
||||||
|
public NPOIFSMiniStore getMiniStore() {
|
||||||
|
return _mini_store;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a new POIFSDocument to the FileSytem
|
||||||
|
*
|
||||||
|
* @param document the POIFSDocument being added
|
||||||
|
*/
|
||||||
|
void addDocument(final NPOIFSDocument document)
|
||||||
|
{
|
||||||
|
_property_table.addProperty(document.getDocumentProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a new DirectoryProperty to the FileSystem
|
||||||
|
*
|
||||||
|
* @param directory the DirectoryProperty being added
|
||||||
|
*/
|
||||||
|
void addDirectory(final DirectoryProperty directory)
|
||||||
|
{
|
||||||
|
_property_table.addProperty(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new document to be added to the root directory
|
||||||
|
*
|
||||||
|
* @param stream the InputStream from which the document's data
|
||||||
|
* will be obtained
|
||||||
|
* @param name the name of the new POIFSDocument
|
||||||
|
*
|
||||||
|
* @return the new DocumentEntry
|
||||||
|
*
|
||||||
|
* @exception IOException on error creating the new POIFSDocument
|
||||||
|
*/
|
||||||
|
|
||||||
|
public DocumentEntry createDocument(final InputStream stream,
|
||||||
|
final String name)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return getRoot().createDocument(name, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new DocumentEntry in the root entry; the data will be
|
||||||
|
* provided later
|
||||||
|
*
|
||||||
|
* @param name the name of the new DocumentEntry
|
||||||
|
* @param size the size of the new DocumentEntry
|
||||||
|
* @param writer the writer of the new DocumentEntry
|
||||||
|
*
|
||||||
|
* @return the new DocumentEntry
|
||||||
|
*
|
||||||
|
* @exception IOException
|
||||||
|
*/
|
||||||
|
|
||||||
|
public DocumentEntry createDocument(final String name, final int size,
|
||||||
|
final POIFSWriterListener writer)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return getRoot().createDocument(name, size, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new DirectoryEntry in the root directory
|
||||||
|
*
|
||||||
|
* @param name the name of the new DirectoryEntry
|
||||||
|
*
|
||||||
|
* @return the new DirectoryEntry
|
||||||
|
*
|
||||||
|
* @exception IOException on name duplication
|
||||||
|
*/
|
||||||
|
|
||||||
|
public DirectoryEntry createDirectory(final String name)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return getRoot().createDirectory(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the filesystem out to the open file. Will thrown an
|
||||||
|
* {@link IllegalArgumentException} if opened from an
|
||||||
|
* {@link InputStream}.
|
||||||
|
*
|
||||||
|
* @exception IOException thrown on errors writing to the stream
|
||||||
|
*/
|
||||||
|
public void writeFilesystem() throws IOException
|
||||||
|
{
|
||||||
|
if(_data instanceof FileBackedDataSource) {
|
||||||
|
// Good, correct type
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"POIFS opened from an inputstream, so writeFilesystem() may " +
|
||||||
|
"not be called. Use writeFilesystem(OutputStream) instead"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
syncWithDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the filesystem out
|
||||||
|
*
|
||||||
|
* @param stream the OutputStream to which the filesystem will be
|
||||||
|
* written
|
||||||
|
*
|
||||||
|
* @exception IOException thrown on errors writing to the stream
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void writeFilesystem(final OutputStream stream)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// Have the datasource updated
|
||||||
|
syncWithDataSource();
|
||||||
|
|
||||||
|
// Now copy the contents to the stream
|
||||||
|
_data.copyTo(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has our in-memory objects write their state
|
||||||
|
* to their backing blocks
|
||||||
|
*/
|
||||||
|
private void syncWithDataSource() throws IOException
|
||||||
|
{
|
||||||
|
// HeaderBlock
|
||||||
|
HeaderBlockWriter hbw = new HeaderBlockWriter(_header);
|
||||||
|
hbw.writeBlock( getBlockAt(0) );
|
||||||
|
|
||||||
|
// BATs
|
||||||
|
for(BATBlock bat : _bat_blocks) {
|
||||||
|
ByteBuffer block = getBlockAt(bat.getOurBlockIndex());
|
||||||
|
BlockAllocationTableWriter.writeBlock(bat, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SBATs
|
||||||
|
_mini_store.syncWithDataSource();
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
_property_table.write(
|
||||||
|
new NPOIFSStream(this, _header.getPropertyStart())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the FileSystem, freeing any underlying files, streams
|
||||||
|
* and buffers. After this, you will be unable to read or
|
||||||
|
* write from the FileSystem.
|
||||||
|
*/
|
||||||
|
public void close() throws IOException {
|
||||||
|
_data.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read in a file and write it back out again
|
||||||
|
*
|
||||||
|
* @param args names of the files; arg[ 0 ] is the input file,
|
||||||
|
* arg[ 1 ] is the output file
|
||||||
|
*
|
||||||
|
* @exception IOException
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static void main(String args[])
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (args.length != 2)
|
||||||
|
{
|
||||||
|
System.err.println(
|
||||||
|
"two arguments required: input filename and output filename");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
FileInputStream istream = new FileInputStream(args[ 0 ]);
|
||||||
|
FileOutputStream ostream = new FileOutputStream(args[ 1 ]);
|
||||||
|
|
||||||
|
new NPOIFSFileSystem(istream).writeFilesystem(ostream);
|
||||||
|
istream.close();
|
||||||
|
ostream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root entry
|
||||||
|
*
|
||||||
|
* @return the root entry
|
||||||
|
*/
|
||||||
|
public DirectoryNode getRoot()
|
||||||
|
{
|
||||||
|
if (_root == null) {
|
||||||
|
_root = new DirectoryNode(_property_table.getRoot(), this, null);
|
||||||
|
}
|
||||||
|
return _root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open a document in the root entry's list of entries
|
||||||
|
*
|
||||||
|
* @param documentName the name of the document to be opened
|
||||||
|
*
|
||||||
|
* @return a newly opened DocumentInputStream
|
||||||
|
*
|
||||||
|
* @exception IOException if the document does not exist or the
|
||||||
|
* name is that of a DirectoryEntry
|
||||||
|
*/
|
||||||
|
|
||||||
|
public DocumentInputStream createDocumentInputStream(
|
||||||
|
final String documentName)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return getRoot().createDocumentInputStream(documentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove an entry
|
||||||
|
*
|
||||||
|
* @param entry to be removed
|
||||||
|
*/
|
||||||
|
|
||||||
|
void remove(EntryNode entry)
|
||||||
|
{
|
||||||
|
_property_table.removeProperty(entry.getProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********** 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()
|
||||||
|
{
|
||||||
|
if (preferArray())
|
||||||
|
{
|
||||||
|
return (( POIFSViewable ) getRoot()).getViewableArray();
|
||||||
|
}
|
||||||
|
return new Object[ 0 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
if (!preferArray())
|
||||||
|
{
|
||||||
|
return (( POIFSViewable ) getRoot()).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 (( POIFSViewable ) getRoot()).preferArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
return "POIFS FileSystem";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********** END begin implementation of POIFSViewable ********** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
||||||
|
*/
|
||||||
|
public int getBigBlockSize() {
|
||||||
|
return bigBlockSize.getBigBlockSize();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
||||||
|
*/
|
||||||
|
public POIFSBigBlockSize getBigBlockSizeDetails() {
|
||||||
|
return bigBlockSize;
|
||||||
|
}
|
||||||
|
protected int getBlockStoreBlockSize() {
|
||||||
|
return getBigBlockSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
211
src/java/org/apache/poi/poifs/filesystem/NPOIFSMiniStore.java
Normal file
211
src/java/org/apache/poi/poifs/filesystem/NPOIFSMiniStore.java
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.property.RootProperty;
|
||||||
|
import org.apache.poi.poifs.storage.BATBlock;
|
||||||
|
import org.apache.poi.poifs.storage.BlockAllocationTableWriter;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
import org.apache.poi.poifs.storage.BATBlock.BATBlockAndIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles the MiniStream (small block store)
|
||||||
|
* in the NIO case for {@link NPOIFSFileSystem}
|
||||||
|
*/
|
||||||
|
public class NPOIFSMiniStore extends BlockStore
|
||||||
|
{
|
||||||
|
private NPOIFSFileSystem _filesystem;
|
||||||
|
private NPOIFSStream _mini_stream;
|
||||||
|
private List<BATBlock> _sbat_blocks;
|
||||||
|
private HeaderBlock _header;
|
||||||
|
private RootProperty _root;
|
||||||
|
|
||||||
|
protected NPOIFSMiniStore(NPOIFSFileSystem filesystem, RootProperty root,
|
||||||
|
List<BATBlock> sbats, HeaderBlock header)
|
||||||
|
{
|
||||||
|
this._filesystem = filesystem;
|
||||||
|
this._sbat_blocks = sbats;
|
||||||
|
this._header = header;
|
||||||
|
this._root = root;
|
||||||
|
|
||||||
|
this._mini_stream = new NPOIFSStream(filesystem, root.getStartBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the block at the given offset.
|
||||||
|
*/
|
||||||
|
protected ByteBuffer getBlockAt(final int offset) throws IOException {
|
||||||
|
// Which big block is this?
|
||||||
|
int byteOffset = offset * POIFSConstants.SMALL_BLOCK_SIZE;
|
||||||
|
int bigBlockNumber = byteOffset / _filesystem.getBigBlockSize();
|
||||||
|
int bigBlockOffset = byteOffset % _filesystem.getBigBlockSize();
|
||||||
|
|
||||||
|
// Now locate the data block for it
|
||||||
|
Iterator<ByteBuffer> it = _mini_stream.getBlockIterator();
|
||||||
|
for(int i=0; i<bigBlockNumber; i++) {
|
||||||
|
it.next();
|
||||||
|
}
|
||||||
|
ByteBuffer dataBlock = it.next();
|
||||||
|
|
||||||
|
// Our blocks are small, so duplicating it is fine
|
||||||
|
byte[] data = new byte[POIFSConstants.SMALL_BLOCK_SIZE];
|
||||||
|
dataBlock.position(
|
||||||
|
dataBlock.position() + bigBlockOffset
|
||||||
|
);
|
||||||
|
dataBlock.get(data, 0, data.length);
|
||||||
|
|
||||||
|
// Return a ByteBuffer on this
|
||||||
|
ByteBuffer miniBuffer = ByteBuffer.wrap(data);
|
||||||
|
return miniBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the block, extending the underlying stream if needed
|
||||||
|
*/
|
||||||
|
protected ByteBuffer createBlockIfNeeded(final int offset) throws IOException {
|
||||||
|
// TODO Extend the stream if needed
|
||||||
|
// TODO Needs append support on the underlying stream
|
||||||
|
return getBlockAt(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the BATBlock that handles the specified offset,
|
||||||
|
* and the relative index within it
|
||||||
|
*/
|
||||||
|
protected BATBlockAndIndex getBATBlockAndIndex(final int offset) {
|
||||||
|
return BATBlock.getSBATBlockAndIndex(
|
||||||
|
offset, _header, _sbat_blocks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Works out what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected int getNextBlock(final int offset) {
|
||||||
|
BATBlockAndIndex bai = getBATBlockAndIndex(offset);
|
||||||
|
return bai.getBlock().getValueAt( bai.getIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the record of what block follows the specified one.
|
||||||
|
*/
|
||||||
|
protected void setNextBlock(final int offset, final int nextBlock) {
|
||||||
|
BATBlockAndIndex bai = getBATBlockAndIndex(offset);
|
||||||
|
bai.getBlock().setValueAt(
|
||||||
|
bai.getIndex(), nextBlock
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a free block, and returns its offset.
|
||||||
|
* This method will extend the file if needed, and if doing
|
||||||
|
* so, allocate new FAT blocks to address the extra space.
|
||||||
|
*/
|
||||||
|
protected int getFreeBlock() throws IOException {
|
||||||
|
int sectorsPerSBAT = _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock();
|
||||||
|
|
||||||
|
// First up, do we have any spare ones?
|
||||||
|
int offset = 0;
|
||||||
|
for(int i=0; i<_sbat_blocks.size(); i++) {
|
||||||
|
// Check this one
|
||||||
|
BATBlock sbat = _sbat_blocks.get(i);
|
||||||
|
if(sbat.hasFreeSectors()) {
|
||||||
|
// Claim one of them and return it
|
||||||
|
for(int j=0; j<sectorsPerSBAT; j++) {
|
||||||
|
int sbatValue = sbat.getValueAt(j);
|
||||||
|
if(sbatValue == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
// Bingo
|
||||||
|
return offset + j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move onto the next SBAT
|
||||||
|
offset += sectorsPerSBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, then there aren't any
|
||||||
|
// free sectors in any of the SBATs
|
||||||
|
// So, we need to extend the chain and add another
|
||||||
|
|
||||||
|
// Create a new BATBlock
|
||||||
|
BATBlock newSBAT = BATBlock.createEmptyBATBlock(_filesystem.getBigBlockSizeDetails(), false);
|
||||||
|
int batForSBAT = _filesystem.getFreeBlock();
|
||||||
|
newSBAT.setOurBlockIndex(batForSBAT);
|
||||||
|
|
||||||
|
// Are we the first SBAT?
|
||||||
|
if(_header.getSBATCount() == 0) {
|
||||||
|
_header.setSBATStart(batForSBAT);
|
||||||
|
_header.setSBATBlockCount(1);
|
||||||
|
} else {
|
||||||
|
// Find the end of the SBAT stream, and add the sbat in there
|
||||||
|
ChainLoopDetector loopDetector = _filesystem.getChainLoopDetector();
|
||||||
|
int batOffset = _header.getSBATStart();
|
||||||
|
while(true) {
|
||||||
|
loopDetector.claim(batOffset);
|
||||||
|
int nextBat = _filesystem.getNextBlock(batOffset);
|
||||||
|
if(nextBat == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
batOffset = nextBat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it in at the end
|
||||||
|
_filesystem.setNextBlock(batOffset, batForSBAT);
|
||||||
|
|
||||||
|
// And update the count
|
||||||
|
_header.setSBATBlockCount(
|
||||||
|
_header.getSBATCount() + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish allocating
|
||||||
|
_filesystem.setNextBlock(batForSBAT, POIFSConstants.END_OF_CHAIN);
|
||||||
|
_sbat_blocks.add(newSBAT);
|
||||||
|
|
||||||
|
// Return our first spot
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChainLoopDetector getChainLoopDetector() throws IOException {
|
||||||
|
return new ChainLoopDetector( _root.getSize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getBlockStoreBlockSize() {
|
||||||
|
return POIFSConstants.SMALL_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the SBATs to their backing blocks
|
||||||
|
*/
|
||||||
|
protected void syncWithDataSource() throws IOException {
|
||||||
|
for(BATBlock sbat : _sbat_blocks) {
|
||||||
|
ByteBuffer block = _filesystem.getBlockAt(sbat.getOurBlockIndex());
|
||||||
|
BlockAllocationTableWriter.writeBlock(sbat, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
224
src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java
Normal file
224
src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.filesystem.BlockStore.ChainLoopDetector;
|
||||||
|
import org.apache.poi.poifs.property.Property;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This handles reading and writing a stream within a
|
||||||
|
* {@link NPOIFSFileSystem}. It can supply an iterator
|
||||||
|
* to read blocks, and way to write out to existing and
|
||||||
|
* new blocks.
|
||||||
|
* Most users will want a higher level version of this,
|
||||||
|
* which deals with properties to track which stream
|
||||||
|
* this is.
|
||||||
|
* This only works on big block streams, it doesn't
|
||||||
|
* handle small block ones.
|
||||||
|
* This uses the new NIO code
|
||||||
|
*
|
||||||
|
* TODO Implement a streaming write method, and append
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class NPOIFSStream implements Iterable<ByteBuffer>
|
||||||
|
{
|
||||||
|
private BlockStore blockStore;
|
||||||
|
private int startBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for an existing stream. It's up to you
|
||||||
|
* to know how to get the start block (eg from a
|
||||||
|
* {@link HeaderBlock} or a {@link Property})
|
||||||
|
*/
|
||||||
|
public NPOIFSStream(BlockStore blockStore, int startBlock) {
|
||||||
|
this.blockStore = blockStore;
|
||||||
|
this.startBlock = startBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a new stream. A start block won't
|
||||||
|
* be allocated until you begin writing to it.
|
||||||
|
*/
|
||||||
|
public NPOIFSStream(BlockStore blockStore) {
|
||||||
|
this.blockStore = blockStore;
|
||||||
|
this.startBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What block does this stream start at?
|
||||||
|
* Will be {@link POIFSConstants#END_OF_CHAIN} for a
|
||||||
|
* new stream that hasn't been written to yet.
|
||||||
|
*/
|
||||||
|
public int getStartBlock() {
|
||||||
|
return startBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator that'll supply one {@link ByteBuffer}
|
||||||
|
* per block in the stream.
|
||||||
|
*/
|
||||||
|
public Iterator<ByteBuffer> iterator() {
|
||||||
|
return getBlockIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<ByteBuffer> getBlockIterator() {
|
||||||
|
if(startBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Can't read from a new stream before it has been written to"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new StreamBlockByteBufferIterator(startBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the contents of the stream to the new
|
||||||
|
* set of bytes.
|
||||||
|
* Note - if this is property based, you'll still
|
||||||
|
* need to update the size in the property yourself
|
||||||
|
*/
|
||||||
|
public void updateContents(byte[] contents) throws IOException {
|
||||||
|
// How many blocks are we going to need?
|
||||||
|
int blockSize = blockStore.getBlockStoreBlockSize();
|
||||||
|
int blocks = (int)Math.ceil(contents.length / blockSize);
|
||||||
|
|
||||||
|
// Make sure we don't encounter a loop whilst overwriting
|
||||||
|
// the existing blocks
|
||||||
|
ChainLoopDetector loopDetector = blockStore.getChainLoopDetector();
|
||||||
|
|
||||||
|
// Start writing
|
||||||
|
int prevBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
int nextBlock = startBlock;
|
||||||
|
for(int i=0; i<blocks; i++) {
|
||||||
|
int thisBlock = nextBlock;
|
||||||
|
|
||||||
|
// Allocate a block if needed, otherwise figure
|
||||||
|
// out what the next block will be
|
||||||
|
if(thisBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
thisBlock = blockStore.getFreeBlock();
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
|
||||||
|
// We're on the end of the chain
|
||||||
|
nextBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
|
||||||
|
// Mark the previous block as carrying on to us if needed
|
||||||
|
if(prevBlock != POIFSConstants.END_OF_CHAIN) {
|
||||||
|
blockStore.setNextBlock(prevBlock, thisBlock);
|
||||||
|
}
|
||||||
|
blockStore.setNextBlock(thisBlock, POIFSConstants.END_OF_CHAIN);
|
||||||
|
|
||||||
|
// If we've just written the first block on a
|
||||||
|
// new stream, save the start block offset
|
||||||
|
if(this.startBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
this.startBlock = thisBlock;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
nextBlock = blockStore.getNextBlock(thisBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write it
|
||||||
|
ByteBuffer buffer = blockStore.createBlockIfNeeded(thisBlock);
|
||||||
|
buffer.put(contents, i*blockSize, blockSize);
|
||||||
|
|
||||||
|
// Update pointers
|
||||||
|
prevBlock = thisBlock;
|
||||||
|
}
|
||||||
|
int lastBlock = prevBlock;
|
||||||
|
|
||||||
|
// If we're overwriting, free any remaining blocks
|
||||||
|
NPOIFSStream toFree = new NPOIFSStream(blockStore, nextBlock);
|
||||||
|
toFree.free(loopDetector);
|
||||||
|
|
||||||
|
// Mark the end of the stream
|
||||||
|
blockStore.setNextBlock(lastBlock, POIFSConstants.END_OF_CHAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Streaming write support
|
||||||
|
// TODO then convert fixed sized write to use streaming internally
|
||||||
|
// TODO Append write support (probably streaming)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees all blocks in the stream
|
||||||
|
*/
|
||||||
|
public void free() throws IOException {
|
||||||
|
ChainLoopDetector loopDetector = blockStore.getChainLoopDetector();
|
||||||
|
free(loopDetector);
|
||||||
|
}
|
||||||
|
private void free(ChainLoopDetector loopDetector) {
|
||||||
|
int nextBlock = startBlock;
|
||||||
|
while(nextBlock != POIFSConstants.END_OF_CHAIN) {
|
||||||
|
int thisBlock = nextBlock;
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
nextBlock = blockStore.getNextBlock(thisBlock);
|
||||||
|
blockStore.setNextBlock(thisBlock, POIFSConstants.UNUSED_BLOCK);
|
||||||
|
}
|
||||||
|
this.startBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that handles a streaming read of one stream
|
||||||
|
*/
|
||||||
|
protected class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
|
||||||
|
private ChainLoopDetector loopDetector;
|
||||||
|
private int nextBlock;
|
||||||
|
|
||||||
|
protected StreamBlockByteBufferIterator(int firstBlock) {
|
||||||
|
this.nextBlock = firstBlock;
|
||||||
|
try {
|
||||||
|
this.loopDetector = blockStore.getChainLoopDetector();
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if(nextBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer next() {
|
||||||
|
if(nextBlock == POIFSConstants.END_OF_CHAIN) {
|
||||||
|
throw new IndexOutOfBoundsException("Can't read past the end of the stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loopDetector.claim(nextBlock);
|
||||||
|
ByteBuffer data = blockStore.getBlockAt(nextBlock);
|
||||||
|
nextBlock = blockStore.getNextBlock(nextBlock);
|
||||||
|
return data;
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.storage.DataInputBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides methods to read a DocumentEntry managed by a
|
||||||
|
* {@link POIFSFileSystem} instance.
|
||||||
|
*
|
||||||
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
|
*/
|
||||||
|
public final class ODocumentInputStream extends DocumentInputStream {
|
||||||
|
/** current offset into the Document */
|
||||||
|
private int _current_offset;
|
||||||
|
|
||||||
|
/** current marked offset into the Document (used by mark and reset) */
|
||||||
|
private int _marked_offset;
|
||||||
|
|
||||||
|
/** the Document's size */
|
||||||
|
private int _document_size;
|
||||||
|
|
||||||
|
/** have we been closed? */
|
||||||
|
private boolean _closed;
|
||||||
|
|
||||||
|
/** the actual Document */
|
||||||
|
private POIFSDocument _document;
|
||||||
|
|
||||||
|
/** the data block containing the current stream pointer */
|
||||||
|
private DataInputBlock _currentBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an InputStream from the specified DocumentEntry
|
||||||
|
*
|
||||||
|
* @param document the DocumentEntry to be read
|
||||||
|
*
|
||||||
|
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
|
||||||
|
* been deleted?)
|
||||||
|
*/
|
||||||
|
public ODocumentInputStream(DocumentEntry document) throws IOException {
|
||||||
|
if (!(document instanceof DocumentNode)) {
|
||||||
|
throw new IOException("Cannot open internal document storage");
|
||||||
|
}
|
||||||
|
DocumentNode documentNode = (DocumentNode)document;
|
||||||
|
if(documentNode.getDocument() == null) {
|
||||||
|
throw new IOException("Cannot open internal document storage");
|
||||||
|
}
|
||||||
|
|
||||||
|
_current_offset = 0;
|
||||||
|
_marked_offset = 0;
|
||||||
|
_document_size = document.getSize();
|
||||||
|
_closed = false;
|
||||||
|
_document = documentNode.getDocument();
|
||||||
|
_currentBlock = getDataInputBlock(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an InputStream from the specified Document
|
||||||
|
*
|
||||||
|
* @param document the Document to be read
|
||||||
|
*/
|
||||||
|
public ODocumentInputStream(POIFSDocument document) {
|
||||||
|
_current_offset = 0;
|
||||||
|
_marked_offset = 0;
|
||||||
|
_document_size = document.getSize();
|
||||||
|
_closed = false;
|
||||||
|
_document = document;
|
||||||
|
_currentBlock = getDataInputBlock(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
return _document_size - _current_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
_closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark(int ignoredReadlimit) {
|
||||||
|
_marked_offset = _current_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataInputBlock getDataInputBlock(int offset) {
|
||||||
|
return _document.getDataInputBlock(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (atEOD()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
int result = _currentBlock.readUByte();
|
||||||
|
_current_offset++;
|
||||||
|
if (_currentBlock.available() < 1) {
|
||||||
|
_currentBlock = getDataInputBlock(_current_offset);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (b == null) {
|
||||||
|
throw new IllegalArgumentException("buffer must not be null");
|
||||||
|
}
|
||||||
|
if (off < 0 || len < 0 || b.length < off + len) {
|
||||||
|
throw new IndexOutOfBoundsException("can't read past buffer boundaries");
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (atEOD()) {
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
int limit = Math.min(available(), len);
|
||||||
|
readFully(b, off, limit);
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repositions this stream to the position at the time the mark() method was
|
||||||
|
* last called on this input stream. If mark() has not been called this
|
||||||
|
* method repositions the stream to its beginning.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
_current_offset = _marked_offset;
|
||||||
|
_currentBlock = getDataInputBlock(_current_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
dieIfClosed();
|
||||||
|
if (n < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int new_offset = _current_offset + (int) n;
|
||||||
|
|
||||||
|
if (new_offset < _current_offset) {
|
||||||
|
|
||||||
|
// wrap around in converting a VERY large long to an int
|
||||||
|
new_offset = _document_size;
|
||||||
|
} else if (new_offset > _document_size) {
|
||||||
|
new_offset = _document_size;
|
||||||
|
}
|
||||||
|
long rval = new_offset - _current_offset;
|
||||||
|
|
||||||
|
_current_offset = new_offset;
|
||||||
|
_currentBlock = getDataInputBlock(_current_offset);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dieIfClosed() throws IOException {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IOException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean atEOD() {
|
||||||
|
return _current_offset == _document_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAvaliable(int requestedSize) {
|
||||||
|
if (_closed) {
|
||||||
|
throw new IllegalStateException("cannot perform requested operation on a closed stream");
|
||||||
|
}
|
||||||
|
if (requestedSize > _document_size - _current_offset) {
|
||||||
|
throw new RuntimeException("Buffer underrun - requested " + requestedSize
|
||||||
|
+ " bytes but " + (_document_size - _current_offset) + " was available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte readByte() {
|
||||||
|
return (byte) readUByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double readDouble() {
|
||||||
|
return Double.longBitsToDouble(readLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short readShort() {
|
||||||
|
return (short) readUShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
|
checkAvaliable(len);
|
||||||
|
int blockAvailable = _currentBlock.available();
|
||||||
|
if (blockAvailable > len) {
|
||||||
|
_currentBlock.readFully(buf, off, len);
|
||||||
|
_current_offset += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// else read big amount in chunks
|
||||||
|
int remaining = len;
|
||||||
|
int writePos = off;
|
||||||
|
while (remaining > 0) {
|
||||||
|
boolean blockIsExpiring = remaining >= blockAvailable;
|
||||||
|
int reqSize;
|
||||||
|
if (blockIsExpiring) {
|
||||||
|
reqSize = blockAvailable;
|
||||||
|
} else {
|
||||||
|
reqSize = remaining;
|
||||||
|
}
|
||||||
|
_currentBlock.readFully(buf, writePos, reqSize);
|
||||||
|
remaining -= reqSize;
|
||||||
|
writePos += reqSize;
|
||||||
|
_current_offset += reqSize;
|
||||||
|
if (blockIsExpiring) {
|
||||||
|
if (_current_offset == _document_size) {
|
||||||
|
if (remaining > 0) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"reached end of document stream unexpectedly");
|
||||||
|
}
|
||||||
|
_currentBlock = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_currentBlock = getDataInputBlock(_current_offset);
|
||||||
|
blockAvailable = _currentBlock.available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long readLong() {
|
||||||
|
checkAvaliable(SIZE_LONG);
|
||||||
|
int blockAvailable = _currentBlock.available();
|
||||||
|
long result;
|
||||||
|
if (blockAvailable > SIZE_LONG) {
|
||||||
|
result = _currentBlock.readLongLE();
|
||||||
|
} else {
|
||||||
|
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
|
||||||
|
if (blockAvailable == SIZE_LONG) {
|
||||||
|
result = _currentBlock.readLongLE();
|
||||||
|
} else {
|
||||||
|
result = nextBlock.readLongLE(_currentBlock, blockAvailable);
|
||||||
|
}
|
||||||
|
_currentBlock = nextBlock;
|
||||||
|
}
|
||||||
|
_current_offset += SIZE_LONG;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readInt() {
|
||||||
|
checkAvaliable(SIZE_INT);
|
||||||
|
int blockAvailable = _currentBlock.available();
|
||||||
|
int result;
|
||||||
|
if (blockAvailable > SIZE_INT) {
|
||||||
|
result = _currentBlock.readIntLE();
|
||||||
|
} else {
|
||||||
|
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
|
||||||
|
if (blockAvailable == SIZE_INT) {
|
||||||
|
result = _currentBlock.readIntLE();
|
||||||
|
} else {
|
||||||
|
result = nextBlock.readIntLE(_currentBlock, blockAvailable);
|
||||||
|
}
|
||||||
|
_currentBlock = nextBlock;
|
||||||
|
}
|
||||||
|
_current_offset += SIZE_INT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUShort() {
|
||||||
|
checkAvaliable(SIZE_SHORT);
|
||||||
|
int blockAvailable = _currentBlock.available();
|
||||||
|
int result;
|
||||||
|
if (blockAvailable > SIZE_SHORT) {
|
||||||
|
result = _currentBlock.readUShortLE();
|
||||||
|
} else {
|
||||||
|
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
|
||||||
|
if (blockAvailable == SIZE_SHORT) {
|
||||||
|
result = _currentBlock.readUShortLE();
|
||||||
|
} else {
|
||||||
|
result = nextBlock.readUShortLE(_currentBlock);
|
||||||
|
}
|
||||||
|
_currentBlock = nextBlock;
|
||||||
|
}
|
||||||
|
_current_offset += SIZE_SHORT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUByte() {
|
||||||
|
checkAvaliable(1);
|
||||||
|
int result = _currentBlock.readUByte();
|
||||||
|
_current_offset++;
|
||||||
|
if (_currentBlock.available() < 1) {
|
||||||
|
_currentBlock = getDataInputBlock(_current_offset);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
279
src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
Normal file
279
src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import org.apache.poi.util.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an Ole10Native record which is wrapped around certain binary
|
||||||
|
* files being embedded in OLE2 documents.
|
||||||
|
*
|
||||||
|
* @author Rainer Schwarze
|
||||||
|
*/
|
||||||
|
public class Ole10Native {
|
||||||
|
// (the fields as they appear in the raw record:)
|
||||||
|
private final int totalSize; // 4 bytes, total size of record not including this field
|
||||||
|
private short flags1; // 2 bytes, unknown, mostly [02 00]
|
||||||
|
private final String label; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private final String fileName; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private short flags2; // 2 bytes, unknown, mostly [00 00]
|
||||||
|
// private byte unknown1Length; // 1 byte, specifying the length of the following byte array (unknown1)
|
||||||
|
private byte[] unknown1; // see below
|
||||||
|
private byte[] unknown2; // 3 bytes, unknown, mostly [00 00 00]
|
||||||
|
private final String command; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private final int dataSize; // 4 bytes (if space), size of following buffer
|
||||||
|
private final byte[] dataBuffer; // varying size, the actual native data
|
||||||
|
private short flags3; // some final flags? or zero terminators?, sometimes not there
|
||||||
|
public static final String OLE10_NATIVE = "\u0001Ole10Native";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
|
||||||
|
* to include a stream "{01}Ole10Native" which contains the actual
|
||||||
|
* data relevant for this class.
|
||||||
|
*
|
||||||
|
* @param poifs POI Filesystem object
|
||||||
|
* @return Returns an instance of this class
|
||||||
|
* @throws IOException on IO error
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
|
||||||
|
boolean plain = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
poifs.getRoot().getEntry("\u0001Ole10ItemName");
|
||||||
|
plain = true;
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
plain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentInputStream dis = poifs.createDocumentInputStream(OLE10_NATIVE);
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
IOUtils.copy(dis, bos);
|
||||||
|
byte[] data = bos.toByteArray();
|
||||||
|
|
||||||
|
return new Ole10Native(data, 0, plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance and fills the fields based on the data in the given buffer.
|
||||||
|
*
|
||||||
|
* @param data The buffer containing the Ole10Native record
|
||||||
|
* @param offset The start offset of the record in the buffer
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public Ole10Native(byte[] data, int offset) throws Ole10NativeException {
|
||||||
|
this(data, offset, false);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates an instance and fills the fields based on the data in the given buffer.
|
||||||
|
*
|
||||||
|
* @param data The buffer containing the Ole10Native record
|
||||||
|
* @param offset The start offset of the record in the buffer
|
||||||
|
* @param plain Specified 'plain' format without filename
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public Ole10Native(byte[] data, int offset, boolean plain) throws Ole10NativeException {
|
||||||
|
int ofs = offset; // current offset, initialized to start
|
||||||
|
|
||||||
|
if (data.length<offset+2) {
|
||||||
|
throw new Ole10NativeException("data is too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize = LittleEndian.getInt(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
|
if (plain) {
|
||||||
|
dataBuffer = new byte[totalSize-4];
|
||||||
|
System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
|
||||||
|
dataSize = totalSize - 4;
|
||||||
|
|
||||||
|
byte[] oleLabel = new byte[8];
|
||||||
|
System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8));
|
||||||
|
label = "ole-"+ HexDump.toHex(oleLabel);
|
||||||
|
fileName = label;
|
||||||
|
command = label;
|
||||||
|
} else {
|
||||||
|
flags1 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
int len = getStringLength(data, ofs);
|
||||||
|
label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
len = getStringLength(data, ofs);
|
||||||
|
fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
flags2 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
len = LittleEndian.getUnsignedByte(data, ofs);
|
||||||
|
unknown1 = new byte[len];
|
||||||
|
ofs += len;
|
||||||
|
len = 3;
|
||||||
|
unknown2 = new byte[len];
|
||||||
|
ofs += len;
|
||||||
|
len = getStringLength(data, ofs);
|
||||||
|
command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
|
||||||
|
if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) {
|
||||||
|
dataSize = LittleEndian.getInt(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
|
if (dataSize > totalSize || dataSize<0) {
|
||||||
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
|
}
|
||||||
|
|
||||||
|
dataBuffer = new byte[dataSize];
|
||||||
|
System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
|
||||||
|
ofs += dataSize;
|
||||||
|
|
||||||
|
if (unknown1.length > 0) {
|
||||||
|
flags3 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
} else {
|
||||||
|
flags3 = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper - determine length of zero terminated string (ASCIIZ).
|
||||||
|
*/
|
||||||
|
private static int getStringLength(byte[] data, int ofs) {
|
||||||
|
int len = 0;
|
||||||
|
while (len+ofs<data.length && data[ofs + len] != 0) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the totalSize field - the total length of the structure
|
||||||
|
* is totalSize + 4 (value of this field + size of this field).
|
||||||
|
*
|
||||||
|
* @return the totalSize
|
||||||
|
*/
|
||||||
|
public int getTotalSize() {
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags1 - currently unknown - usually 0x0002.
|
||||||
|
*
|
||||||
|
* @return the flags1
|
||||||
|
*/
|
||||||
|
public short getFlags1() {
|
||||||
|
return flags1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the label field - usually the name of the file (without directory) but
|
||||||
|
* probably may be any name specified during packaging/embedding the data.
|
||||||
|
*
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fileName field - usually the name of the file being embedded
|
||||||
|
* including the full path.
|
||||||
|
*
|
||||||
|
* @return the fileName
|
||||||
|
*/
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags2 - currently unknown - mostly 0x0000.
|
||||||
|
*
|
||||||
|
* @return the flags2
|
||||||
|
*/
|
||||||
|
public short getFlags2() {
|
||||||
|
return flags2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns unknown1 field - currently unknown.
|
||||||
|
*
|
||||||
|
* @return the unknown1
|
||||||
|
*/
|
||||||
|
public byte[] getUnknown1() {
|
||||||
|
return unknown1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}.
|
||||||
|
*
|
||||||
|
* @return the unknown2
|
||||||
|
*/
|
||||||
|
public byte[] getUnknown2() {
|
||||||
|
return unknown2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command field - usually the name of the file being embedded
|
||||||
|
* including the full path, may be a command specified during embedding the file.
|
||||||
|
*
|
||||||
|
* @return the command
|
||||||
|
*/
|
||||||
|
public String getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the embedded file. If the size is 0 (zero), no data has been
|
||||||
|
* embedded. To be sure, that no data has been embedded, check whether
|
||||||
|
* {@link #getDataBuffer()} returns <code>null</code>.
|
||||||
|
*
|
||||||
|
* @return the dataSize
|
||||||
|
*/
|
||||||
|
public int getDataSize() {
|
||||||
|
return dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the buffer containing the embedded file's data, or <code>null</code>
|
||||||
|
* if no data was embedded. Note that an embedding may provide information about
|
||||||
|
* the data, but the actual data is not included. (So label, filename etc. are
|
||||||
|
* available, but this method returns <code>null</code>.)
|
||||||
|
*
|
||||||
|
* @return the dataBuffer
|
||||||
|
*/
|
||||||
|
public byte[] getDataBuffer() {
|
||||||
|
return dataBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the flags3 - currently unknown.
|
||||||
|
*
|
||||||
|
* @return the flags3
|
||||||
|
*/
|
||||||
|
public short getFlags3() {
|
||||||
|
return flags3;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
public class Ole10NativeException extends Exception {
|
||||||
|
public Ole10NativeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,9 @@ package org.apache.poi.poifs.filesystem;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class POIFSDocumentPath
|
* Class POIFSDocumentPath
|
||||||
*
|
*
|
||||||
@ -30,6 +33,8 @@ import java.io.File;
|
|||||||
|
|
||||||
public class POIFSDocumentPath
|
public class POIFSDocumentPath
|
||||||
{
|
{
|
||||||
|
private static final POILogger log = POILogFactory.getLogger(POIFSDocumentPath.class);
|
||||||
|
|
||||||
private String[] components;
|
private String[] components;
|
||||||
private int hashcode = 0;
|
private int hashcode = 0;
|
||||||
|
|
||||||
@ -125,12 +130,17 @@ public class POIFSDocumentPath
|
|||||||
{
|
{
|
||||||
for (int j = 0; j < components.length; j++)
|
for (int j = 0; j < components.length; j++)
|
||||||
{
|
{
|
||||||
if ((components[ j ] == null)
|
if (components[ j ] == null)
|
||||||
|| (components[ j ].length() == 0))
|
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"components cannot contain null or empty strings");
|
"components cannot contain null");
|
||||||
}
|
}
|
||||||
|
if (components[ j ].length() == 0)
|
||||||
|
{
|
||||||
|
log.log(POILogger.WARN, "Directory under " + path + " has an empty name, " +
|
||||||
|
"not all OLE2 readers will handle this file correctly!");
|
||||||
|
}
|
||||||
|
|
||||||
this.components[ j + path.components.length ] =
|
this.components[ j + path.components.length ] =
|
||||||
components[ j ];
|
components[ j ];
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.poifs.dev.POIFSViewable;
|
import org.apache.poi.poifs.dev.POIFSViewable;
|
||||||
import org.apache.poi.poifs.property.DirectoryProperty;
|
import org.apache.poi.poifs.property.DirectoryProperty;
|
||||||
@ -42,11 +43,12 @@ import org.apache.poi.poifs.storage.BlockAllocationTableWriter;
|
|||||||
import org.apache.poi.poifs.storage.BlockList;
|
import org.apache.poi.poifs.storage.BlockList;
|
||||||
import org.apache.poi.poifs.storage.BlockWritable;
|
import org.apache.poi.poifs.storage.BlockWritable;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlockConstants;
|
import org.apache.poi.poifs.storage.HeaderBlockConstants;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlockReader;
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlockWriter;
|
import org.apache.poi.poifs.storage.HeaderBlockWriter;
|
||||||
import org.apache.poi.poifs.storage.RawDataBlockList;
|
import org.apache.poi.poifs.storage.RawDataBlockList;
|
||||||
import org.apache.poi.poifs.storage.SmallBlockTableReader;
|
import org.apache.poi.poifs.storage.SmallBlockTableReader;
|
||||||
import org.apache.poi.poifs.storage.SmallBlockTableWriter;
|
import org.apache.poi.poifs.storage.SmallBlockTableWriter;
|
||||||
|
import org.apache.poi.util.CloseIgnoringInputStream;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.LongField;
|
import org.apache.poi.util.LongField;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
@ -64,47 +66,32 @@ public class POIFSFileSystem
|
|||||||
{
|
{
|
||||||
private static final POILogger _logger =
|
private static final POILogger _logger =
|
||||||
POILogFactory.getLogger(POIFSFileSystem.class);
|
POILogFactory.getLogger(POIFSFileSystem.class);
|
||||||
|
|
||||||
private static final class CloseIgnoringInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final InputStream _is;
|
|
||||||
public CloseIgnoringInputStream(InputStream is) {
|
|
||||||
_is = is;
|
|
||||||
}
|
|
||||||
public int read() throws IOException {
|
|
||||||
return _is.read();
|
|
||||||
}
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
return _is.read(b, off, len);
|
|
||||||
}
|
|
||||||
public void close() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method for clients that want to avoid the auto-close behaviour of the constructor.
|
* Convenience method for clients that want to avoid the auto-close behaviour of the constructor.
|
||||||
*/
|
*/
|
||||||
public static InputStream createNonClosingInputStream(InputStream is) {
|
public static InputStream createNonClosingInputStream(InputStream is) {
|
||||||
return new CloseIgnoringInputStream(is);
|
return new CloseIgnoringInputStream(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyTable _property_table;
|
private PropertyTable _property_table;
|
||||||
private List _documents;
|
private List _documents;
|
||||||
private DirectoryNode _root;
|
private DirectoryNode _root;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What big block size the file uses. Most files
|
* What big block size the file uses. Most files
|
||||||
* use 512 bytes, but a few use 4096
|
* use 512 bytes, but a few use 4096
|
||||||
*/
|
*/
|
||||||
private int bigBlockSize = POIFSConstants.BIG_BLOCK_SIZE;
|
private POIFSBigBlockSize bigBlockSize =
|
||||||
|
POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, intended for writing
|
* Constructor, intended for writing
|
||||||
*/
|
*/
|
||||||
public POIFSFileSystem()
|
public POIFSFileSystem()
|
||||||
{
|
{
|
||||||
_property_table = new PropertyTable();
|
HeaderBlock header_block = new HeaderBlock(bigBlockSize);
|
||||||
|
_property_table = new PropertyTable(header_block);
|
||||||
_documents = new ArrayList();
|
_documents = new ArrayList();
|
||||||
_root = null;
|
_root = null;
|
||||||
}
|
}
|
||||||
@ -112,20 +99,20 @@ public class POIFSFileSystem
|
|||||||
/**
|
/**
|
||||||
* Create a POIFSFileSystem from an <tt>InputStream</tt>. Normally the stream is read until
|
* Create a POIFSFileSystem from an <tt>InputStream</tt>. Normally the stream is read until
|
||||||
* EOF. The stream is always closed.<p/>
|
* EOF. The stream is always closed.<p/>
|
||||||
*
|
*
|
||||||
* Some streams are usable after reaching EOF (typically those that return <code>true</code>
|
* Some streams are usable after reaching EOF (typically those that return <code>true</code>
|
||||||
* for <tt>markSupported()</tt>). In the unlikely case that the caller has such a stream
|
* for <tt>markSupported()</tt>). In the unlikely case that the caller has such a stream
|
||||||
* <i>and</i> needs to use it after this constructor completes, a work around is to wrap the
|
* <i>and</i> needs to use it after this constructor completes, a work around is to wrap the
|
||||||
* stream in order to trap the <tt>close()</tt> call. A convenience method (
|
* stream in order to trap the <tt>close()</tt> call. A convenience method (
|
||||||
* <tt>createNonClosingInputStream()</tt>) has been provided for this purpose:
|
* <tt>createNonClosingInputStream()</tt>) has been provided for this purpose:
|
||||||
* <pre>
|
* <pre>
|
||||||
* InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(is);
|
* InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(is);
|
||||||
* HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
|
* HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
|
||||||
* is.reset();
|
* is.reset();
|
||||||
* doSomethingElse(is);
|
* doSomethingElse(is);
|
||||||
* </pre>
|
* </pre>
|
||||||
* Note also the special case of <tt>ByteArrayInputStream</tt> for which the <tt>close()</tt>
|
* Note also the special case of <tt>ByteArrayInputStream</tt> for which the <tt>close()</tt>
|
||||||
* method does nothing.
|
* method does nothing.
|
||||||
* <pre>
|
* <pre>
|
||||||
* ByteArrayInputStream bais = ...
|
* ByteArrayInputStream bais = ...
|
||||||
* HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.close() !
|
* HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.close() !
|
||||||
@ -144,49 +131,57 @@ public class POIFSFileSystem
|
|||||||
this();
|
this();
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
|
||||||
HeaderBlockReader header_block_reader;
|
HeaderBlock header_block;
|
||||||
RawDataBlockList data_blocks;
|
RawDataBlockList data_blocks;
|
||||||
try {
|
try {
|
||||||
// read the header block from the stream
|
// read the header block from the stream
|
||||||
header_block_reader = new HeaderBlockReader(stream);
|
header_block = new HeaderBlock(stream);
|
||||||
bigBlockSize = header_block_reader.getBigBlockSize();
|
bigBlockSize = header_block.getBigBlockSize();
|
||||||
|
|
||||||
// read the rest of the stream into blocks
|
// read the rest of the stream into blocks
|
||||||
data_blocks = new RawDataBlockList(stream, bigBlockSize);
|
data_blocks = new RawDataBlockList(stream, bigBlockSize);
|
||||||
success = true;
|
success = true;
|
||||||
} finally {
|
} finally {
|
||||||
closeInputStream(stream, success);
|
closeInputStream(stream, success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// set up the block allocation table (necessary for the
|
// set up the block allocation table (necessary for the
|
||||||
// data_blocks to be manageable
|
// data_blocks to be manageable
|
||||||
new BlockAllocationTableReader(header_block_reader.getBATCount(),
|
new BlockAllocationTableReader(header_block.getBigBlockSize(),
|
||||||
header_block_reader.getBATArray(),
|
header_block.getBATCount(),
|
||||||
header_block_reader.getXBATCount(),
|
header_block.getBATArray(),
|
||||||
header_block_reader.getXBATIndex(),
|
header_block.getXBATCount(),
|
||||||
|
header_block.getXBATIndex(),
|
||||||
data_blocks);
|
data_blocks);
|
||||||
|
|
||||||
// get property table from the document
|
// get property table from the document
|
||||||
PropertyTable properties =
|
PropertyTable properties =
|
||||||
new PropertyTable(header_block_reader.getPropertyStart(),
|
new PropertyTable(header_block, data_blocks);
|
||||||
data_blocks);
|
|
||||||
|
|
||||||
// init documents
|
// init documents
|
||||||
processProperties(SmallBlockTableReader
|
processProperties(
|
||||||
.getSmallDocumentBlocks(data_blocks, properties
|
SmallBlockTableReader.getSmallDocumentBlocks(
|
||||||
.getRoot(), header_block_reader
|
bigBlockSize, data_blocks, properties.getRoot(),
|
||||||
.getSBATStart()), data_blocks, properties.getRoot()
|
header_block.getSBATStart()
|
||||||
.getChildren(), null);
|
),
|
||||||
|
data_blocks,
|
||||||
|
properties.getRoot().getChildren(),
|
||||||
|
null,
|
||||||
|
header_block.getPropertyStart()
|
||||||
|
);
|
||||||
|
|
||||||
|
// For whatever reason CLSID of root is always 0.
|
||||||
|
getRoot().setStorageClsid(properties.getRoot().getStorageClsid());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param stream the stream to be closed
|
* @param stream the stream to be closed
|
||||||
* @param success <code>false</code> if an exception is currently being thrown in the calling method
|
* @param success <code>false</code> if an exception is currently being thrown in the calling method
|
||||||
*/
|
*/
|
||||||
private void closeInputStream(InputStream stream, boolean success) {
|
private void closeInputStream(InputStream stream, boolean success) {
|
||||||
|
|
||||||
if(stream.markSupported() && !(stream instanceof ByteArrayInputStream)) {
|
if(stream.markSupported() && !(stream instanceof ByteArrayInputStream)) {
|
||||||
String msg = "POIFS is closing the supplied input stream of type ("
|
String msg = "POIFS is closing the supplied input stream of type ("
|
||||||
+ stream.getClass().getName() + ") which supports mark/reset. "
|
+ stream.getClass().getName() + ") which supports mark/reset. "
|
||||||
+ "This will be a problem for the caller if the stream will still be used. "
|
+ "This will be a problem for the caller if the stream will still be used. "
|
||||||
+ "If that is the case the caller should wrap the input stream to avoid this close logic. "
|
+ "If that is the case the caller should wrap the input stream to avoid this close logic. "
|
||||||
@ -199,7 +194,7 @@ public class POIFSFileSystem
|
|||||||
if(success) {
|
if(success) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
// else not success? Try block did not complete normally
|
// else not success? Try block did not complete normally
|
||||||
// just print stack trace and leave original ex to be thrown
|
// just print stack trace and leave original ex to be thrown
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -207,15 +202,15 @@ public class POIFSFileSystem
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the supplied InputStream (which MUST
|
* Checks that the supplied InputStream (which MUST
|
||||||
* support mark and reset, or be a PushbackInputStream)
|
* support mark and reset, or be a PushbackInputStream)
|
||||||
* has a POIFS (OLE2) header at the start of it.
|
* has a POIFS (OLE2) header at the start of it.
|
||||||
* If your InputStream does not support mark / reset,
|
* If your InputStream does not support mark / reset,
|
||||||
* then wrap it in a PushBackInputStream, then be
|
* then wrap it in a PushBackInputStream, then be
|
||||||
* sure to always use that, and not the original!
|
* sure to always use that, and not the original!
|
||||||
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream
|
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream
|
||||||
*/
|
*/
|
||||||
public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
|
public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
|
||||||
// We want to peek at the first 8 bytes
|
// We want to peek at the first 8 bytes
|
||||||
inp.mark(8);
|
inp.mark(8);
|
||||||
|
|
||||||
byte[] header = new byte[8];
|
byte[] header = new byte[8];
|
||||||
@ -229,7 +224,7 @@ public class POIFSFileSystem
|
|||||||
} else {
|
} else {
|
||||||
inp.reset();
|
inp.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did it match the signature?
|
// Did it match the signature?
|
||||||
return (signature.get() == HeaderBlockConstants._signature);
|
return (signature.get() == HeaderBlockConstants._signature);
|
||||||
}
|
}
|
||||||
@ -288,7 +283,7 @@ public class POIFSFileSystem
|
|||||||
{
|
{
|
||||||
return getRoot().createDirectory(name);
|
return getRoot().createDirectory(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the filesystem out
|
* Write the filesystem out
|
||||||
*
|
*
|
||||||
@ -307,11 +302,11 @@ public class POIFSFileSystem
|
|||||||
|
|
||||||
// create the small block store, and the SBAT
|
// create the small block store, and the SBAT
|
||||||
SmallBlockTableWriter sbtw =
|
SmallBlockTableWriter sbtw =
|
||||||
new SmallBlockTableWriter(_documents, _property_table.getRoot());
|
new SmallBlockTableWriter(bigBlockSize, _documents, _property_table.getRoot());
|
||||||
|
|
||||||
// create the block allocation table
|
// create the block allocation table
|
||||||
BlockAllocationTableWriter bat =
|
BlockAllocationTableWriter bat =
|
||||||
new BlockAllocationTableWriter();
|
new BlockAllocationTableWriter(bigBlockSize);
|
||||||
|
|
||||||
// create a list of BATManaged objects: the documents plus the
|
// create a list of BATManaged objects: the documents plus the
|
||||||
// property table and the small block table
|
// property table and the small block table
|
||||||
@ -349,7 +344,7 @@ public class POIFSFileSystem
|
|||||||
int batStartBlock = bat.createBlocks();
|
int batStartBlock = bat.createBlocks();
|
||||||
|
|
||||||
// get the extended block allocation table blocks
|
// get the extended block allocation table blocks
|
||||||
HeaderBlockWriter header_block_writer = new HeaderBlockWriter();
|
HeaderBlockWriter header_block_writer = new HeaderBlockWriter(bigBlockSize);
|
||||||
BATBlock[] xbat_blocks =
|
BATBlock[] xbat_blocks =
|
||||||
header_block_writer.setBATBlocks(bat.countBlocks(),
|
header_block_writer.setBATBlocks(bat.countBlocks(),
|
||||||
batStartBlock);
|
batStartBlock);
|
||||||
@ -491,7 +486,8 @@ public class POIFSFileSystem
|
|||||||
private void processProperties(final BlockList small_blocks,
|
private void processProperties(final BlockList small_blocks,
|
||||||
final BlockList big_blocks,
|
final BlockList big_blocks,
|
||||||
final Iterator properties,
|
final Iterator properties,
|
||||||
final DirectoryNode dir)
|
final DirectoryNode dir,
|
||||||
|
final int headerPropertiesStartAt)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
while (properties.hasNext())
|
while (properties.hasNext())
|
||||||
@ -511,7 +507,8 @@ public class POIFSFileSystem
|
|||||||
|
|
||||||
processProperties(
|
processProperties(
|
||||||
small_blocks, big_blocks,
|
small_blocks, big_blocks,
|
||||||
(( DirectoryProperty ) property).getChildren(), new_dir);
|
(( DirectoryProperty ) property).getChildren(),
|
||||||
|
new_dir, headerPropertiesStartAt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -522,14 +519,15 @@ public class POIFSFileSystem
|
|||||||
if (property.shouldUseSmallBlocks())
|
if (property.shouldUseSmallBlocks())
|
||||||
{
|
{
|
||||||
document =
|
document =
|
||||||
new POIFSDocument(name, small_blocks
|
new POIFSDocument(name,
|
||||||
.fetchBlocks(startBlock), size);
|
small_blocks.fetchBlocks(startBlock, headerPropertiesStartAt),
|
||||||
|
size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
document =
|
document =
|
||||||
new POIFSDocument(name,
|
new POIFSDocument(name,
|
||||||
big_blocks.fetchBlocks(startBlock),
|
big_blocks.fetchBlocks(startBlock, headerPropertiesStartAt),
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
parent.createDocument(document);
|
parent.createDocument(document);
|
||||||
@ -552,10 +550,7 @@ public class POIFSFileSystem
|
|||||||
{
|
{
|
||||||
return (( POIFSViewable ) getRoot()).getViewableArray();
|
return (( POIFSViewable ) getRoot()).getViewableArray();
|
||||||
}
|
}
|
||||||
else
|
return new Object[ 0 ];
|
||||||
{
|
|
||||||
return new Object[ 0 ];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -572,10 +567,7 @@ public class POIFSFileSystem
|
|||||||
{
|
{
|
||||||
return (( POIFSViewable ) getRoot()).getViewableIterator();
|
return (( POIFSViewable ) getRoot()).getViewableIterator();
|
||||||
}
|
}
|
||||||
else
|
return Collections.EMPTY_LIST.iterator();
|
||||||
{
|
|
||||||
return Collections.EMPTY_LIST.iterator();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -607,9 +599,15 @@ public class POIFSFileSystem
|
|||||||
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
||||||
*/
|
*/
|
||||||
public int getBigBlockSize() {
|
public int getBigBlockSize() {
|
||||||
return bigBlockSize;
|
return bigBlockSize.getBigBlockSize();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
||||||
|
*/
|
||||||
|
public POIFSBigBlockSize getBigBlockSizeDetails() {
|
||||||
|
return bigBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********** END begin implementation of POIFSViewable ********** */
|
/* ********** END begin implementation of POIFSViewable ********** */
|
||||||
} // end public class POIFSFileSystem
|
} // end public class POIFSFileSystem
|
||||||
|
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.nio;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A POIFS {@link DataSource} backed by a byte array.
|
||||||
|
*/
|
||||||
|
public class ByteArrayBackedDataSource extends DataSource {
|
||||||
|
private byte[] buffer;
|
||||||
|
private long size;
|
||||||
|
|
||||||
|
public ByteArrayBackedDataSource(byte[] data, int size) {
|
||||||
|
this.buffer = data;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
public ByteArrayBackedDataSource(byte[] data) {
|
||||||
|
this(data, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer read(int length, long position) {
|
||||||
|
if(position >= size) {
|
||||||
|
throw new IndexOutOfBoundsException(
|
||||||
|
"Unable to read " + length + " bytes from " +
|
||||||
|
position + " in stream of length " + size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int toRead = (int)Math.min(length, size - position);
|
||||||
|
return ByteBuffer.wrap(buffer, (int)position, toRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ByteBuffer src, long position) {
|
||||||
|
// Extend if needed
|
||||||
|
long endPosition = position + src.capacity();
|
||||||
|
if(endPosition > buffer.length) {
|
||||||
|
extend(endPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now copy
|
||||||
|
src.get(buffer, (int)position, src.capacity());
|
||||||
|
|
||||||
|
// Update size if needed
|
||||||
|
if(endPosition > size) {
|
||||||
|
size = endPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extend(long length) {
|
||||||
|
// Consider extending by a bit more than requested
|
||||||
|
long difference = length - buffer.length;
|
||||||
|
if(difference < buffer.length*0.25) {
|
||||||
|
difference = (long)(buffer.length*0.25);
|
||||||
|
}
|
||||||
|
if(difference < 4096) {
|
||||||
|
difference = 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] nb = new byte[(int)(difference+buffer.length)];
|
||||||
|
System.arraycopy(buffer, 0, nb, 0, (int)size);
|
||||||
|
buffer = nb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(OutputStream stream) throws IOException {
|
||||||
|
stream.write(buffer, 0, (int)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
buffer = null;
|
||||||
|
size = -1;
|
||||||
|
}
|
||||||
|
}
|
35
src/java/org/apache/poi/poifs/nio/DataSource.java
Normal file
35
src/java/org/apache/poi/poifs/nio/DataSource.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.nio;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common definition of how we read and write bytes
|
||||||
|
*/
|
||||||
|
public abstract class DataSource {
|
||||||
|
public abstract ByteBuffer read(int length, long position) throws IOException;
|
||||||
|
public abstract void write(ByteBuffer src, long position) throws IOException;
|
||||||
|
public abstract long size() throws IOException;
|
||||||
|
/** Close the underlying stream */
|
||||||
|
public abstract void close() throws IOException;
|
||||||
|
/** Copies the contents to the specified OutputStream */
|
||||||
|
public abstract void copyTo(OutputStream stream) throws IOException;
|
||||||
|
}
|
88
src/java/org/apache/poi/poifs/nio/FileBackedDataSource.java
Normal file
88
src/java/org/apache/poi/poifs/nio/FileBackedDataSource.java
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.nio;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A POIFS {@link DataSource} backed by a File
|
||||||
|
*/
|
||||||
|
public class FileBackedDataSource extends DataSource {
|
||||||
|
private FileChannel channel;
|
||||||
|
|
||||||
|
public FileBackedDataSource(File file) throws FileNotFoundException {
|
||||||
|
if(!file.exists()) {
|
||||||
|
throw new FileNotFoundException(file.toString());
|
||||||
|
}
|
||||||
|
this.channel = (new RandomAccessFile(file, "r")).getChannel();
|
||||||
|
}
|
||||||
|
public FileBackedDataSource(FileChannel channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteBuffer read(int length, long position) throws IOException {
|
||||||
|
if(position >= size()) {
|
||||||
|
throw new IllegalArgumentException("Position " + position + " past the end of the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read
|
||||||
|
channel.position(position);
|
||||||
|
ByteBuffer dst = ByteBuffer.allocate(length);
|
||||||
|
int worked = IOUtils.readFully(channel, dst);
|
||||||
|
|
||||||
|
// Check
|
||||||
|
if(worked == -1) {
|
||||||
|
throw new IllegalArgumentException("Position " + position + " past the end of the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ready it for reading
|
||||||
|
dst.position(0);
|
||||||
|
|
||||||
|
// All done
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ByteBuffer src, long position) throws IOException {
|
||||||
|
channel.write(src, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(OutputStream stream) throws IOException {
|
||||||
|
// Wrap the OutputSteam as a channel
|
||||||
|
WritableByteChannel out = Channels.newChannel(stream);
|
||||||
|
// Now do the transfer
|
||||||
|
channel.transferTo(0, channel.size(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long size() throws IOException {
|
||||||
|
return channel.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,44 +14,35 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.property;
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
import org.apache.poi.poifs.storage.SmallDocumentBlock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directory property
|
* Directory property
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public class DirectoryProperty extends Property implements Parent { // TODO - fix instantiable superclass
|
||||||
|
|
||||||
public class DirectoryProperty
|
/** List of Property instances */
|
||||||
extends Property
|
private List<Property> _children;
|
||||||
implements Parent
|
|
||||||
{
|
|
||||||
|
|
||||||
// List of Property instances
|
/** set of children's names */
|
||||||
private List _children;
|
private Set<String> _children_names;
|
||||||
|
|
||||||
// set of children's names
|
|
||||||
private Set _children_names;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor
|
* Default constructor
|
||||||
*
|
*
|
||||||
* @param name the name of the directory
|
* @param name the name of the directory
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public DirectoryProperty(String name)
|
public DirectoryProperty(String name)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
_children = new ArrayList();
|
_children = new ArrayList<Property>();
|
||||||
_children_names = new HashSet();
|
_children_names = new HashSet<String>();
|
||||||
setName(name);
|
setName(name);
|
||||||
setSize(0);
|
setSize(0);
|
||||||
setPropertyType(PropertyConstants.DIRECTORY_TYPE);
|
setPropertyType(PropertyConstants.DIRECTORY_TYPE);
|
||||||
@ -67,13 +57,12 @@ public class DirectoryProperty
|
|||||||
* @param array byte data
|
* @param array byte data
|
||||||
* @param offset offset into byte data
|
* @param offset offset into byte data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected DirectoryProperty(final int index, final byte [] array,
|
protected DirectoryProperty(final int index, final byte [] array,
|
||||||
final int offset)
|
final int offset)
|
||||||
{
|
{
|
||||||
super(index, array, offset);
|
super(index, array, offset);
|
||||||
_children = new ArrayList();
|
_children = new ArrayList<Property>();
|
||||||
_children_names = new HashSet();
|
_children_names = new HashSet<String>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,8 +73,7 @@ public class DirectoryProperty
|
|||||||
*
|
*
|
||||||
* @return true if the name change could be made, else false
|
* @return true if the name change could be made, else false
|
||||||
*/
|
*/
|
||||||
|
public boolean changeName(Property property, String newName)
|
||||||
public boolean changeName(final Property property, final String newName)
|
|
||||||
{
|
{
|
||||||
boolean result;
|
boolean result;
|
||||||
String oldName = property.getName();
|
String oldName = property.getName();
|
||||||
@ -116,8 +104,7 @@ public class DirectoryProperty
|
|||||||
*
|
*
|
||||||
* @return true if the Property could be deleted, else false
|
* @return true if the Property could be deleted, else false
|
||||||
*/
|
*/
|
||||||
|
public boolean deleteChild(Property property)
|
||||||
public boolean deleteChild(final Property property)
|
|
||||||
{
|
{
|
||||||
boolean result = _children.remove(property);
|
boolean result = _children.remove(property);
|
||||||
|
|
||||||
@ -128,9 +115,7 @@ public class DirectoryProperty
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PropertyComparator
|
public static class PropertyComparator implements Comparator<Property> {
|
||||||
implements Comparator
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object equality, implemented as object identity
|
* Object equality, implemented as object identity
|
||||||
@ -139,7 +124,6 @@ public class DirectoryProperty
|
|||||||
*
|
*
|
||||||
* @return true if identical, else false
|
* @return true if identical, else false
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
return this == o;
|
return this == o;
|
||||||
@ -160,12 +144,11 @@ public class DirectoryProperty
|
|||||||
* zero if o1 == o2,
|
* zero if o1 == o2,
|
||||||
* positive value if o1 > o2.
|
* positive value if o1 > o2.
|
||||||
*/
|
*/
|
||||||
|
public int compare(Property o1, Property o2)
|
||||||
public int compare(Object o1, Object o2)
|
|
||||||
{
|
{
|
||||||
String VBA_PROJECT = "_VBA_PROJECT";
|
String VBA_PROJECT = "_VBA_PROJECT";
|
||||||
String name1 = (( Property ) o1).getName();
|
String name1 = o1.getName();
|
||||||
String name2 = (( Property ) o2).getName();
|
String name2 = o2.getName();
|
||||||
int result = name1.length() - name2.length();
|
int result = name1.length() - name2.length();
|
||||||
|
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
@ -200,14 +183,11 @@ public class DirectoryProperty
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // end private class PropertyComparator
|
}
|
||||||
|
|
||||||
/* ********** START extension of Property ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if a directory type Property
|
* @return true if a directory type Property
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean isDirectory()
|
public boolean isDirectory()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -217,13 +197,11 @@ public class DirectoryProperty
|
|||||||
* Perform whatever activities need to be performed prior to
|
* Perform whatever activities need to be performed prior to
|
||||||
* writing
|
* writing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected void preWrite()
|
protected void preWrite()
|
||||||
{
|
{
|
||||||
if (_children.size() > 0)
|
if (_children.size() > 0)
|
||||||
{
|
{
|
||||||
Property[] children =
|
Property[] children = _children.toArray(new Property[ 0 ]);
|
||||||
( Property [] ) _children.toArray(new Property[ 0 ]);
|
|
||||||
|
|
||||||
Arrays.sort(children, new PropertyComparator());
|
Arrays.sort(children, new PropertyComparator());
|
||||||
int midpoint = children.length / 2;
|
int midpoint = children.length / 2;
|
||||||
@ -259,17 +237,13 @@ public class DirectoryProperty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END extension of Property ********** */
|
|
||||||
/* ********** START implementation of Parent ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an iterator over the children of this Parent; all elements
|
* Get an iterator over the children of this Parent; all elements
|
||||||
* are instances of Property.
|
* are instances of Property.
|
||||||
*
|
*
|
||||||
* @return Iterator of children; may refer to an empty collection
|
* @return Iterator of children; may refer to an empty collection
|
||||||
*/
|
*/
|
||||||
|
public Iterator<Property> getChildren()
|
||||||
public Iterator getChildren()
|
|
||||||
{
|
{
|
||||||
return _children.iterator();
|
return _children.iterator();
|
||||||
}
|
}
|
||||||
@ -282,7 +256,6 @@ public class DirectoryProperty
|
|||||||
* @exception IOException if we already have a child with the same
|
* @exception IOException if we already have a child with the same
|
||||||
* name
|
* name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void addChild(final Property property)
|
public void addChild(final Property property)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -295,7 +268,4 @@ public class DirectoryProperty
|
|||||||
_children_names.add(name);
|
_children_names.add(name);
|
||||||
_children.add(property);
|
_children.add(property);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* ********** END implementation of Parent ********** */
|
|
||||||
} // end public class DirectoryProperty
|
|
||||||
|
|
||||||
|
128
src/java/org/apache/poi/poifs/property/NPropertyTable.java
Normal file
128
src/java/org/apache/poi/poifs/property/NPropertyTable.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSStream;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class embodies the Property Table for a {@link NPOIFSFileSystem};
|
||||||
|
* this is basically the directory for all of the documents in the
|
||||||
|
* filesystem.
|
||||||
|
*/
|
||||||
|
public final class NPropertyTable extends PropertyTableBase {
|
||||||
|
private POIFSBigBlockSize _bigBigBlockSize;
|
||||||
|
|
||||||
|
public NPropertyTable(HeaderBlock headerBlock)
|
||||||
|
{
|
||||||
|
super(headerBlock);
|
||||||
|
_bigBigBlockSize = headerBlock.getBigBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reading constructor (used when we've read in a file and we want
|
||||||
|
* to extract the property table from it). Populates the
|
||||||
|
* properties thoroughly
|
||||||
|
*
|
||||||
|
* @param headerBlock the header block of the file
|
||||||
|
* @param filesystem the filesystem to read from
|
||||||
|
*
|
||||||
|
* @exception IOException if anything goes wrong (which should be
|
||||||
|
* a result of the input being NFG)
|
||||||
|
*/
|
||||||
|
public NPropertyTable(final HeaderBlock headerBlock,
|
||||||
|
final NPOIFSFileSystem filesystem)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
headerBlock,
|
||||||
|
buildProperties(
|
||||||
|
(new NPOIFSStream(filesystem, headerBlock.getPropertyStart())).iterator(),
|
||||||
|
headerBlock.getBigBlockSize()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_bigBigBlockSize = headerBlock.getBigBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds
|
||||||
|
* @param startAt
|
||||||
|
* @param filesystem
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static List<Property> buildProperties(final Iterator<ByteBuffer> dataSource,
|
||||||
|
final POIFSBigBlockSize bigBlockSize) throws IOException
|
||||||
|
{
|
||||||
|
List<Property> properties = new ArrayList<Property>();
|
||||||
|
while(dataSource.hasNext()) {
|
||||||
|
ByteBuffer bb = dataSource.next();
|
||||||
|
|
||||||
|
// Turn it into an array
|
||||||
|
byte[] data;
|
||||||
|
if(bb.hasArray() && bb.arrayOffset() == 0 &&
|
||||||
|
bb.array().length == bigBlockSize.getBigBlockSize()) {
|
||||||
|
data = bb.array();
|
||||||
|
} else {
|
||||||
|
data = new byte[bigBlockSize.getBigBlockSize()];
|
||||||
|
bb.get(data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyFactory.convertToProperties(data, properties);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of BigBlock's this instance uses
|
||||||
|
*
|
||||||
|
* @return count of BigBlock instances
|
||||||
|
*/
|
||||||
|
public int countBlocks()
|
||||||
|
{
|
||||||
|
int size = _properties.size() * POIFSConstants.PROPERTY_SIZE;
|
||||||
|
return (int)Math.ceil(size / _bigBigBlockSize.getBigBlockSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the properties out into the given low-level stream
|
||||||
|
*/
|
||||||
|
public void write(NPOIFSStream stream) throws IOException {
|
||||||
|
// TODO - Use a streaming write
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
for(Property property : _properties) {
|
||||||
|
property.writeData(baos);
|
||||||
|
}
|
||||||
|
stream.updateContents(baos.toByteArray());
|
||||||
|
|
||||||
|
// Update the start position if needed
|
||||||
|
if(getStartBlock() != stream.getStartBlock()) {
|
||||||
|
setStartBlock(stream.getStartBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.property;
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
@ -39,9 +39,7 @@ import org.apache.poi.util.ShortField;
|
|||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class Property
|
public abstract class Property implements Child, POIFSViewable {
|
||||||
implements Child, POIFSViewable
|
|
||||||
{
|
|
||||||
static final private byte _default_fill = ( byte ) 0x00;
|
static final private byte _default_fill = ( byte ) 0x00;
|
||||||
static final private int _name_size_offset = 0x40;
|
static final private int _name_size_offset = 0x40;
|
||||||
static final private int _max_name_length =
|
static final private int _max_name_length =
|
||||||
@ -67,7 +65,7 @@ public abstract class Property
|
|||||||
static final protected byte _NODE_RED = 0;
|
static final protected byte _NODE_RED = 0;
|
||||||
|
|
||||||
// documents must be at least this size to be stored in big blocks
|
// documents must be at least this size to be stored in big blocks
|
||||||
static final private int _big_block_minimum_bytes = 4096;
|
static final private int _big_block_minimum_bytes = POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE;
|
||||||
private String _name;
|
private String _name;
|
||||||
private ShortField _name_size;
|
private ShortField _name_size;
|
||||||
private ByteField _property_type;
|
private ByteField _property_type;
|
||||||
@ -88,10 +86,6 @@ public abstract class Property
|
|||||||
private Child _next_child;
|
private Child _next_child;
|
||||||
private Child _previous_child;
|
private Child _previous_child;
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected Property()
|
protected Property()
|
||||||
{
|
{
|
||||||
_raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
|
_raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
|
||||||
@ -129,8 +123,7 @@ public abstract class Property
|
|||||||
* @param array byte data
|
* @param array byte data
|
||||||
* @param offset offset into byte data
|
* @param offset offset into byte data
|
||||||
*/
|
*/
|
||||||
|
protected Property(int index, byte [] array, int offset)
|
||||||
protected Property(final int index, final byte [] array, final int offset)
|
|
||||||
{
|
{
|
||||||
_raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
|
_raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
|
||||||
System.arraycopy(array, offset, _raw_data, 0,
|
System.arraycopy(array, offset, _raw_data, 0,
|
||||||
@ -187,8 +180,7 @@ public abstract class Property
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream.
|
* stream.
|
||||||
*/
|
*/
|
||||||
|
public void writeData(OutputStream stream)
|
||||||
public void writeData(final OutputStream stream)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
stream.write(_raw_data);
|
stream.write(_raw_data);
|
||||||
@ -200,8 +192,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param startBlock the start block index
|
* @param startBlock the start block index
|
||||||
*/
|
*/
|
||||||
|
public void setStartBlock(int startBlock)
|
||||||
public void setStartBlock(final int startBlock)
|
|
||||||
{
|
{
|
||||||
_start_block.set(startBlock, _raw_data);
|
_start_block.set(startBlock, _raw_data);
|
||||||
}
|
}
|
||||||
@ -209,7 +200,6 @@ public abstract class Property
|
|||||||
/**
|
/**
|
||||||
* @return the start block
|
* @return the start block
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int getStartBlock()
|
public int getStartBlock()
|
||||||
{
|
{
|
||||||
return _start_block.get();
|
return _start_block.get();
|
||||||
@ -220,7 +210,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return size in bytes
|
* @return size in bytes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int getSize()
|
public int getSize()
|
||||||
{
|
{
|
||||||
return _size.get();
|
return _size.get();
|
||||||
@ -232,7 +221,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return true if the size is less than _big_block_minimum_bytes
|
* @return true if the size is less than _big_block_minimum_bytes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean shouldUseSmallBlocks()
|
public boolean shouldUseSmallBlocks()
|
||||||
{
|
{
|
||||||
return Property.isSmall(_size.get());
|
return Property.isSmall(_size.get());
|
||||||
@ -246,8 +234,7 @@ public abstract class Property
|
|||||||
* @return true if the length is less than
|
* @return true if the length is less than
|
||||||
* _big_block_minimum_bytes
|
* _big_block_minimum_bytes
|
||||||
*/
|
*/
|
||||||
|
public static boolean isSmall(int length)
|
||||||
public static boolean isSmall(final int length)
|
|
||||||
{
|
{
|
||||||
return length < _big_block_minimum_bytes;
|
return length < _big_block_minimum_bytes;
|
||||||
}
|
}
|
||||||
@ -257,7 +244,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return property name as String
|
* @return property name as String
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return _name;
|
return _name;
|
||||||
@ -266,7 +252,6 @@ public abstract class Property
|
|||||||
/**
|
/**
|
||||||
* @return true if a directory type Property
|
* @return true if a directory type Property
|
||||||
*/
|
*/
|
||||||
|
|
||||||
abstract public boolean isDirectory();
|
abstract public boolean isDirectory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,7 +269,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param name the new name
|
* @param name the new name
|
||||||
*/
|
*/
|
||||||
protected final void setName(final String name)
|
protected void setName(String name)
|
||||||
{
|
{
|
||||||
char[] char_array = name.toCharArray();
|
char[] char_array = name.toCharArray();
|
||||||
int limit = Math.min(char_array.length, _max_name_length);
|
int limit = Math.min(char_array.length, _max_name_length);
|
||||||
@ -329,8 +314,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param propertyType the property type (root, file, directory)
|
* @param propertyType the property type (root, file, directory)
|
||||||
*/
|
*/
|
||||||
|
protected void setPropertyType(byte propertyType)
|
||||||
protected void setPropertyType(final byte propertyType)
|
|
||||||
{
|
{
|
||||||
_property_type.set(propertyType, _raw_data);
|
_property_type.set(propertyType, _raw_data);
|
||||||
}
|
}
|
||||||
@ -340,8 +324,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param nodeColor the node color (red or black)
|
* @param nodeColor the node color (red or black)
|
||||||
*/
|
*/
|
||||||
|
protected void setNodeColor(byte nodeColor)
|
||||||
protected void setNodeColor(final byte nodeColor)
|
|
||||||
{
|
{
|
||||||
_node_color.set(nodeColor, _raw_data);
|
_node_color.set(nodeColor, _raw_data);
|
||||||
}
|
}
|
||||||
@ -351,8 +334,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param child the child property's index in the Property Table
|
* @param child the child property's index in the Property Table
|
||||||
*/
|
*/
|
||||||
|
protected void setChildProperty(int child)
|
||||||
protected void setChildProperty(final int child)
|
|
||||||
{
|
{
|
||||||
_child_property.set(child, _raw_data);
|
_child_property.set(child, _raw_data);
|
||||||
}
|
}
|
||||||
@ -362,7 +344,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return child property index
|
* @return child property index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected int getChildIndex()
|
protected int getChildIndex()
|
||||||
{
|
{
|
||||||
return _child_property.get();
|
return _child_property.get();
|
||||||
@ -373,8 +354,7 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @param size the size of the document, in bytes
|
* @param size the size of the document, in bytes
|
||||||
*/
|
*/
|
||||||
|
protected void setSize(int size)
|
||||||
protected void setSize(final int size)
|
|
||||||
{
|
{
|
||||||
_size.set(size, _raw_data);
|
_size.set(size, _raw_data);
|
||||||
}
|
}
|
||||||
@ -385,8 +365,7 @@ public abstract class Property
|
|||||||
* @param index this Property's index within its containing
|
* @param index this Property's index within its containing
|
||||||
* Property Table
|
* Property Table
|
||||||
*/
|
*/
|
||||||
|
protected void setIndex(int index)
|
||||||
protected void setIndex(final int index)
|
|
||||||
{
|
{
|
||||||
_index = index;
|
_index = index;
|
||||||
}
|
}
|
||||||
@ -396,7 +375,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return the index of this Property within its Property Table
|
* @return the index of this Property within its Property Table
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected int getIndex()
|
protected int getIndex()
|
||||||
{
|
{
|
||||||
return _index;
|
return _index;
|
||||||
@ -406,7 +384,6 @@ public abstract class Property
|
|||||||
* Perform whatever activities need to be performed prior to
|
* Perform whatever activities need to be performed prior to
|
||||||
* writing
|
* writing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
abstract protected void preWrite();
|
abstract protected void preWrite();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -414,7 +391,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return index of next sibling
|
* @return index of next sibling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int getNextChildIndex()
|
int getNextChildIndex()
|
||||||
{
|
{
|
||||||
return _next_property.get();
|
return _next_property.get();
|
||||||
@ -425,7 +401,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return index of previous sibling
|
* @return index of previous sibling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int getPreviousChildIndex()
|
int getPreviousChildIndex()
|
||||||
{
|
{
|
||||||
return _previous_property.get();
|
return _previous_property.get();
|
||||||
@ -438,20 +413,16 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return true if the index is valid
|
* @return true if the index is valid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static boolean isValidIndex(int index)
|
static boolean isValidIndex(int index)
|
||||||
{
|
{
|
||||||
return index != _NO_INDEX;
|
return index != _NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** START implementation of Child ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next Child, if any
|
* Get the next Child, if any
|
||||||
*
|
*
|
||||||
* @return the next Child; may return null
|
* @return the next Child; may return null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Child getNextChild()
|
public Child getNextChild()
|
||||||
{
|
{
|
||||||
return _next_child;
|
return _next_child;
|
||||||
@ -462,7 +433,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return the previous Child; may return null
|
* @return the previous Child; may return null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Child getPreviousChild()
|
public Child getPreviousChild()
|
||||||
{
|
{
|
||||||
return _previous_child;
|
return _previous_child;
|
||||||
@ -474,8 +444,7 @@ public abstract class Property
|
|||||||
* @param child the new 'next' child; may be null, which has the
|
* @param child the new 'next' child; may be null, which has the
|
||||||
* effect of saying there is no 'next' child
|
* effect of saying there is no 'next' child
|
||||||
*/
|
*/
|
||||||
|
public void setNextChild(Child child)
|
||||||
public void setNextChild(final Child child)
|
|
||||||
{
|
{
|
||||||
_next_child = child;
|
_next_child = child;
|
||||||
_next_property.set((child == null) ? _NO_INDEX
|
_next_property.set((child == null) ? _NO_INDEX
|
||||||
@ -489,8 +458,7 @@ public abstract class Property
|
|||||||
* @param child the new 'previous' child; may be null, which has
|
* @param child the new 'previous' child; may be null, which has
|
||||||
* the effect of saying there is no 'previous' child
|
* the effect of saying there is no 'previous' child
|
||||||
*/
|
*/
|
||||||
|
public void setPreviousChild(Child child)
|
||||||
public void setPreviousChild(final Child child)
|
|
||||||
{
|
{
|
||||||
_previous_child = child;
|
_previous_child = child;
|
||||||
_previous_property.set((child == null) ? _NO_INDEX
|
_previous_property.set((child == null) ? _NO_INDEX
|
||||||
@ -498,16 +466,12 @@ public abstract class Property
|
|||||||
.getIndex(), _raw_data);
|
.getIndex(), _raw_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END implementation of Child ********** */
|
|
||||||
/* ********** START begin implementation of POIFSViewable ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of objects, some of which may implement
|
* Get an array of objects, some of which may implement
|
||||||
* POIFSViewable
|
* POIFSViewable
|
||||||
*
|
*
|
||||||
* @return an array of Object; may not be null, but may be empty
|
* @return an array of Object; may not be null, but may be empty
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Object [] getViewableArray()
|
public Object [] getViewableArray()
|
||||||
{
|
{
|
||||||
Object[] results = new Object[ 5 ];
|
Object[] results = new Object[ 5 ];
|
||||||
@ -518,11 +482,11 @@ public abstract class Property
|
|||||||
long time = _days_1.get();
|
long time = _days_1.get();
|
||||||
|
|
||||||
time <<= 32;
|
time <<= 32;
|
||||||
time += (( long ) _seconds_1.get()) & 0x0000FFFFL;
|
time += _seconds_1.get() & 0x0000FFFFL;
|
||||||
results[ 3 ] = "Time 1 = " + time;
|
results[ 3 ] = "Time 1 = " + time;
|
||||||
time = _days_2.get();
|
time = _days_2.get();
|
||||||
time <<= 32;
|
time <<= 32;
|
||||||
time += (( long ) _seconds_2.get()) & 0x0000FFFFL;
|
time += _seconds_2.get() & 0x0000FFFFL;
|
||||||
results[ 4 ] = "Time 2 = " + time;
|
results[ 4 ] = "Time 2 = " + time;
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
@ -534,7 +498,6 @@ public abstract class Property
|
|||||||
* @return an Iterator; may not be null, but may have an empty
|
* @return an Iterator; may not be null, but may have an empty
|
||||||
* back end store
|
* back end store
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Iterator getViewableIterator()
|
public Iterator getViewableIterator()
|
||||||
{
|
{
|
||||||
return Collections.EMPTY_LIST.iterator();
|
return Collections.EMPTY_LIST.iterator();
|
||||||
@ -547,7 +510,6 @@ public abstract class Property
|
|||||||
* @return true if a viewer should call getViewableArray, false if
|
* @return true if a viewer should call getViewableArray, false if
|
||||||
* a viewer should call getViewableIterator
|
* a viewer should call getViewableIterator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean preferArray()
|
public boolean preferArray()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -559,7 +521,6 @@ public abstract class Property
|
|||||||
*
|
*
|
||||||
* @return short description
|
* @return short description
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public String getShortDescription()
|
public String getShortDescription()
|
||||||
{
|
{
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
@ -567,7 +528,4 @@ public abstract class Property
|
|||||||
buffer.append("Property: \"").append(getName()).append("\"");
|
buffer.append("Property: \"").append(getName()).append("\"");
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* ********** END begin implementation of POIFSViewable ********** */
|
|
||||||
} // end public abstract class Property
|
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import org.apache.poi.poifs.storage.ListManagedBlock;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for turning an array of RawDataBlock instances containing
|
* Factory for turning an array of RawDataBlock instances containing
|
||||||
* Proprty data into an array of proper Property objects.
|
* Property data into an array of proper Property objects.
|
||||||
*
|
*
|
||||||
* The array produced may be sparse, in that any portion of data that
|
* The array produced may be sparse, in that any portion of data that
|
||||||
* should correspond to a Property, but which does not map to a proper
|
* should correspond to a Property, but which does not map to a proper
|
||||||
@ -40,7 +40,6 @@ import org.apache.poi.poifs.storage.ListManagedBlock;
|
|||||||
|
|
||||||
class PropertyFactory
|
class PropertyFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
// no need for an accessible constructor
|
// no need for an accessible constructor
|
||||||
private PropertyFactory()
|
private PropertyFactory()
|
||||||
{
|
{
|
||||||
@ -56,48 +55,52 @@ class PropertyFactory
|
|||||||
*
|
*
|
||||||
* @exception IOException if any of the blocks are empty
|
* @exception IOException if any of the blocks are empty
|
||||||
*/
|
*/
|
||||||
|
static List<Property> convertToProperties(ListManagedBlock [] blocks)
|
||||||
static List convertToProperties(ListManagedBlock [] blocks)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
List properties = new ArrayList();
|
List<Property> properties = new ArrayList<Property>();
|
||||||
|
|
||||||
for (int j = 0; j < blocks.length; j++)
|
for (int j = 0; j < blocks.length; j++) {
|
||||||
{
|
byte[] data = blocks[ j ].getData();
|
||||||
byte[] data = blocks[ j ].getData();
|
convertToProperties(data, properties);
|
||||||
int property_count = data.length
|
|
||||||
/ POIFSConstants.PROPERTY_SIZE;
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
for (int k = 0; k < property_count; k++)
|
|
||||||
{
|
|
||||||
switch (data[ offset + PropertyConstants.PROPERTY_TYPE_OFFSET ])
|
|
||||||
{
|
|
||||||
|
|
||||||
case PropertyConstants.DIRECTORY_TYPE :
|
|
||||||
properties
|
|
||||||
.add(new DirectoryProperty(properties.size(),
|
|
||||||
data, offset));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PropertyConstants.DOCUMENT_TYPE :
|
|
||||||
properties.add(new DocumentProperty(properties.size(),
|
|
||||||
data, offset));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PropertyConstants.ROOT_TYPE :
|
|
||||||
properties.add(new RootProperty(properties.size(),
|
|
||||||
data, offset));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
|
||||||
properties.add(null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
offset += POIFSConstants.PROPERTY_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void convertToProperties(byte[] data, List<Property> properties)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int property_count = data.length / POIFSConstants.PROPERTY_SIZE;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
for (int k = 0; k < property_count; k++) {
|
||||||
|
switch (data[ offset + PropertyConstants.PROPERTY_TYPE_OFFSET ]) {
|
||||||
|
case PropertyConstants.DIRECTORY_TYPE :
|
||||||
|
properties.add(
|
||||||
|
new DirectoryProperty(properties.size(), data, offset)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PropertyConstants.DOCUMENT_TYPE :
|
||||||
|
properties.add(
|
||||||
|
new DocumentProperty(properties.size(), data, offset)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PropertyConstants.ROOT_TYPE :
|
||||||
|
properties.add(
|
||||||
|
new RootProperty(properties.size(), data, offset)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default :
|
||||||
|
properties.add(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += POIFSConstants.PROPERTY_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // end package scope class PropertyFactory
|
} // end package scope class PropertyFactory
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,46 +14,34 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.property;
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import java.util.*;
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
|
||||||
import org.apache.poi.poifs.filesystem.BATManaged;
|
|
||||||
import org.apache.poi.poifs.storage.BlockWritable;
|
import org.apache.poi.poifs.storage.BlockWritable;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
import org.apache.poi.poifs.storage.PropertyBlock;
|
import org.apache.poi.poifs.storage.PropertyBlock;
|
||||||
import org.apache.poi.poifs.storage.RawDataBlock;
|
|
||||||
import org.apache.poi.poifs.storage.RawDataBlockList;
|
import org.apache.poi.poifs.storage.RawDataBlockList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class embodies the Property Table for the filesystem; this is
|
* This class embodies the Property Table for the {@link POIFSFileSystem};
|
||||||
* basically the dsirectory for all of the documents in the
|
* this is basically the directory for all of the documents in the
|
||||||
* filesystem.
|
* filesystem.
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class PropertyTable extends PropertyTableBase implements BlockWritable {
|
||||||
|
private POIFSBigBlockSize _bigBigBlockSize;
|
||||||
|
private BlockWritable[] _blocks;
|
||||||
|
|
||||||
public class PropertyTable
|
public PropertyTable(HeaderBlock headerBlock)
|
||||||
implements BATManaged, BlockWritable
|
|
||||||
{
|
|
||||||
private int _start_block;
|
|
||||||
private List _properties;
|
|
||||||
private BlockWritable[] _blocks;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*/
|
|
||||||
|
|
||||||
public PropertyTable()
|
|
||||||
{
|
{
|
||||||
_start_block = POIFSConstants.END_OF_CHAIN;
|
super(headerBlock);
|
||||||
_properties = new ArrayList();
|
_bigBigBlockSize = headerBlock.getBigBlockSize();
|
||||||
addProperty(new RootProperty());
|
|
||||||
_blocks = null;
|
_blocks = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,68 +50,32 @@ public class PropertyTable
|
|||||||
* to extract the property table from it). Populates the
|
* to extract the property table from it). Populates the
|
||||||
* properties thoroughly
|
* properties thoroughly
|
||||||
*
|
*
|
||||||
* @param startBlock the first block of the property table
|
* @param headerBlock the header block of the file
|
||||||
* @param blockList the list of blocks
|
* @param blockList the list of blocks
|
||||||
*
|
*
|
||||||
* @exception IOException if anything goes wrong (which should be
|
* @exception IOException if anything goes wrong (which should be
|
||||||
* a result of the input being NFG)
|
* a result of the input being NFG)
|
||||||
*/
|
*/
|
||||||
|
public PropertyTable(final HeaderBlock headerBlock,
|
||||||
public PropertyTable(final int startBlock,
|
|
||||||
final RawDataBlockList blockList)
|
final RawDataBlockList blockList)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
_start_block = POIFSConstants.END_OF_CHAIN;
|
super(
|
||||||
|
headerBlock,
|
||||||
|
PropertyFactory.convertToProperties(
|
||||||
|
blockList.fetchBlocks(headerBlock.getPropertyStart(), -1)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
_bigBigBlockSize = headerBlock.getBigBlockSize();
|
||||||
_blocks = null;
|
_blocks = null;
|
||||||
_properties =
|
|
||||||
PropertyFactory
|
|
||||||
.convertToProperties(blockList.fetchBlocks(startBlock));
|
|
||||||
populatePropertyTree(( DirectoryProperty ) _properties.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a property to the list of properties we manage
|
|
||||||
*
|
|
||||||
* @param property the new Property to manage
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void addProperty(final Property property)
|
|
||||||
{
|
|
||||||
_properties.add(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a property from the list of properties we manage
|
|
||||||
*
|
|
||||||
* @param property the Property to be removed
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void removeProperty(final Property property)
|
|
||||||
{
|
|
||||||
_properties.remove(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the root property
|
|
||||||
*
|
|
||||||
* @return the root property
|
|
||||||
*/
|
|
||||||
|
|
||||||
public RootProperty getRoot()
|
|
||||||
{
|
|
||||||
|
|
||||||
// it's always the first element in the List
|
|
||||||
return ( RootProperty ) _properties.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare to be written
|
* Prepare to be written
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void preWrite()
|
public void preWrite()
|
||||||
{
|
{
|
||||||
Property[] properties =
|
Property[] properties = _properties.toArray(new Property[_properties.size()]);
|
||||||
( Property [] ) _properties.toArray(new Property[ 0 ]);
|
|
||||||
|
|
||||||
// give each property its index
|
// give each property its index
|
||||||
for (int k = 0; k < properties.length; k++)
|
for (int k = 0; k < properties.length; k++)
|
||||||
@ -133,7 +84,7 @@ public class PropertyTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allocate the blocks for the property table
|
// allocate the blocks for the property table
|
||||||
_blocks = PropertyBlock.createPropertyBlockArray(_properties);
|
_blocks = PropertyBlock.createPropertyBlockArray(_bigBigBlockSize, _properties);
|
||||||
|
|
||||||
// prepare each property for writing
|
// prepare each property for writing
|
||||||
for (int k = 0; k < properties.length; k++)
|
for (int k = 0; k < properties.length; k++)
|
||||||
@ -141,83 +92,18 @@ public class PropertyTable
|
|||||||
properties[ k ].preWrite();
|
properties[ k ].preWrite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the start block for the property table
|
|
||||||
*
|
|
||||||
* @return start block index
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int getStartBlock()
|
|
||||||
{
|
|
||||||
return _start_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populatePropertyTree(DirectoryProperty root)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
int index = root.getChildIndex();
|
|
||||||
|
|
||||||
if (!Property.isValidIndex(index))
|
|
||||||
{
|
|
||||||
|
|
||||||
// property has no children
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Stack children = new Stack();
|
|
||||||
|
|
||||||
children.push(_properties.get(index));
|
|
||||||
while (!children.empty())
|
|
||||||
{
|
|
||||||
Property property = ( Property ) children.pop();
|
|
||||||
|
|
||||||
root.addChild(property);
|
|
||||||
if (property.isDirectory())
|
|
||||||
{
|
|
||||||
populatePropertyTree(( DirectoryProperty ) property);
|
|
||||||
}
|
|
||||||
index = property.getPreviousChildIndex();
|
|
||||||
if (Property.isValidIndex(index))
|
|
||||||
{
|
|
||||||
children.push(_properties.get(index));
|
|
||||||
}
|
|
||||||
index = property.getNextChildIndex();
|
|
||||||
if (Property.isValidIndex(index))
|
|
||||||
{
|
|
||||||
children.push(_properties.get(index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ********** START implementation of BATManaged ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of BigBlock's this instance uses
|
* Return the number of BigBlock's this instance uses
|
||||||
*
|
*
|
||||||
* @return count of BigBlock instances
|
* @return count of BigBlock instances
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int countBlocks()
|
public int countBlocks()
|
||||||
{
|
{
|
||||||
return (_blocks == null) ? 0
|
return (_blocks == null) ? 0
|
||||||
: _blocks.length;
|
: _blocks.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the start block for this instance
|
|
||||||
*
|
|
||||||
* @param index index into the array of BigBlock instances making
|
|
||||||
* up the the filesystem
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void setStartBlock(final int index)
|
|
||||||
{
|
|
||||||
_start_block = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ********** END implementation of BATManaged ********** */
|
|
||||||
/* ********** START implementation of BlockWritable ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the storage to an OutputStream
|
* Write the storage to an OutputStream
|
||||||
*
|
*
|
||||||
@ -227,7 +113,6 @@ public class PropertyTable
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void writeBlocks(final OutputStream stream)
|
public void writeBlocks(final OutputStream stream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -239,7 +124,4 @@ public class PropertyTable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* ********** END implementation of BlockWritable ********** */
|
|
||||||
} // end public class PropertyTable
|
|
||||||
|
|
||||||
|
153
src/java/org/apache/poi/poifs/property/PropertyTableBase.java
Normal file
153
src/java/org/apache/poi/poifs/property/PropertyTableBase.java
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.filesystem.BATManaged;
|
||||||
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class embodies the Property Table for the filesystem,
|
||||||
|
* which looks up entries in the filesystem to their
|
||||||
|
* chain of blocks.
|
||||||
|
* This is the core support, there are implementations
|
||||||
|
* for the different block schemes as needed.
|
||||||
|
*/
|
||||||
|
public abstract class PropertyTableBase implements BATManaged {
|
||||||
|
private final HeaderBlock _header_block;
|
||||||
|
protected final List<Property> _properties;
|
||||||
|
|
||||||
|
public PropertyTableBase(final HeaderBlock header_block)
|
||||||
|
{
|
||||||
|
_header_block = header_block;
|
||||||
|
_properties = new ArrayList<Property>();
|
||||||
|
addProperty(new RootProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reading constructor (used when we've read in a file and we want
|
||||||
|
* to extract the property table from it). Populates the
|
||||||
|
* properties thoroughly
|
||||||
|
*
|
||||||
|
* @param startBlock the first block of the property table
|
||||||
|
* @param blockList the list of blocks
|
||||||
|
*
|
||||||
|
* @exception IOException if anything goes wrong (which should be
|
||||||
|
* a result of the input being NFG)
|
||||||
|
*/
|
||||||
|
public PropertyTableBase(final HeaderBlock header_block,
|
||||||
|
final List<Property> properties)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
_header_block = header_block;
|
||||||
|
_properties = properties;
|
||||||
|
populatePropertyTree( (DirectoryProperty)_properties.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a property to the list of properties we manage
|
||||||
|
*
|
||||||
|
* @param property the new Property to manage
|
||||||
|
*/
|
||||||
|
public void addProperty(Property property)
|
||||||
|
{
|
||||||
|
_properties.add(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a property from the list of properties we manage
|
||||||
|
*
|
||||||
|
* @param property the Property to be removed
|
||||||
|
*/
|
||||||
|
public void removeProperty(final Property property)
|
||||||
|
{
|
||||||
|
_properties.remove(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root property
|
||||||
|
*
|
||||||
|
* @return the root property
|
||||||
|
*/
|
||||||
|
public RootProperty getRoot()
|
||||||
|
{
|
||||||
|
// it's always the first element in the List
|
||||||
|
return ( RootProperty ) _properties.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populatePropertyTree(DirectoryProperty root)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int index = root.getChildIndex();
|
||||||
|
|
||||||
|
if (!Property.isValidIndex(index))
|
||||||
|
{
|
||||||
|
|
||||||
|
// property has no children
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Stack<Property> children = new Stack<Property>();
|
||||||
|
|
||||||
|
children.push(_properties.get(index));
|
||||||
|
while (!children.empty())
|
||||||
|
{
|
||||||
|
Property property = children.pop();
|
||||||
|
|
||||||
|
root.addChild(property);
|
||||||
|
if (property.isDirectory())
|
||||||
|
{
|
||||||
|
populatePropertyTree(( DirectoryProperty ) property);
|
||||||
|
}
|
||||||
|
index = property.getPreviousChildIndex();
|
||||||
|
if (Property.isValidIndex(index))
|
||||||
|
{
|
||||||
|
children.push(_properties.get(index));
|
||||||
|
}
|
||||||
|
index = property.getNextChildIndex();
|
||||||
|
if (Property.isValidIndex(index))
|
||||||
|
{
|
||||||
|
children.push(_properties.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start block for the property table
|
||||||
|
*
|
||||||
|
* @return start block index
|
||||||
|
*/
|
||||||
|
public int getStartBlock()
|
||||||
|
{
|
||||||
|
return _header_block.getPropertyStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the start block for this instance
|
||||||
|
*
|
||||||
|
* @param index index into the array of BigBlock instances making
|
||||||
|
* up the the filesystem
|
||||||
|
*/
|
||||||
|
public void setStartBlock(final int index)
|
||||||
|
{
|
||||||
|
_header_block.setPropertyStart(index);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,14 +14,9 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.property;
|
package org.apache.poi.poifs.property;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.poifs.storage.SmallDocumentBlock;
|
import org.apache.poi.poifs.storage.SmallDocumentBlock;
|
||||||
|
|
||||||
@ -31,18 +25,12 @@ import org.apache.poi.poifs.storage.SmallDocumentBlock;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class RootProperty extends DirectoryProperty {
|
||||||
public class RootProperty
|
private static final String NAME = "Root Entry";
|
||||||
extends DirectoryProperty
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*/
|
|
||||||
|
|
||||||
RootProperty()
|
RootProperty()
|
||||||
{
|
{
|
||||||
super("Root Entry");
|
super(NAME);
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
setNodeColor(_NODE_BLACK);
|
setNodeColor(_NODE_BLACK);
|
||||||
@ -57,7 +45,6 @@ public class RootProperty
|
|||||||
* @param array byte data
|
* @param array byte data
|
||||||
* @param offset offset into byte data
|
* @param offset offset into byte data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected RootProperty(final int index, final byte [] array,
|
protected RootProperty(final int index, final byte [] array,
|
||||||
final int offset)
|
final int offset)
|
||||||
{
|
{
|
||||||
@ -69,10 +56,17 @@ public class RootProperty
|
|||||||
*
|
*
|
||||||
* @param size size in terms of small blocks
|
* @param size size in terms of small blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setSize(int size)
|
public void setSize(int size)
|
||||||
{
|
{
|
||||||
super.setSize(SmallDocumentBlock.calcSize(size));
|
super.setSize(SmallDocumentBlock.calcSize(size));
|
||||||
}
|
}
|
||||||
} // end public class RootProperty
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fixed name "Root Entry", as the
|
||||||
|
* raw property doesn't have a real name set
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,19 +14,18 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A block of block allocation table entries. BATBlocks are created
|
* A block of block allocation table entries. BATBlocks are created
|
||||||
@ -35,163 +33,37 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class BATBlock extends BigBlock {
|
||||||
public class BATBlock
|
/**
|
||||||
extends BigBlock
|
* For a regular fat block, these are 128 / 1024
|
||||||
{
|
* next sector values.
|
||||||
private static final int _entries_per_block =
|
* For a XFat (DIFat) block, these are 127 / 1023
|
||||||
POIFSConstants.BIG_BLOCK_SIZE / LittleEndianConsts.INT_SIZE;
|
* next sector values, then a chaining value.
|
||||||
private static final int _entries_per_xbat_block = _entries_per_block
|
*/
|
||||||
- 1;
|
private int[] _values;
|
||||||
private static final int _xbat_chain_offset =
|
|
||||||
_entries_per_xbat_block * LittleEndianConsts.INT_SIZE;
|
/**
|
||||||
private static final byte _default_value = ( byte ) 0xFF;
|
* Does this BATBlock have any free sectors in it?
|
||||||
private IntegerField[] _fields;
|
*/
|
||||||
private byte[] _data;
|
private boolean _has_free_sectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where in the file are we?
|
||||||
|
*/
|
||||||
|
private int ourBlockIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a single instance initialized with default values
|
* Create a single instance initialized with default values
|
||||||
*/
|
*/
|
||||||
|
private BATBlock(POIFSBigBlockSize bigBlockSize)
|
||||||
private BATBlock()
|
|
||||||
{
|
{
|
||||||
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
|
super(bigBlockSize);
|
||||||
Arrays.fill(_data, _default_value);
|
|
||||||
_fields = new IntegerField[ _entries_per_block ];
|
int _entries_per_block = bigBlockSize.getBATEntriesPerBlock();
|
||||||
int offset = 0;
|
_values = new int[_entries_per_block];
|
||||||
|
_has_free_sectors = true;
|
||||||
|
|
||||||
for (int j = 0; j < _entries_per_block; j++)
|
Arrays.fill(_values, POIFSConstants.UNUSED_BLOCK);
|
||||||
{
|
|
||||||
_fields[ j ] = new IntegerField(offset);
|
|
||||||
offset += LittleEndianConsts.INT_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an array of BATBlocks from an array of int block
|
|
||||||
* allocation table entries
|
|
||||||
*
|
|
||||||
* @param entries the array of int entries
|
|
||||||
*
|
|
||||||
* @return the newly created array of BATBlocks
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static BATBlock [] createBATBlocks(final int [] entries)
|
|
||||||
{
|
|
||||||
int block_count = calculateStorageRequirements(entries.length);
|
|
||||||
BATBlock[] blocks = new BATBlock[ block_count ];
|
|
||||||
int index = 0;
|
|
||||||
int remaining = entries.length;
|
|
||||||
|
|
||||||
for (int j = 0; j < entries.length; j += _entries_per_block)
|
|
||||||
{
|
|
||||||
blocks[ index++ ] = new BATBlock(entries, j,
|
|
||||||
(remaining > _entries_per_block)
|
|
||||||
? j + _entries_per_block
|
|
||||||
: entries.length);
|
|
||||||
remaining -= _entries_per_block;
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an array of XBATBlocks from an array of int block
|
|
||||||
* allocation table entries
|
|
||||||
*
|
|
||||||
* @param entries the array of int entries
|
|
||||||
* @param startBlock the start block of the array of XBAT blocks
|
|
||||||
*
|
|
||||||
* @return the newly created array of BATBlocks
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static BATBlock [] createXBATBlocks(final int [] entries,
|
|
||||||
final int startBlock)
|
|
||||||
{
|
|
||||||
int block_count =
|
|
||||||
calculateXBATStorageRequirements(entries.length);
|
|
||||||
BATBlock[] blocks = new BATBlock[ block_count ];
|
|
||||||
int index = 0;
|
|
||||||
int remaining = entries.length;
|
|
||||||
|
|
||||||
if (block_count != 0)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < entries.length; j += _entries_per_xbat_block)
|
|
||||||
{
|
|
||||||
blocks[ index++ ] =
|
|
||||||
new BATBlock(entries, j,
|
|
||||||
(remaining > _entries_per_xbat_block)
|
|
||||||
? j + _entries_per_xbat_block
|
|
||||||
: entries.length);
|
|
||||||
remaining -= _entries_per_xbat_block;
|
|
||||||
}
|
|
||||||
for (index = 0; index < blocks.length - 1; index++)
|
|
||||||
{
|
|
||||||
blocks[ index ].setXBATChain(startBlock + index + 1);
|
|
||||||
}
|
|
||||||
blocks[ index ].setXBATChain(POIFSConstants.END_OF_CHAIN);
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate how many BATBlocks are needed to hold a specified
|
|
||||||
* number of BAT entries.
|
|
||||||
*
|
|
||||||
* @param entryCount the number of entries
|
|
||||||
*
|
|
||||||
* @return the number of BATBlocks needed
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static int calculateStorageRequirements(final int entryCount)
|
|
||||||
{
|
|
||||||
return (entryCount + _entries_per_block - 1) / _entries_per_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate how many XBATBlocks are needed to hold a specified
|
|
||||||
* number of BAT entries.
|
|
||||||
*
|
|
||||||
* @param entryCount the number of entries
|
|
||||||
*
|
|
||||||
* @return the number of XBATBlocks needed
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static int calculateXBATStorageRequirements(final int entryCount)
|
|
||||||
{
|
|
||||||
return (entryCount + _entries_per_xbat_block - 1)
|
|
||||||
/ _entries_per_xbat_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return number of entries per block
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final int entriesPerBlock()
|
|
||||||
{
|
|
||||||
return _entries_per_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return number of entries per XBAT block
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final int entriesPerXBATBlock()
|
|
||||||
{
|
|
||||||
return _entries_per_xbat_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return offset of chain index of XBAT block
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final int getXBATChainOffset()
|
|
||||||
{
|
|
||||||
return _xbat_chain_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setXBATChain(int chainIndex)
|
|
||||||
{
|
|
||||||
_fields[ _entries_per_xbat_block ].set(chainIndex, _data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,19 +77,271 @@ public class BATBlock
|
|||||||
* k, start_index <= k < end_index)
|
* k, start_index <= k < end_index)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private BATBlock(final int [] entries, final int start_index,
|
private BATBlock(POIFSBigBlockSize bigBlockSize, final int [] entries,
|
||||||
final int end_index)
|
final int start_index, final int end_index)
|
||||||
{
|
{
|
||||||
this();
|
this(bigBlockSize);
|
||||||
for (int k = start_index; k < end_index; k++)
|
for (int k = start_index; k < end_index; k++) {
|
||||||
{
|
_values[k - start_index] = entries[k];
|
||||||
_fields[ k - start_index ].set(entries[ k ], _data);
|
}
|
||||||
|
|
||||||
|
// Do we have any free sectors?
|
||||||
|
if(end_index - start_index == _values.length) {
|
||||||
|
recomputeFree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void recomputeFree() {
|
||||||
|
boolean hasFree = false;
|
||||||
|
for(int k=0; k<_values.length; k++) {
|
||||||
|
if(_values[k] == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
hasFree = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_has_free_sectors = hasFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single BATBlock from the byte buffer, which must hold at least
|
||||||
|
* one big block of data to be read.
|
||||||
|
*/
|
||||||
|
public static BATBlock createBATBlock(final POIFSBigBlockSize bigBlockSize, ByteBuffer data)
|
||||||
|
{
|
||||||
|
// Create an empty block
|
||||||
|
BATBlock block = new BATBlock(bigBlockSize);
|
||||||
|
|
||||||
|
// Fill it
|
||||||
|
byte[] buffer = new byte[LittleEndian.INT_SIZE];
|
||||||
|
for(int i=0; i<block._values.length; i++) {
|
||||||
|
data.get(buffer);
|
||||||
|
block._values[i] = LittleEndian.getInt(buffer);
|
||||||
|
}
|
||||||
|
block.recomputeFree();
|
||||||
|
|
||||||
|
// All done
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a single BATBlock, with all the values set to empty.
|
||||||
|
*/
|
||||||
|
public static BATBlock createEmptyBATBlock(final POIFSBigBlockSize bigBlockSize, boolean isXBAT) {
|
||||||
|
BATBlock block = new BATBlock(bigBlockSize);
|
||||||
|
if(isXBAT) {
|
||||||
|
block.setXBATChain(bigBlockSize, POIFSConstants.END_OF_CHAIN);
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of BATBlocks from an array of int block
|
||||||
|
* allocation table entries
|
||||||
|
*
|
||||||
|
* @param entries the array of int entries
|
||||||
|
*
|
||||||
|
* @return the newly created array of BATBlocks
|
||||||
|
*/
|
||||||
|
public static BATBlock [] createBATBlocks(final POIFSBigBlockSize bigBlockSize, final int [] entries)
|
||||||
|
{
|
||||||
|
int block_count = calculateStorageRequirements(bigBlockSize, entries.length);
|
||||||
|
BATBlock[] blocks = new BATBlock[ block_count ];
|
||||||
|
int index = 0;
|
||||||
|
int remaining = entries.length;
|
||||||
|
|
||||||
|
int _entries_per_block = bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
for (int j = 0; j < entries.length; j += _entries_per_block)
|
||||||
|
{
|
||||||
|
blocks[ index++ ] = new BATBlock(bigBlockSize, entries, j,
|
||||||
|
(remaining > _entries_per_block)
|
||||||
|
? j + _entries_per_block
|
||||||
|
: entries.length);
|
||||||
|
remaining -= _entries_per_block;
|
||||||
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of XBATBlocks from an array of int block
|
||||||
|
* allocation table entries
|
||||||
|
*
|
||||||
|
* @param entries the array of int entries
|
||||||
|
* @param startBlock the start block of the array of XBAT blocks
|
||||||
|
*
|
||||||
|
* @return the newly created array of BATBlocks
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static BATBlock [] createXBATBlocks(final POIFSBigBlockSize bigBlockSize,
|
||||||
|
final int [] entries,
|
||||||
|
final int startBlock)
|
||||||
|
{
|
||||||
|
int block_count =
|
||||||
|
calculateXBATStorageRequirements(bigBlockSize, entries.length);
|
||||||
|
BATBlock[] blocks = new BATBlock[ block_count ];
|
||||||
|
int index = 0;
|
||||||
|
int remaining = entries.length;
|
||||||
|
|
||||||
|
int _entries_per_xbat_block = bigBlockSize.getXBATEntriesPerBlock();
|
||||||
|
if (block_count != 0)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < entries.length; j += _entries_per_xbat_block)
|
||||||
|
{
|
||||||
|
blocks[ index++ ] =
|
||||||
|
new BATBlock(bigBlockSize, entries, j,
|
||||||
|
(remaining > _entries_per_xbat_block)
|
||||||
|
? j + _entries_per_xbat_block
|
||||||
|
: entries.length);
|
||||||
|
remaining -= _entries_per_xbat_block;
|
||||||
|
}
|
||||||
|
for (index = 0; index < blocks.length - 1; index++)
|
||||||
|
{
|
||||||
|
blocks[ index ].setXBATChain(bigBlockSize, startBlock + index + 1);
|
||||||
|
}
|
||||||
|
blocks[ index ].setXBATChain(bigBlockSize, POIFSConstants.END_OF_CHAIN);
|
||||||
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate how many BATBlocks are needed to hold a specified
|
||||||
|
* number of BAT entries.
|
||||||
|
*
|
||||||
|
* @param entryCount the number of entries
|
||||||
|
*
|
||||||
|
* @return the number of BATBlocks needed
|
||||||
|
*/
|
||||||
|
public static int calculateStorageRequirements(final POIFSBigBlockSize bigBlockSize, final int entryCount)
|
||||||
|
{
|
||||||
|
int _entries_per_block = bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
return (entryCount + _entries_per_block - 1) / _entries_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate how many XBATBlocks are needed to hold a specified
|
||||||
|
* number of BAT entries.
|
||||||
|
*
|
||||||
|
* @param entryCount the number of entries
|
||||||
|
*
|
||||||
|
* @return the number of XBATBlocks needed
|
||||||
|
*/
|
||||||
|
public static int calculateXBATStorageRequirements(final POIFSBigBlockSize bigBlockSize, final int entryCount)
|
||||||
|
{
|
||||||
|
int _entries_per_xbat_block = bigBlockSize.getXBATEntriesPerBlock();
|
||||||
|
return (entryCount + _entries_per_xbat_block - 1)
|
||||||
|
/ _entries_per_xbat_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the maximum size of a file which is addressable given the
|
||||||
|
* number of FAT (BAT) sectors specified. (We don't care if those BAT
|
||||||
|
* blocks come from the 109 in the header, or from header + XBATS, it
|
||||||
|
* won't affect the calculation)
|
||||||
|
*
|
||||||
|
* The actual file size will be between [size of fatCount-1 blocks] and
|
||||||
|
* [size of fatCount blocks].
|
||||||
|
* For 512 byte block sizes, this means we may over-estimate by up to 65kb.
|
||||||
|
* For 4096 byte block sizes, this means we may over-estimate by up to 4mb
|
||||||
|
*/
|
||||||
|
public static int calculateMaximumSize(final POIFSBigBlockSize bigBlockSize,
|
||||||
|
final int numBATs) {
|
||||||
|
int size = 1; // Header isn't FAT addressed
|
||||||
|
|
||||||
|
// The header has up to 109 BATs, and extra ones are referenced
|
||||||
|
// from XBATs
|
||||||
|
// However, all BATs can contain 128/1024 blocks
|
||||||
|
size += (numBATs * bigBlockSize.getBATEntriesPerBlock());
|
||||||
|
|
||||||
|
// So far we've been in sector counts, turn into bytes
|
||||||
|
return size * bigBlockSize.getBigBlockSize();
|
||||||
|
}
|
||||||
|
public static int calculateMaximumSize(final HeaderBlock header)
|
||||||
|
{
|
||||||
|
return calculateMaximumSize(header.getBigBlockSize(), header.getBATCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the BATBlock that handles the specified offset,
|
||||||
|
* and the relative index within it.
|
||||||
|
* The List of BATBlocks must be in sequential order
|
||||||
|
*/
|
||||||
|
public static BATBlockAndIndex getBATBlockAndIndex(final int offset,
|
||||||
|
final HeaderBlock header, final List<BATBlock> bats) {
|
||||||
|
POIFSBigBlockSize bigBlockSize = header.getBigBlockSize();
|
||||||
|
|
||||||
|
int whichBAT = (int)Math.floor(offset / bigBlockSize.getBATEntriesPerBlock());
|
||||||
|
int index = offset % bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
return new BATBlockAndIndex( index, bats.get(whichBAT) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the BATBlock that handles the specified offset,
|
||||||
|
* and the relative index within it, for the mini stream.
|
||||||
|
* The List of BATBlocks must be in sequential order
|
||||||
|
*/
|
||||||
|
public static BATBlockAndIndex getSBATBlockAndIndex(final int offset,
|
||||||
|
final HeaderBlock header, final List<BATBlock> sbats) {
|
||||||
|
POIFSBigBlockSize bigBlockSize = header.getBigBlockSize();
|
||||||
|
|
||||||
|
// SBATs are so much easier, as they're chained streams
|
||||||
|
int whichSBAT = (int)Math.floor(offset / bigBlockSize.getBATEntriesPerBlock());
|
||||||
|
int index = offset % bigBlockSize.getBATEntriesPerBlock();
|
||||||
|
return new BATBlockAndIndex( index, sbats.get(whichSBAT) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setXBATChain(final POIFSBigBlockSize bigBlockSize, int chainIndex)
|
||||||
|
{
|
||||||
|
int _entries_per_xbat_block = bigBlockSize.getXBATEntriesPerBlock();
|
||||||
|
_values[ _entries_per_xbat_block ] = chainIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this BATBlock have any free sectors in it, or
|
||||||
|
* is it full?
|
||||||
|
*/
|
||||||
|
public boolean hasFreeSectors() {
|
||||||
|
return _has_free_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValueAt(int relativeOffset) {
|
||||||
|
if(relativeOffset >= _values.length) {
|
||||||
|
throw new ArrayIndexOutOfBoundsException(
|
||||||
|
"Unable to fetch offset " + relativeOffset + " as the " +
|
||||||
|
"BAT only contains " + _values.length + " entries"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _values[relativeOffset];
|
||||||
|
}
|
||||||
|
public void setValueAt(int relativeOffset, int value) {
|
||||||
|
int oldValue = _values[relativeOffset];
|
||||||
|
_values[relativeOffset] = value;
|
||||||
|
|
||||||
|
// Do we need to re-compute the free?
|
||||||
|
if(value == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
_has_free_sectors = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(oldValue == POIFSConstants.UNUSED_BLOCK) {
|
||||||
|
recomputeFree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record where in the file we live
|
||||||
|
*/
|
||||||
|
public void setOurBlockIndex(int index) {
|
||||||
|
this.ourBlockIndex = index;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve where in the file we live
|
||||||
|
*/
|
||||||
|
public int getOurBlockIndex() {
|
||||||
|
return ourBlockIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ********** START extension of BigBlock ********** */
|
/* ********** START extension of BigBlock ********** */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the block's data to an OutputStream
|
* Write the block's data to an OutputStream
|
||||||
*
|
*
|
||||||
* @param stream the OutputStream to which the stored data should
|
* @param stream the OutputStream to which the stored data should
|
||||||
@ -226,13 +350,51 @@ public class BATBlock
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void writeData(final OutputStream stream)
|
void writeData(final OutputStream stream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
doWriteData(stream, _data);
|
// Save it out
|
||||||
|
stream.write( serialize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeData(final ByteBuffer block)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// Save it out
|
||||||
|
block.put( serialize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] serialize() {
|
||||||
|
// Create the empty array
|
||||||
|
byte[] data = new byte[ bigBlockSize.getBigBlockSize() ];
|
||||||
|
|
||||||
|
// Fill in the values
|
||||||
|
int offset = 0;
|
||||||
|
for(int i=0; i<_values.length; i++) {
|
||||||
|
LittleEndian.putInt(data, offset, _values[i]);
|
||||||
|
offset += LittleEndian.INT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END extension of BigBlock ********** */
|
/* ********** END extension of BigBlock ********** */
|
||||||
} // end public class BATBlock
|
|
||||||
|
|
||||||
|
public static class BATBlockAndIndex {
|
||||||
|
private final int index;
|
||||||
|
private final BATBlock block;
|
||||||
|
private BATBlockAndIndex(int index, BATBlock block) {
|
||||||
|
this.index = index;
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
public BATBlock getBlock() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ package org.apache.poi.poifs.storage;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class of all POIFS block storage classes. All
|
* Abstract base class of all POIFS block storage classes. All
|
||||||
* extensions of BigBlock should write 512 bytes of data when
|
* extensions of BigBlock should write 512 or 4096 bytes of data when
|
||||||
* requested to write their data.
|
* requested to write their data (as per their BigBlockSize).
|
||||||
*
|
*
|
||||||
* This class has package scope, as there is no reason at this time to
|
* This class has package scope, as there is no reason at this time to
|
||||||
* make the class public.
|
* make the class public.
|
||||||
@ -33,9 +33,21 @@ package org.apache.poi.poifs.storage;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
|
||||||
abstract class BigBlock
|
abstract class BigBlock
|
||||||
implements BlockWritable
|
implements BlockWritable
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Either 512 bytes ({@link POIFSConstants#SMALLER_BIG_BLOCK_SIZE})
|
||||||
|
* or 4096 bytes ({@link POIFSConstants#LARGER_BIG_BLOCK_SIZE})
|
||||||
|
*/
|
||||||
|
protected POIFSBigBlockSize bigBlockSize;
|
||||||
|
|
||||||
|
protected BigBlock(POIFSBigBlockSize bigBlockSize) {
|
||||||
|
this.bigBlockSize = bigBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of write for extending classes that
|
* Default implementation of write for extending classes that
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,7 +14,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
@ -23,6 +21,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.util.IntList;
|
import org.apache.poi.util.IntList;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
@ -43,10 +42,22 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class BlockAllocationTableReader {
|
||||||
public class BlockAllocationTableReader
|
|
||||||
{
|
/**
|
||||||
private IntList _entries;
|
* Maximum number size (in blocks) of the allocation table as supported by
|
||||||
|
* POI.<br/>
|
||||||
|
*
|
||||||
|
* This constant has been chosen to help POI identify corrupted data in the
|
||||||
|
* header block (rather than crash immediately with {@link OutOfMemoryError}
|
||||||
|
* ). It's not clear if the compound document format actually specifies any
|
||||||
|
* upper limits. For files with 512 byte blocks, having an allocation table
|
||||||
|
* of 65,335 blocks would correspond to a total file size of 4GB. Needless
|
||||||
|
* to say, POI probably cannot handle files anywhere near that size.
|
||||||
|
*/
|
||||||
|
private static final int MAX_BLOCK_COUNT = 65535;
|
||||||
|
private final IntList _entries;
|
||||||
|
private POIFSBigBlockSize bigBlockSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a BlockAllocationTableReader for an existing filesystem. Side
|
* create a BlockAllocationTableReader for an existing filesystem. Side
|
||||||
@ -66,33 +77,41 @@ public class BlockAllocationTableReader
|
|||||||
* @exception IOException if, in trying to create the table, we
|
* @exception IOException if, in trying to create the table, we
|
||||||
* encounter logic errors
|
* encounter logic errors
|
||||||
*/
|
*/
|
||||||
|
public BlockAllocationTableReader(POIFSBigBlockSize bigBlockSize, int block_count, int [] block_array,
|
||||||
|
int xbat_count, int xbat_index, BlockList raw_block_list) throws IOException {
|
||||||
|
this(bigBlockSize);
|
||||||
|
|
||||||
|
sanityCheckBlockCount(block_count);
|
||||||
|
|
||||||
public BlockAllocationTableReader(final int block_count,
|
// We want to get the whole of the FAT table
|
||||||
final int [] block_array,
|
// To do this:
|
||||||
final int xbat_count,
|
// * Work through raw_block_list, which points to the
|
||||||
final int xbat_index,
|
// first (up to) 109 BAT blocks
|
||||||
final BlockList raw_block_list)
|
// * Jump to the XBAT offset, and read in XBATs which
|
||||||
throws IOException
|
// point to more BAT blocks
|
||||||
{
|
|
||||||
this();
|
|
||||||
if (block_count <= 0)
|
|
||||||
{
|
|
||||||
throw new IOException(
|
|
||||||
"Illegal block count; minimum count is 1, got " + block_count
|
|
||||||
+ " instead");
|
|
||||||
}
|
|
||||||
|
|
||||||
// acquire raw data blocks containing the BAT block data
|
|
||||||
RawDataBlock blocks[] = new RawDataBlock[ block_count ];
|
|
||||||
int limit = Math.min(block_count, block_array.length);
|
int limit = Math.min(block_count, block_array.length);
|
||||||
int block_index;
|
int block_index;
|
||||||
|
|
||||||
|
// This will hold all of the BAT blocks in order
|
||||||
|
RawDataBlock blocks[] = new RawDataBlock[ block_count ];
|
||||||
|
|
||||||
|
// Process the first (up to) 109 BAT blocks
|
||||||
for (block_index = 0; block_index < limit; block_index++)
|
for (block_index = 0; block_index < limit; block_index++)
|
||||||
{
|
{
|
||||||
|
// Check that the sector number of the BAT block is a valid one
|
||||||
|
int nextOffset = block_array[ block_index ];
|
||||||
|
if(nextOffset > raw_block_list.blockCount()) {
|
||||||
|
throw new IOException("Your file contains " + raw_block_list.blockCount() +
|
||||||
|
" sectors, but the initial DIFAT array at index " + block_index +
|
||||||
|
" referenced block # " + nextOffset + ". This isn't allowed and " +
|
||||||
|
" your file is corrupt");
|
||||||
|
}
|
||||||
|
// Record the sector number of this BAT block
|
||||||
blocks[ block_index ] =
|
blocks[ block_index ] =
|
||||||
( RawDataBlock ) raw_block_list
|
( RawDataBlock ) raw_block_list.remove(nextOffset);
|
||||||
.remove(block_array[ block_index ]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process additional BAT blocks via the XBATs
|
||||||
if (block_index < block_count)
|
if (block_index < block_count)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -103,9 +122,12 @@ public class BlockAllocationTableReader
|
|||||||
"BAT count exceeds limit, yet XBAT index indicates no valid entries");
|
"BAT count exceeds limit, yet XBAT index indicates no valid entries");
|
||||||
}
|
}
|
||||||
int chain_index = xbat_index;
|
int chain_index = xbat_index;
|
||||||
int max_entries_per_block = BATBlock.entriesPerXBATBlock();
|
int max_entries_per_block = bigBlockSize.getXBATEntriesPerBlock();
|
||||||
int chain_index_offset = BATBlock.getXBATChainOffset();
|
int chain_index_offset = bigBlockSize.getNextXBATChainOffset();
|
||||||
|
|
||||||
|
// Each XBAT block contains either:
|
||||||
|
// (maximum number of sector indexes) + index of next XBAT
|
||||||
|
// some sector indexes + FREE sectors to max # + EndOfChain
|
||||||
for (int j = 0; j < xbat_count; j++)
|
for (int j = 0; j < xbat_count; j++)
|
||||||
{
|
{
|
||||||
limit = Math.min(block_count - block_index,
|
limit = Math.min(block_count - block_index,
|
||||||
@ -132,8 +154,8 @@ public class BlockAllocationTableReader
|
|||||||
throw new IOException("Could not find all blocks");
|
throw new IOException("Could not find all blocks");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now that we have all of the raw data blocks, go through and
|
// Now that we have all of the raw data blocks which make
|
||||||
// create the indices
|
// up the FAT, go through and create the indices
|
||||||
setEntries(blocks, raw_block_list);
|
setEntries(blocks, raw_block_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,25 +167,31 @@ public class BlockAllocationTableReader
|
|||||||
*
|
*
|
||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
BlockAllocationTableReader(POIFSBigBlockSize bigBlockSize, ListManagedBlock[] blocks, BlockList raw_block_list)
|
||||||
BlockAllocationTableReader(final ListManagedBlock [] blocks,
|
throws IOException {
|
||||||
final BlockList raw_block_list)
|
this(bigBlockSize);
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
this();
|
|
||||||
setEntries(blocks, raw_block_list);
|
setEntries(blocks, raw_block_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
BlockAllocationTableReader(POIFSBigBlockSize bigBlockSize) {
|
||||||
* Constructor BlockAllocationTableReader
|
this.bigBlockSize = bigBlockSize;
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
BlockAllocationTableReader()
|
|
||||||
{
|
|
||||||
_entries = new IntList();
|
_entries = new IntList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sanityCheckBlockCount(int block_count) throws IOException {
|
||||||
|
if (block_count <= 0) {
|
||||||
|
throw new IOException(
|
||||||
|
"Illegal block count; minimum count is 1, got " +
|
||||||
|
block_count + " instead"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (block_count > MAX_BLOCK_COUNT) {
|
||||||
|
throw new IOException(
|
||||||
|
"Block count " + block_count +
|
||||||
|
" is too high. POI maximum is " + MAX_BLOCK_COUNT + "."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* walk the entries from a specified point and return the
|
* walk the entries from a specified point and return the
|
||||||
@ -177,21 +205,43 @@ public class BlockAllocationTableReader
|
|||||||
*
|
*
|
||||||
* @exception IOException if there is a problem acquiring the blocks
|
* @exception IOException if there is a problem acquiring the blocks
|
||||||
*/
|
*/
|
||||||
|
ListManagedBlock[] fetchBlocks(int startBlock, int headerPropertiesStartBlock,
|
||||||
ListManagedBlock [] fetchBlocks(final int startBlock,
|
BlockList blockList) throws IOException {
|
||||||
final BlockList blockList)
|
List<ListManagedBlock> blocks = new ArrayList<ListManagedBlock>();
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
List blocks = new ArrayList();
|
|
||||||
int currentBlock = startBlock;
|
int currentBlock = startBlock;
|
||||||
|
boolean firstPass = true;
|
||||||
|
ListManagedBlock dataBlock = null;
|
||||||
|
|
||||||
while (currentBlock != POIFSConstants.END_OF_CHAIN)
|
// Process the chain from the start to the end
|
||||||
{
|
// Normally we have header, data, end
|
||||||
blocks.add(blockList.remove(currentBlock));
|
// Sometimes we have data, header, end
|
||||||
currentBlock = _entries.get(currentBlock);
|
// For those cases, stop at the header, not the end
|
||||||
|
while (currentBlock != POIFSConstants.END_OF_CHAIN) {
|
||||||
|
try {
|
||||||
|
// Grab the data at the current block offset
|
||||||
|
dataBlock = blockList.remove(currentBlock);
|
||||||
|
blocks.add(dataBlock);
|
||||||
|
// Now figure out which block we go to next
|
||||||
|
currentBlock = _entries.get(currentBlock);
|
||||||
|
firstPass = false;
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(currentBlock == headerPropertiesStartBlock) {
|
||||||
|
// Special case where things are in the wrong order
|
||||||
|
System.err.println("Warning, header block comes after data blocks in POIFS block listing");
|
||||||
|
currentBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
} else if(currentBlock == 0 && firstPass) {
|
||||||
|
// Special case where the termination isn't done right
|
||||||
|
// on an empty set
|
||||||
|
System.err.println("Warning, incorrectly terminated empty data blocks in POIFS block listing (should end at -2, ended at 0)");
|
||||||
|
currentBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
|
} else {
|
||||||
|
// Ripple up
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ( ListManagedBlock [] ) blocks
|
|
||||||
.toArray(new ListManagedBlock[ 0 ]);
|
return blocks.toArray(new ListManagedBlock[blocks.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods for debugging reader
|
// methods for debugging reader
|
||||||
@ -203,19 +253,14 @@ public class BlockAllocationTableReader
|
|||||||
*
|
*
|
||||||
* @return true if the specific block is used, else false
|
* @return true if the specific block is used, else false
|
||||||
*/
|
*/
|
||||||
|
boolean isUsed(int index) {
|
||||||
|
|
||||||
boolean isUsed(final int index)
|
try {
|
||||||
{
|
return _entries.get(index) != -1;
|
||||||
boolean rval = false;
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
// ignored
|
||||||
try
|
return false;
|
||||||
{
|
|
||||||
rval = _entries.get(index) != -1;
|
|
||||||
}
|
}
|
||||||
catch (IndexOutOfBoundsException ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,18 +274,11 @@ public class BlockAllocationTableReader
|
|||||||
*
|
*
|
||||||
* @exception IOException if the current block is unused
|
* @exception IOException if the current block is unused
|
||||||
*/
|
*/
|
||||||
|
int getNextBlockIndex(int index) throws IOException {
|
||||||
int getNextBlockIndex(final int index)
|
if (isUsed(index)) {
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (isUsed(index))
|
|
||||||
{
|
|
||||||
return _entries.get(index);
|
return _entries.get(index);
|
||||||
}
|
}
|
||||||
else
|
throw new IOException("index " + index + " is unused");
|
||||||
{
|
|
||||||
throw new IOException("index " + index + " is unused");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,15 +287,9 @@ public class BlockAllocationTableReader
|
|||||||
* @param blocks the array of blocks containing the indices
|
* @param blocks the array of blocks containing the indices
|
||||||
* @param raw_blocks the list of blocks being managed. Unused
|
* @param raw_blocks the list of blocks being managed. Unused
|
||||||
* blocks will be eliminated from the list
|
* blocks will be eliminated from the list
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
*/
|
||||||
|
private void setEntries(ListManagedBlock[] blocks, BlockList raw_blocks) throws IOException {
|
||||||
private void setEntries(final ListManagedBlock [] blocks,
|
int limit = bigBlockSize.getBATEntriesPerBlock();
|
||||||
final BlockList raw_blocks)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
int limit = BATBlock.entriesPerBlock();
|
|
||||||
|
|
||||||
for (int block_index = 0; block_index < blocks.length; block_index++)
|
for (int block_index = 0; block_index < blocks.length; block_index++)
|
||||||
{
|
{
|
||||||
@ -281,5 +313,4 @@ public class BlockAllocationTableReader
|
|||||||
}
|
}
|
||||||
raw_blocks.setBAT(this);
|
raw_blocks.setBAT(this);
|
||||||
}
|
}
|
||||||
} // end class BlockAllocationTableReader
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,20 +14,17 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import java.util.*;
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.poifs.filesystem.BATManaged;
|
import org.apache.poi.poifs.filesystem.BATManaged;
|
||||||
import org.apache.poi.util.IntList;
|
import org.apache.poi.util.IntList;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class manages and creates the Block Allocation Table, which is
|
* This class manages and creates the Block Allocation Table, which is
|
||||||
@ -45,23 +41,21 @@ import org.apache.poi.util.LittleEndianConsts;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class BlockAllocationTableWriter implements BlockWritable, BATManaged {
|
||||||
public class BlockAllocationTableWriter
|
|
||||||
implements BlockWritable, BATManaged
|
|
||||||
{
|
|
||||||
private IntList _entries;
|
private IntList _entries;
|
||||||
private BATBlock[] _blocks;
|
private BATBlock[] _blocks;
|
||||||
private int _start_block;
|
private int _start_block;
|
||||||
|
private POIFSBigBlockSize _bigBlockSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a BlockAllocationTableWriter
|
* create a BlockAllocationTableWriter
|
||||||
*/
|
*/
|
||||||
|
public BlockAllocationTableWriter(POIFSBigBlockSize bigBlockSize)
|
||||||
public BlockAllocationTableWriter()
|
|
||||||
{
|
{
|
||||||
_start_block = POIFSConstants.END_OF_CHAIN;
|
_bigBlockSize = bigBlockSize;
|
||||||
_entries = new IntList();
|
_start_block = POIFSConstants.END_OF_CHAIN;
|
||||||
_blocks = new BATBlock[ 0 ];
|
_entries = new IntList();
|
||||||
|
_blocks = new BATBlock[ 0 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +63,6 @@ public class BlockAllocationTableWriter
|
|||||||
*
|
*
|
||||||
* @return start block index of BAT blocks
|
* @return start block index of BAT blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int createBlocks()
|
public int createBlocks()
|
||||||
{
|
{
|
||||||
int xbat_blocks = 0;
|
int xbat_blocks = 0;
|
||||||
@ -78,12 +71,13 @@ public class BlockAllocationTableWriter
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int calculated_bat_blocks =
|
int calculated_bat_blocks =
|
||||||
BATBlock.calculateStorageRequirements(bat_blocks
|
BATBlock.calculateStorageRequirements(_bigBlockSize,
|
||||||
|
bat_blocks
|
||||||
+ xbat_blocks
|
+ xbat_blocks
|
||||||
+ _entries.size());
|
+ _entries.size());
|
||||||
int calculated_xbat_blocks =
|
int calculated_xbat_blocks =
|
||||||
HeaderBlockWriter
|
HeaderBlockWriter.calculateXBATStorageRequirements(
|
||||||
.calculateXBATStorageRequirements(calculated_bat_blocks);
|
_bigBlockSize, calculated_bat_blocks);
|
||||||
|
|
||||||
if ((bat_blocks == calculated_bat_blocks)
|
if ((bat_blocks == calculated_bat_blocks)
|
||||||
&& (xbat_blocks == calculated_xbat_blocks))
|
&& (xbat_blocks == calculated_xbat_blocks))
|
||||||
@ -92,11 +86,8 @@ public class BlockAllocationTableWriter
|
|||||||
// stable ... we're OK
|
// stable ... we're OK
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
bat_blocks = calculated_bat_blocks;
|
||||||
{
|
xbat_blocks = calculated_xbat_blocks;
|
||||||
bat_blocks = calculated_bat_blocks;
|
|
||||||
xbat_blocks = calculated_xbat_blocks;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
int startBlock = allocateSpace(bat_blocks);
|
int startBlock = allocateSpace(bat_blocks);
|
||||||
|
|
||||||
@ -112,7 +103,6 @@ public class BlockAllocationTableWriter
|
|||||||
*
|
*
|
||||||
* @return the starting index of the blocks
|
* @return the starting index of the blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int allocateSpace(final int blockCount)
|
public int allocateSpace(final int blockCount)
|
||||||
{
|
{
|
||||||
int startBlock = _entries.size();
|
int startBlock = _entries.size();
|
||||||
@ -136,7 +126,6 @@ public class BlockAllocationTableWriter
|
|||||||
*
|
*
|
||||||
* @return the starting block index
|
* @return the starting block index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int getStartBlock()
|
public int getStartBlock()
|
||||||
{
|
{
|
||||||
return _start_block;
|
return _start_block;
|
||||||
@ -145,14 +134,11 @@ public class BlockAllocationTableWriter
|
|||||||
/**
|
/**
|
||||||
* create the BATBlocks
|
* create the BATBlocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void simpleCreateBlocks()
|
void simpleCreateBlocks()
|
||||||
{
|
{
|
||||||
_blocks = BATBlock.createBATBlocks(_entries.toArray());
|
_blocks = BATBlock.createBATBlocks(_bigBlockSize, _entries.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** START implementation of BlockWritable ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the storage to an OutputStream
|
* Write the storage to an OutputStream
|
||||||
*
|
*
|
||||||
@ -162,7 +148,6 @@ public class BlockAllocationTableWriter
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void writeBlocks(final OutputStream stream)
|
public void writeBlocks(final OutputStream stream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -171,16 +156,21 @@ public class BlockAllocationTableWriter
|
|||||||
_blocks[ j ].writeBlocks(stream);
|
_blocks[ j ].writeBlocks(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END implementation of BlockWritable ********** */
|
/**
|
||||||
/* ********** START implementation of BATManaged ********** */
|
* Write the BAT into its associated block
|
||||||
|
*/
|
||||||
|
public static void writeBlock(final BATBlock bat, final ByteBuffer block)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
bat.writeData(block);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of BigBlock's this instance uses
|
* Return the number of BigBlock's this instance uses
|
||||||
*
|
*
|
||||||
* @return count of BigBlock instances
|
* @return count of BigBlock instances
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int countBlocks()
|
public int countBlocks()
|
||||||
{
|
{
|
||||||
return _blocks.length;
|
return _blocks.length;
|
||||||
@ -188,15 +178,9 @@ public class BlockAllocationTableWriter
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the start block for this instance
|
* Set the start block for this instance
|
||||||
*
|
|
||||||
* @param start_block
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setStartBlock(int start_block)
|
public void setStartBlock(int start_block)
|
||||||
{
|
{
|
||||||
_start_block = start_block;
|
_start_block = start_block;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* ********** END implementation of BATManaged ********** */
|
|
||||||
} // end class BlockAllocationTableWriter
|
|
||||||
|
|
||||||
|
@ -59,13 +59,14 @@ public interface BlockList
|
|||||||
* blocks are removed from the list.
|
* blocks are removed from the list.
|
||||||
*
|
*
|
||||||
* @param startBlock the index of the first block in the stream
|
* @param startBlock the index of the first block in the stream
|
||||||
|
* @param headerPropertiesStartBlock the index of the first header block in the stream
|
||||||
*
|
*
|
||||||
* @return the stream as an array of correctly ordered blocks
|
* @return the stream as an array of correctly ordered blocks
|
||||||
*
|
*
|
||||||
* @exception IOException if blocks are missing
|
* @exception IOException if blocks are missing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public ListManagedBlock [] fetchBlocks(final int startBlock)
|
public ListManagedBlock [] fetchBlocks(final int startBlock, final int headerPropertiesStartBlock)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,5 +79,7 @@ public interface BlockList
|
|||||||
|
|
||||||
public void setBAT(final BlockAllocationTableReader bat)
|
public void setBAT(final BlockAllocationTableReader bat)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
public int blockCount();
|
||||||
} // end public interface BlockList
|
} // end public interface BlockList
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,30 +14,20 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple implementation of BlockList
|
* A simple implementation of BlockList
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org
|
* @author Marc Johnson (mjohnson at apache dot org
|
||||||
*/
|
*/
|
||||||
|
abstract class BlockListImpl implements BlockList {
|
||||||
class BlockListImpl
|
|
||||||
implements BlockList
|
|
||||||
{
|
|
||||||
private ListManagedBlock[] _blocks;
|
private ListManagedBlock[] _blocks;
|
||||||
private BlockAllocationTableReader _bat;
|
private BlockAllocationTableReader _bat;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor BlockListImpl
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected BlockListImpl()
|
protected BlockListImpl()
|
||||||
{
|
{
|
||||||
_blocks = new ListManagedBlock[ 0 ];
|
_blocks = new ListManagedBlock[ 0 ];
|
||||||
@ -50,21 +39,17 @@ class BlockListImpl
|
|||||||
*
|
*
|
||||||
* @param blocks blocks to be managed
|
* @param blocks blocks to be managed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected void setBlocks(final ListManagedBlock [] blocks)
|
protected void setBlocks(final ListManagedBlock [] blocks)
|
||||||
{
|
{
|
||||||
_blocks = blocks;
|
_blocks = blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** START implementation of BlockList ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove the specified block from the list
|
* remove the specified block from the list
|
||||||
*
|
*
|
||||||
* @param index the index of the specified block; if the index is
|
* @param index the index of the specified block; if the index is
|
||||||
* out of range, that's ok
|
* out of range, that's ok
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void zap(final int index)
|
public void zap(final int index)
|
||||||
{
|
{
|
||||||
if ((index >= 0) && (index < _blocks.length))
|
if ((index >= 0) && (index < _blocks.length))
|
||||||
@ -73,6 +58,14 @@ class BlockListImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit testing method. Gets, without sanity checks or
|
||||||
|
* removing.
|
||||||
|
*/
|
||||||
|
protected ListManagedBlock get(final int index) {
|
||||||
|
return _blocks[index];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove and return the specified block from the list
|
* remove and return the specified block from the list
|
||||||
*
|
*
|
||||||
@ -83,7 +76,6 @@ class BlockListImpl
|
|||||||
* @exception IOException if the index is out of range or has
|
* @exception IOException if the index is out of range or has
|
||||||
* already been removed
|
* already been removed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public ListManagedBlock remove(final int index)
|
public ListManagedBlock remove(final int index)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -94,15 +86,17 @@ class BlockListImpl
|
|||||||
result = _blocks[ index ];
|
result = _blocks[ index ];
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
throw new IOException("block[ " + index
|
throw new IOException(
|
||||||
+ " ] already removed");
|
"block[ " + index + " ] already removed - " +
|
||||||
|
"does your POIFS have circular or duplicate block references?"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_blocks[ index ] = null;
|
_blocks[ index ] = null;
|
||||||
}
|
}
|
||||||
catch (ArrayIndexOutOfBoundsException ignored)
|
catch (ArrayIndexOutOfBoundsException ignored)
|
||||||
{
|
{
|
||||||
throw new IOException("Cannot remove block[ " + index
|
throw new IOException("Cannot remove block[ " + index
|
||||||
+ " ]; out of range[ 0 - " +
|
+ " ]; out of range[ 0 - " +
|
||||||
(_blocks.length-1) + " ]");
|
(_blocks.length-1) + " ]");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -118,8 +112,7 @@ class BlockListImpl
|
|||||||
*
|
*
|
||||||
* @exception IOException if blocks are missing
|
* @exception IOException if blocks are missing
|
||||||
*/
|
*/
|
||||||
|
public ListManagedBlock [] fetchBlocks(final int startBlock, final int headerPropertiesStartBlock)
|
||||||
public ListManagedBlock [] fetchBlocks(final int startBlock)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
if (_bat == null)
|
if (_bat == null)
|
||||||
@ -127,17 +120,14 @@ class BlockListImpl
|
|||||||
throw new IOException(
|
throw new IOException(
|
||||||
"Improperly initialized list: no block allocation table provided");
|
"Improperly initialized list: no block allocation table provided");
|
||||||
}
|
}
|
||||||
return _bat.fetchBlocks(startBlock, this);
|
return _bat.fetchBlocks(startBlock, headerPropertiesStartBlock, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the associated BlockAllocationTable
|
* set the associated BlockAllocationTable
|
||||||
*
|
*
|
||||||
* @param bat the associated BlockAllocationTable
|
* @param bat the associated BlockAllocationTable
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setBAT(final BlockAllocationTableReader bat)
|
public void setBAT(final BlockAllocationTableReader bat)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -148,7 +138,21 @@ class BlockListImpl
|
|||||||
}
|
}
|
||||||
_bat = bat;
|
_bat = bat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END implementation of BlockList ********** */
|
/**
|
||||||
} // end package-scope class BlockListImpl
|
* Returns the count of the number of blocks
|
||||||
|
*/
|
||||||
|
public int blockCount() {
|
||||||
|
return _blocks.length;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the number of remaining blocks
|
||||||
|
*/
|
||||||
|
protected int remainingBlocks() {
|
||||||
|
int c = 0;
|
||||||
|
for(int i=0; i<_blocks.length; i++) {
|
||||||
|
if(_blocks[i] != null) c++;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
186
src/java/org/apache/poi/poifs/storage/DataInputBlock.java
Normal file
186
src/java/org/apache/poi/poifs/storage/DataInputBlock.java
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a <tt>byte</tt> array and provides simple data input access.
|
||||||
|
* Internally, this class maintains a buffer read index, so that for the most part, primitive
|
||||||
|
* data can be read in a data-input-stream-like manner.<p/>
|
||||||
|
*
|
||||||
|
* Note - the calling class should call the {@link #available()} method to detect end-of-buffer
|
||||||
|
* and move to the next data block when the current is exhausted.
|
||||||
|
* For optimisation reasons, no error handling is performed in this class. Thus, mistakes in
|
||||||
|
* calling code ran may raise ugly exceptions here, like {@link ArrayIndexOutOfBoundsException},
|
||||||
|
* etc .<p/>
|
||||||
|
*
|
||||||
|
* The multi-byte primitive input methods ({@link #readUShortLE()}, {@link #readIntLE()} and
|
||||||
|
* {@link #readLongLE()}) have corresponding 'spanning read' methods which (when required) perform
|
||||||
|
* a read across the block boundary. These spanning read methods take the previous
|
||||||
|
* {@link DataInputBlock} as a parameter.
|
||||||
|
* Reads of larger amounts of data (into <tt>byte</tt> array buffers) must be managed by the caller
|
||||||
|
* since these could conceivably involve more than two blocks.
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class DataInputBlock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possibly any size (usually 512K or 64K). Assumed to be at least 8 bytes for all blocks
|
||||||
|
* before the end of the stream. The last block in the stream can be any size except zero.
|
||||||
|
*/
|
||||||
|
private final byte[] _buf;
|
||||||
|
private int _readIndex;
|
||||||
|
private int _maxIndex;
|
||||||
|
|
||||||
|
DataInputBlock(byte[] data, int startOffset) {
|
||||||
|
_buf = data;
|
||||||
|
_readIndex = startOffset;
|
||||||
|
_maxIndex = _buf.length;
|
||||||
|
}
|
||||||
|
public int available() {
|
||||||
|
return _maxIndex-_readIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int readUByte() {
|
||||||
|
return _buf[_readIndex++] & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a <tt>short</tt> which was encoded in <em>little endian</em> format.
|
||||||
|
*/
|
||||||
|
public int readUShortLE() {
|
||||||
|
int i = _readIndex;
|
||||||
|
|
||||||
|
int b0 = _buf[i++] & 0xFF;
|
||||||
|
int b1 = _buf[i++] & 0xFF;
|
||||||
|
_readIndex = i;
|
||||||
|
return (b1 << 8) + (b0 << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a <tt>short</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
|
||||||
|
*/
|
||||||
|
public int readUShortLE(DataInputBlock prevBlock) {
|
||||||
|
// simple case - will always be one byte in each block
|
||||||
|
int i = prevBlock._buf.length-1;
|
||||||
|
|
||||||
|
int b0 = prevBlock._buf[i++] & 0xFF;
|
||||||
|
int b1 = _buf[_readIndex++] & 0xFF;
|
||||||
|
return (b1 << 8) + (b0 << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an <tt>int</tt> which was encoded in <em>little endian</em> format.
|
||||||
|
*/
|
||||||
|
public int readIntLE() {
|
||||||
|
int i = _readIndex;
|
||||||
|
|
||||||
|
int b0 = _buf[i++] & 0xFF;
|
||||||
|
int b1 = _buf[i++] & 0xFF;
|
||||||
|
int b2 = _buf[i++] & 0xFF;
|
||||||
|
int b3 = _buf[i++] & 0xFF;
|
||||||
|
_readIndex = i;
|
||||||
|
return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an <tt>int</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
|
||||||
|
*/
|
||||||
|
public int readIntLE(DataInputBlock prevBlock, int prevBlockAvailable) {
|
||||||
|
byte[] buf = new byte[4];
|
||||||
|
|
||||||
|
readSpanning(prevBlock, prevBlockAvailable, buf);
|
||||||
|
int b0 = buf[0] & 0xFF;
|
||||||
|
int b1 = buf[1] & 0xFF;
|
||||||
|
int b2 = buf[2] & 0xFF;
|
||||||
|
int b3 = buf[3] & 0xFF;
|
||||||
|
return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a <tt>long</tt> which was encoded in <em>little endian</em> format.
|
||||||
|
*/
|
||||||
|
public long readLongLE() {
|
||||||
|
int i = _readIndex;
|
||||||
|
|
||||||
|
int b0 = _buf[i++] & 0xFF;
|
||||||
|
int b1 = _buf[i++] & 0xFF;
|
||||||
|
int b2 = _buf[i++] & 0xFF;
|
||||||
|
int b3 = _buf[i++] & 0xFF;
|
||||||
|
int b4 = _buf[i++] & 0xFF;
|
||||||
|
int b5 = _buf[i++] & 0xFF;
|
||||||
|
int b6 = _buf[i++] & 0xFF;
|
||||||
|
int b7 = _buf[i++] & 0xFF;
|
||||||
|
_readIndex = i;
|
||||||
|
return (((long)b7 << 56) +
|
||||||
|
((long)b6 << 48) +
|
||||||
|
((long)b5 << 40) +
|
||||||
|
((long)b4 << 32) +
|
||||||
|
((long)b3 << 24) +
|
||||||
|
(b2 << 16) +
|
||||||
|
(b1 << 8) +
|
||||||
|
(b0 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a <tt>long</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
|
||||||
|
*/
|
||||||
|
public long readLongLE(DataInputBlock prevBlock, int prevBlockAvailable) {
|
||||||
|
byte[] buf = new byte[8];
|
||||||
|
|
||||||
|
readSpanning(prevBlock, prevBlockAvailable, buf);
|
||||||
|
|
||||||
|
int b0 = buf[0] & 0xFF;
|
||||||
|
int b1 = buf[1] & 0xFF;
|
||||||
|
int b2 = buf[2] & 0xFF;
|
||||||
|
int b3 = buf[3] & 0xFF;
|
||||||
|
int b4 = buf[4] & 0xFF;
|
||||||
|
int b5 = buf[5] & 0xFF;
|
||||||
|
int b6 = buf[6] & 0xFF;
|
||||||
|
int b7 = buf[7] & 0xFF;
|
||||||
|
return (((long)b7 << 56) +
|
||||||
|
((long)b6 << 48) +
|
||||||
|
((long)b5 << 40) +
|
||||||
|
((long)b4 << 32) +
|
||||||
|
((long)b3 << 24) +
|
||||||
|
(b2 << 16) +
|
||||||
|
(b1 << 8) +
|
||||||
|
(b0 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a small amount of data from across the boundary between two blocks.
|
||||||
|
* The {@link #_readIndex} of this (the second) block is updated accordingly.
|
||||||
|
* Note- this method (and other code) assumes that the second {@link DataInputBlock}
|
||||||
|
* always is big enough to complete the read without being exhausted.
|
||||||
|
*/
|
||||||
|
private void readSpanning(DataInputBlock prevBlock, int prevBlockAvailable, byte[] buf) {
|
||||||
|
System.arraycopy(prevBlock._buf, prevBlock._readIndex, buf, 0, prevBlockAvailable);
|
||||||
|
int secondReadLen = buf.length-prevBlockAvailable;
|
||||||
|
System.arraycopy(_buf, 0, buf, prevBlockAvailable, secondReadLen);
|
||||||
|
_readIndex = secondReadLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads <tt>len</tt> bytes from this block into the supplied buffer.
|
||||||
|
*/
|
||||||
|
public void readFully(byte[] buf, int off, int len) {
|
||||||
|
System.arraycopy(_buf, _readIndex, buf, off, len);
|
||||||
|
_readIndex += len;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,31 +14,24 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A block of document data.
|
* A block of document data.
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class DocumentBlock extends BigBlock {
|
||||||
public class DocumentBlock
|
|
||||||
extends BigBlock
|
|
||||||
{
|
|
||||||
private static final byte _default_value = ( byte ) 0xFF;
|
private static final byte _default_value = ( byte ) 0xFF;
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private int _bytes_read;
|
private int _bytes_read;
|
||||||
@ -55,6 +47,11 @@ public class DocumentBlock
|
|||||||
public DocumentBlock(final RawDataBlock block)
|
public DocumentBlock(final RawDataBlock block)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
super(
|
||||||
|
block.getBigBlockSize() == POIFSConstants.SMALLER_BIG_BLOCK_SIZE ?
|
||||||
|
POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS :
|
||||||
|
POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS
|
||||||
|
);
|
||||||
_data = block.getData();
|
_data = block.getData();
|
||||||
_bytes_read = _data.length;
|
_bytes_read = _data.length;
|
||||||
}
|
}
|
||||||
@ -67,10 +64,10 @@ public class DocumentBlock
|
|||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public DocumentBlock(final InputStream stream)
|
public DocumentBlock(final InputStream stream, POIFSBigBlockSize bigBlockSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
this();
|
this(bigBlockSize);
|
||||||
int count = IOUtils.readFully(stream, _data);
|
int count = IOUtils.readFully(stream, _data);
|
||||||
|
|
||||||
_bytes_read = (count == -1) ? 0
|
_bytes_read = (count == -1) ? 0
|
||||||
@ -81,9 +78,10 @@ public class DocumentBlock
|
|||||||
* Create a single instance initialized with default values
|
* Create a single instance initialized with default values
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private DocumentBlock()
|
private DocumentBlock(POIFSBigBlockSize bigBlockSize)
|
||||||
{
|
{
|
||||||
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
|
super(bigBlockSize);
|
||||||
|
_data = new byte[ bigBlockSize.getBigBlockSize() ];
|
||||||
Arrays.fill(_data, _default_value);
|
Arrays.fill(_data, _default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +104,7 @@ public class DocumentBlock
|
|||||||
|
|
||||||
public boolean partiallyRead()
|
public boolean partiallyRead()
|
||||||
{
|
{
|
||||||
return _bytes_read != POIFSConstants.BIG_BLOCK_SIZE;
|
return _bytes_read != bigBlockSize.getBigBlockSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,26 +127,27 @@ public class DocumentBlock
|
|||||||
* input array
|
* input array
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static DocumentBlock [] convert(final byte [] array,
|
public static DocumentBlock [] convert(final POIFSBigBlockSize bigBlockSize,
|
||||||
|
final byte [] array,
|
||||||
final int size)
|
final int size)
|
||||||
{
|
{
|
||||||
DocumentBlock[] rval =
|
DocumentBlock[] rval =
|
||||||
new DocumentBlock[ (size + POIFSConstants.BIG_BLOCK_SIZE - 1) / POIFSConstants.BIG_BLOCK_SIZE ];
|
new DocumentBlock[ (size + bigBlockSize.getBigBlockSize() - 1) / bigBlockSize.getBigBlockSize() ];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
for (int k = 0; k < rval.length; k++)
|
for (int k = 0; k < rval.length; k++)
|
||||||
{
|
{
|
||||||
rval[ k ] = new DocumentBlock();
|
rval[ k ] = new DocumentBlock(bigBlockSize);
|
||||||
if (offset < array.length)
|
if (offset < array.length)
|
||||||
{
|
{
|
||||||
int length = Math.min(POIFSConstants.BIG_BLOCK_SIZE,
|
int length = Math.min(bigBlockSize.getBigBlockSize(),
|
||||||
array.length - offset);
|
array.length - offset);
|
||||||
|
|
||||||
System.arraycopy(array, offset, rval[ k ]._data, 0, length);
|
System.arraycopy(array, offset, rval[ k ]._data, 0, length);
|
||||||
if (length != POIFSConstants.BIG_BLOCK_SIZE)
|
if (length != bigBlockSize.getBigBlockSize())
|
||||||
{
|
{
|
||||||
Arrays.fill(rval[ k ]._data, length,
|
Arrays.fill(rval[ k ]._data, length,
|
||||||
POIFSConstants.BIG_BLOCK_SIZE,
|
bigBlockSize.getBigBlockSize(),
|
||||||
_default_value);
|
_default_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,50 +155,26 @@ public class DocumentBlock
|
|||||||
{
|
{
|
||||||
Arrays.fill(rval[ k ]._data, _default_value);
|
Arrays.fill(rval[ k ]._data, _default_value);
|
||||||
}
|
}
|
||||||
offset += POIFSConstants.BIG_BLOCK_SIZE;
|
offset += bigBlockSize.getBigBlockSize();
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) {
|
||||||
* read data from an array of DocumentBlocks
|
if(blocks == null || blocks.length == 0) {
|
||||||
*
|
return null;
|
||||||
* @param blocks the blocks to read from
|
|
||||||
* @param buffer the buffer to write the data into
|
|
||||||
* @param offset the offset into the array of blocks to read from
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static void read(final DocumentBlock [] blocks,
|
|
||||||
final byte [] buffer, final int offset)
|
|
||||||
{
|
|
||||||
int firstBlockIndex = offset / POIFSConstants.BIG_BLOCK_SIZE;
|
|
||||||
int firstBlockOffset = offset % POIFSConstants.BIG_BLOCK_SIZE;
|
|
||||||
int lastBlockIndex = (offset + buffer.length - 1)
|
|
||||||
/ POIFSConstants.BIG_BLOCK_SIZE;
|
|
||||||
|
|
||||||
if (firstBlockIndex == lastBlockIndex)
|
|
||||||
{
|
|
||||||
System.arraycopy(blocks[ firstBlockIndex ]._data,
|
|
||||||
firstBlockOffset, buffer, 0, buffer.length);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Key things about the size of the block
|
||||||
int buffer_offset = 0;
|
POIFSBigBlockSize bigBlockSize = blocks[0].bigBlockSize;
|
||||||
|
int BLOCK_SHIFT = bigBlockSize.getHeaderValue();
|
||||||
|
int BLOCK_SIZE = bigBlockSize.getBigBlockSize();
|
||||||
|
int BLOCK_MASK = BLOCK_SIZE - 1;
|
||||||
|
|
||||||
System.arraycopy(blocks[ firstBlockIndex ]._data,
|
// Now do the offset lookup
|
||||||
firstBlockOffset, buffer, buffer_offset,
|
int firstBlockIndex = offset >> BLOCK_SHIFT;
|
||||||
POIFSConstants.BIG_BLOCK_SIZE
|
int firstBlockOffset= offset & BLOCK_MASK;
|
||||||
- firstBlockOffset);
|
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
|
||||||
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE - firstBlockOffset;
|
|
||||||
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
|
|
||||||
{
|
|
||||||
System.arraycopy(blocks[ j ]._data, 0, buffer, buffer_offset,
|
|
||||||
POIFSConstants.BIG_BLOCK_SIZE);
|
|
||||||
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
System.arraycopy(blocks[ lastBlockIndex ]._data, 0, buffer,
|
|
||||||
buffer_offset, buffer.length - buffer_offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** START extension of BigBlock ********** */
|
/* ********** START extension of BigBlock ********** */
|
||||||
|
388
src/java/org/apache/poi/poifs/storage/HeaderBlock.java
Normal file
388
src/java/org/apache/poi/poifs/storage/HeaderBlock.java
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
|
||||||
|
import org.apache.poi.util.HexDump;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.util.IntegerField;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
|
import org.apache.poi.util.LongField;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
import org.apache.poi.util.ShortField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The block containing the archive header
|
||||||
|
*/
|
||||||
|
public final class HeaderBlock implements HeaderBlockConstants {
|
||||||
|
private static final POILogger _logger =
|
||||||
|
POILogFactory.getLogger(HeaderBlock.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What big block size the file uses. Most files
|
||||||
|
* use 512 bytes, but a few use 4096
|
||||||
|
*/
|
||||||
|
private final POIFSBigBlockSize bigBlockSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of big block allocation table blocks (int).
|
||||||
|
* (Number of FAT Sectors in Microsoft parlance).
|
||||||
|
*/
|
||||||
|
private int _bat_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start of the property set block (int index of the property set
|
||||||
|
* chain's first big block).
|
||||||
|
*/
|
||||||
|
private int _property_start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start of the small block allocation table (int index of small
|
||||||
|
* block allocation table's first big block)
|
||||||
|
*/
|
||||||
|
private int _sbat_start;
|
||||||
|
/**
|
||||||
|
* Number of small block allocation table blocks (int)
|
||||||
|
* (Number of MiniFAT Sectors in Microsoft parlance)
|
||||||
|
*/
|
||||||
|
private int _sbat_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Big block index for extension to the big block allocation table
|
||||||
|
*/
|
||||||
|
private int _xbat_start;
|
||||||
|
/**
|
||||||
|
* Number of big block allocation table blocks (int)
|
||||||
|
* (Number of DIFAT Sectors in Microsoft parlance)
|
||||||
|
*/
|
||||||
|
private int _xbat_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data. Only ever 512 bytes, because 4096 byte
|
||||||
|
* files use zeros for the extra header space.
|
||||||
|
*/
|
||||||
|
private final byte[] _data;
|
||||||
|
|
||||||
|
private static final byte _default_value = ( byte ) 0xFF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new HeaderBlockReader from an InputStream
|
||||||
|
*
|
||||||
|
* @param stream the source InputStream
|
||||||
|
*
|
||||||
|
* @exception IOException on errors or bad data
|
||||||
|
*/
|
||||||
|
public HeaderBlock(InputStream stream) throws IOException {
|
||||||
|
// Grab the first 512 bytes
|
||||||
|
// (For 4096 sized blocks, the remaining 3584 bytes are zero)
|
||||||
|
// Then, process the contents
|
||||||
|
this(readFirst512(stream));
|
||||||
|
|
||||||
|
// Fetch the rest of the block if needed
|
||||||
|
if(bigBlockSize.getBigBlockSize() != 512) {
|
||||||
|
int rest = bigBlockSize.getBigBlockSize() - 512;
|
||||||
|
byte[] tmp = new byte[rest];
|
||||||
|
IOUtils.readFully(stream, tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeaderBlock(ByteBuffer buffer) throws IOException {
|
||||||
|
this(IOUtils.toByteArray(buffer, POIFSConstants.SMALLER_BIG_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HeaderBlock(byte[] data) throws IOException {
|
||||||
|
this._data = data;
|
||||||
|
|
||||||
|
// verify signature
|
||||||
|
long signature = LittleEndian.getLong(_data, _signature_offset);
|
||||||
|
|
||||||
|
if (signature != _signature) {
|
||||||
|
// Is it one of the usual suspects?
|
||||||
|
byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
|
||||||
|
if(_data[0] == OOXML_FILE_HEADER[0] &&
|
||||||
|
_data[1] == OOXML_FILE_HEADER[1] &&
|
||||||
|
_data[2] == OOXML_FILE_HEADER[2] &&
|
||||||
|
_data[3] == OOXML_FILE_HEADER[3]) {
|
||||||
|
throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)");
|
||||||
|
}
|
||||||
|
if ((signature & 0xFF8FFFFFFFFFFFFFL) == 0x0010000200040009L) {
|
||||||
|
// BIFF2 raw stream starts with BOF (sid=0x0009, size=0x0004, data=0x00t0)
|
||||||
|
throw new IllegalArgumentException("The supplied data appears to be in BIFF2 format. "
|
||||||
|
+ "POI only supports BIFF8 format");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give a generic error
|
||||||
|
throw new IOException("Invalid header signature; read "
|
||||||
|
+ longToHex(signature) + ", expected "
|
||||||
|
+ longToHex(_signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Figure out our block size
|
||||||
|
if (_data[30] == 12) {
|
||||||
|
this.bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS;
|
||||||
|
} else if(_data[30] == 9) {
|
||||||
|
this.bigBlockSize = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS;
|
||||||
|
} else {
|
||||||
|
throw new IOException("Unsupported blocksize (2^"+ _data[30] + "). Expected 2^9 or 2^12.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the fields to read and write the counts and starts
|
||||||
|
_bat_count = new IntegerField(_bat_count_offset, data).get();
|
||||||
|
_property_start = new IntegerField(_property_start_offset,_data).get();
|
||||||
|
_sbat_start = new IntegerField(_sbat_start_offset, _data).get();
|
||||||
|
_sbat_count = new IntegerField(_sbat_block_count_offset, _data).get();
|
||||||
|
_xbat_start = new IntegerField(_xbat_start_offset, _data).get();
|
||||||
|
_xbat_count = new IntegerField(_xbat_count_offset, _data).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single instance initialized with default values
|
||||||
|
*/
|
||||||
|
public HeaderBlock(POIFSBigBlockSize bigBlockSize)
|
||||||
|
{
|
||||||
|
this.bigBlockSize = bigBlockSize;
|
||||||
|
|
||||||
|
// Our data is always 512 big no matter what
|
||||||
|
_data = new byte[ POIFSConstants.SMALLER_BIG_BLOCK_SIZE ];
|
||||||
|
Arrays.fill(_data, _default_value);
|
||||||
|
|
||||||
|
// Set all the default values
|
||||||
|
new LongField(_signature_offset, _signature, _data);
|
||||||
|
new IntegerField(0x08, 0, _data);
|
||||||
|
new IntegerField(0x0c, 0, _data);
|
||||||
|
new IntegerField(0x10, 0, _data);
|
||||||
|
new IntegerField(0x14, 0, _data);
|
||||||
|
new ShortField(0x18, ( short ) 0x3b, _data);
|
||||||
|
new ShortField(0x1a, ( short ) 0x3, _data);
|
||||||
|
new ShortField(0x1c, ( short ) -2, _data);
|
||||||
|
|
||||||
|
new ShortField(0x1e, bigBlockSize.getHeaderValue(), _data);
|
||||||
|
new IntegerField(0x20, 0x6, _data);
|
||||||
|
new IntegerField(0x24, 0, _data);
|
||||||
|
new IntegerField(0x28, 0, _data);
|
||||||
|
new IntegerField(0x34, 0, _data);
|
||||||
|
new IntegerField(0x38, 0x1000, _data);
|
||||||
|
|
||||||
|
// Initialise the variables
|
||||||
|
_bat_count = 0;
|
||||||
|
_sbat_count = 0;
|
||||||
|
_xbat_count = 0;
|
||||||
|
_property_start = POIFSConstants.END_OF_CHAIN;
|
||||||
|
_sbat_start = POIFSConstants.END_OF_CHAIN;
|
||||||
|
_xbat_start = POIFSConstants.END_OF_CHAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readFirst512(InputStream stream) throws IOException {
|
||||||
|
// Grab the first 512 bytes
|
||||||
|
// (For 4096 sized blocks, the remaining 3584 bytes are zero)
|
||||||
|
byte[] data = new byte[512];
|
||||||
|
int bsCount = IOUtils.readFully(stream, data);
|
||||||
|
if(bsCount != 512) {
|
||||||
|
throw alertShortRead(bsCount, 512);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String longToHex(long value) {
|
||||||
|
return new String(HexDump.longToHex(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IOException alertShortRead(int pRead, int expectedReadSize) {
|
||||||
|
int read;
|
||||||
|
if (pRead < 0) {
|
||||||
|
//Can't have -1 bytes read in the error message!
|
||||||
|
read = 0;
|
||||||
|
} else {
|
||||||
|
read = pRead;
|
||||||
|
}
|
||||||
|
String type = " byte" + (read == 1 ? (""): ("s"));
|
||||||
|
|
||||||
|
return new IOException("Unable to read entire header; "
|
||||||
|
+ read + type + " read; expected "
|
||||||
|
+ expectedReadSize + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get start of Property Table
|
||||||
|
*
|
||||||
|
* @return the index of the first block of the Property Table
|
||||||
|
*/
|
||||||
|
public int getPropertyStart() {
|
||||||
|
return _property_start;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set start of Property Table
|
||||||
|
*
|
||||||
|
* @param startBlock the index of the first block of the Property Table
|
||||||
|
*/
|
||||||
|
public void setPropertyStart(final int startBlock) {
|
||||||
|
_property_start = startBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return start of small block (MiniFAT) allocation table
|
||||||
|
*/
|
||||||
|
public int getSBATStart() {
|
||||||
|
return _sbat_start;
|
||||||
|
}
|
||||||
|
public int getSBATCount() {
|
||||||
|
return _sbat_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set start of small block allocation table
|
||||||
|
*
|
||||||
|
* @param startBlock the index of the first big block of the small
|
||||||
|
* block allocation table
|
||||||
|
*/
|
||||||
|
public void setSBATStart(final int startBlock) {
|
||||||
|
_sbat_start = startBlock;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set count of SBAT blocks
|
||||||
|
*
|
||||||
|
* @param count the number of SBAT blocks
|
||||||
|
*/
|
||||||
|
public void setSBATBlockCount(final int count)
|
||||||
|
{
|
||||||
|
_sbat_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return number of BAT blocks
|
||||||
|
*/
|
||||||
|
public int getBATCount() {
|
||||||
|
return _bat_count;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the number of BAT blocks that are used.
|
||||||
|
* This is the number used in both the BAT and XBAT.
|
||||||
|
*/
|
||||||
|
public void setBATCount(final int count) {
|
||||||
|
_bat_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the offsets to the first (up to) 109
|
||||||
|
* BAT sectors.
|
||||||
|
* Any additional BAT sectors are held in the XBAT (DIFAT)
|
||||||
|
* sectors in a chain.
|
||||||
|
* @return BAT offset array
|
||||||
|
*/
|
||||||
|
public int[] getBATArray() {
|
||||||
|
// Read them in
|
||||||
|
int[] result = new int[ Math.min(_bat_count,_max_bats_in_header) ];
|
||||||
|
int offset = _bat_array_offset;
|
||||||
|
for (int j = 0; j < result.length; j++) {
|
||||||
|
result[ j ] = LittleEndian.getInt(_data, offset);
|
||||||
|
offset += LittleEndianConsts.INT_SIZE;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the offsets of the first (up to) 109
|
||||||
|
* BAT sectors.
|
||||||
|
*/
|
||||||
|
public void setBATArray(int[] bat_array) {
|
||||||
|
int count = Math.min(bat_array.length, _max_bats_in_header);
|
||||||
|
int blank = _max_bats_in_header - count;
|
||||||
|
|
||||||
|
int offset = _bat_array_offset;
|
||||||
|
for(int i=0; i<count; i++) {
|
||||||
|
LittleEndian.putInt(_data, offset, bat_array[i]);
|
||||||
|
offset += LittleEndianConsts.INT_SIZE;
|
||||||
|
}
|
||||||
|
for(int i=0; i<blank; i++) {
|
||||||
|
LittleEndian.putInt(_data, offset, POIFSConstants.UNUSED_BLOCK);
|
||||||
|
offset += LittleEndianConsts.INT_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return XBAT (DIFAT) count
|
||||||
|
*/
|
||||||
|
public int getXBATCount() {
|
||||||
|
return _xbat_count;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the number of XBAT (DIFAT) blocks used
|
||||||
|
*/
|
||||||
|
public void setXBATCount(final int count) {
|
||||||
|
_xbat_count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return XBAT (DIFAT) index
|
||||||
|
*/
|
||||||
|
public int getXBATIndex() {
|
||||||
|
return _xbat_start;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the first XBAT (DIFAT) block location
|
||||||
|
*/
|
||||||
|
public void setXBATStart(final int startBlock) {
|
||||||
|
_xbat_start = startBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
||||||
|
*/
|
||||||
|
public POIFSBigBlockSize getBigBlockSize() {
|
||||||
|
return bigBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the block's data to an OutputStream
|
||||||
|
*
|
||||||
|
* @param stream the OutputStream to which the stored data should
|
||||||
|
* be written
|
||||||
|
*
|
||||||
|
* @exception IOException on problems writing to the specified
|
||||||
|
* stream
|
||||||
|
*/
|
||||||
|
void writeData(final OutputStream stream)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// Update the counts and start positions
|
||||||
|
new IntegerField(_bat_count_offset, _bat_count, _data);
|
||||||
|
new IntegerField(_property_start_offset, _property_start, _data);
|
||||||
|
new IntegerField(_sbat_start_offset, _sbat_start, _data);
|
||||||
|
new IntegerField(_sbat_block_count_offset, _sbat_count, _data);
|
||||||
|
new IntegerField(_xbat_start_offset, _xbat_start, _data);
|
||||||
|
new IntegerField(_xbat_count_offset, _xbat_count, _data);
|
||||||
|
|
||||||
|
// Write the main data out
|
||||||
|
stream.write(_data, 0, 512);
|
||||||
|
|
||||||
|
// Now do the padding if needed
|
||||||
|
for(int i=POIFSConstants.SMALLER_BIG_BLOCK_SIZE; i<bigBlockSize.getBigBlockSize(); i++) {
|
||||||
|
stream.write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,31 +14,30 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LongField;
|
|
||||||
import org.apache.poi.util.ShortField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants used in reading/writing the Header block
|
* Constants used in reading/writing the Header block
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface HeaderBlockConstants
|
public interface HeaderBlockConstants
|
||||||
{
|
{
|
||||||
public static final long _signature = 0xE11AB1A1E011CFD0L;
|
public static final long _signature = 0xE11AB1A1E011CFD0L;
|
||||||
public static final int _bat_array_offset = 0x4c;
|
public static final int _bat_array_offset = 0x4c;
|
||||||
public static final int _max_bats_in_header =
|
public static final int _max_bats_in_header =
|
||||||
(POIFSConstants.BIG_BLOCK_SIZE - _bat_array_offset)
|
(POIFSConstants.SMALLER_BIG_BLOCK_SIZE - _bat_array_offset)
|
||||||
/ LittleEndianConsts.INT_SIZE;
|
/ LittleEndianConsts.INT_SIZE; // If 4k blocks, rest is blank
|
||||||
|
|
||||||
|
// Note - in Microsoft terms:
|
||||||
|
// BAT ~= FAT
|
||||||
|
// SBAT ~= MiniFAT
|
||||||
|
// XBAT ~= DIFat
|
||||||
|
|
||||||
// useful offsets
|
// useful offsets
|
||||||
public static final int _signature_offset = 0;
|
public static final int _signature_offset = 0;
|
||||||
public static final int _bat_count_offset = 0x2C;
|
public static final int _bat_count_offset = 0x2C;
|
||||||
|
@ -1,205 +0,0 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
|
||||||
this work for additional information regarding copyright ownership.
|
|
||||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
(the "License"); you may not use this file except in compliance with
|
|
||||||
the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
==================================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
|
||||||
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
|
|
||||||
import org.apache.poi.util.IOUtils;
|
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
import org.apache.poi.util.LongField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The block containing the archive header
|
|
||||||
*
|
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class HeaderBlockReader
|
|
||||||
implements HeaderBlockConstants
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* What big block size the file uses. Most files
|
|
||||||
* use 512 bytes, but a few use 4096
|
|
||||||
*/
|
|
||||||
private int bigBlockSize = POIFSConstants.BIG_BLOCK_SIZE;
|
|
||||||
|
|
||||||
// number of big block allocation table blocks (int)
|
|
||||||
private IntegerField _bat_count;
|
|
||||||
|
|
||||||
// start of the property set block (int index of the property set
|
|
||||||
// chain's first big block)
|
|
||||||
private IntegerField _property_start;
|
|
||||||
|
|
||||||
// start of the small block allocation table (int index of small
|
|
||||||
// block allocation table's first big block)
|
|
||||||
private IntegerField _sbat_start;
|
|
||||||
|
|
||||||
// big block index for extension to the big block allocation table
|
|
||||||
private IntegerField _xbat_start;
|
|
||||||
private IntegerField _xbat_count;
|
|
||||||
private byte[] _data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a new HeaderBlockReader from an InputStream
|
|
||||||
*
|
|
||||||
* @param stream the source InputStream
|
|
||||||
*
|
|
||||||
* @exception IOException on errors or bad data
|
|
||||||
*/
|
|
||||||
|
|
||||||
public HeaderBlockReader(final InputStream stream)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// At this point, we don't know how big our
|
|
||||||
// block sizes are
|
|
||||||
// So, read the first 32 bytes to check, then
|
|
||||||
// read the rest of the block
|
|
||||||
byte[] blockStart = new byte[32];
|
|
||||||
int bsCount = IOUtils.readFully(stream, blockStart);
|
|
||||||
if(bsCount != 32) {
|
|
||||||
alertShortRead(bsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out our block size
|
|
||||||
if(blockStart[30] == 12) {
|
|
||||||
bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
_data = new byte[ bigBlockSize ];
|
|
||||||
System.arraycopy(blockStart, 0, _data, 0, blockStart.length);
|
|
||||||
|
|
||||||
// Now we can read the rest of our header
|
|
||||||
int byte_count = IOUtils.readFully(stream, _data, blockStart.length, _data.length - blockStart.length);
|
|
||||||
if (byte_count+bsCount != bigBlockSize) {
|
|
||||||
alertShortRead(byte_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify signature
|
|
||||||
LongField signature = new LongField(_signature_offset, _data);
|
|
||||||
|
|
||||||
if (signature.get() != _signature)
|
|
||||||
{
|
|
||||||
// Is it one of the usual suspects?
|
|
||||||
byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
|
|
||||||
if(_data[0] == OOXML_FILE_HEADER[0] &&
|
|
||||||
_data[1] == OOXML_FILE_HEADER[1] &&
|
|
||||||
_data[2] == OOXML_FILE_HEADER[2] &&
|
|
||||||
_data[3] == OOXML_FILE_HEADER[3]) {
|
|
||||||
throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. POI only supports OLE2 Office documents");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give a generic error
|
|
||||||
throw new IOException("Invalid header signature; read "
|
|
||||||
+ signature.get() + ", expected "
|
|
||||||
+ _signature);
|
|
||||||
}
|
|
||||||
_bat_count = new IntegerField(_bat_count_offset, _data);
|
|
||||||
_property_start = new IntegerField(_property_start_offset, _data);
|
|
||||||
_sbat_start = new IntegerField(_sbat_start_offset, _data);
|
|
||||||
_xbat_start = new IntegerField(_xbat_start_offset, _data);
|
|
||||||
_xbat_count = new IntegerField(_xbat_count_offset, _data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void alertShortRead(int read) throws IOException {
|
|
||||||
if (read == -1)
|
|
||||||
//Cant have -1 bytes read in the error message!
|
|
||||||
read = 0;
|
|
||||||
String type = " byte" + ((read == 1) ? ("")
|
|
||||||
: ("s"));
|
|
||||||
|
|
||||||
throw new IOException("Unable to read entire header; "
|
|
||||||
+ read + type + " read; expected "
|
|
||||||
+ bigBlockSize + " bytes");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get start of Property Table
|
|
||||||
*
|
|
||||||
* @return the index of the first block of the Property Table
|
|
||||||
*/
|
|
||||||
public int getPropertyStart()
|
|
||||||
{
|
|
||||||
return _property_start.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return start of small block allocation table
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int getSBATStart()
|
|
||||||
{
|
|
||||||
return _sbat_start.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return number of BAT blocks
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int getBATCount()
|
|
||||||
{
|
|
||||||
return _bat_count.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return BAT array
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int [] getBATArray()
|
|
||||||
{
|
|
||||||
int[] result = new int[ _max_bats_in_header ];
|
|
||||||
int offset = _bat_array_offset;
|
|
||||||
|
|
||||||
for (int j = 0; j < _max_bats_in_header; j++)
|
|
||||||
{
|
|
||||||
result[ j ] = LittleEndian.getInt(_data, offset);
|
|
||||||
offset += LittleEndianConsts.INT_SIZE;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return XBAT count
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int getXBATCount()
|
|
||||||
{
|
|
||||||
return _xbat_count.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return XBAT index
|
|
||||||
*/
|
|
||||||
|
|
||||||
public int getXBATIndex()
|
|
||||||
{
|
|
||||||
return _xbat_start.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
|
|
||||||
*/
|
|
||||||
public int getBigBlockSize() {
|
|
||||||
return bigBlockSize;
|
|
||||||
}
|
|
||||||
} // end public class HeaderBlockReader
|
|
||||||
|
|
@ -19,80 +19,38 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
import org.apache.poi.util.LongField;
|
|
||||||
import org.apache.poi.util.ShortField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The block containing the archive header
|
* The block containing the archive header
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public class HeaderBlockWriter implements HeaderBlockConstants, BlockWritable
|
||||||
public class HeaderBlockWriter
|
|
||||||
extends BigBlock
|
|
||||||
implements HeaderBlockConstants
|
|
||||||
{
|
{
|
||||||
private static final byte _default_value = ( byte ) 0xFF;
|
private final HeaderBlock _header_block;
|
||||||
|
|
||||||
// number of big block allocation table blocks (int)
|
|
||||||
private IntegerField _bat_count;
|
|
||||||
|
|
||||||
// start of the property set block (int index of the property set
|
|
||||||
// chain's first big block)
|
|
||||||
private IntegerField _property_start;
|
|
||||||
|
|
||||||
// start of the small block allocation table (int index of small
|
|
||||||
// block allocation table's first big block)
|
|
||||||
private IntegerField _sbat_start;
|
|
||||||
|
|
||||||
// number of big blocks holding the small block allocation table
|
|
||||||
private IntegerField _sbat_block_count;
|
|
||||||
|
|
||||||
// big block index for extension to the big block allocation table
|
|
||||||
private IntegerField _xbat_start;
|
|
||||||
private IntegerField _xbat_count;
|
|
||||||
private byte[] _data;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a single instance initialized with default values
|
* Create a single instance initialized with default values
|
||||||
*/
|
*/
|
||||||
|
public HeaderBlockWriter(POIFSBigBlockSize bigBlockSize)
|
||||||
public HeaderBlockWriter()
|
|
||||||
{
|
{
|
||||||
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
|
_header_block = new HeaderBlock(bigBlockSize);
|
||||||
Arrays.fill(_data, _default_value);
|
}
|
||||||
new LongField(_signature_offset, _signature, _data);
|
|
||||||
new IntegerField(0x08, 0, _data);
|
/**
|
||||||
new IntegerField(0x0c, 0, _data);
|
* Create a single instance initialized with the specified
|
||||||
new IntegerField(0x10, 0, _data);
|
* existing values
|
||||||
new IntegerField(0x14, 0, _data);
|
*/
|
||||||
new ShortField(0x18, ( short ) 0x3b, _data);
|
public HeaderBlockWriter(HeaderBlock headerBlock)
|
||||||
new ShortField(0x1a, ( short ) 0x3, _data);
|
{
|
||||||
new ShortField(0x1c, ( short ) -2, _data);
|
_header_block = headerBlock;
|
||||||
new ShortField(0x1e, ( short ) 0x9, _data);
|
|
||||||
new IntegerField(0x20, 0x6, _data);
|
|
||||||
new IntegerField(0x24, 0, _data);
|
|
||||||
new IntegerField(0x28, 0, _data);
|
|
||||||
_bat_count = new IntegerField(_bat_count_offset, 0, _data);
|
|
||||||
_property_start = new IntegerField(_property_start_offset,
|
|
||||||
POIFSConstants.END_OF_CHAIN,
|
|
||||||
_data);
|
|
||||||
new IntegerField(0x34, 0, _data);
|
|
||||||
new IntegerField(0x38, 0x1000, _data);
|
|
||||||
_sbat_start = new IntegerField(_sbat_start_offset,
|
|
||||||
POIFSConstants.END_OF_CHAIN, _data);
|
|
||||||
_sbat_block_count = new IntegerField(_sbat_block_count_offset, 0,
|
|
||||||
_data);
|
|
||||||
_xbat_start = new IntegerField(_xbat_start_offset,
|
|
||||||
POIFSConstants.END_OF_CHAIN, _data);
|
|
||||||
_xbat_count = new IntegerField(_xbat_count_offset, 0, _data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,16 +69,19 @@ public class HeaderBlockWriter
|
|||||||
final int startBlock)
|
final int startBlock)
|
||||||
{
|
{
|
||||||
BATBlock[] rvalue;
|
BATBlock[] rvalue;
|
||||||
|
POIFSBigBlockSize bigBlockSize = _header_block.getBigBlockSize();
|
||||||
|
|
||||||
_bat_count.set(blockCount, _data);
|
_header_block.setBATCount(blockCount);
|
||||||
|
|
||||||
|
// Set the BAT locations
|
||||||
int limit = Math.min(blockCount, _max_bats_in_header);
|
int limit = Math.min(blockCount, _max_bats_in_header);
|
||||||
int offset = _bat_array_offset;
|
int[] bat_blocks = new int[limit];
|
||||||
|
for (int j = 0; j < limit; j++) {
|
||||||
for (int j = 0; j < limit; j++)
|
bat_blocks[j] = startBlock + j;
|
||||||
{
|
|
||||||
new IntegerField(offset, startBlock + j, _data);
|
|
||||||
offset += LittleEndianConsts.INT_SIZE;
|
|
||||||
}
|
}
|
||||||
|
_header_block.setBATArray(bat_blocks);
|
||||||
|
|
||||||
|
// Now do the XBATs
|
||||||
if (blockCount > _max_bats_in_header)
|
if (blockCount > _max_bats_in_header)
|
||||||
{
|
{
|
||||||
int excess_blocks = blockCount - _max_bats_in_header;
|
int excess_blocks = blockCount - _max_bats_in_header;
|
||||||
@ -131,16 +92,16 @@ public class HeaderBlockWriter
|
|||||||
excess_block_array[ j ] = startBlock + j
|
excess_block_array[ j ] = startBlock + j
|
||||||
+ _max_bats_in_header;
|
+ _max_bats_in_header;
|
||||||
}
|
}
|
||||||
rvalue = BATBlock.createXBATBlocks(excess_block_array,
|
rvalue = BATBlock.createXBATBlocks(bigBlockSize, excess_block_array,
|
||||||
startBlock + blockCount);
|
startBlock + blockCount);
|
||||||
_xbat_start.set(startBlock + blockCount, _data);
|
_header_block.setXBATStart(startBlock + blockCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rvalue = BATBlock.createXBATBlocks(new int[ 0 ], 0);
|
rvalue = BATBlock.createXBATBlocks(bigBlockSize, new int[ 0 ], 0);
|
||||||
_xbat_start.set(POIFSConstants.END_OF_CHAIN, _data);
|
_header_block.setXBATStart(POIFSConstants.END_OF_CHAIN);
|
||||||
}
|
}
|
||||||
_xbat_count.set(rvalue.length, _data);
|
_header_block.setXBATCount(rvalue.length);
|
||||||
return rvalue;
|
return rvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +111,9 @@ public class HeaderBlockWriter
|
|||||||
* @param startBlock the index of the first block of the Property
|
* @param startBlock the index of the first block of the Property
|
||||||
* Table
|
* Table
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setPropertyStart(final int startBlock)
|
public void setPropertyStart(final int startBlock)
|
||||||
{
|
{
|
||||||
_property_start.set(startBlock, _data);
|
_header_block.setPropertyStart(startBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,10 +122,9 @@ public class HeaderBlockWriter
|
|||||||
* @param startBlock the index of the first big block of the small
|
* @param startBlock the index of the first big block of the small
|
||||||
* block allocation table
|
* block allocation table
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setSBATStart(final int startBlock)
|
public void setSBATStart(final int startBlock)
|
||||||
{
|
{
|
||||||
_sbat_start.set(startBlock, _data);
|
_header_block.setSBATStart(startBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,10 +132,9 @@ public class HeaderBlockWriter
|
|||||||
*
|
*
|
||||||
* @param count the number of SBAT blocks
|
* @param count the number of SBAT blocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setSBATBlockCount(final int count)
|
public void setSBATBlockCount(final int count)
|
||||||
{
|
{
|
||||||
_sbat_block_count.set(count, _data);
|
_header_block.setSBATBlockCount(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,11 +146,11 @@ public class HeaderBlockWriter
|
|||||||
* @return number of XBAT blocks needed
|
* @return number of XBAT blocks needed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int calculateXBATStorageRequirements(final int blockCount)
|
static int calculateXBATStorageRequirements(POIFSBigBlockSize bigBlockSize, final int blockCount)
|
||||||
{
|
{
|
||||||
return (blockCount > _max_bats_in_header)
|
return (blockCount > _max_bats_in_header)
|
||||||
? BATBlock.calculateXBATStorageRequirements(blockCount
|
? BATBlock.calculateXBATStorageRequirements(
|
||||||
- _max_bats_in_header)
|
bigBlockSize, blockCount - _max_bats_in_header)
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,11 +165,29 @@ public class HeaderBlockWriter
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
|
public void writeBlocks(final OutputStream stream)
|
||||||
void writeData(final OutputStream stream)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
doWriteData(stream, _data);
|
_header_block.writeData(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the block's data to an existing block
|
||||||
|
*
|
||||||
|
* @param block the ByteBuffer of the block to which the
|
||||||
|
* stored data should be written
|
||||||
|
*
|
||||||
|
* @exception IOException on problems writing to the block
|
||||||
|
*/
|
||||||
|
public void writeBlock(ByteBuffer block)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(
|
||||||
|
_header_block.getBigBlockSize().getBigBlockSize()
|
||||||
|
);
|
||||||
|
_header_block.writeData(baos);
|
||||||
|
|
||||||
|
block.put(baos.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END extension of BigBlock ********** */
|
/* ********** END extension of BigBlock ********** */
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,31 +14,22 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import java.util.*;
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
|
||||||
import org.apache.poi.poifs.property.Property;
|
import org.apache.poi.poifs.property.Property;
|
||||||
import org.apache.poi.util.IntegerField;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A block of Property instances
|
* A block of Property instances
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class PropertyBlock extends BigBlock {
|
||||||
public class PropertyBlock
|
|
||||||
extends BigBlock
|
|
||||||
{
|
|
||||||
private static final int _properties_per_block =
|
|
||||||
POIFSConstants.BIG_BLOCK_SIZE / POIFSConstants.PROPERTY_SIZE;
|
|
||||||
private Property[] _properties;
|
private Property[] _properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,10 +39,12 @@ public class PropertyBlock
|
|||||||
* @param offset the offset into the properties array
|
* @param offset the offset into the properties array
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private PropertyBlock(final Property [] properties, final int offset)
|
private PropertyBlock(final POIFSBigBlockSize bigBlockSize, final Property [] properties, final int offset)
|
||||||
{
|
{
|
||||||
_properties = new Property[ _properties_per_block ];
|
super(bigBlockSize);
|
||||||
for (int j = 0; j < _properties_per_block; j++)
|
|
||||||
|
_properties = new Property[ bigBlockSize.getPropertiesPerBlock() ];
|
||||||
|
for (int j = 0; j < _properties.length; j++)
|
||||||
{
|
{
|
||||||
_properties[ j ] = properties[ j + offset ];
|
_properties[ j ] = properties[ j + offset ];
|
||||||
}
|
}
|
||||||
@ -70,8 +62,9 @@ public class PropertyBlock
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public static BlockWritable [] createPropertyBlockArray(
|
public static BlockWritable [] createPropertyBlockArray(
|
||||||
final List properties)
|
final POIFSBigBlockSize bigBlockSize, final List<Property> properties)
|
||||||
{
|
{
|
||||||
|
int _properties_per_block = bigBlockSize.getPropertiesPerBlock();
|
||||||
int block_count =
|
int block_count =
|
||||||
(properties.size() + _properties_per_block - 1)
|
(properties.size() + _properties_per_block - 1)
|
||||||
/ _properties_per_block;
|
/ _properties_per_block;
|
||||||
@ -101,7 +94,7 @@ public class PropertyBlock
|
|||||||
|
|
||||||
for (int j = 0; j < block_count; j++)
|
for (int j = 0; j < block_count; j++)
|
||||||
{
|
{
|
||||||
rvalue[ j ] = new PropertyBlock(to_be_written,
|
rvalue[ j ] = new PropertyBlock(bigBlockSize, to_be_written,
|
||||||
j * _properties_per_block);
|
j * _properties_per_block);
|
||||||
}
|
}
|
||||||
return rvalue;
|
return rvalue;
|
||||||
@ -122,6 +115,7 @@ public class PropertyBlock
|
|||||||
void writeData(final OutputStream stream)
|
void writeData(final OutputStream stream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
int _properties_per_block = bigBlockSize.getPropertiesPerBlock();
|
||||||
for (int j = 0; j < _properties_per_block; j++)
|
for (int j = 0; j < _properties_per_block; j++)
|
||||||
{
|
{
|
||||||
_properties[ j ].writeData(stream);
|
_properties[ j ].writeData(stream);
|
||||||
|
@ -51,13 +51,14 @@ public class RawDataBlock
|
|||||||
*/
|
*/
|
||||||
public RawDataBlock(final InputStream stream)
|
public RawDataBlock(final InputStream stream)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
this(stream, POIFSConstants.BIG_BLOCK_SIZE);
|
this(stream, POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Constructor RawDataBlock
|
* Constructor RawDataBlock
|
||||||
*
|
*
|
||||||
* @param stream the InputStream from which the data will be read
|
* @param stream the InputStream from which the data will be read
|
||||||
* @param blockSize the size of the POIFS blocks, normally 512 bytes {@link POIFSConstants#BIG_BLOCK_SIZE}
|
* @param blockSize the size of the POIFS blocks, normally 512 bytes
|
||||||
|
* {@link org.apache.poi.poifs.common.POIFSConstants#SMALLER_BIG_BLOCK_SIZE}
|
||||||
*
|
*
|
||||||
* @exception IOException on I/O errors, and if an insufficient
|
* @exception IOException on I/O errors, and if an insufficient
|
||||||
* amount of data is read (the InputStream must
|
* amount of data is read (the InputStream must
|
||||||
@ -111,6 +112,10 @@ public class RawDataBlock
|
|||||||
public boolean hasData() {
|
public boolean hasData() {
|
||||||
return _hasData;
|
return _hasData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "RawDataBlock of size " + _data.length;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********** START implementation of ListManagedBlock ********** */
|
/* ********** START implementation of ListManagedBlock ********** */
|
||||||
|
|
||||||
@ -130,6 +135,13 @@ public class RawDataBlock
|
|||||||
}
|
}
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What's the big block size?
|
||||||
|
*/
|
||||||
|
public int getBigBlockSize() {
|
||||||
|
return _data.length;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********** END implementation of ListManagedBlock ********** */
|
/* ********** END implementation of ListManagedBlock ********** */
|
||||||
} // end public class RawDataBlock
|
} // end public class RawDataBlock
|
||||||
|
@ -23,6 +23,8 @@ import java.io.*;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of RawDataBlocks instances, and methods to manage the list
|
* A list of RawDataBlocks instances, and methods to manage the list
|
||||||
*
|
*
|
||||||
@ -43,14 +45,14 @@ public class RawDataBlockList
|
|||||||
* block is read
|
* block is read
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public RawDataBlockList(final InputStream stream, int bigBlockSize)
|
public RawDataBlockList(final InputStream stream, POIFSBigBlockSize bigBlockSize)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
List blocks = new ArrayList();
|
List<RawDataBlock> blocks = new ArrayList<RawDataBlock>();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
RawDataBlock block = new RawDataBlock(stream, bigBlockSize);
|
RawDataBlock block = new RawDataBlock(stream, bigBlockSize.getBigBlockSize());
|
||||||
|
|
||||||
// If there was data, add the block to the list
|
// If there was data, add the block to the list
|
||||||
if(block.hasData()) {
|
if(block.hasData()) {
|
||||||
@ -62,7 +64,7 @@ public class RawDataBlockList
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setBlocks(( RawDataBlock [] ) blocks.toArray(new RawDataBlock[ 0 ]));
|
setBlocks( blocks.toArray(new RawDataBlock[ blocks.size() ]) );
|
||||||
}
|
}
|
||||||
} // end public class RawDataBlockList
|
} // end public class RawDataBlockList
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,25 +14,21 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.property.RootProperty;
|
import org.apache.poi.poifs.property.RootProperty;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements reading the small document block list from an
|
* This class implements reading the small document block list from an
|
||||||
* existing file
|
* existing file
|
||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class SmallBlockTableReader {
|
||||||
public class SmallBlockTableReader
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetch the small document block list from an existing file
|
* fetch the small document block list from an existing file
|
||||||
@ -48,17 +43,23 @@ public class SmallBlockTableReader
|
|||||||
*
|
*
|
||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static BlockList getSmallDocumentBlocks(
|
public static BlockList getSmallDocumentBlocks(
|
||||||
|
final POIFSBigBlockSize bigBlockSize,
|
||||||
final RawDataBlockList blockList, final RootProperty root,
|
final RawDataBlockList blockList, final RootProperty root,
|
||||||
final int sbatStart)
|
final int sbatStart)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
BlockList list =
|
// Fetch the blocks which hold the Small Blocks stream
|
||||||
new SmallDocumentBlockList(SmallDocumentBlock
|
ListManagedBlock [] smallBlockBlocks =
|
||||||
.extract(blockList.fetchBlocks(root.getStartBlock())));
|
blockList.fetchBlocks(root.getStartBlock(), -1);
|
||||||
|
|
||||||
|
// Turn that into a list
|
||||||
|
BlockList list =new SmallDocumentBlockList(
|
||||||
|
SmallDocumentBlock.extract(bigBlockSize, smallBlockBlocks));
|
||||||
|
|
||||||
new BlockAllocationTableReader(blockList.fetchBlocks(sbatStart),
|
// Process
|
||||||
|
new BlockAllocationTableReader(bigBlockSize,
|
||||||
|
blockList.fetchBlocks(sbatStart, -1),
|
||||||
list);
|
list);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
import org.apache.poi.poifs.filesystem.BATManaged;
|
import org.apache.poi.poifs.filesystem.BATManaged;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSDocument;
|
import org.apache.poi.poifs.filesystem.POIFSDocument;
|
||||||
@ -50,10 +51,11 @@ public class SmallBlockTableWriter
|
|||||||
* @param root the Filesystem's root property
|
* @param root the Filesystem's root property
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public SmallBlockTableWriter(final List documents,
|
public SmallBlockTableWriter(final POIFSBigBlockSize bigBlockSize,
|
||||||
|
final List documents,
|
||||||
final RootProperty root)
|
final RootProperty root)
|
||||||
{
|
{
|
||||||
_sbat = new BlockAllocationTableWriter();
|
_sbat = new BlockAllocationTableWriter(bigBlockSize);
|
||||||
_small_blocks = new ArrayList();
|
_small_blocks = new ArrayList();
|
||||||
_root = root;
|
_root = root;
|
||||||
Iterator iter = documents.iterator();
|
Iterator iter = documents.iterator();
|
||||||
@ -76,7 +78,7 @@ public class SmallBlockTableWriter
|
|||||||
}
|
}
|
||||||
_sbat.simpleCreateBlocks();
|
_sbat.simpleCreateBlocks();
|
||||||
_root.setSize(_small_blocks.size());
|
_root.setSize(_small_blocks.size());
|
||||||
_big_block_count = SmallDocumentBlock.fill(_small_blocks);
|
_big_block_count = SmallDocumentBlock.fill(bigBlockSize,_small_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,14 +14,17 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.common.POIFSBigBlockSize;
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,26 +33,34 @@ import org.apache.poi.poifs.common.POIFSConstants;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
public final class SmallDocumentBlock implements BlockWritable, ListManagedBlock {
|
||||||
|
private static final int BLOCK_SHIFT = 6;
|
||||||
|
|
||||||
public class SmallDocumentBlock
|
|
||||||
implements BlockWritable, ListManagedBlock
|
|
||||||
{
|
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
private static final byte _default_fill = ( byte ) 0xff;
|
private static final byte _default_fill = ( byte ) 0xff;
|
||||||
private static final int _block_size = 64;
|
private static final int _block_size = 1 << BLOCK_SHIFT;
|
||||||
private static final int _blocks_per_big_block =
|
private static final int BLOCK_MASK = _block_size-1;
|
||||||
POIFSConstants.BIG_BLOCK_SIZE / _block_size;
|
|
||||||
|
|
||||||
private SmallDocumentBlock(final byte [] data, final int index)
|
private final int _blocks_per_big_block;
|
||||||
|
private final POIFSBigBlockSize _bigBlockSize;
|
||||||
|
|
||||||
|
private SmallDocumentBlock(final POIFSBigBlockSize bigBlockSize, final byte [] data, final int index)
|
||||||
{
|
{
|
||||||
this();
|
this(bigBlockSize);
|
||||||
System.arraycopy(data, index * _block_size, _data, 0, _block_size);
|
System.arraycopy(data, index * _block_size, _data, 0, _block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SmallDocumentBlock()
|
private SmallDocumentBlock(final POIFSBigBlockSize bigBlockSize)
|
||||||
{
|
{
|
||||||
|
_bigBlockSize = bigBlockSize;
|
||||||
|
_blocks_per_big_block = getBlocksPerBigBlock(bigBlockSize);
|
||||||
_data = new byte[ _block_size ];
|
_data = new byte[ _block_size ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getBlocksPerBigBlock(final POIFSBigBlockSize bigBlockSize)
|
||||||
|
{
|
||||||
|
return bigBlockSize.getBigBlockSize() / _block_size;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert a single long array into an array of SmallDocumentBlock
|
* convert a single long array into an array of SmallDocumentBlock
|
||||||
@ -62,9 +72,9 @@ public class SmallDocumentBlock
|
|||||||
* @return an array of SmallDocumentBlock instances, filled from
|
* @return an array of SmallDocumentBlock instances, filled from
|
||||||
* the array
|
* the array
|
||||||
*/
|
*/
|
||||||
|
public static SmallDocumentBlock [] convert(POIFSBigBlockSize bigBlockSize,
|
||||||
public static SmallDocumentBlock [] convert(final byte [] array,
|
byte [] array,
|
||||||
final int size)
|
int size)
|
||||||
{
|
{
|
||||||
SmallDocumentBlock[] rval =
|
SmallDocumentBlock[] rval =
|
||||||
new SmallDocumentBlock[ (size + _block_size - 1) / _block_size ];
|
new SmallDocumentBlock[ (size + _block_size - 1) / _block_size ];
|
||||||
@ -72,7 +82,7 @@ public class SmallDocumentBlock
|
|||||||
|
|
||||||
for (int k = 0; k < rval.length; k++)
|
for (int k = 0; k < rval.length; k++)
|
||||||
{
|
{
|
||||||
rval[ k ] = new SmallDocumentBlock();
|
rval[ k ] = new SmallDocumentBlock(bigBlockSize);
|
||||||
if (offset < array.length)
|
if (offset < array.length)
|
||||||
{
|
{
|
||||||
int length = Math.min(_block_size, array.length - offset);
|
int length = Math.min(_block_size, array.length - offset);
|
||||||
@ -101,9 +111,10 @@ public class SmallDocumentBlock
|
|||||||
*
|
*
|
||||||
* @return number of big blocks the list encompasses
|
* @return number of big blocks the list encompasses
|
||||||
*/
|
*/
|
||||||
|
public static int fill(POIFSBigBlockSize bigBlockSize, List blocks)
|
||||||
public static int fill(final List blocks)
|
|
||||||
{
|
{
|
||||||
|
int _blocks_per_big_block = getBlocksPerBigBlock(bigBlockSize);
|
||||||
|
|
||||||
int count = blocks.size();
|
int count = blocks.size();
|
||||||
int big_block_count = (count + _blocks_per_big_block - 1)
|
int big_block_count = (count + _blocks_per_big_block - 1)
|
||||||
/ _blocks_per_big_block;
|
/ _blocks_per_big_block;
|
||||||
@ -111,7 +122,7 @@ public class SmallDocumentBlock
|
|||||||
|
|
||||||
for (; count < full_count; count++)
|
for (; count < full_count; count++)
|
||||||
{
|
{
|
||||||
blocks.add(makeEmptySmallDocumentBlock());
|
blocks.add(makeEmptySmallDocumentBlock(bigBlockSize));
|
||||||
}
|
}
|
||||||
return big_block_count;
|
return big_block_count;
|
||||||
}
|
}
|
||||||
@ -128,9 +139,9 @@ public class SmallDocumentBlock
|
|||||||
* @exception ArrayIndexOutOfBoundsException if, somehow, the store
|
* @exception ArrayIndexOutOfBoundsException if, somehow, the store
|
||||||
* contains less data than size indicates
|
* contains less data than size indicates
|
||||||
*/
|
*/
|
||||||
|
public static SmallDocumentBlock [] convert(POIFSBigBlockSize bigBlockSize,
|
||||||
public static SmallDocumentBlock [] convert(final BlockWritable [] store,
|
BlockWritable [] store,
|
||||||
final int size)
|
int size)
|
||||||
throws IOException, ArrayIndexOutOfBoundsException
|
throws IOException, ArrayIndexOutOfBoundsException
|
||||||
{
|
{
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
@ -145,7 +156,7 @@ public class SmallDocumentBlock
|
|||||||
|
|
||||||
for (int index = 0; index < rval.length; index++)
|
for (int index = 0; index < rval.length; index++)
|
||||||
{
|
{
|
||||||
rval[ index ] = new SmallDocumentBlock(data, index);
|
rval[ index ] = new SmallDocumentBlock(bigBlockSize, data, index);
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -157,13 +168,12 @@ public class SmallDocumentBlock
|
|||||||
* data
|
* data
|
||||||
*
|
*
|
||||||
* @return a List of SmallDocumentBlock's extracted from the input
|
* @return a List of SmallDocumentBlock's extracted from the input
|
||||||
*
|
|
||||||
* @exception IOException
|
|
||||||
*/
|
*/
|
||||||
|
public static List extract(POIFSBigBlockSize bigBlockSize, ListManagedBlock [] blocks)
|
||||||
public static List extract(ListManagedBlock [] blocks)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
int _blocks_per_big_block = getBlocksPerBigBlock(bigBlockSize);
|
||||||
|
|
||||||
List sdbs = new ArrayList();
|
List sdbs = new ArrayList();
|
||||||
|
|
||||||
for (int j = 0; j < blocks.length; j++)
|
for (int j = 0; j < blocks.length; j++)
|
||||||
@ -172,52 +182,16 @@ public class SmallDocumentBlock
|
|||||||
|
|
||||||
for (int k = 0; k < _blocks_per_big_block; k++)
|
for (int k = 0; k < _blocks_per_big_block; k++)
|
||||||
{
|
{
|
||||||
sdbs.add(new SmallDocumentBlock(data, k));
|
sdbs.add(new SmallDocumentBlock(bigBlockSize, data, k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sdbs;
|
return sdbs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) {
|
||||||
* read data from an array of SmallDocumentBlocks
|
int firstBlockIndex = offset >> BLOCK_SHIFT;
|
||||||
*
|
int firstBlockOffset= offset & BLOCK_MASK;
|
||||||
* @param blocks the blocks to read from
|
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
|
||||||
* @param buffer the buffer to write the data into
|
|
||||||
* @param offset the offset into the array of blocks to read from
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static void read(final BlockWritable [] blocks,
|
|
||||||
final byte [] buffer, final int offset)
|
|
||||||
{
|
|
||||||
int firstBlockIndex = offset / _block_size;
|
|
||||||
int firstBlockOffset = offset % _block_size;
|
|
||||||
int lastBlockIndex = (offset + buffer.length - 1) / _block_size;
|
|
||||||
|
|
||||||
if (firstBlockIndex == lastBlockIndex)
|
|
||||||
{
|
|
||||||
System.arraycopy(
|
|
||||||
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
|
|
||||||
firstBlockOffset, buffer, 0, buffer.length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int buffer_offset = 0;
|
|
||||||
|
|
||||||
System.arraycopy(
|
|
||||||
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
|
|
||||||
firstBlockOffset, buffer, buffer_offset,
|
|
||||||
_block_size - firstBlockOffset);
|
|
||||||
buffer_offset += _block_size - firstBlockOffset;
|
|
||||||
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
|
|
||||||
{
|
|
||||||
System.arraycopy((( SmallDocumentBlock ) blocks[ j ])._data,
|
|
||||||
0, buffer, buffer_offset, _block_size);
|
|
||||||
buffer_offset += _block_size;
|
|
||||||
}
|
|
||||||
System.arraycopy(
|
|
||||||
(( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0,
|
|
||||||
buffer, buffer_offset, buffer.length - buffer_offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,27 +201,24 @@ public class SmallDocumentBlock
|
|||||||
*
|
*
|
||||||
* @return total size
|
* @return total size
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static int calcSize(int size)
|
public static int calcSize(int size)
|
||||||
{
|
{
|
||||||
return size * _block_size;
|
return size * _block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SmallDocumentBlock makeEmptySmallDocumentBlock()
|
private static SmallDocumentBlock makeEmptySmallDocumentBlock(POIFSBigBlockSize bigBlockSize)
|
||||||
{
|
{
|
||||||
SmallDocumentBlock block = new SmallDocumentBlock();
|
SmallDocumentBlock block = new SmallDocumentBlock(bigBlockSize);
|
||||||
|
|
||||||
Arrays.fill(block._data, _default_fill);
|
Arrays.fill(block._data, _default_fill);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int convertToBlockCount(final int size)
|
private static int convertToBlockCount(int size)
|
||||||
{
|
{
|
||||||
return (size + _block_size - 1) / _block_size;
|
return (size + _block_size - 1) / _block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** START implementation of BlockWritable ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the storage to an OutputStream
|
* Write the storage to an OutputStream
|
||||||
*
|
*
|
||||||
@ -257,16 +228,12 @@ public class SmallDocumentBlock
|
|||||||
* @exception IOException on problems writing to the specified
|
* @exception IOException on problems writing to the specified
|
||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
|
public void writeBlocks(OutputStream stream)
|
||||||
public void writeBlocks(final OutputStream stream)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
stream.write(_data);
|
stream.write(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END implementation of BlockWritable ********** */
|
|
||||||
/* ********** START implementation of ListManagedBlock ********** */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the data from the block
|
* Get the data from the block
|
||||||
*
|
*
|
||||||
@ -274,13 +241,11 @@ public class SmallDocumentBlock
|
|||||||
*
|
*
|
||||||
* @exception IOException if there is no data
|
* @exception IOException if there is no data
|
||||||
*/
|
*/
|
||||||
|
public byte [] getData() {
|
||||||
public byte [] getData()
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********** END implementation of ListManagedBlock ********** */
|
public POIFSBigBlockSize getBigBlockSize() {
|
||||||
} // end public class SmallDocumentBlock
|
return _bigBlockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class SmallDocumentBlockList
|
|||||||
public SmallDocumentBlockList(final List blocks)
|
public SmallDocumentBlockList(final List blocks)
|
||||||
{
|
{
|
||||||
setBlocks(( SmallDocumentBlock [] ) blocks
|
setBlocks(( SmallDocumentBlock [] ) blocks
|
||||||
.toArray(new SmallDocumentBlock[ 0 ]));
|
.toArray(new SmallDocumentBlock[ blocks.size() ]));
|
||||||
}
|
}
|
||||||
} // end public class SmallDocumentBlockList
|
} // end public class SmallDocumentBlockList
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ package org.apache.poi.util;
|
|||||||
* Utility classes for dealing with arrays.
|
* Utility classes for dealing with arrays.
|
||||||
*
|
*
|
||||||
* @author Glen Stampoultzis
|
* @author Glen Stampoultzis
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
public class ArrayUtil
|
public class ArrayUtil
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
@ -15,7 +14,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.util;
|
package org.apache.poi.util;
|
||||||
|
|
||||||
@ -89,34 +87,20 @@ import java.util.*;
|
|||||||
*
|
*
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
*/
|
*/
|
||||||
public final class BinaryTree // final for performance
|
//for performance
|
||||||
|
public class BinaryTree extends AbstractMap {
|
||||||
extends AbstractMap
|
final Node[] _root;
|
||||||
{
|
int _size = 0;
|
||||||
private Node[] _root = new Node[]
|
int _modifications = 0;
|
||||||
{
|
private final Set[] _key_set = new Set[] { null, null };
|
||||||
null, null
|
private final Set[] _entry_set = new Set[] { null, null };
|
||||||
};
|
private final Collection[] _value_collection = new Collection[] { null, null };
|
||||||
private int _size = 0;
|
static int _KEY = 0;
|
||||||
private int _modifications = 0;
|
static int _VALUE = 1;
|
||||||
private Set[] _key_set = new Set[]
|
private static int _INDEX_SUM = _KEY + _VALUE;
|
||||||
{
|
private static int _MINIMUM_INDEX = 0;
|
||||||
null, null
|
private static int _INDEX_COUNT = 2;
|
||||||
};
|
private static String[] _data_name = new String[]
|
||||||
private Set[] _entry_set = new Set[]
|
|
||||||
{
|
|
||||||
null, null
|
|
||||||
};
|
|
||||||
private Collection[] _value_collection = new Collection[]
|
|
||||||
{
|
|
||||||
null, null
|
|
||||||
};
|
|
||||||
private static final int _KEY = 0;
|
|
||||||
private static final int _VALUE = 1;
|
|
||||||
private static final int _INDEX_SUM = _KEY + _VALUE;
|
|
||||||
private static final int _MINIMUM_INDEX = 0;
|
|
||||||
private static final int _INDEX_COUNT = 2;
|
|
||||||
private static final String[] _data_name = new String[]
|
|
||||||
{
|
{
|
||||||
"key", "value"
|
"key", "value"
|
||||||
};
|
};
|
||||||
@ -124,9 +108,8 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* Construct a new BinaryTree
|
* Construct a new BinaryTree
|
||||||
*/
|
*/
|
||||||
|
public BinaryTree() {
|
||||||
public BinaryTree()
|
_root = new Node[]{ null, null, };
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,11 +129,11 @@ public final class BinaryTree // final for performance
|
|||||||
* or duplicate values in the
|
* or duplicate values in the
|
||||||
* map
|
* map
|
||||||
*/
|
*/
|
||||||
|
public BinaryTree(Map map)
|
||||||
public BinaryTree(final Map map)
|
|
||||||
throws ClassCastException, NullPointerException,
|
throws ClassCastException, NullPointerException,
|
||||||
IllegalArgumentException
|
IllegalArgumentException
|
||||||
{
|
{
|
||||||
|
this();
|
||||||
putAll(map);
|
putAll(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,8 +150,7 @@ public final class BinaryTree // final for performance
|
|||||||
* inappropriate type for this map.
|
* inappropriate type for this map.
|
||||||
* @exception NullPointerException if the value is null
|
* @exception NullPointerException if the value is null
|
||||||
*/
|
*/
|
||||||
|
public Object getKeyForValue(Object value)
|
||||||
public Object getKeyForValue(final Object value)
|
|
||||||
throws ClassCastException, NullPointerException
|
throws ClassCastException, NullPointerException
|
||||||
{
|
{
|
||||||
return doGet(( Comparable ) value, _VALUE);
|
return doGet(( Comparable ) value, _VALUE);
|
||||||
@ -182,8 +164,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return previous key associated with specified value, or null
|
* @return previous key associated with specified value, or null
|
||||||
* if there was no mapping for value.
|
* if there was no mapping for value.
|
||||||
*/
|
*/
|
||||||
|
public Object removeValue(Object value)
|
||||||
public Object removeValue(final Object value)
|
|
||||||
{
|
{
|
||||||
return doRemove(( Comparable ) value, _VALUE);
|
return doRemove(( Comparable ) value, _VALUE);
|
||||||
}
|
}
|
||||||
@ -207,7 +188,6 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return a set view of the mappings contained in this map.
|
* @return a set view of the mappings contained in this map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Set entrySetByValue()
|
public Set entrySetByValue()
|
||||||
{
|
{
|
||||||
if (_entry_set[ _VALUE ] == null)
|
if (_entry_set[ _VALUE ] == null)
|
||||||
@ -423,8 +403,7 @@ public final class BinaryTree // final for performance
|
|||||||
* key. null if the specified key or value could not be
|
* key. null if the specified key or value could not be
|
||||||
* found
|
* found
|
||||||
*/
|
*/
|
||||||
|
private Object doRemove(Comparable o, int index)
|
||||||
private Object doRemove(final Comparable o, final int index)
|
|
||||||
{
|
{
|
||||||
Node node = lookup(o, index);
|
Node node = lookup(o, index);
|
||||||
Object rval = null;
|
Object rval = null;
|
||||||
@ -447,8 +426,7 @@ public final class BinaryTree // final for performance
|
|||||||
* key was mapped); null if we couldn't find the specified
|
* key was mapped); null if we couldn't find the specified
|
||||||
* object
|
* object
|
||||||
*/
|
*/
|
||||||
|
private Object doGet(Comparable o, int index)
|
||||||
private Object doGet(final Comparable o, final int index)
|
|
||||||
{
|
{
|
||||||
checkNonNullComparable(o, index);
|
checkNonNullComparable(o, index);
|
||||||
Node node = lookup(o, index);
|
Node node = lookup(o, index);
|
||||||
@ -464,8 +442,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return _VALUE (if _KEY was specified), else _KEY
|
* @return _VALUE (if _KEY was specified), else _KEY
|
||||||
*/
|
*/
|
||||||
|
private int oppositeIndex(int index)
|
||||||
private int oppositeIndex(final int index)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// old trick ... to find the opposite of a value, m or n,
|
// old trick ... to find the opposite of a value, m or n,
|
||||||
@ -483,8 +460,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return the desired Node, or null if there is no mapping of the
|
* @return the desired Node, or null if there is no mapping of the
|
||||||
* specified data
|
* specified data
|
||||||
*/
|
*/
|
||||||
|
public Node lookup(Comparable data, int index)
|
||||||
private Node lookup(final Comparable data, final int index)
|
|
||||||
{
|
{
|
||||||
Node rval = null;
|
Node rval = null;
|
||||||
Node node = _root[ index ];
|
Node node = _root[ index ];
|
||||||
@ -498,11 +474,8 @@ public final class BinaryTree // final for performance
|
|||||||
rval = node;
|
rval = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
node = (cmp < 0) ? node.getLeft(index)
|
||||||
{
|
: node.getRight(index);
|
||||||
node = (cmp < 0) ? node.getLeft(index)
|
|
||||||
: node.getRight(index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -516,10 +489,9 @@ public final class BinaryTree // final for performance
|
|||||||
* @return negative value if o1 < o2; 0 if o1 == o2; positive
|
* @return negative value if o1 < o2; 0 if o1 == o2; positive
|
||||||
* value if o1 > o2
|
* value if o1 > o2
|
||||||
*/
|
*/
|
||||||
|
private static int compare(Comparable o1, Comparable o2)
|
||||||
private static int compare(final Comparable o1, final Comparable o2)
|
|
||||||
{
|
{
|
||||||
return (( Comparable ) o1).compareTo(o2);
|
return o1.compareTo(o2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -532,8 +504,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return the smallest node, from the specified node, in the
|
* @return the smallest node, from the specified node, in the
|
||||||
* specified mapping
|
* specified mapping
|
||||||
*/
|
*/
|
||||||
|
static Node leastNode(Node node, int index)
|
||||||
private static Node leastNode(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
Node rval = node;
|
Node rval = node;
|
||||||
|
|
||||||
@ -555,8 +526,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return the specified node
|
* @return the specified node
|
||||||
*/
|
*/
|
||||||
|
static Node nextGreater(Node node, int index)
|
||||||
private Node nextGreater(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
Node rval = null;
|
Node rval = null;
|
||||||
|
|
||||||
@ -601,9 +571,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param to the node whose color we're changing; may be null
|
* @param to the node whose color we're changing; may be null
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static void copyColor(Node from, Node to, int index)
|
||||||
private static void copyColor(final Node from, final Node to,
|
|
||||||
final int index)
|
|
||||||
{
|
{
|
||||||
if (to != null)
|
if (to != null)
|
||||||
{
|
{
|
||||||
@ -627,11 +595,9 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static boolean isRed(Node node, int index)
|
||||||
private static boolean isRed(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return ((node == null) ? false
|
return node == null ? false : node.isRed(index);
|
||||||
: node.isRed(index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -641,11 +607,9 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static boolean isBlack(Node node, int index)
|
||||||
private static boolean isBlack(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return ((node == null) ? true
|
return node == null ? true : node.isBlack(index);
|
||||||
: node.isBlack(index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -654,8 +618,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static void makeRed(Node node, int index)
|
||||||
private static void makeRed(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
@ -669,8 +632,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static void makeBlack(Node node, int index)
|
||||||
private static void makeBlack(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
@ -685,8 +647,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static Node getGrandParent(Node node, int index)
|
||||||
private static Node getGrandParent(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return getParent(getParent(node, index), index);
|
return getParent(getParent(node, index), index);
|
||||||
}
|
}
|
||||||
@ -698,8 +659,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static Node getParent(Node node, int index)
|
||||||
private static Node getParent(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return ((node == null) ? null
|
return ((node == null) ? null
|
||||||
: node.getParent(index));
|
: node.getParent(index));
|
||||||
@ -712,8 +672,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static Node getRightChild(Node node, int index)
|
||||||
private static Node getRightChild(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return (node == null) ? null
|
return (node == null) ? null
|
||||||
: node.getRight(index);
|
: node.getRight(index);
|
||||||
@ -726,8 +685,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static Node getLeftChild(Node node, int index)
|
||||||
private static Node getLeftChild(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return (node == null) ? null
|
return (node == null) ? null
|
||||||
: node.getLeft(index);
|
: node.getLeft(index);
|
||||||
@ -744,15 +702,14 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static boolean isLeftChild(Node node, int index) {
|
||||||
private static boolean isLeftChild(final Node node, final int index)
|
if (node == null) {
|
||||||
{
|
return true;
|
||||||
return (node == null) ? true
|
}
|
||||||
: ((node.getParent(index) == null) ? false
|
if (node.getParent(index) == null) {
|
||||||
: (node
|
return false;
|
||||||
== node.getParent(
|
}
|
||||||
index).getLeft(
|
return node == node.getParent(index).getLeft(index);
|
||||||
index)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -766,15 +723,15 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node (may be null) in question
|
* @param node the node (may be null) in question
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private static boolean isRightChild(Node node, int index)
|
||||||
private static boolean isRightChild(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
return (node == null) ? true
|
if (node == null) {
|
||||||
: ((node.getParent(index) == null) ? false
|
return true;
|
||||||
: (node
|
}
|
||||||
== node.getParent(
|
if (node.getParent(index) == null) {
|
||||||
index).getRight(
|
return false;
|
||||||
index)));
|
}
|
||||||
|
return node == node.getParent(index).getRight(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -783,8 +740,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node to be rotated
|
* @param node the node to be rotated
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private void rotateLeft(Node node, int index)
|
||||||
private void rotateLeft(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
Node right_child = node.getRight(index);
|
Node right_child = node.getRight(index);
|
||||||
|
|
||||||
@ -818,8 +774,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node to be rotated
|
* @param node the node to be rotated
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private void rotateRight(Node node, int index)
|
||||||
private void rotateRight(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
Node left_child = node.getLeft(index);
|
Node left_child = node.getLeft(index);
|
||||||
|
|
||||||
@ -854,8 +809,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param inserted_node the node to be inserted
|
* @param inserted_node the node to be inserted
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private void doRedBlackInsert(Node inserted_node, int index)
|
||||||
private void doRedBlackInsert(final Node inserted_node, final int index)
|
|
||||||
{
|
{
|
||||||
Node current_node = inserted_node;
|
Node current_node = inserted_node;
|
||||||
|
|
||||||
@ -931,8 +885,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @param deleted_node the node to be deleted
|
* @param deleted_node the node to be deleted
|
||||||
*/
|
*/
|
||||||
|
void doRedBlackDelete(Node deleted_node)
|
||||||
private void doRedBlackDelete(final Node deleted_node)
|
|
||||||
{
|
{
|
||||||
for (int index = _MINIMUM_INDEX; index < _INDEX_COUNT; index++)
|
for (int index = _MINIMUM_INDEX; index < _INDEX_COUNT; index++)
|
||||||
{
|
{
|
||||||
@ -1023,9 +976,8 @@ public final class BinaryTree // final for performance
|
|||||||
* @param replacement_node the node being replaced
|
* @param replacement_node the node being replaced
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private void doRedBlackDeleteFixup(Node replacement_node,
|
||||||
private void doRedBlackDeleteFixup(final Node replacement_node,
|
int index)
|
||||||
final int index)
|
|
||||||
{
|
{
|
||||||
Node current_node = replacement_node;
|
Node current_node = replacement_node;
|
||||||
|
|
||||||
@ -1121,8 +1073,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param y another node
|
* @param y another node
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
private void swapPosition(Node x, Node y, int index)
|
||||||
private void swapPosition(final Node x, final Node y, final int index)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// Save initial values.
|
// Save initial values.
|
||||||
@ -1244,9 +1195,8 @@ public final class BinaryTree // final for performance
|
|||||||
* @exception NullPointerException if o is null
|
* @exception NullPointerException if o is null
|
||||||
* @exception ClassCastException if o is not Comparable
|
* @exception ClassCastException if o is not Comparable
|
||||||
*/
|
*/
|
||||||
|
private static void checkNonNullComparable(Object o,
|
||||||
private static void checkNonNullComparable(final Object o,
|
int index)
|
||||||
final int index)
|
|
||||||
{
|
{
|
||||||
if (o == null)
|
if (o == null)
|
||||||
{
|
{
|
||||||
@ -1268,8 +1218,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @exception NullPointerException if key is null
|
* @exception NullPointerException if key is null
|
||||||
* @exception ClassCastException if key is not Comparable
|
* @exception ClassCastException if key is not Comparable
|
||||||
*/
|
*/
|
||||||
|
private static void checkKey(Object key)
|
||||||
private static void checkKey(final Object key)
|
|
||||||
{
|
{
|
||||||
checkNonNullComparable(key, _KEY);
|
checkNonNullComparable(key, _KEY);
|
||||||
}
|
}
|
||||||
@ -1282,8 +1231,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @exception NullPointerException if value is null
|
* @exception NullPointerException if value is null
|
||||||
* @exception ClassCastException if value is not Comparable
|
* @exception ClassCastException if value is not Comparable
|
||||||
*/
|
*/
|
||||||
|
private static void checkValue(Object value)
|
||||||
private static void checkValue(final Object value)
|
|
||||||
{
|
{
|
||||||
checkNonNullComparable(value, _VALUE);
|
checkNonNullComparable(value, _VALUE);
|
||||||
}
|
}
|
||||||
@ -1298,8 +1246,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @exception NullPointerException if key or value is null
|
* @exception NullPointerException if key or value is null
|
||||||
* @exception ClassCastException if key or value is not Comparable
|
* @exception ClassCastException if key or value is not Comparable
|
||||||
*/
|
*/
|
||||||
|
private static void checkKeyAndValue(Object key, Object value)
|
||||||
private static void checkKeyAndValue(final Object key, final Object value)
|
|
||||||
{
|
{
|
||||||
checkKey(key);
|
checkKey(key);
|
||||||
checkValue(value);
|
checkValue(value);
|
||||||
@ -1310,7 +1257,6 @@ public final class BinaryTree // final for performance
|
|||||||
* concurrent modification of the map through the map and through
|
* concurrent modification of the map through the map and through
|
||||||
* an Iterator from one of its Set or Collection views
|
* an Iterator from one of its Set or Collection views
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void modify()
|
private void modify()
|
||||||
{
|
{
|
||||||
_modifications++;
|
_modifications++;
|
||||||
@ -1319,7 +1265,6 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* bump up the size and note that the map has changed
|
* bump up the size and note that the map has changed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void grow()
|
private void grow()
|
||||||
{
|
{
|
||||||
modify();
|
modify();
|
||||||
@ -1329,7 +1274,6 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* decrement the size and note that the map has changed
|
* decrement the size and note that the map has changed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void shrink()
|
private void shrink()
|
||||||
{
|
{
|
||||||
modify();
|
modify();
|
||||||
@ -1344,8 +1288,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @exception IllegalArgumentException if the node already exists
|
* @exception IllegalArgumentException if the node already exists
|
||||||
* in the value mapping
|
* in the value mapping
|
||||||
*/
|
*/
|
||||||
|
private void insertValue(Node newNode)
|
||||||
private void insertValue(final Node newNode)
|
|
||||||
throws IllegalArgumentException
|
throws IllegalArgumentException
|
||||||
{
|
{
|
||||||
Node node = _root[ _VALUE ];
|
Node node = _root[ _VALUE ];
|
||||||
@ -1400,7 +1343,6 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return the number of key-value mappings in this map.
|
* @return the number of key-value mappings in this map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int size()
|
public int size()
|
||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
@ -1419,8 +1361,7 @@ public final class BinaryTree // final for performance
|
|||||||
* type for this map.
|
* type for this map.
|
||||||
* @exception NullPointerException if the key is null
|
* @exception NullPointerException if the key is null
|
||||||
*/
|
*/
|
||||||
|
public boolean containsKey(Object key)
|
||||||
public boolean containsKey(final Object key)
|
|
||||||
throws ClassCastException, NullPointerException
|
throws ClassCastException, NullPointerException
|
||||||
{
|
{
|
||||||
checkKey(key);
|
checkKey(key);
|
||||||
@ -1436,8 +1377,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return true if this map maps one or more keys to the specified
|
* @return true if this map maps one or more keys to the specified
|
||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
|
public boolean containsValue(Object value)
|
||||||
public boolean containsValue(final Object value)
|
|
||||||
{
|
{
|
||||||
checkValue(value);
|
checkValue(value);
|
||||||
return lookup(( Comparable ) value, _VALUE) != null;
|
return lookup(( Comparable ) value, _VALUE) != null;
|
||||||
@ -1456,8 +1396,7 @@ public final class BinaryTree // final for performance
|
|||||||
* type for this map.
|
* type for this map.
|
||||||
* @exception NullPointerException if the key is null
|
* @exception NullPointerException if the key is null
|
||||||
*/
|
*/
|
||||||
|
public Object get(Object key)
|
||||||
public Object get(final Object key)
|
|
||||||
throws ClassCastException, NullPointerException
|
throws ClassCastException, NullPointerException
|
||||||
{
|
{
|
||||||
return doGet(( Comparable ) key, _KEY);
|
return doGet(( Comparable ) key, _KEY);
|
||||||
@ -1483,8 +1422,7 @@ public final class BinaryTree // final for performance
|
|||||||
* value duplicates an
|
* value duplicates an
|
||||||
* existing value
|
* existing value
|
||||||
*/
|
*/
|
||||||
|
public Object put(Object key, Object value)
|
||||||
public Object put(final Object key, final Object value)
|
|
||||||
throws ClassCastException, NullPointerException,
|
throws ClassCastException, NullPointerException,
|
||||||
IllegalArgumentException
|
IllegalArgumentException
|
||||||
{
|
{
|
||||||
@ -1562,8 +1500,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return previous value associated with specified key, or null
|
* @return previous value associated with specified key, or null
|
||||||
* if there was no mapping for key.
|
* if there was no mapping for key.
|
||||||
*/
|
*/
|
||||||
|
public Object remove(Object key)
|
||||||
public Object remove(final Object key)
|
|
||||||
{
|
{
|
||||||
return doRemove(( Comparable ) key, _KEY);
|
return doRemove(( Comparable ) key, _KEY);
|
||||||
}
|
}
|
||||||
@ -1571,7 +1508,6 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* Removes all mappings from this map
|
* Removes all mappings from this map
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void clear()
|
public void clear()
|
||||||
{
|
{
|
||||||
modify();
|
modify();
|
||||||
@ -1592,7 +1528,6 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return a set view of the keys contained in this map.
|
* @return a set view of the keys contained in this map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Set keySet()
|
public Set keySet()
|
||||||
{
|
{
|
||||||
if (_key_set[ _KEY ] == null)
|
if (_key_set[ _KEY ] == null)
|
||||||
@ -1650,7 +1585,6 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return a collection view of the values contained in this map.
|
* @return a collection view of the values contained in this map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Collection values()
|
public Collection values()
|
||||||
{
|
{
|
||||||
if (_value_collection[ _KEY ] == null)
|
if (_value_collection[ _KEY ] == null)
|
||||||
@ -1723,7 +1657,6 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return a set view of the mappings contained in this map.
|
* @return a set view of the mappings contained in this map.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Set entrySet()
|
public Set entrySet()
|
||||||
{
|
{
|
||||||
if (_entry_set[ _KEY ] == null)
|
if (_entry_set[ _KEY ] == null)
|
||||||
@ -1803,8 +1736,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @param type
|
* @param type
|
||||||
*/
|
*/
|
||||||
|
BinaryTreeIterator(int type)
|
||||||
BinaryTreeIterator(final int type)
|
|
||||||
{
|
{
|
||||||
_type = type;
|
_type = type;
|
||||||
_expected_modifications = BinaryTree.this._modifications;
|
_expected_modifications = BinaryTree.this._modifications;
|
||||||
@ -1825,7 +1757,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return true if the iterator has more elements.
|
* @return true if the iterator has more elements.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final boolean hasNext()
|
public boolean hasNext()
|
||||||
{
|
{
|
||||||
return _next_node != null;
|
return _next_node != null;
|
||||||
}
|
}
|
||||||
@ -1842,7 +1774,7 @@ public final class BinaryTree // final for performance
|
|||||||
* back
|
* back
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final Object next()
|
public Object next()
|
||||||
throws NoSuchElementException, ConcurrentModificationException
|
throws NoSuchElementException, ConcurrentModificationException
|
||||||
{
|
{
|
||||||
if (_next_node == null)
|
if (_next_node == null)
|
||||||
@ -1878,7 +1810,7 @@ public final class BinaryTree // final for performance
|
|||||||
* back
|
* back
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final void remove()
|
public void remove()
|
||||||
throws IllegalStateException, ConcurrentModificationException
|
throws IllegalStateException, ConcurrentModificationException
|
||||||
{
|
{
|
||||||
if (_last_returned_node == null)
|
if (_last_returned_node == null)
|
||||||
@ -1897,7 +1829,7 @@ public final class BinaryTree // final for performance
|
|||||||
/* ********** END implementation of Iterator ********** */
|
/* ********** END implementation of Iterator ********** */
|
||||||
} // end private abstract class BinaryTreeIterator
|
} // end private abstract class BinaryTreeIterator
|
||||||
|
|
||||||
// final for performance
|
// for performance
|
||||||
private static final class Node
|
private static final class Node
|
||||||
implements Map.Entry
|
implements Map.Entry
|
||||||
{
|
{
|
||||||
@ -1917,7 +1849,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Node(final Comparable key, final Comparable value)
|
Node(Comparable key, Comparable value)
|
||||||
{
|
{
|
||||||
_data = new Comparable[]
|
_data = new Comparable[]
|
||||||
{
|
{
|
||||||
@ -1949,8 +1881,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return the key or value
|
* @return the key or value
|
||||||
*/
|
*/
|
||||||
|
public Comparable getData(int index)
|
||||||
private Comparable getData(final int index)
|
|
||||||
{
|
{
|
||||||
return _data[ index ];
|
return _data[ index ];
|
||||||
}
|
}
|
||||||
@ -1961,8 +1892,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the new left node
|
* @param node the new left node
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void setLeft(Node node, int index)
|
||||||
private void setLeft(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
_left[ index ] = node;
|
_left[ index ] = node;
|
||||||
}
|
}
|
||||||
@ -1975,7 +1905,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return the left node -- may be null
|
* @return the left node -- may be null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private Node getLeft(final int index)
|
public Node getLeft(int index)
|
||||||
{
|
{
|
||||||
return _left[ index ];
|
return _left[ index ];
|
||||||
}
|
}
|
||||||
@ -1986,8 +1916,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the new right node
|
* @param node the new right node
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void setRight(Node node, int index)
|
||||||
private void setRight(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
_right[ index ] = node;
|
_right[ index ] = node;
|
||||||
}
|
}
|
||||||
@ -2000,7 +1929,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @return the right node -- may be null
|
* @return the right node -- may be null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private Node getRight(final int index)
|
public Node getRight(int index)
|
||||||
{
|
{
|
||||||
return _right[ index ];
|
return _right[ index ];
|
||||||
}
|
}
|
||||||
@ -2011,8 +1940,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the new parent node
|
* @param node the new parent node
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void setParent(Node node, int index)
|
||||||
private void setParent(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
_parent[ index ] = node;
|
_parent[ index ] = node;
|
||||||
}
|
}
|
||||||
@ -2024,8 +1952,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return the parent node -- may be null
|
* @return the parent node -- may be null
|
||||||
*/
|
*/
|
||||||
|
public Node getParent(int index)
|
||||||
private Node getParent(final int index)
|
|
||||||
{
|
{
|
||||||
return _parent[ index ];
|
return _parent[ index ];
|
||||||
}
|
}
|
||||||
@ -2036,8 +1963,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node to swap with
|
* @param node the node to swap with
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void swapColors(Node node, int index)
|
||||||
private void swapColors(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// Swap colors -- old hacker's trick
|
// Swap colors -- old hacker's trick
|
||||||
@ -2053,8 +1979,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return true if black (which is represented as a true boolean)
|
* @return true if black (which is represented as a true boolean)
|
||||||
*/
|
*/
|
||||||
|
public boolean isBlack(int index)
|
||||||
private boolean isBlack(final int index)
|
|
||||||
{
|
{
|
||||||
return _black[ index ];
|
return _black[ index ];
|
||||||
}
|
}
|
||||||
@ -2066,8 +1991,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @return true if non-black
|
* @return true if non-black
|
||||||
*/
|
*/
|
||||||
|
public boolean isRed(int index)
|
||||||
private boolean isRed(final int index)
|
|
||||||
{
|
{
|
||||||
return !_black[ index ];
|
return !_black[ index ];
|
||||||
}
|
}
|
||||||
@ -2077,8 +2001,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void setBlack(int index)
|
||||||
private void setBlack(final int index)
|
|
||||||
{
|
{
|
||||||
_black[ index ] = true;
|
_black[ index ] = true;
|
||||||
}
|
}
|
||||||
@ -2088,8 +2011,7 @@ public final class BinaryTree // final for performance
|
|||||||
*
|
*
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void setRed(int index)
|
||||||
private void setRed(final int index)
|
|
||||||
{
|
{
|
||||||
_black[ index ] = false;
|
_black[ index ] = false;
|
||||||
}
|
}
|
||||||
@ -2100,8 +2022,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param node the node whose color we're adopting
|
* @param node the node whose color we're adopting
|
||||||
* @param index _KEY or _VALUE
|
* @param index _KEY or _VALUE
|
||||||
*/
|
*/
|
||||||
|
public void copyColor(Node node, int index)
|
||||||
private void copyColor(final Node node, final int index)
|
|
||||||
{
|
{
|
||||||
_black[ index ] = node._black[ index ];
|
_black[ index ] = node._black[ index ];
|
||||||
}
|
}
|
||||||
@ -2111,7 +2032,6 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* @return the key corresponding to this entry.
|
* @return the key corresponding to this entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Object getKey()
|
public Object getKey()
|
||||||
{
|
{
|
||||||
return _data[ _KEY ];
|
return _data[ _KEY ];
|
||||||
@ -2120,7 +2040,6 @@ public final class BinaryTree // final for performance
|
|||||||
/**
|
/**
|
||||||
* @return the value corresponding to this entry.
|
* @return the value corresponding to this entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Object getValue()
|
public Object getValue()
|
||||||
{
|
{
|
||||||
return _data[ _VALUE ];
|
return _data[ _VALUE ];
|
||||||
@ -2133,10 +2052,7 @@ public final class BinaryTree // final for performance
|
|||||||
* @param ignored
|
* @param ignored
|
||||||
*
|
*
|
||||||
* @return does not return
|
* @return does not return
|
||||||
*
|
|
||||||
* @exception UnsupportedOperationException
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public Object setValue(Object ignored)
|
public Object setValue(Object ignored)
|
||||||
throws UnsupportedOperationException
|
throws UnsupportedOperationException
|
||||||
{
|
{
|
||||||
@ -2154,7 +2070,6 @@ public final class BinaryTree // final for performance
|
|||||||
* @return true if the specified object is equal to this map
|
* @return true if the specified object is equal to this map
|
||||||
* entry.
|
* entry.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
if (this == o)
|
if (this == o)
|
||||||
@ -2188,5 +2103,4 @@ public final class BinaryTree // final for performance
|
|||||||
|
|
||||||
/* ********** END implementation of Map.Entry ********** */
|
/* ********** END implementation of Map.Entry ********** */
|
||||||
}
|
}
|
||||||
} // end public class BinaryTree
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
|
|
||||||
package org.apache.poi.util;
|
package org.apache.poi.util;
|
||||||
|
|
||||||
@ -27,18 +26,15 @@ import java.util.*;
|
|||||||
* @author Jason Height (jheight at apache dot org)
|
* @author Jason Height (jheight at apache dot org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class BitFieldFactory
|
public class BitFieldFactory {
|
||||||
{
|
|
||||||
private static Map instances = new HashMap();
|
private static Map instances = new HashMap();
|
||||||
|
|
||||||
public static BitField getInstance(final int mask) {
|
public static BitField getInstance(int mask) {
|
||||||
BitField f = (BitField)instances.get(new Integer(mask));
|
BitField f = (BitField)instances.get(Integer.valueOf(mask));
|
||||||
if (f == null) {
|
if (f == null) {
|
||||||
f = new BitField(mask);
|
f = new BitField(mask);
|
||||||
instances.put(new Integer(mask), f);
|
instances.put(Integer.valueOf(mask), f);
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // end public class BitFieldFactory
|
|
||||||
|
|
||||||
|
41
src/java/org/apache/poi/util/CloseIgnoringInputStream.java
Normal file
41
src/java/org/apache/poi/util/CloseIgnoringInputStream.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.util;
|
||||||
|
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around an {@link InputStream}, which
|
||||||
|
* ignores close requests made to it.
|
||||||
|
*
|
||||||
|
* Useful with {@link POIFSFileSystem}, where you want
|
||||||
|
* to control the close yourself.
|
||||||
|
*/
|
||||||
|
public class CloseIgnoringInputStream extends FilterInputStream {
|
||||||
|
public CloseIgnoringInputStream(InputStream in) {
|
||||||
|
super(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
// Does nothing and ignores you
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.util;
|
||||||
|
/**
|
||||||
|
* Implementors of this interface allow client code to 'delay' writing to a certain section of a
|
||||||
|
* data output stream.<br/>
|
||||||
|
* A typical application is for writing BIFF records when the size is not known until well after
|
||||||
|
* the header has been written. The client code can call {@link #createDelayedOutput(int)}
|
||||||
|
* to reserve two bytes of the output for the 'ushort size' header field. The delayed output can
|
||||||
|
* be written at any stage.
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public interface DelayableLittleEndianOutput extends LittleEndianOutput {
|
||||||
|
/**
|
||||||
|
* Creates an output stream intended for outputting a sequence of <tt>size</tt> bytes.
|
||||||
|
*/
|
||||||
|
LittleEndianOutput createDelayedOutput(int size);
|
||||||
|
}
|
84
src/java/org/apache/poi/util/FontMetricsDumper.java
Normal file
84
src/java/org/apache/poi/util/FontMetricsDumper.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.util;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public class FontMetricsDumper
|
||||||
|
{
|
||||||
|
public static void main( String[] args ) throws IOException
|
||||||
|
{
|
||||||
|
|
||||||
|
Properties props = new Properties();
|
||||||
|
|
||||||
|
Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||||
|
for ( int i = 0; i < allFonts.length; i++ )
|
||||||
|
{
|
||||||
|
String fontName = allFonts[i].getFontName();
|
||||||
|
|
||||||
|
Font font = new Font(fontName, Font.BOLD, 10);
|
||||||
|
FontMetrics fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
|
||||||
|
int fontHeight = fontMetrics.getHeight();
|
||||||
|
|
||||||
|
props.setProperty("font." + fontName + ".height", fontHeight+"");
|
||||||
|
StringBuffer characters = new StringBuffer();
|
||||||
|
for (char c = 'a'; c <= 'z'; c++)
|
||||||
|
{
|
||||||
|
characters.append( c + ", " );
|
||||||
|
}
|
||||||
|
for (char c = 'A'; c <= 'Z'; c++)
|
||||||
|
{
|
||||||
|
characters.append( c + ", " );
|
||||||
|
}
|
||||||
|
for (char c = '0'; c <= '9'; c++)
|
||||||
|
{
|
||||||
|
characters.append( c + ", " );
|
||||||
|
}
|
||||||
|
StringBuffer widths = new StringBuffer();
|
||||||
|
for (char c = 'a'; c <= 'z'; c++)
|
||||||
|
{
|
||||||
|
widths.append( fontMetrics.getWidths()[c] + ", " );
|
||||||
|
}
|
||||||
|
for (char c = 'A'; c <= 'Z'; c++)
|
||||||
|
{
|
||||||
|
widths.append( fontMetrics.getWidths()[c] + ", " );
|
||||||
|
}
|
||||||
|
for (char c = '0'; c <= '9'; c++)
|
||||||
|
{
|
||||||
|
widths.append( fontMetrics.getWidths()[c] + ", " );
|
||||||
|
}
|
||||||
|
props.setProperty("font." + fontName + ".characters", characters.toString());
|
||||||
|
props.setProperty("font." + fontName + ".widths", widths.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileOutputStream fileOut = new FileOutputStream("font_metrics.properties");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
props.store(fileOut, "Font Metrics");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileOut.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user