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.

POIXMLDocumentPart.java 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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;
  16. import java.io.IOException;
  17. import java.util.*;
  18. import java.net.URI;
  19. import org.apache.xmlbeans.XmlOptions;
  20. import org.apache.poi.util.POILogger;
  21. import org.apache.poi.util.POILogFactory;
  22. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  23. import org.apache.poi.openxml4j.opc.*;
  24. /**
  25. * Represents an entry of a OOXML package.
  26. *
  27. * <p>
  28. * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.apache.poi.openxml4j.opc.PackagePart}.
  29. * </p>
  30. *
  31. * @author Yegor Kozlov
  32. */
  33. public class POIXMLDocumentPart {
  34. private static POILogger logger = POILogFactory.getLogger(POIXMLDocumentPart.class);
  35. public static final XmlOptions DEFAULT_XML_OPTIONS;
  36. static {
  37. DEFAULT_XML_OPTIONS = new XmlOptions();
  38. DEFAULT_XML_OPTIONS.setSaveOuter();
  39. DEFAULT_XML_OPTIONS.setUseDefaultNamespace();
  40. DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces();
  41. }
  42. private PackagePart packagePart;
  43. private PackageRelationship packageRel;
  44. private POIXMLDocumentPart parent;
  45. private List<POIXMLDocumentPart> relations;
  46. /**
  47. * Construct POIXMLDocumentPart representing a "core document" package part.
  48. */
  49. public POIXMLDocumentPart(OPCPackage pkg) {
  50. PackageRelationship coreRel = pkg.getRelationshipsByType(
  51. PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0);
  52. this.relations = new LinkedList<POIXMLDocumentPart>();
  53. this.packagePart = pkg.getPart(coreRel);
  54. this.packageRel = coreRel;
  55. }
  56. /**
  57. * Creates new POIXMLDocumentPart - called by client code to create new parts from scratch.
  58. *
  59. * @see #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean)
  60. */
  61. public POIXMLDocumentPart(){
  62. this.relations = new LinkedList<POIXMLDocumentPart>();
  63. }
  64. /**
  65. * Creates an POIXMLDocumentPart representing the given package part and relationship.
  66. * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an exisiting file.
  67. *
  68. * @param part - The package part that holds xml data represenring this sheet.
  69. * @param rel - the relationship of the given package part
  70. * @see #read(POIXMLFactory, java.util.Map)
  71. */
  72. public POIXMLDocumentPart(PackagePart part, PackageRelationship rel){
  73. this.relations = new LinkedList<POIXMLDocumentPart>();
  74. this.packagePart = part;
  75. this.packageRel = rel;
  76. }
  77. /**
  78. * Provides access to the underlying PackagePart
  79. *
  80. * @return the underlying PackagePart
  81. */
  82. public final PackagePart getPackagePart(){
  83. return packagePart;
  84. }
  85. /**
  86. * Provides access to the PackageRelationship that identifies this POIXMLDocumentPart
  87. *
  88. * @return the PackageRelationship that identifies this POIXMLDocumentPart
  89. */
  90. public final PackageRelationship getPackageRelationship(){
  91. return packageRel;
  92. }
  93. /**
  94. * Returns the list of child relations for this POIXMLDocumentPart
  95. *
  96. * @return child relations
  97. */
  98. public final List<POIXMLDocumentPart> getRelations(){
  99. return relations;
  100. }
  101. /**
  102. * Add a new child POIXMLDocumentPart
  103. *
  104. * @param part the child to add
  105. */
  106. protected final void addRelation(POIXMLDocumentPart part){
  107. relations.add(part);
  108. }
  109. /**
  110. * Remove the specified part in this package.
  111. */
  112. public final void removeRelation(POIXMLDocumentPart part){
  113. getPackagePart().removeRelationship(part.getPackageRelationship().getId());
  114. getPackagePart().getPackage().removePart(part.getPackagePart());
  115. relations.remove(part);
  116. }
  117. /**
  118. * Returns the parent POIXMLDocumentPart. All parts except root have not-null parent.
  119. *
  120. * @return the parent POIXMLDocumentPart or <code>null</code> for the root element.
  121. */
  122. public final POIXMLDocumentPart getParent(){
  123. return parent;
  124. }
  125. @Override
  126. public String toString(){
  127. return packagePart == null ? null : packagePart.toString();
  128. }
  129. /**
  130. * Save the content in the underlying package part.
  131. * Default implementation is empty meaning that the package part is left unmodified.
  132. *
  133. * Sub-classes should override and add logic to marshal the "model" into Ooxml4J.
  134. *
  135. * For example, the code saving a generic XML entry may look as follows:
  136. * <pre><code>
  137. * protected void commit() throws IOException {
  138. * PackagePart part = getPackagePart();
  139. * OutputStream out = part.getOutputStream();
  140. * XmlObject bean = getXmlBean(); //the "model" which holds changes in memory
  141. * bean.save(out, DEFAULT_XML_OPTIONS);
  142. * out.close();
  143. * }
  144. * </code></pre>
  145. *
  146. */
  147. protected void commit() throws IOException {
  148. }
  149. /**
  150. * Save changes in the underlying OOXML package.
  151. * Recursively fires {@link #commit()} for each package part
  152. *
  153. * @param alreadySaved context set containing already visited nodes
  154. */
  155. protected final void onSave(Set<PackagePart> alreadySaved) throws IOException{
  156. commit();
  157. alreadySaved.add(this.getPackagePart());
  158. for(POIXMLDocumentPart p : relations){
  159. if (!alreadySaved.contains(p.getPackagePart())) {
  160. p.onSave(alreadySaved);
  161. }
  162. }
  163. }
  164. /**
  165. * Create a new child POIXMLDocumentPart
  166. *
  167. * @param descriptor the part descriptor
  168. * @param factory the factory that will create an instance of the requested relation
  169. * @return the created child POIXMLDocumentPart
  170. */
  171. public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory){
  172. return createRelationship(descriptor, factory, -1, false);
  173. }
  174. public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx){
  175. return createRelationship(descriptor, factory, idx, false);
  176. }
  177. /**
  178. * Create a new child POIXMLDocumentPart
  179. *
  180. * @param descriptor the part descriptor
  181. * @param factory the factory that will create an instance of the requested relation
  182. * @param idx part number
  183. * @param noRelation if true, then no relationship is added.
  184. * @return the created child POIXMLDocumentPart
  185. */
  186. protected final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, boolean noRelation){
  187. try {
  188. PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx));
  189. PackageRelationship rel = null;
  190. if(!noRelation) rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation());
  191. PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType());
  192. POIXMLDocumentPart doc = factory.newDocumentPart(descriptor);
  193. doc.packageRel = rel;
  194. doc.packagePart = part;
  195. doc.parent = this;
  196. addRelation(doc);
  197. return doc;
  198. } catch (Exception e){
  199. throw new POIXMLException(e);
  200. }
  201. }
  202. /**
  203. * Iterate through the underlying PackagePart and create child POIXMLFactory instances
  204. * using the specified factory
  205. *
  206. * @param factory the factory object that creates POIXMLFactory instances
  207. * @param context context map containing already visited noted keyed by targetURI
  208. */
  209. protected void read(POIXMLFactory factory, Map<PackagePart, POIXMLDocumentPart> context) throws OpenXML4JException {
  210. PackageRelationshipCollection rels = packagePart.getRelationships();
  211. for (PackageRelationship rel : rels) {
  212. if(rel.getTargetMode() == TargetMode.INTERNAL){
  213. URI uri = rel.getTargetURI();
  214. PackagePart p;
  215. if(uri.getRawFragment() != null) {
  216. /*
  217. * For internal references (e.g. '#Sheet1!A1') the package part is null
  218. */
  219. p = null;
  220. } else {
  221. PackagePartName relName = PackagingURIHelper.createPartName(uri);
  222. p = packagePart.getPackage().getPart(relName);
  223. if(p == null) {
  224. logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI());
  225. continue;
  226. }
  227. }
  228. if (!context.containsKey(p)) {
  229. POIXMLDocumentPart childPart = factory.createDocumentPart(rel, p);
  230. childPart.parent = this;
  231. addRelation(childPart);
  232. if(p != null){
  233. context.put(p, childPart);
  234. if(p.hasRelationships()) childPart.read(factory, context);
  235. }
  236. }
  237. else {
  238. addRelation(context.get(p));
  239. }
  240. }
  241. }
  242. }
  243. /**
  244. * Fired when a new package part is created
  245. */
  246. protected void onDocumentCreate() throws IOException {
  247. }
  248. /**
  249. * Fired when a package part is read
  250. */
  251. protected void onDocumentRead() throws IOException{
  252. }
  253. }