You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MutablePropertySet.java 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hpsf;
  16. import java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.FileNotFoundException;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.io.UnsupportedEncodingException;
  23. import java.util.Iterator;
  24. import java.util.LinkedList;
  25. import java.util.ListIterator;
  26. import org.apache.poi.poifs.filesystem.DirectoryEntry;
  27. import org.apache.poi.poifs.filesystem.Entry;
  28. import org.apache.poi.util.LittleEndian;
  29. import org.apache.poi.util.LittleEndianConsts;
  30. /**
  31. * <p>Adds writing support to the {@link PropertySet} class.</p>
  32. *
  33. * <p>Please be aware that this class' functionality will be merged into the
  34. * {@link PropertySet} class at a later time, so the API will change.</p>
  35. *
  36. * @author Rainer Klute <a
  37. * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
  38. */
  39. public class MutablePropertySet extends PropertySet
  40. {
  41. /**
  42. * <p>Constructs a <code>MutablePropertySet</code> instance. Its
  43. * primary task is to initialize the immutable field with their proper
  44. * values. It also sets fields that might change to reasonable defaults.</p>
  45. */
  46. public MutablePropertySet()
  47. {
  48. /* Initialize the "byteOrder" field. */
  49. byteOrder = LittleEndian.getUShort(BYTE_ORDER_ASSERTION);
  50. /* Initialize the "format" field. */
  51. format = LittleEndian.getUShort(FORMAT_ASSERTION);
  52. /* Initialize "osVersion" field as if the property has been created on
  53. * a Win32 platform, whether this is the case or not. */
  54. osVersion = (OS_WIN32 << 16) | 0x0A04;
  55. /* Initailize the "classID" field. */
  56. classID = new ClassID();
  57. /* Initialize the sections. Since property set must have at least
  58. * one section it is added right here. */
  59. sections = new LinkedList();
  60. sections.add(new MutableSection());
  61. }
  62. /**
  63. * <p>Constructs a <code>MutablePropertySet</code> by doing a deep copy of
  64. * an existing <code>PropertySet</code>. All nested elements, i.e.
  65. * <code>Section</code>s and <code>Property</code> instances, will be their
  66. * mutable counterparts in the new <code>MutablePropertySet</code>.</p>
  67. *
  68. * @param ps The property set to copy
  69. */
  70. public MutablePropertySet(final PropertySet ps)
  71. {
  72. byteOrder = ps.getByteOrder();
  73. format = ps.getFormat();
  74. osVersion = ps.getOSVersion();
  75. setClassID(ps.getClassID());
  76. clearSections();
  77. if (sections == null)
  78. sections = new LinkedList();
  79. for (final Iterator i = ps.getSections().iterator(); i.hasNext();)
  80. {
  81. final MutableSection s = new MutableSection((Section) (i.next()));
  82. addSection(s);
  83. }
  84. }
  85. /**
  86. * <p>The length of the property set stream header.</p>
  87. */
  88. private final int OFFSET_HEADER =
  89. BYTE_ORDER_ASSERTION.length + /* Byte order */
  90. FORMAT_ASSERTION.length + /* Format */
  91. LittleEndianConsts.INT_SIZE + /* OS version */
  92. ClassID.LENGTH + /* Class ID */
  93. LittleEndianConsts.INT_SIZE; /* Section count */
  94. /**
  95. * <p>Sets the "byteOrder" property.</p>
  96. *
  97. * @param byteOrder the byteOrder value to set
  98. */
  99. public void setByteOrder(final int byteOrder)
  100. {
  101. this.byteOrder = byteOrder;
  102. }
  103. /**
  104. * <p>Sets the "format" property.</p>
  105. *
  106. * @param format the format value to set
  107. */
  108. public void setFormat(final int format)
  109. {
  110. this.format = format;
  111. }
  112. /**
  113. * <p>Sets the "osVersion" property.</p>
  114. *
  115. * @param osVersion the osVersion value to set
  116. */
  117. public void setOSVersion(final int osVersion)
  118. {
  119. this.osVersion = osVersion;
  120. }
  121. /**
  122. * <p>Sets the property set stream's low-level "class ID"
  123. * field.</p>
  124. *
  125. * @param classID The property set stream's low-level "class ID" field.
  126. *
  127. * @see PropertySet#getClassID()
  128. */
  129. public void setClassID(final ClassID classID)
  130. {
  131. this.classID = classID;
  132. }
  133. /**
  134. * <p>Removes all sections from this property set.</p>
  135. */
  136. public void clearSections()
  137. {
  138. sections = null;
  139. }
  140. /**
  141. * <p>Adds a section to this property set.</p>
  142. *
  143. * @param section The {@link Section} to add. It will be appended
  144. * after any sections that are already present in the property set
  145. * and thus become the last section.
  146. */
  147. public void addSection(final Section section)
  148. {
  149. if (sections == null)
  150. sections = new LinkedList();
  151. sections.add(section);
  152. }
  153. /**
  154. * <p>Writes the property set to an output stream.</p>
  155. *
  156. * @param out the output stream to write the section to
  157. * @exception IOException if an error when writing to the output stream
  158. * occurs
  159. * @exception WritingNotSupportedException if HPSF does not yet support
  160. * writing a property's variant type.
  161. */
  162. public void write(final OutputStream out)
  163. throws WritingNotSupportedException, IOException
  164. {
  165. /* Write the number of sections in this property set stream. */
  166. final int nrSections = sections.size();
  167. int length = 0;
  168. /* Write the property set's header. */
  169. length += TypeWriter.writeToStream(out, (short) getByteOrder());
  170. length += TypeWriter.writeToStream(out, (short) getFormat());
  171. length += TypeWriter.writeToStream(out, getOSVersion());
  172. length += TypeWriter.writeToStream(out, getClassID());
  173. length += TypeWriter.writeToStream(out, nrSections);
  174. int offset = OFFSET_HEADER;
  175. /* Write the section list, i.e. the references to the sections. Each
  176. * entry in the section list consist of the section's class ID and the
  177. * section's offset relative to the beginning of the stream. */
  178. offset += nrSections * (ClassID.LENGTH + LittleEndian.INT_SIZE);
  179. final int sectionsBegin = offset;
  180. for (final ListIterator i = sections.listIterator(); i.hasNext();)
  181. {
  182. final MutableSection s = (MutableSection) i.next();
  183. final ClassID formatID = s.getFormatID();
  184. if (formatID == null)
  185. throw new NoFormatIDException();
  186. length += TypeWriter.writeToStream(out, s.getFormatID());
  187. length += TypeWriter.writeUIntToStream(out, offset);
  188. try
  189. {
  190. offset += s.getSize();
  191. }
  192. catch (HPSFRuntimeException ex)
  193. {
  194. final Throwable cause = ex.getReason();
  195. if (cause instanceof UnsupportedEncodingException) {
  196. throw new IllegalPropertySetDataException(cause);
  197. }
  198. throw ex;
  199. }
  200. }
  201. /* Write the sections themselves. */
  202. offset = sectionsBegin;
  203. for (final ListIterator i = sections.listIterator(); i.hasNext();)
  204. {
  205. final MutableSection s = (MutableSection) i.next();
  206. offset += s.write(out);
  207. }
  208. }
  209. /**
  210. * <p>Returns the contents of this property set stream as an input stream.
  211. * The latter can be used for example to write the property set into a POIFS
  212. * document. The input stream represents a snapshot of the property set.
  213. * If the latter is modified while the input stream is still being
  214. * read, the modifications will not be reflected in the input stream but in
  215. * the {@link MutablePropertySet} only.</p>
  216. *
  217. * @return the contents of this property set stream
  218. *
  219. * @throws WritingNotSupportedException if HPSF does not yet support writing
  220. * of a property's variant type.
  221. * @throws IOException if an I/O exception occurs.
  222. */
  223. public InputStream toInputStream()
  224. throws IOException, WritingNotSupportedException
  225. {
  226. final ByteArrayOutputStream psStream = new ByteArrayOutputStream();
  227. write(psStream);
  228. psStream.close();
  229. final byte[] streamData = psStream.toByteArray();
  230. return new ByteArrayInputStream(streamData);
  231. }
  232. /**
  233. * <p>Writes a property set to a document in a POI filesystem directory.</p>
  234. *
  235. * @param dir The directory in the POI filesystem to write the document to.
  236. * @param name The document's name. If there is already a document with the
  237. * same name in the directory the latter will be overwritten.
  238. *
  239. * @throws WritingNotSupportedException
  240. * @throws IOException
  241. */
  242. public void write(final DirectoryEntry dir, final String name)
  243. throws WritingNotSupportedException, IOException
  244. {
  245. /* If there is already an entry with the same name, remove it. */
  246. try
  247. {
  248. final Entry e = dir.getEntry(name);
  249. e.delete();
  250. }
  251. catch (FileNotFoundException ex)
  252. {
  253. /* Entry not found, no need to remove it. */
  254. }
  255. /* Create the new entry. */
  256. dir.createDocument(name, toInputStream());
  257. }
  258. }