| /* ==================================================================== |
| 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.OutputStream; |
| |
| import org.apache.poi.poifs.common.POIFSConstants; |
| import org.apache.poi.poifs.property.DocumentProperty; |
| |
| /** |
| * This class provides methods to write a DocumentEntry managed by a |
| * {@link POIFSFileSystem} instance. |
| */ |
| public final class DocumentOutputStream extends OutputStream { |
| /** the Document's size, i.e. the size of the big block data - mini block data is cached and not counted */ |
| private int _document_size = 0; |
| |
| /** have we been closed? */ |
| private boolean _closed = false; |
| |
| /** the actual Document */ |
| private POIFSDocument _document; |
| /** and its Property */ |
| private DocumentProperty _property; |
| |
| /** our buffer, when null we're into normal blocks */ |
| private ByteArrayOutputStream _buffer = |
| new ByteArrayOutputStream(POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE); |
| |
| /** our main block stream, when we're into normal blocks */ |
| private POIFSStream _stream; |
| private OutputStream _stream_output; |
| |
| /** a write limit or -1 if unlimited */ |
| private final long _limit; |
| |
| |
| /** |
| * Create an OutputStream from the specified DocumentEntry. |
| * The specified entry will be emptied. |
| * |
| * @param document the DocumentEntry to be written |
| */ |
| public DocumentOutputStream(DocumentEntry document) throws IOException { |
| this(document, -1); |
| } |
| |
| /** |
| * Create an OutputStream to create the specified new Entry |
| * |
| * @param parent Where to create the Entry |
| * @param name Name of the new entry |
| */ |
| public DocumentOutputStream(DirectoryEntry parent, String name) throws IOException { |
| this(createDocument(parent, name), -1); |
| } |
| |
| /** |
| * Create a DocumentOutputStream |
| * |
| * @param document the DocumentEntry to which the data is actually written |
| * @param limit the maximum number of bytes that can be written |
| */ |
| DocumentOutputStream(DocumentEntry document, long limit) throws IOException { |
| this(getDocument(document), limit); |
| } |
| |
| DocumentOutputStream(POIFSDocument document, long limit) throws IOException { |
| _document = document; |
| _document.free(); |
| |
| _property = document.getDocumentProperty(); |
| |
| _limit = limit; |
| } |
| |
| private static POIFSDocument getDocument(DocumentEntry document) throws IOException { |
| if (!(document instanceof DocumentNode)) { |
| throw new IOException("Cannot open internal document storage, " + document + " not a Document Node"); |
| } |
| return new POIFSDocument((DocumentNode)document); |
| } |
| |
| private static DocumentEntry createDocument(DirectoryEntry parent, String name) throws IOException { |
| if (!(parent instanceof DirectoryNode)) { |
| throw new IOException("Cannot open internal directory storage, " + parent + " not a Directory Node"); |
| } |
| |
| // Have an empty one created for now |
| return parent.createDocument(name, new ByteArrayInputStream(new byte[0])); |
| } |
| |
| private void checkBufferSize() throws IOException { |
| // Have we gone over the mini stream limit yet? |
| if (_buffer.size() > POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE) { |
| // Will need to be in the main stream |
| byte[] data = _buffer.toByteArray(); |
| _buffer = null; |
| write(data, 0, data.length); |
| } else { |
| // So far, mini stream will work, keep going |
| } |
| } |
| |
| public void write(int b) throws IOException { |
| write(new byte[] { (byte)b }, 0, 1); |
| } |
| |
| @Override |
| public void write(byte[] b, int off, int len) throws IOException { |
| if (_closed) { |
| throw new IOException("cannot perform requested operation on a closed stream"); |
| } |
| if (_limit > -1 && (size() + len) > _limit) { |
| throw new IOException("tried to write too much data"); |
| } |
| |
| if (_buffer != null) { |
| _buffer.write(b, off, len); |
| checkBufferSize(); |
| } else { |
| if (_stream == null) { |
| _stream = new POIFSStream(_document.getFileSystem()); |
| _stream_output = _stream.getOutputStream(); |
| } |
| _stream_output.write(b, off, len); |
| _document_size += len; |
| } |
| } |
| |
| public void close() throws IOException { |
| // Do we have a pending buffer for the mini stream? |
| if (_buffer != null) { |
| // It's not much data, so ask POIFSDocument to do it for us |
| _document.replaceContents(new ByteArrayInputStream(_buffer.toByteArray())); |
| } |
| else { |
| // We've been writing to the stream as we've gone along |
| // Update the details on the property now |
| _stream_output.close(); |
| _property.updateSize(_document_size); |
| _property.setStartBlock(_stream.getStartBlock()); |
| } |
| |
| // No more! |
| _closed = true; |
| } |
| |
| /** |
| * @return the amount of written bytes |
| */ |
| public long size() { |
| return _document_size + (_buffer == null ? 0L : _buffer.size()); |
| } |
| } |