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.property.Property;
import org.apache.poi.poifs.property.PropertyTable;
import org.apache.poi.poifs.storage.BATBlock;
return new CloseIgnoringInputStream(is);
}
- private PropertyTable _property_table;
- private List<BATBlock> _bat_blocks;
+ private NPropertyTable _property_table;
+ private List<BATBlock> _bat_blocks;
private HeaderBlock _header;
private DirectoryNode _root;
public NPOIFSFileSystem()
{
_header = new HeaderBlock(bigBlockSize);
- _property_table = new PropertyTable(_header);// TODO Needs correct type
+ _property_table = new NPropertyTable(_header);
_bat_blocks = new ArrayList<BATBlock>();
_root = null;
}
int maxSize = BATBlock.calculateMaximumSize(_header);
ByteBuffer data = ByteBuffer.allocate(maxSize);
// Copy in the header
+ headerBuffer.position(0);
data.put(headerBuffer);
- data.position(_header.getBigBlockSize().getBigBlockSize());
+ data.position(headerBuffer.capacity());
// Now read the rest of the stream
IOUtils.readFully(channel, data);
success = true;
// We're now able to load steams
// Use this to read in the properties
- // TODO
+ _property_table = new NPropertyTable(_header, this);
}
/**
public void writeFilesystem(final OutputStream stream)
throws IOException
{
-
- // get the property table ready
- _property_table.preWrite();
-
// create the small block store, and the SBAT
SmallBlockTableWriter sbtw =
new SmallBlockTableWriter(bigBlockSize, _documents, _property_table.getRoot());
writers.add(header_block_writer);
writers.addAll(_documents);
- writers.add(_property_table);
writers.add(sbtw);
writers.add(sbtw.getSBAT());
writers.add(bat);
writer.writeBlocks(stream);
}
+
+ // Finally have the property table serialise itself
+ _property_table.write(
+ new NPOIFSStream(this, _header.getPropertyStart())
+ );
}
/**
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+ }
+}
fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
- // Check the FAT was properly processed
- // TODO
+ // Check the FAT was properly processed:
+ // Verify we only got one block
+ fs.getBATBlockAndIndex(0);
+ fs.getBATBlockAndIndex(1);
+ try {
+ fs.getBATBlockAndIndex(140);
+ fail("Should only be one BAT, but a 2nd was found");
+ } catch(IndexOutOfBoundsException e) {}
+
+ // Verify a few next offsets
+ // 97 -> 98 -> END
+ assertEquals(98, fs.getNextBlock(97));
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
// Check the properties
// TODO
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi"));
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
// Check the FAT was properly processed
- // TODO
+ // Verify we only got one block
+ fs.getBATBlockAndIndex(0);
+ fs.getBATBlockAndIndex(1);
+ try {
+ fs.getBATBlockAndIndex(1040);
+ fail("Should only be one BAT, but a 2nd was found");
+ } catch(IndexOutOfBoundsException e) {}
+
+ // Verify a few next offsets
+ // 0 -> 1 -> 2 -> END
+ assertEquals(1, fs.getNextBlock(0));
+ assertEquals(2, fs.getNextBlock(1));
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(2));
// Check the properties
// TODO
* out what the next one is
*/
public void testNextBlock() throws Exception {
- NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
-
- // 0 -> 21 are simple
- for(int i=0; i<21; i++) {
- assertEquals(i+1, fs.getNextBlock(i));
- }
- // 21 jumps to 89, then ends
- assertEquals(89, fs.getNextBlock(21));
- assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(89));
-
- // 22 -> 88 simple sequential stream
- for(int i=22; i<88; i++) {
- assertEquals(i+1, fs.getNextBlock(i));
- }
- assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(88));
-
- // 90 -> 96 is another stream
- for(int i=90; i<96; i++) {
- assertEquals(i+1, fs.getNextBlock(i));
+ NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
+ NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
+ for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
+ // 0 -> 21 are simple
+ for(int i=0; i<21; i++) {
+ assertEquals(i+1, fs.getNextBlock(i));
+ }
+ // 21 jumps to 89, then ends
+ assertEquals(89, fs.getNextBlock(21));
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(89));
+
+ // 22 -> 88 simple sequential stream
+ for(int i=22; i<88; i++) {
+ assertEquals(i+1, fs.getNextBlock(i));
+ }
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(88));
+
+ // 90 -> 96 is another stream
+ for(int i=90; i<96; i++) {
+ assertEquals(i+1, fs.getNextBlock(i));
+ }
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(96));
+
+ // 97+98 is another
+ assertEquals(98, fs.getNextBlock(97));
+ assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
+
+ // 99 is our FAT block
+ assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
+
+ // 100 onwards is free
+ for(int i=100; i<fs.getBigBlockSizeDetails().getBATEntriesPerBlock(); i++) {
+ assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(i));
+ }
}
- assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(96));
-
- // 97+98 is another
- assertEquals(98, fs.getNextBlock(97));
- assertEquals(POIFSConstants.END_OF_CHAIN, fs.getNextBlock(98));
- // 99 is our FAT block
- assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(99));
-
- // 100 onwards is free
- for(int i=100; i<fs.getBigBlockSizeDetails().getBATEntriesPerBlock(); i++) {
- assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(i));
- }
+ // TODO Check a few bits of a 4096 byte file
}
/**
* Check we get the right data back for each block
*/
public void testGetBlock() throws Exception {
- NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
- ByteBuffer b;
-
- // The 0th block is the first data block
- b = fs.getBlockAt(0);
- assertEquals((byte)0x9e, b.get());
- assertEquals((byte)0x75, b.get());
- assertEquals((byte)0x97, b.get());
- assertEquals((byte)0xf6, b.get());
-
- // And the next block
- b = fs.getBlockAt(1);
- assertEquals((byte)0x86, b.get());
- assertEquals((byte)0x09, b.get());
- assertEquals((byte)0x22, b.get());
- assertEquals((byte)0xfb, b.get());
-
- // Check the final block too
- b = fs.getBlockAt(99);
- assertEquals((byte)0x01, b.get());
- assertEquals((byte)0x00, b.get());
- assertEquals((byte)0x00, b.get());
- assertEquals((byte)0x00, b.get());
- assertEquals((byte)0x02, b.get());
- assertEquals((byte)0x00, b.get());
- assertEquals((byte)0x00, b.get());
- assertEquals((byte)0x00, b.get());
+ NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
+ NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
+ for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
+ ByteBuffer b;
+
+ // The 0th block is the first data block
+ b = fs.getBlockAt(0);
+ assertEquals((byte)0x9e, b.get());
+ assertEquals((byte)0x75, b.get());
+ assertEquals((byte)0x97, b.get());
+ assertEquals((byte)0xf6, b.get());
+
+ // And the next block
+ b = fs.getBlockAt(1);
+ assertEquals((byte)0x86, b.get());
+ assertEquals((byte)0x09, b.get());
+ assertEquals((byte)0x22, b.get());
+ assertEquals((byte)0xfb, b.get());
+
+ // Check the final block too
+ b = fs.getBlockAt(99);
+ assertEquals((byte)0x01, b.get());
+ assertEquals((byte)0x00, b.get());
+ assertEquals((byte)0x00, b.get());
+ assertEquals((byte)0x00, b.get());
+ assertEquals((byte)0x02, b.get());
+ assertEquals((byte)0x00, b.get());
+ assertEquals((byte)0x00, b.get());
+ assertEquals((byte)0x00, b.get());
+ }
+
+ // TODO Check a few bits of a 4096 byte file
}
/**