001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hpsf;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.ByteArrayOutputStream;
022: import java.io.FileNotFoundException;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.OutputStream;
026: import java.io.UnsupportedEncodingException;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.ListIterator;
030:
031: import org.apache.poi.poifs.filesystem.DirectoryEntry;
032: import org.apache.poi.poifs.filesystem.Entry;
033: import org.apache.poi.util.LittleEndian;
034: import org.apache.poi.util.LittleEndianConsts;
035:
036: /**
037: * <p>Adds writing support to the {@link PropertySet} class.</p>
038: *
039: * <p>Please be aware that this class' functionality will be merged into the
040: * {@link PropertySet} class at a later time, so the API will change.</p>
041: *
042: * @author Rainer Klute <a
043: * href="mailto:klute@rainer-klute.de"><klute@rainer-klute.de></a>
044: * @version $Id: MutablePropertySet.java 489730 2006-12-22 19:18:16Z bayard $
045: * @since 2003-02-19
046: */
047: public class MutablePropertySet extends PropertySet {
048:
049: /**
050: * <p>Constructs a <code>MutablePropertySet</code> instance. Its
051: * primary task is to initialize the immutable field with their proper
052: * values. It also sets fields that might change to reasonable defaults.</p>
053: */
054: public MutablePropertySet() {
055: /* Initialize the "byteOrder" field. */
056: byteOrder = LittleEndian.getUShort(BYTE_ORDER_ASSERTION);
057:
058: /* Initialize the "format" field. */
059: format = LittleEndian.getUShort(FORMAT_ASSERTION);
060:
061: /* Initialize "osVersion" field as if the property has been created on
062: * a Win32 platform, whether this is the case or not. */
063: osVersion = (OS_WIN32 << 16) | 0x0A04;
064:
065: /* Initailize the "classID" field. */
066: classID = new ClassID();
067:
068: /* Initialize the sections. Since property set must have at least
069: * one section it is added right here. */
070: sections = new LinkedList();
071: sections.add(new MutableSection());
072: }
073:
074: /**
075: * <p>Constructs a <code>MutablePropertySet</code> by doing a deep copy of
076: * an existing <code>PropertySet</code>. All nested elements, i.e.
077: * <code>Section</code>s and <code>Property</code> instances, will be their
078: * mutable counterparts in the new <code>MutablePropertySet</code>.</p>
079: *
080: * @param ps The property set to copy
081: */
082: public MutablePropertySet(final PropertySet ps) {
083: byteOrder = ps.getByteOrder();
084: format = ps.getFormat();
085: osVersion = ps.getOSVersion();
086: setClassID(ps.getClassID());
087: clearSections();
088: for (final Iterator i = ps.getSections().iterator(); i
089: .hasNext();) {
090: final MutableSection s = new MutableSection((Section) (i
091: .next()));
092: addSection(s);
093: }
094: }
095:
096: /**
097: * <p>The length of the property set stream header.</p>
098: */
099: private final int OFFSET_HEADER = BYTE_ORDER_ASSERTION.length + /* Byte order */
100: FORMAT_ASSERTION.length + /* Format */
101: LittleEndianConsts.INT_SIZE + /* OS version */
102: ClassID.LENGTH + /* Class ID */
103: LittleEndianConsts.INT_SIZE; /* Section count */
104:
105: /**
106: * <p>Sets the "byteOrder" property.</p>
107: *
108: * @param byteOrder the byteOrder value to set
109: */
110: public void setByteOrder(final int byteOrder) {
111: this .byteOrder = byteOrder;
112: }
113:
114: /**
115: * <p>Sets the "format" property.</p>
116: *
117: * @param format the format value to set
118: */
119: public void setFormat(final int format) {
120: this .format = format;
121: }
122:
123: /**
124: * <p>Sets the "osVersion" property.</p>
125: *
126: * @param osVersion the osVersion value to set
127: */
128: public void setOSVersion(final int osVersion) {
129: this .osVersion = osVersion;
130: }
131:
132: /**
133: * <p>Sets the property set stream's low-level "class ID"
134: * field.</p>
135: *
136: * @param classID The property set stream's low-level "class ID" field.
137: *
138: * @see PropertySet#getClassID()
139: */
140: public void setClassID(final ClassID classID) {
141: this .classID = classID;
142: }
143:
144: /**
145: * <p>Removes all sections from this property set.</p>
146: */
147: public void clearSections() {
148: sections = null;
149: }
150:
151: /**
152: * <p>Adds a section to this property set.</p>
153: *
154: * @param section The {@link Section} to add. It will be appended
155: * after any sections that are already present in the property set
156: * and thus become the last section.
157: */
158: public void addSection(final Section section) {
159: if (sections == null)
160: sections = new LinkedList();
161: sections.add(section);
162: }
163:
164: /**
165: * <p>Writes the property set to an output stream.</p>
166: *
167: * @param out the output stream to write the section to
168: * @exception IOException if an error when writing to the output stream
169: * occurs
170: * @exception WritingNotSupportedException if HPSF does not yet support
171: * writing a property's variant type.
172: */
173: public void write(final OutputStream out)
174: throws WritingNotSupportedException, IOException {
175: /* Write the number of sections in this property set stream. */
176: final int nrSections = sections.size();
177: int length = 0;
178:
179: /* Write the property set's header. */
180: length += TypeWriter.writeToStream(out, (short) getByteOrder());
181: length += TypeWriter.writeToStream(out, (short) getFormat());
182: length += TypeWriter.writeToStream(out, getOSVersion());
183: length += TypeWriter.writeToStream(out, getClassID());
184: length += TypeWriter.writeToStream(out, nrSections);
185: int offset = OFFSET_HEADER;
186:
187: /* Write the section list, i.e. the references to the sections. Each
188: * entry in the section list consist of the section's class ID and the
189: * section's offset relative to the beginning of the stream. */
190: offset += nrSections * (ClassID.LENGTH + LittleEndian.INT_SIZE);
191: final int sectionsBegin = offset;
192: for (final ListIterator i = sections.listIterator(); i
193: .hasNext();) {
194: final MutableSection s = (MutableSection) i.next();
195: final ClassID formatID = s.getFormatID();
196: if (formatID == null)
197: throw new NoFormatIDException();
198: length += TypeWriter.writeToStream(out, s.getFormatID());
199: length += TypeWriter.writeUIntToStream(out, offset);
200: try {
201: offset += s.getSize();
202: } catch (HPSFRuntimeException ex) {
203: final Throwable cause = ex.getReason();
204: if (cause instanceof UnsupportedEncodingException)
205: throw new IllegalPropertySetDataException(cause);
206: else
207: throw ex;
208: }
209: }
210:
211: /* Write the sections themselves. */
212: offset = sectionsBegin;
213: for (final ListIterator i = sections.listIterator(); i
214: .hasNext();) {
215: final MutableSection s = (MutableSection) i.next();
216: offset += s.write(out);
217: }
218: }
219:
220: /**
221: * <p>Returns the contents of this property set stream as an input stream.
222: * The latter can be used for example to write the property set into a POIFS
223: * document. The input stream represents a snapshot of the property set.
224: * If the latter is modified while the input stream is still being
225: * read, the modifications will not be reflected in the input stream but in
226: * the {@link MutablePropertySet} only.</p>
227: *
228: * @return the contents of this property set stream
229: *
230: * @throws WritingNotSupportedException if HPSF does not yet support writing
231: * of a property's variant type.
232: * @throws IOException if an I/O exception occurs.
233: */
234: public InputStream toInputStream() throws IOException,
235: WritingNotSupportedException {
236: final ByteArrayOutputStream psStream = new ByteArrayOutputStream();
237: write(psStream);
238: psStream.close();
239: final byte[] streamData = psStream.toByteArray();
240: return new ByteArrayInputStream(streamData);
241: }
242:
243: /**
244: * <p>Writes a property set to a document in a POI filesystem directory.</p>
245: *
246: * @param dir The directory in the POI filesystem to write the document to.
247: * @param name The document's name. If there is already a document with the
248: * same name in the directory the latter will be overwritten.
249: *
250: * @throws WritingNotSupportedException
251: * @throws IOException
252: */
253: public void write(final DirectoryEntry dir, final String name)
254: throws WritingNotSupportedException, IOException {
255: /* If there is already an entry with the same name, remove it. */
256: try {
257: final Entry e = dir.getEntry(name);
258: e.delete();
259: } catch (FileNotFoundException ex) {
260: /* Entry not found, no need to remove it. */
261: }
262: /* Create the new entry. */
263: dir.createDocument(name, toInputStream());
264: }
265:
266: }
|