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.

TestOPCComplianceCoreProperties.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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.openxml4j.opc.compliance;
  16. import java.io.ByteArrayInputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.URI;
  23. import java.net.URISyntaxException;
  24. import junit.framework.AssertionFailedError;
  25. import junit.framework.TestCase;
  26. import org.apache.poi.POIDataSamples;
  27. import org.apache.poi.openxml4j.OpenXML4JTestDataSamples;
  28. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  29. import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
  30. import org.apache.poi.openxml4j.opc.ContentTypes;
  31. import org.apache.poi.openxml4j.opc.OPCPackage;
  32. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  33. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  34. import org.apache.poi.openxml4j.opc.TargetMode;
  35. import org.apache.poi.util.IOUtils;
  36. import org.apache.poi.util.TempFile;
  37. /**
  38. * Test core properties Open Packaging Convention compliance.
  39. *
  40. * M4.1: The format designer shall specify and the format producer shall create
  41. * at most one core properties relationship for a package. A format consumer
  42. * shall consider more than one core properties relationship for a package to be
  43. * an error. If present, the relationship shall target the Core Properties part.
  44. * (POI relaxes this on reading, as Office sometimes breaks this)
  45. *
  46. * M4.2: The format designer shall not specify and the format producer shall not
  47. * create Core Properties that use the Markup Compatibility namespace as defined
  48. * in Annex F, "Standard Namespaces and Content Types". A format consumer shall
  49. * consider the use of the Markup Compatibility namespace to be an error.
  50. *
  51. * M4.3: Producers shall not create a document element that contains refinements
  52. * to the Dublin Core elements, except for the two specified in the schema:
  53. * <dcterms:created> and <dcterms:modified> Consumers shall consider a document
  54. * element that violates this constraint to be an error.
  55. *
  56. * M4.4: Producers shall not create a document element that contains the
  57. * xml:lang attribute. Consumers shall consider a document element that violates
  58. * this constraint to be an error.
  59. *
  60. * M4.5: Producers shall not create a document element that contains the
  61. * xsi:type attribute, except for a <dcterms:created> or <dcterms:modified>
  62. * element where the xsi:type attribute shall be present and shall hold the
  63. * value dcterms:W3CDTF, where dcterms is the namespace prefix of the Dublin
  64. * Core namespace. Consumers shall consider a document element that violates
  65. * this constraint to be an error.
  66. *
  67. * @author Julien Chable
  68. */
  69. public final class TestOPCComplianceCoreProperties extends TestCase {
  70. public void testCorePropertiesPart() {
  71. OPCPackage pkg;
  72. try {
  73. InputStream is = OpenXML4JTestDataSamples.openComplianceSampleStream("OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx");
  74. pkg = OPCPackage.open(is);
  75. } catch (InvalidFormatException e) {
  76. throw new RuntimeException(e);
  77. } catch (IOException e) {
  78. throw new RuntimeException(e);
  79. }
  80. pkg.revert();
  81. }
  82. private static String extractInvalidFormatMessage(String sampleNameSuffix) {
  83. InputStream is = OpenXML4JTestDataSamples.openComplianceSampleStream("OPCCompliance_CoreProperties_" + sampleNameSuffix);
  84. OPCPackage pkg;
  85. try {
  86. pkg = OPCPackage.open(is);
  87. } catch (InvalidFormatException e) {
  88. // no longer required for successful test
  89. return e.getMessage();
  90. } catch (IOException e) {
  91. throw new RuntimeException(e);
  92. }
  93. pkg.revert();
  94. throw new AssertionFailedError("expected OPC compliance exception was not thrown");
  95. }
  96. /**
  97. * Test M4.1 rule.
  98. */
  99. public void testOnlyOneCorePropertiesPart() throws Exception {
  100. // We have relaxed this check, so we can read the file anyway
  101. try {
  102. extractInvalidFormatMessage("OnlyOneCorePropertiesPartFAIL.docx");
  103. fail("M4.1 should be being relaxed");
  104. } catch (AssertionFailedError e) {}
  105. // We will use the first core properties, and ignore the others
  106. InputStream is = OpenXML4JTestDataSamples.openSampleStream("MultipleCoreProperties.docx");
  107. OPCPackage pkg = OPCPackage.open(is);
  108. // We can see 2 by type
  109. assertEquals(2, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  110. // But only the first one by relationship
  111. assertEquals(1, pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size());
  112. // It should be core.xml not the older core1.xml
  113. assertEquals(
  114. "/docProps/core.xml",
  115. pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).get(0).getPartName().toString()
  116. );
  117. }
  118. private static URI createURI(String text) {
  119. try {
  120. return new URI(text);
  121. } catch (URISyntaxException e) {
  122. throw new RuntimeException(e);
  123. }
  124. }
  125. /**
  126. * Test M4.1 rule.
  127. */
  128. public void testOnlyOneCorePropertiesPart_AddRelationship() {
  129. InputStream is = OpenXML4JTestDataSamples.openComplianceSampleStream("OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx");
  130. OPCPackage pkg;
  131. try {
  132. pkg = OPCPackage.open(is);
  133. } catch (InvalidFormatException e) {
  134. throw new RuntimeException(e);
  135. } catch (IOException e) {
  136. throw new RuntimeException(e);
  137. }
  138. URI partUri = createURI("/docProps/core2.xml");
  139. try {
  140. pkg.addRelationship(PackagingURIHelper.createPartName(partUri), TargetMode.INTERNAL,
  141. PackageRelationshipTypes.CORE_PROPERTIES);
  142. // no longer fail on compliance error
  143. //fail("expected OPC compliance exception was not thrown");
  144. } catch (InvalidFormatException e) {
  145. throw new RuntimeException(e);
  146. } catch (InvalidOperationException e) {
  147. // expected during successful test
  148. assertEquals("OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead.", e.getMessage());
  149. }
  150. pkg.revert();
  151. }
  152. /**
  153. * Test M4.1 rule.
  154. */
  155. public void testOnlyOneCorePropertiesPart_AddPart() {
  156. String sampleFileName = "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";
  157. OPCPackage pkg = null;
  158. try {
  159. pkg = OPCPackage.open(POIDataSamples.getOpenXML4JInstance().getFile(sampleFileName).getPath());
  160. } catch (Exception e) {
  161. throw new RuntimeException(e);
  162. }
  163. URI partUri = createURI("/docProps/core2.xml");
  164. try {
  165. pkg.createPart(PackagingURIHelper.createPartName(partUri),
  166. ContentTypes.CORE_PROPERTIES_PART);
  167. // no longer fail on compliance error
  168. //fail("expected OPC compliance exception was not thrown");
  169. } catch (InvalidFormatException e) {
  170. throw new RuntimeException(e);
  171. } catch (InvalidOperationException e) {
  172. // expected during successful test
  173. assertEquals("OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package !", e.getMessage());
  174. }
  175. pkg.revert();
  176. }
  177. /**
  178. * Test M4.2 rule.
  179. */
  180. public void testDoNotUseCompatibilityMarkup() {
  181. String msg = extractInvalidFormatMessage("DoNotUseCompatibilityMarkupFAIL.docx");
  182. assertEquals("OPC Compliance error [M4.2]: A format consumer shall consider the use of the Markup Compatibility namespace to be an error.", msg);
  183. }
  184. /**
  185. * Test M4.3 rule.
  186. */
  187. public void testDCTermsNamespaceLimitedUse() {
  188. String msg = extractInvalidFormatMessage("DCTermsNamespaceLimitedUseFAIL.docx");
  189. assertEquals("OPC Compliance error [M4.3]: Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.", msg);
  190. }
  191. /**
  192. * Test M4.4 rule.
  193. */
  194. public void testUnauthorizedXMLLangAttribute() {
  195. String msg = extractInvalidFormatMessage("UnauthorizedXMLLangAttributeFAIL.docx");
  196. assertEquals("OPC Compliance error [M4.4]: Producers shall not create a document element that contains the xml:lang attribute. Consumers shall consider a document element that violates this constraint to be an error.", msg);
  197. }
  198. /**
  199. * Test M4.5 rule.
  200. */
  201. public void testLimitedXSITypeAttribute_NotPresent() {
  202. String msg = extractInvalidFormatMessage("LimitedXSITypeAttribute_NotPresentFAIL.docx");
  203. assertEquals("The element 'created' must have the 'xsi:type' attribute present !", msg);
  204. }
  205. /**
  206. * Test M4.5 rule.
  207. */
  208. public void testLimitedXSITypeAttribute_PresentWithUnauthorizedValue() {
  209. String msg = extractInvalidFormatMessage("LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx");
  210. assertEquals("The element 'modified' must have the 'xsi:type' attribute with the value 'dcterms:W3CDTF', but had 'W3CDTF' !", msg);
  211. }
  212. /**
  213. * Document with no core properties - testing at the OPC level,
  214. * saving into a new stream
  215. */
  216. public void testNoCoreProperties_saveNew() throws Exception {
  217. String sampleFileName = "OPCCompliance_NoCoreProperties.xlsx";
  218. OPCPackage pkg = OPCPackage.open(POIDataSamples.getOpenXML4JInstance().getFile(sampleFileName).getPath());
  219. // Verify it has empty properties
  220. assertEquals(0, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  221. assertNotNull(pkg.getPackageProperties());
  222. assertNotNull(pkg.getPackageProperties().getLanguageProperty());
  223. assertNull(pkg.getPackageProperties().getLanguageProperty().getValue());
  224. // Save and re-load
  225. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  226. pkg.save(baos);
  227. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  228. pkg = OPCPackage.open(bais);
  229. // An Empty Properties part has been added in the save/load
  230. assertEquals(1, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  231. assertNotNull(pkg.getPackageProperties());
  232. assertNotNull(pkg.getPackageProperties().getLanguageProperty());
  233. assertNull(pkg.getPackageProperties().getLanguageProperty().getValue());
  234. // Open a new copy of it
  235. pkg = OPCPackage.open(POIDataSamples.getOpenXML4JInstance().getFile(sampleFileName).getPath());
  236. // Save and re-load, without having touched the properties yet
  237. baos = new ByteArrayOutputStream();
  238. pkg.save(baos);
  239. bais = new ByteArrayInputStream(baos.toByteArray());
  240. pkg = OPCPackage.open(bais);
  241. // Check that this too added empty properties without error
  242. assertEquals(1, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  243. assertNotNull(pkg.getPackageProperties());
  244. assertNotNull(pkg.getPackageProperties().getLanguageProperty());
  245. assertNull(pkg.getPackageProperties().getLanguageProperty().getValue());
  246. }
  247. /**
  248. * Document with no core properties - testing at the OPC level,
  249. * from a temp-file, saving in-place
  250. */
  251. public void testNoCoreProperties_saveInPlace() throws Exception {
  252. String sampleFileName = "OPCCompliance_NoCoreProperties.xlsx";
  253. // Copy this into a temp file, so we can play with it
  254. File tmp = TempFile.createTempFile("poi-test", ".opc");
  255. FileOutputStream out = new FileOutputStream(tmp);
  256. IOUtils.copy(
  257. POIDataSamples.getOpenXML4JInstance().openResourceAsStream(sampleFileName),
  258. out);
  259. out.close();
  260. // Open it from that temp file
  261. OPCPackage pkg = OPCPackage.open(tmp);
  262. // Empty properties
  263. assertEquals(0, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  264. assertNotNull(pkg.getPackageProperties());
  265. assertNotNull(pkg.getPackageProperties().getLanguageProperty());
  266. assertNull(pkg.getPackageProperties().getLanguageProperty().getValue());
  267. // Save and close
  268. pkg.close();
  269. // Re-open and check
  270. pkg = OPCPackage.open(tmp);
  271. // An Empty Properties part has been added in the save/load
  272. assertEquals(1, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
  273. assertNotNull(pkg.getPackageProperties());
  274. assertNotNull(pkg.getPackageProperties().getLanguageProperty());
  275. assertNull(pkg.getPackageProperties().getLanguageProperty().getValue());
  276. // Finish and tidy
  277. pkg.revert();
  278. tmp.delete();
  279. }
  280. }