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.

TestWrite.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  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.basic;
  16. import static org.hamcrest.MatcherAssert.assertThat;
  17. import static org.hamcrest.core.IsEqual.equalTo;
  18. import static org.junit.jupiter.api.Assertions.assertArrayEquals;
  19. import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
  20. import static org.junit.jupiter.api.Assertions.assertEquals;
  21. import static org.junit.jupiter.api.Assertions.assertNotNull;
  22. import static org.junit.jupiter.api.Assertions.assertNull;
  23. import static org.junit.jupiter.api.Assertions.assertThrows;
  24. import static org.junit.jupiter.api.Assertions.assertTrue;
  25. import java.io.ByteArrayInputStream;
  26. import java.io.File;
  27. import java.io.FileOutputStream;
  28. import java.io.IOException;
  29. import java.io.InputStream;
  30. import java.io.OutputStream;
  31. import java.io.UnsupportedEncodingException;
  32. import java.nio.charset.Charset;
  33. import java.util.ArrayList;
  34. import java.util.Date;
  35. import java.util.HashMap;
  36. import java.util.List;
  37. import java.util.Locale;
  38. import java.util.Map;
  39. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  40. import org.apache.poi.POIDataSamples;
  41. import org.apache.poi.hpsf.ClassID;
  42. import org.apache.poi.hpsf.ClassIDPredefined;
  43. import org.apache.poi.hpsf.DocumentSummaryInformation;
  44. import org.apache.poi.hpsf.HPSFException;
  45. import org.apache.poi.hpsf.NoFormatIDException;
  46. import org.apache.poi.hpsf.NoPropertySetStreamException;
  47. import org.apache.poi.hpsf.Property;
  48. import org.apache.poi.hpsf.PropertySet;
  49. import org.apache.poi.hpsf.PropertySetFactory;
  50. import org.apache.poi.hpsf.Section;
  51. import org.apache.poi.hpsf.SummaryInformation;
  52. import org.apache.poi.hpsf.UnsupportedVariantTypeException;
  53. import org.apache.poi.hpsf.Variant;
  54. import org.apache.poi.hpsf.VariantSupport;
  55. import org.apache.poi.hpsf.WritingNotSupportedException;
  56. import org.apache.poi.hpsf.wellknown.PropertyIDMap;
  57. import org.apache.poi.poifs.eventfilesystem.POIFSReader;
  58. import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener;
  59. import org.apache.poi.poifs.filesystem.DirectoryEntry;
  60. import org.apache.poi.poifs.filesystem.DocumentInputStream;
  61. import org.apache.poi.poifs.filesystem.DocumentNode;
  62. import org.apache.poi.poifs.filesystem.DocumentOutputStream;
  63. import org.apache.poi.poifs.filesystem.POIFSDocument;
  64. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  65. import org.apache.poi.util.CodePageUtil;
  66. import org.apache.poi.util.IOUtils;
  67. import org.apache.poi.util.LittleEndianConsts;
  68. import org.apache.poi.util.TempFile;
  69. import org.junit.jupiter.api.Assumptions;
  70. import org.junit.jupiter.api.Test;
  71. /**
  72. * Tests HPSF's writing functionality
  73. */
  74. class TestWrite {
  75. private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance();
  76. private static final int CODEPAGE_DEFAULT = -1;
  77. private static final String POI_FS = "TestHPSFWritingFunctionality.doc";
  78. private static final String IMPROPER_DEFAULT_CHARSET_MESSAGE =
  79. "Your default character set is " + getDefaultCharsetName() +
  80. ". However, this testcase must be run in an environment " +
  81. "with a default character set supporting at least " +
  82. "8-bit-characters. You can achieve this by setting the " +
  83. "LANG environment variable to a proper value, e.g. " +
  84. "\"de_DE\".";
  85. /**
  86. * Writes an empty property set to a POIFS and reads it back in.
  87. *
  88. * @throws IOException if an I/O exception occurs
  89. */
  90. @Test
  91. void withoutAFormatID() throws Exception {
  92. final File filename = TempFile.createTempFile(POI_FS, ".doc");
  93. /* Create a mutable property set with a section that does not have the
  94. * formatID set: */
  95. final PropertySet ps = new PropertySet();
  96. ps.clearSections();
  97. ps.addSection(new Section());
  98. /* Write it to a POIFS and the latter to disk: */
  99. try (OutputStream out = new FileOutputStream(filename);
  100. POIFSFileSystem poiFs = new POIFSFileSystem();
  101. UnsynchronizedByteArrayOutputStream psStream = new UnsynchronizedByteArrayOutputStream()) {
  102. assertThrows(NoFormatIDException.class, () -> ps.write(psStream));
  103. poiFs.createDocument(psStream.toInputStream(), SummaryInformation.DEFAULT_STREAM_NAME);
  104. poiFs.writeFilesystem(out);
  105. }
  106. }
  107. /**
  108. * Writes an empty property set to a POIFS and reads it back in.
  109. *
  110. * @throws IOException if an I/O exception occurs
  111. * @throws UnsupportedVariantTypeException if HPSF does not yet support
  112. * a variant type to be written
  113. */
  114. @Test
  115. void writeEmptyPropertySet()
  116. throws IOException, UnsupportedVariantTypeException {
  117. final File filename = TempFile.createTempFile(POI_FS, ".doc");
  118. /* Create a mutable property set and write it to a POIFS: */
  119. try (OutputStream out = new FileOutputStream(filename);
  120. POIFSFileSystem poiFs = new POIFSFileSystem();
  121. UnsynchronizedByteArrayOutputStream psStream = new UnsynchronizedByteArrayOutputStream()) {
  122. final PropertySet ps = new PropertySet();
  123. final Section s = ps.getSections().get(0);
  124. s.setFormatID(SummaryInformation.FORMAT_ID);
  125. ps.write(psStream);
  126. poiFs.createDocument(psStream.toInputStream(), SummaryInformation.DEFAULT_STREAM_NAME);
  127. poiFs.writeFilesystem(out);
  128. }
  129. /* Read the POIFS: */
  130. final POIFSReader r = new POIFSReader();
  131. final List<PropertySet> psa = new ArrayList<>();
  132. r.registerListener(getListener(psa), SummaryInformation.DEFAULT_STREAM_NAME);
  133. r.read(filename);
  134. assertEquals(1, psa.size());
  135. }
  136. /**
  137. * <p>Writes a simple property set with a SummaryInformation section to a
  138. * POIFS and reads it back in.</p>
  139. *
  140. * @throws IOException if an I/O exception occurs
  141. * @throws UnsupportedVariantTypeException if HPSF does not yet support
  142. * a variant type to be written
  143. */
  144. @Test
  145. void writeSimplePropertySet()
  146. throws IOException, UnsupportedVariantTypeException {
  147. final String AUTHOR = "Rainer Klute";
  148. final String TITLE = "Test Document";
  149. final File filename = TempFile.createTempFile(POI_FS, ".doc");
  150. try (OutputStream out = new FileOutputStream(filename);
  151. POIFSFileSystem poiFs = new POIFSFileSystem()) {
  152. final PropertySet ps = new PropertySet();
  153. final Section si = new Section();
  154. si.setFormatID(SummaryInformation.FORMAT_ID);
  155. ps.clearSections();
  156. ps.addSection(si);
  157. final Property p = new Property();
  158. p.setID(PropertyIDMap.PID_AUTHOR);
  159. p.setType(Variant.VT_LPWSTR);
  160. p.setValue(AUTHOR);
  161. si.setProperty(p);
  162. si.setProperty(PropertyIDMap.PID_TITLE, Variant.VT_LPSTR, TITLE);
  163. poiFs.createDocument(ps.toInputStream(), SummaryInformation.DEFAULT_STREAM_NAME);
  164. poiFs.writeFilesystem(out);
  165. }
  166. /* Read the POIFS: */
  167. final List<PropertySet> psa = new ArrayList<>();
  168. final POIFSReader r = new POIFSReader();
  169. r.registerListener(getListener(psa), SummaryInformation.DEFAULT_STREAM_NAME);
  170. r.read(filename);
  171. assertEquals(1, psa.size());
  172. assertTrue(psa.get(0).isSummaryInformation());
  173. final Section s = psa.get(0).getSections().get(0);
  174. Object p1 = s.getProperty(PropertyIDMap.PID_AUTHOR);
  175. Object p2 = s.getProperty(PropertyIDMap.PID_TITLE);
  176. assertEquals(AUTHOR, p1);
  177. assertEquals(TITLE, p2);
  178. }
  179. /**
  180. * Writes a simple property set with two sections to a POIFS and reads it
  181. * back in.
  182. *
  183. * @throws IOException if an I/O exception occurs
  184. * @throws WritingNotSupportedException if HPSF does not yet support
  185. * a variant type to be written
  186. */
  187. @Test
  188. void writeTwoSections() throws WritingNotSupportedException, IOException {
  189. final String STREAM_NAME = "PropertySetStream";
  190. final String SECTION1 = "Section 1";
  191. final String SECTION2 = "Section 2";
  192. final ClassID FORMATID = ClassIDPredefined.EXCEL_V12.getClassID();
  193. final File filename = TempFile.createTempFile(POI_FS, ".doc");
  194. try (OutputStream out = new FileOutputStream(filename);
  195. POIFSFileSystem poiFs = new POIFSFileSystem()) {
  196. final PropertySet ps = new PropertySet();
  197. ps.clearSections();
  198. final Section s1 = new Section();
  199. s1.setFormatID(FORMATID);
  200. s1.setProperty(2, SECTION1);
  201. ps.addSection(s1);
  202. final Section s2 = new Section();
  203. s2.setFormatID(FORMATID);
  204. s2.setProperty(2, SECTION2);
  205. ps.addSection(s2);
  206. poiFs.createDocument(ps.toInputStream(), STREAM_NAME);
  207. poiFs.writeFilesystem(out);
  208. }
  209. /* Read the POIFS: */
  210. final PropertySet[] psa = new PropertySet[1];
  211. final POIFSReader r = new POIFSReader();
  212. final POIFSReaderListener listener = (event) ->
  213. assertDoesNotThrow(() -> psa[0] = PropertySetFactory.create(event.getStream()));
  214. r.registerListener(listener,STREAM_NAME);
  215. r.read(filename);
  216. assertNotNull(psa[0]);
  217. Section s = (psa[0].getSections().get(0));
  218. assertEquals(s.getFormatID(), FORMATID);
  219. Object p = s.getProperty(2);
  220. assertEquals(SECTION1, p);
  221. s = (psa[0].getSections().get(1));
  222. p = s.getProperty(2);
  223. assertEquals(SECTION2, p);
  224. }
  225. private static POIFSReaderListener getListener(List<PropertySet> psa) {
  226. return event -> assertDoesNotThrow(() -> psa.add(PropertySetFactory.create(event.getStream())));
  227. }
  228. /**
  229. * Writes and reads back various variant types and checks whether the
  230. * stuff that has been read back equals the stuff that was written.
  231. */
  232. @Test
  233. void variantTypes() throws Exception {
  234. final int codepage = CODEPAGE_DEFAULT;
  235. Assumptions.assumeTrue(hasProperDefaultCharset(), IMPROPER_DEFAULT_CHARSET_MESSAGE);
  236. check(Variant.VT_EMPTY, null, codepage);
  237. check(Variant.VT_BOOL, Boolean.TRUE, codepage);
  238. check(Variant.VT_BOOL, Boolean.FALSE, codepage);
  239. check( Variant.VT_CF, new byte[] { 8, 0, 0, 0, 1, 0, 0, 0, 1, 2, 3, 4 }, codepage );
  240. check(Variant.VT_I4, 27, codepage);
  241. check(Variant.VT_I8, 28L, codepage);
  242. check(Variant.VT_R8, 29.0d, codepage);
  243. check(Variant.VT_I4, -27, codepage);
  244. check(Variant.VT_I8, -28L, codepage);
  245. check(Variant.VT_R8, -29.0d, codepage);
  246. check(Variant.VT_FILETIME, new Date(), codepage);
  247. check(Variant.VT_I4, Integer.MAX_VALUE, codepage);
  248. check(Variant.VT_I4, Integer.MIN_VALUE, codepage);
  249. check(Variant.VT_I8, Long.MAX_VALUE, codepage);
  250. check(Variant.VT_I8, Long.MIN_VALUE, codepage);
  251. check(Variant.VT_R8, Double.MAX_VALUE, codepage);
  252. check(Variant.VT_R8, Double.MIN_VALUE, codepage);
  253. checkString(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage);
  254. checkString(Variant.VT_LPWSTR, "\u00e4\u00f6\u00fc\u00df\u00c4\u00d6\u00dc", codepage);
  255. }
  256. /**
  257. * Writes and reads back strings using several different codepages and
  258. * checks whether the stuff that has been read back equals the stuff that
  259. * was written.
  260. */
  261. @Test
  262. void codepages() throws UnsupportedVariantTypeException, IOException
  263. {
  264. final int[] validCodepages = {CODEPAGE_DEFAULT, CodePageUtil.CP_UTF8, CodePageUtil.CP_UNICODE, CodePageUtil.CP_WINDOWS_1252};
  265. for (final int cp : validCodepages) {
  266. if (cp == -1 && !hasProperDefaultCharset())
  267. {
  268. System.err.println(IMPROPER_DEFAULT_CHARSET_MESSAGE +
  269. " This testcase is skipped for the default codepage.");
  270. continue;
  271. }
  272. final long t = (cp == CodePageUtil.CP_UNICODE) ? Variant.VT_LPWSTR : Variant.VT_LPSTR;
  273. checkString(t, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp);
  274. if (cp == CodePageUtil.CP_UTF16 || cp == CodePageUtil.CP_UTF8) {
  275. check(t, "\u79D1\u5B78", cp);
  276. }
  277. }
  278. final int[] invalidCodepages = new int[] {0, 1, 2, 4711, 815};
  279. for (int cp : invalidCodepages) {
  280. assertThrows(UnsupportedEncodingException.class,
  281. () -> checkString(Variant.VT_LPSTR, "\u00e4\u00f6\u00fc\u00c4\u00d6\u00dc\u00df", cp),
  282. "UnsupportedEncodingException for codepage " + cp + " expected.");
  283. }
  284. }
  285. /**
  286. * Tests whether writing 8-bit characters to a Unicode property succeeds.
  287. */
  288. @Test
  289. void unicodeWrite8Bit() throws WritingNotSupportedException, IOException, NoPropertySetStreamException {
  290. final String TITLE = "This is a sample title";
  291. final PropertySet mps = new PropertySet();
  292. final Section ms = mps.getSections().get(0);
  293. ms.setFormatID(SummaryInformation.FORMAT_ID);
  294. final Property p = new Property();
  295. p.setID(PropertyIDMap.PID_TITLE);
  296. p.setType(Variant.VT_LPSTR);
  297. p.setValue(TITLE);
  298. ms.setProperty(p);
  299. UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream();
  300. mps.write(out);
  301. byte[] bytes = out.toByteArray();
  302. PropertySet psr = new PropertySet(bytes);
  303. assertTrue(psr.isSummaryInformation());
  304. Section sr = psr.getSections().get(0);
  305. String title = (String) sr.getProperty(PropertyIDMap.PID_TITLE);
  306. assertEquals(TITLE, title);
  307. }
  308. private void checkString(final long variantType, final String value, final int codepage)
  309. throws UnsupportedVariantTypeException, IOException {
  310. for (int i=0; i<value.length(); i++) {
  311. check(variantType, value.substring(0, i), codepage);
  312. }
  313. }
  314. /**
  315. * Writes a property and reads it back in.
  316. *
  317. * @param variantType The property's variant type.
  318. * @param value The property's value.
  319. * @param codepage The codepage to use for writing and reading.
  320. * @throws UnsupportedVariantTypeException if the variant is not supported.
  321. * @throws IOException if an I/O exception occurs.
  322. */
  323. private void check(final long variantType, final Object value, final int codepage)
  324. throws UnsupportedVariantTypeException, IOException
  325. {
  326. final UnsynchronizedByteArrayOutputStream out = new UnsynchronizedByteArrayOutputStream();
  327. VariantSupport.write(out, variantType, value, codepage);
  328. final byte[] b = out.toByteArray();
  329. final Object objRead =
  330. VariantSupport.read(b, 0, b.length + LittleEndianConsts.INT_SIZE, variantType, codepage);
  331. if (objRead instanceof byte[]) {
  332. assertArrayEquals((byte[])value, (byte[])objRead);
  333. } else if (value != null && !value.equals(objRead)) {
  334. assertEquals(value, objRead);
  335. }
  336. }
  337. /**
  338. * Tests writing and reading back a proper dictionary.
  339. */
  340. @Test
  341. void dictionary() throws IOException, HPSFException {
  342. final File copy = TempFile.createTempFile("Test-HPSF", "ole2");
  343. copy.deleteOnExit();
  344. /* Write: */
  345. try (OutputStream out = new FileOutputStream(copy);
  346. POIFSFileSystem poiFs = new POIFSFileSystem()) {
  347. final PropertySet ps1 = new PropertySet();
  348. final Section s = ps1.getSections().get(0);
  349. final Map<Long, String> m = new HashMap<>(3, 1.0f);
  350. m.put(1L, "String 1");
  351. m.put(2L, "String 2");
  352. m.put(3L, "String 3");
  353. s.setDictionary(m);
  354. s.setFormatID(DocumentSummaryInformation.FORMAT_ID[0]);
  355. int codepage = CodePageUtil.CP_UNICODE;
  356. s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
  357. poiFs.createDocument(ps1.toInputStream(), "Test");
  358. poiFs.writeFilesystem(out);
  359. /* Read back: */
  360. final List<POIFile> psf = Util.readPropertySets(copy);
  361. assertEquals(1, psf.size());
  362. final byte[] bytes = psf.get(0).getBytes();
  363. final InputStream in = new ByteArrayInputStream(bytes);
  364. final PropertySet ps2 = PropertySetFactory.create(in);
  365. /* Check if the result is a DocumentSummaryInformation stream, as
  366. * specified. */
  367. assertTrue(ps2.isDocumentSummaryInformation());
  368. /* Compare the property set stream with the corresponding one
  369. * from the origin file and check whether they are equal. */
  370. assertEquals(ps1, ps2);
  371. }
  372. }
  373. /**
  374. * Tests that when using POIFS, we can do an in-place write
  375. * without needing to stream in + out the whole kitchen sink
  376. */
  377. @Test
  378. void inPlacePOIFSWrite() throws Exception {
  379. // We need to work on a File for in-place changes, so create a temp one
  380. final File copy = TempFile.createTempFile("Test-HPSF", "ole2");
  381. copy.deleteOnExit();
  382. // Copy a test file over to our temp location
  383. try (FileOutputStream out = new FileOutputStream(copy);
  384. InputStream inp = _samples.openResourceAsStream("TestShiftJIS.doc")) {
  385. IOUtils.copy(inp, out);
  386. }
  387. // Open the copy in read/write mode
  388. try (POIFSFileSystem fs = new POIFSFileSystem(copy, false)) {
  389. DirectoryEntry root = fs.getRoot();
  390. // Read the properties in there
  391. DocumentNode sinfDoc = (DocumentNode) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  392. DocumentNode dinfDoc = (DocumentNode) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  393. InputStream sinfStream = new DocumentInputStream(sinfDoc);
  394. SummaryInformation sinf = (SummaryInformation) PropertySetFactory.create(sinfStream);
  395. sinfStream.close();
  396. assertEquals(131077, sinf.getOSVersion());
  397. InputStream dinfStream = new DocumentInputStream(dinfDoc);
  398. DocumentSummaryInformation dinf = (DocumentSummaryInformation) PropertySetFactory.create(dinfStream);
  399. dinfStream.close();
  400. assertEquals(131077, dinf.getOSVersion());
  401. // Check they start as we expect
  402. assertEquals("Reiichiro Hori", sinf.getAuthor());
  403. assertEquals("Microsoft Word 9.0", sinf.getApplicationName());
  404. assertEquals("\u7b2c1\u7ae0", sinf.getTitle());
  405. assertEquals("", dinf.getCompany());
  406. assertNull(dinf.getManager());
  407. // Do an in-place replace via an InputStream
  408. assertNotNull(sinfDoc);
  409. assertNotNull(dinfDoc);
  410. new POIFSDocument(sinfDoc).replaceContents(sinf.toInputStream());
  411. new POIFSDocument(dinfDoc).replaceContents(dinf.toInputStream());
  412. // Check it didn't get changed
  413. sinfDoc = (DocumentNode) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  414. dinfDoc = (DocumentNode) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  415. InputStream sinfStream2 = new DocumentInputStream(sinfDoc);
  416. sinf = (SummaryInformation) PropertySetFactory.create(sinfStream2);
  417. sinfStream2.close();
  418. assertEquals(131077, sinf.getOSVersion());
  419. InputStream dinfStream2 = new DocumentInputStream(dinfDoc);
  420. dinf = (DocumentSummaryInformation) PropertySetFactory.create(dinfStream2);
  421. dinfStream2.close();
  422. assertEquals(131077, dinf.getOSVersion());
  423. }
  424. // Start again!
  425. try (FileOutputStream out = new FileOutputStream(copy);
  426. InputStream inp = _samples.openResourceAsStream("TestShiftJIS.doc")) {
  427. IOUtils.copy(inp, out);
  428. }
  429. try (POIFSFileSystem fs = new POIFSFileSystem(copy, false)) {
  430. DirectoryEntry root = fs.getRoot();
  431. // Read the properties in once more
  432. DocumentNode sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  433. DocumentNode dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  434. InputStream sinfStream3 = new DocumentInputStream(sinfDoc);
  435. SummaryInformation sinf = (SummaryInformation)PropertySetFactory.create(sinfStream3);
  436. sinfStream3.close();
  437. assertEquals(131077, sinf.getOSVersion());
  438. InputStream dinfStream3 = new DocumentInputStream(dinfDoc);
  439. DocumentSummaryInformation dinf = (DocumentSummaryInformation)PropertySetFactory.create(dinfStream3);
  440. dinfStream3.close();
  441. assertEquals(131077, dinf.getOSVersion());
  442. // Have them write themselves in-place with no changes, as an OutputStream
  443. OutputStream soufStream = new DocumentOutputStream(sinfDoc);
  444. sinf.write(soufStream);
  445. soufStream.close();
  446. OutputStream doufStream = new DocumentOutputStream(dinfDoc);
  447. dinf.write(doufStream);
  448. doufStream.close();
  449. // And also write to some bytes for checking
  450. UnsynchronizedByteArrayOutputStream sinfBytes = new UnsynchronizedByteArrayOutputStream();
  451. sinf.write(sinfBytes);
  452. UnsynchronizedByteArrayOutputStream dinfBytes = new UnsynchronizedByteArrayOutputStream();
  453. dinf.write(dinfBytes);
  454. // Check that the filesystem can give us back the same bytes
  455. sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  456. dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  457. InputStream sinfStream4 = new DocumentInputStream(sinfDoc);
  458. byte[] sinfData = IOUtils.toByteArray(sinfStream4);
  459. sinfStream4.close();
  460. InputStream dinfStream4 = new DocumentInputStream(dinfDoc);
  461. byte[] dinfData = IOUtils.toByteArray(dinfStream4);
  462. dinfStream4.close();
  463. assertThat(sinfBytes.toByteArray(), equalTo(sinfData));
  464. assertThat(dinfBytes.toByteArray(), equalTo(dinfData));
  465. // Read back in as-is
  466. InputStream sinfStream5 = new DocumentInputStream(sinfDoc);
  467. sinf = (SummaryInformation)PropertySetFactory.create(sinfStream5);
  468. sinfStream5.close();
  469. assertEquals(131077, sinf.getOSVersion());
  470. InputStream dinfStream5 = new DocumentInputStream(dinfDoc);
  471. dinf = (DocumentSummaryInformation)PropertySetFactory.create(dinfStream5);
  472. dinfStream5.close();
  473. assertEquals(131077, dinf.getOSVersion());
  474. assertEquals("Reiichiro Hori", sinf.getAuthor());
  475. assertEquals("Microsoft Word 9.0", sinf.getApplicationName());
  476. assertEquals("\u7b2c1\u7ae0", sinf.getTitle());
  477. assertEquals("", dinf.getCompany());
  478. assertNull(dinf.getManager());
  479. // Now alter a few of them
  480. sinf.setAuthor("Changed Author");
  481. sinf.setTitle("Le titre \u00e9tait chang\u00e9");
  482. dinf.setManager("Changed Manager");
  483. // Save this into the filesystem
  484. OutputStream soufStream2 = new DocumentOutputStream(sinfDoc);
  485. sinf.write(soufStream2);
  486. soufStream2.close();
  487. OutputStream doufStream2 = new DocumentOutputStream(dinfDoc);
  488. dinf.write(doufStream2);
  489. doufStream2.close();
  490. // Read them back in again
  491. sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  492. InputStream sinfStream6 = new DocumentInputStream(sinfDoc);
  493. sinf = (SummaryInformation)PropertySetFactory.create(sinfStream6);
  494. sinfStream6.close();
  495. assertEquals(131077, sinf.getOSVersion());
  496. dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  497. InputStream dinfStream6 = new DocumentInputStream(dinfDoc);
  498. dinf = (DocumentSummaryInformation)PropertySetFactory.create(dinfStream6);
  499. dinfStream6.close();
  500. assertEquals(131077, dinf.getOSVersion());
  501. assertEquals("Changed Author", sinf.getAuthor());
  502. assertEquals("Microsoft Word 9.0", sinf.getApplicationName());
  503. assertEquals("Le titre \u00e9tait chang\u00e9", sinf.getTitle());
  504. assertEquals("", dinf.getCompany());
  505. assertEquals("Changed Manager", dinf.getManager());
  506. // Close the whole filesystem, and open it once more
  507. fs.writeFilesystem();
  508. }
  509. try (POIFSFileSystem fs = new POIFSFileSystem(copy)) {
  510. DirectoryEntry root = fs.getRoot();
  511. // Re-check on load
  512. DocumentNode sinfDoc = (DocumentNode) root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME);
  513. InputStream sinfStream7 = new DocumentInputStream(sinfDoc);
  514. SummaryInformation sinf = (SummaryInformation) PropertySetFactory.create(sinfStream7);
  515. sinfStream7.close();
  516. assertEquals(131077, sinf.getOSVersion());
  517. DocumentNode dinfDoc = (DocumentNode) root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
  518. InputStream dinfStream7 = new DocumentInputStream(dinfDoc);
  519. DocumentSummaryInformation dinf = (DocumentSummaryInformation) PropertySetFactory.create(dinfStream7);
  520. dinfStream7.close();
  521. assertEquals(131077, dinf.getOSVersion());
  522. assertEquals("Changed Author", sinf.getAuthor());
  523. assertEquals("Microsoft Word 9.0", sinf.getApplicationName());
  524. assertEquals("Le titre \u00e9tait chang\u00e9", sinf.getTitle());
  525. assertEquals("", dinf.getCompany());
  526. assertEquals("Changed Manager", dinf.getManager());
  527. }
  528. // Tidy up
  529. assertTrue(copy.delete());
  530. }
  531. /**
  532. * Tests writing and reading back a proper dictionary with an invalid
  533. * codepage. (HPSF writes Unicode dictionaries only.)
  534. */
  535. @Test
  536. void dictionaryWithInvalidCodepage() throws IOException {
  537. final File copy = TempFile.createTempFile("Test-HPSF", "ole2");
  538. copy.deleteOnExit();
  539. /* Write: */
  540. final PropertySet ps1 = new PropertySet();
  541. final Section s = ps1.getSections().get(0);
  542. final Map<Long,String> m = new HashMap<>(3, 1.0f);
  543. m.put(1L, "String 1");
  544. m.put(2L, "String 2");
  545. m.put(3L, "String 3");
  546. try (OutputStream out = new FileOutputStream(copy);
  547. POIFSFileSystem poiFs = new POIFSFileSystem()) {
  548. s.setDictionary(m);
  549. s.setFormatID(DocumentSummaryInformation.FORMAT_ID[0]);
  550. int codepage = 12345;
  551. s.setProperty(PropertyIDMap.PID_CODEPAGE, Variant.VT_I2, codepage);
  552. assertThrows(UnsupportedEncodingException.class, () -> poiFs.createDocument(ps1.toInputStream(), "Test"));
  553. poiFs.writeFilesystem(out);
  554. }
  555. }
  556. /**
  557. * <p>Returns the display name of the default character set.</p>
  558. *
  559. * @return the display name of the default character set.
  560. */
  561. private static String getDefaultCharsetName() {
  562. final String charSetName = System.getProperty("file.encoding");
  563. final Charset charSet = Charset.forName(charSetName);
  564. return charSet.displayName(Locale.ROOT);
  565. }
  566. /**
  567. * <p>In order to execute tests with characters beyond US-ASCII, this
  568. * method checks whether the application is runing in an environment
  569. * where the default character set is 16-bit-capable.</p>
  570. *
  571. * @return {@code true} if the default character set is 16-bit-capable,
  572. * else {@code false}.
  573. */
  574. private boolean hasProperDefaultCharset() {
  575. final String charSetName = System.getProperty("file.encoding");
  576. final Charset charSet = Charset.forName(charSetName);
  577. return charSet.newEncoder().canEncode('\u00e4');
  578. }
  579. }