123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- /* ====================================================================
- 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.io.OutputStream;
- import java.nio.ByteBuffer;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Stack;
-
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.apache.poi.poifs.common.POIFSBigBlockSize;
- import org.apache.poi.poifs.common.POIFSConstants;
- import org.apache.poi.poifs.filesystem.BATManaged;
- import org.apache.poi.poifs.filesystem.POIFSFileSystem;
- import org.apache.poi.poifs.filesystem.POIFSStream;
- import org.apache.poi.poifs.storage.HeaderBlock;
- import org.apache.poi.util.IOUtils;
-
- import static org.apache.logging.log4j.util.Unbox.box;
-
- /**
- * This class embodies the Property Table for a {@link POIFSFileSystem};
- * this is basically the directory for all of the documents in the
- * filesystem and looks up entries in the filesystem to their
- * chain of blocks.
- */
- public final class PropertyTable implements BATManaged {
- private static final Logger LOG = LogManager.getLogger(PropertyTable.class);
-
- private final HeaderBlock _header_block;
- private final List<Property> _properties = new ArrayList<>();
- private final POIFSBigBlockSize _bigBigBlockSize;
-
- public PropertyTable(HeaderBlock headerBlock)
- {
- _header_block = headerBlock;
- _bigBigBlockSize = headerBlock.getBigBlockSize();
- 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 headerBlock the header block of the file
- * @param filesystem the filesystem to read from
- *
- * @throws IOException if anything goes wrong (which should be
- * a result of the input being NFG)
- */
- public PropertyTable(final HeaderBlock headerBlock, final POIFSFileSystem filesystem)
- throws IOException {
- this(
- headerBlock,
- new POIFSStream(filesystem, headerBlock.getPropertyStart())
- );
- }
-
- /* only invoked locally and from the junit tests */
- PropertyTable(final HeaderBlock headerBlock, final Iterable<ByteBuffer> dataSource)
- throws IOException {
- _header_block = headerBlock;
- _bigBigBlockSize = headerBlock.getBigBlockSize();
-
- for (ByteBuffer bb : dataSource) {
- // Turn it into an array
- byte[] data;
- if (bb.hasArray() && bb.arrayOffset() == 0 &&
- bb.array().length == _bigBigBlockSize.getBigBlockSize()) {
- data = bb.array();
- } else {
- data = IOUtils.safelyAllocate(_bigBigBlockSize.getBigBlockSize(), POIFSFileSystem.getMaxRecordLength());
-
- int toRead = data.length;
- if (bb.remaining() < _bigBigBlockSize.getBigBlockSize()) {
- // Looks to be a truncated block
- // This isn't allowed, but some third party created files
- // sometimes do this, and we can normally read anyway
- LOG.atWarn().log("Short Property Block, {} bytes instead of the expected {}", box(bb.remaining()),box(_bigBigBlockSize.getBigBlockSize()));
- toRead = bb.remaining();
- }
-
- bb.get(data, 0, toRead);
- }
-
- PropertyFactory.convertToProperties(data, _properties);
- }
-
- if (_properties.get(0) != null) {
- 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);
- }
-
- /**
- * 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);
- }
-
-
-
- /**
- * Return the number of BigBlock's this instance uses
- *
- * @return count of BigBlock instances
- */
- public int countBlocks() {
- long rawSize = _properties.size() * (long)POIFSConstants.PROPERTY_SIZE;
- int blkSize = _bigBigBlockSize.getBigBlockSize();
- int numBlocks = (int)(rawSize / blkSize);
- if ((rawSize % blkSize) != 0) {
- numBlocks++;
- }
- return numBlocks;
- }
-
- /**
- * Prepare to be written
- */
- public void preWrite() {
- List<Property> pList = new ArrayList<>();
- // give each property its index
- int i=0;
- for (Property p : _properties) {
- // only handle non-null properties
- if (p == null) continue;
- p.setIndex(i++);
- pList.add(p);
- }
-
- // prepare each property for writing
- for (Property p : pList) p.preWrite();
- }
-
- /**
- * Writes the properties out into the given low-level stream
- */
- public void write(POIFSStream stream) throws IOException {
- OutputStream os = stream.getOutputStream();
- for(Property property : _properties) {
- if(property != null) {
- property.writeData(os);
- }
- }
- os.close();
-
- // Update the start position if needed
- if(getStartBlock() != stream.getStartBlock()) {
- setStartBlock(stream.getStartBlock());
- }
- }
-
- private void populatePropertyTree(DirectoryProperty root) throws IOException {
- int index = root.getChildIndex();
-
- if (!Property.isValidIndex(index)) {
- // property has no children
- return;
- }
-
- final Stack<Property> children = new Stack<>();
- children.push(_properties.get(index));
- while (!children.empty()) {
- Property property = children.pop();
- if (property == null) {
- // unknown / unsupported / corrupted property, skip
- continue;
- }
-
- root.addChild(property);
- if (property.isDirectory()) {
- populatePropertyTree(( DirectoryProperty ) property);
- }
- index = property.getPreviousChildIndex();
- if (isValidIndex(index)) {
- children.push(_properties.get(index));
- }
- index = property.getNextChildIndex();
- if (isValidIndex(index)) {
- children.push(_properties.get(index));
- }
- }
- }
-
- private boolean isValidIndex(int index) {
- if (! Property.isValidIndex(index))
- return false;
- if (index < 0 || index >= _properties.size()) {
- LOG.atWarn().log("Property index {} outside the valid range 0..{}", box(index),box(_properties.size()));
- return false;
- }
- return true;
- }
- }
|