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:
parent
4d3ddefa5b
commit
574e993120
@ -43,6 +43,7 @@ import org.apache.poi.poifs.nio.ByteArrayBackedDataSource;
|
|||||||
import org.apache.poi.poifs.nio.DataSource;
|
import org.apache.poi.poifs.nio.DataSource;
|
||||||
import org.apache.poi.poifs.nio.FileBackedDataSource;
|
import org.apache.poi.poifs.nio.FileBackedDataSource;
|
||||||
import org.apache.poi.poifs.property.DirectoryProperty;
|
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.Property;
|
||||||
import org.apache.poi.poifs.property.PropertyTable;
|
import org.apache.poi.poifs.property.PropertyTable;
|
||||||
import org.apache.poi.poifs.storage.BATBlock;
|
import org.apache.poi.poifs.storage.BATBlock;
|
||||||
@ -82,8 +83,8 @@ public class NPOIFSFileSystem
|
|||||||
return new CloseIgnoringInputStream(is);
|
return new CloseIgnoringInputStream(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyTable _property_table;
|
private NPropertyTable _property_table;
|
||||||
private List<BATBlock> _bat_blocks;
|
private List<BATBlock> _bat_blocks;
|
||||||
private HeaderBlock _header;
|
private HeaderBlock _header;
|
||||||
private DirectoryNode _root;
|
private DirectoryNode _root;
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ public class NPOIFSFileSystem
|
|||||||
public NPOIFSFileSystem()
|
public NPOIFSFileSystem()
|
||||||
{
|
{
|
||||||
_header = new HeaderBlock(bigBlockSize);
|
_header = new HeaderBlock(bigBlockSize);
|
||||||
_property_table = new PropertyTable(_header);// TODO Needs correct type
|
_property_table = new NPropertyTable(_header);
|
||||||
_bat_blocks = new ArrayList<BATBlock>();
|
_bat_blocks = new ArrayList<BATBlock>();
|
||||||
_root = null;
|
_root = null;
|
||||||
}
|
}
|
||||||
@ -191,8 +192,9 @@ public class NPOIFSFileSystem
|
|||||||
int maxSize = BATBlock.calculateMaximumSize(_header);
|
int maxSize = BATBlock.calculateMaximumSize(_header);
|
||||||
ByteBuffer data = ByteBuffer.allocate(maxSize);
|
ByteBuffer data = ByteBuffer.allocate(maxSize);
|
||||||
// Copy in the header
|
// Copy in the header
|
||||||
|
headerBuffer.position(0);
|
||||||
data.put(headerBuffer);
|
data.put(headerBuffer);
|
||||||
data.position(_header.getBigBlockSize().getBigBlockSize());
|
data.position(headerBuffer.capacity());
|
||||||
// Now read the rest of the stream
|
// Now read the rest of the stream
|
||||||
IOUtils.readFully(channel, data);
|
IOUtils.readFully(channel, data);
|
||||||
success = true;
|
success = true;
|
||||||
@ -292,7 +294,7 @@ public class NPOIFSFileSystem
|
|||||||
|
|
||||||
// We're now able to load steams
|
// We're now able to load steams
|
||||||
// Use this to read in the properties
|
// 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)
|
public void writeFilesystem(final OutputStream stream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
// get the property table ready
|
|
||||||
_property_table.preWrite();
|
|
||||||
|
|
||||||
// create the small block store, and the SBAT
|
// create the small block store, and the SBAT
|
||||||
SmallBlockTableWriter sbtw =
|
SmallBlockTableWriter sbtw =
|
||||||
new SmallBlockTableWriter(bigBlockSize, _documents, _property_table.getRoot());
|
new SmallBlockTableWriter(bigBlockSize, _documents, _property_table.getRoot());
|
||||||
@ -550,7 +548,6 @@ public class NPOIFSFileSystem
|
|||||||
|
|
||||||
writers.add(header_block_writer);
|
writers.add(header_block_writer);
|
||||||
writers.addAll(_documents);
|
writers.addAll(_documents);
|
||||||
writers.add(_property_table);
|
|
||||||
writers.add(sbtw);
|
writers.add(sbtw);
|
||||||
writers.add(sbtw.getSBAT());
|
writers.add(sbtw.getSBAT());
|
||||||
writers.add(bat);
|
writers.add(bat);
|
||||||
@ -567,6 +564,11 @@ public class NPOIFSFileSystem
|
|||||||
|
|
||||||
writer.writeBlocks(stream);
|
writer.writeBlocks(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally have the property table serialise itself
|
||||||
|
_property_table.write(
|
||||||
|
new NPOIFSStream(this, _header.getPropertyStart())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -55,8 +55,19 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
||||||
// Check the FAT was properly processed
|
// Check the FAT was properly processed:
|
||||||
// TODO
|
// 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
|
// Check the properties
|
||||||
// TODO
|
// TODO
|
||||||
@ -67,7 +78,19 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi"));
|
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi"));
|
||||||
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
||||||
// Check the FAT was properly processed
|
// 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
|
// Check the properties
|
||||||
// TODO
|
// TODO
|
||||||
@ -79,72 +102,81 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* out what the next one is
|
* out what the next one is
|
||||||
*/
|
*/
|
||||||
public void testNextBlock() throws Exception {
|
public void testNextBlock() throws Exception {
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
NPOIFSFileSystem fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
|
||||||
// 0 -> 21 are simple
|
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
|
||||||
for(int i=0; i<21; i++) {
|
// 0 -> 21 are simple
|
||||||
assertEquals(i+1, fs.getNextBlock(i));
|
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
|
// TODO Check a few bits of a 4096 byte file
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check we get the right data back for each block
|
* Check we get the right data back for each block
|
||||||
*/
|
*/
|
||||||
public void testGetBlock() throws Exception {
|
public void testGetBlock() throws Exception {
|
||||||
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
NPOIFSFileSystem fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
ByteBuffer b;
|
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
|
// TODO Check a few bits of a 4096 byte file
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user