Start on PropertyTable support for NPOIFS, and more NPOIFS tests

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1052205 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2010-12-23 09:21:49 +00:00
parent d5a1c8b7da
commit c204206f19
3 changed files with 232 additions and 70 deletions

View File

@ -43,6 +43,7 @@ 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.property.Property;
import org.apache.poi.poifs.property.PropertyTable;
import org.apache.poi.poifs.storage.BATBlock;
@ -82,8 +83,8 @@ public class NPOIFSFileSystem
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;
@ -104,7 +105,7 @@ public class NPOIFSFileSystem
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;
}
@ -191,8 +192,9 @@ public class NPOIFSFileSystem
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;
@ -292,7 +294,7 @@ public class NPOIFSFileSystem
// We're now able to load steams
// Use this to read in the properties
// TODO
_property_table = new NPropertyTable(_header, this);
}
/**
@ -479,10 +481,6 @@ public class NPOIFSFileSystem
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());
@ -550,7 +548,6 @@ public class NPOIFSFileSystem
writers.add(header_block_writer);
writers.addAll(_documents);
writers.add(_property_table);
writers.add(sbtw);
writers.add(sbtw.getSBAT());
writers.add(bat);
@ -567,6 +564,11 @@ public class NPOIFSFileSystem
writer.writeBlocks(stream);
}
// Finally have the property table serialise itself
_property_table.write(
new NPOIFSStream(this, _header.getPropertyStart())
);
}
/**

View 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());
}
}
}

View File

@ -55,8 +55,19 @@ public final class TestNPOIFSFileSystem extends TestCase {
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
@ -67,7 +78,19 @@ public final class TestNPOIFSFileSystem extends TestCase {
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
@ -79,72 +102,81 @@ public final class TestNPOIFSFileSystem extends TestCase {
* 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));
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));
}
}
// 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));
}
// 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;
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());
}
// 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
}
/**