| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| package org.apache.poi; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.poi.openxml4j.exceptions.InvalidFormatException; |
| import org.apache.poi.openxml4j.exceptions.OpenXML4JException; |
| import org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException; |
| import org.apache.poi.openxml4j.opc.OPCPackage; |
| import org.apache.poi.openxml4j.opc.PackagePart; |
| import org.apache.poi.openxml4j.opc.PackagePartName; |
| import org.apache.poi.openxml4j.opc.PackageRelationship; |
| import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; |
| import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; |
| import org.apache.poi.openxml4j.opc.PackagingURIHelper; |
| import org.apache.poi.openxml4j.opc.TargetMode; |
| import org.apache.poi.util.Internal; |
| import org.apache.poi.util.POILogFactory; |
| import org.apache.poi.util.POILogger; |
| import org.apache.poi.xssf.usermodel.XSSFRelation; |
| |
| /** |
| * Represents an entry of a OOXML package. |
| * |
| * <p> |
| * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.apache.poi.openxml4j.opc.PackagePart}. |
| * </p> |
| */ |
| public class POIXMLDocumentPart { |
| private static final POILogger logger = POILogFactory.getLogger(POIXMLDocumentPart.class); |
| |
| private String coreDocumentRel = PackageRelationshipTypes.CORE_DOCUMENT; |
| private PackagePart packagePart; |
| private POIXMLDocumentPart parent; |
| private Map<String,RelationPart> relations = new LinkedHashMap<String,RelationPart>(); |
| |
| /** |
| * The RelationPart is a cached relationship between the document, which contains the RelationPart, |
| * and one of its referenced child document parts. |
| * The child document parts may only belong to one parent, but it's often referenced by other |
| * parents too, having varying {@link PackageRelationship#getId() relationship ids} pointing to it. |
| */ |
| public static class RelationPart { |
| private final PackageRelationship relationship; |
| private final POIXMLDocumentPart documentPart; |
| |
| RelationPart(PackageRelationship relationship, POIXMLDocumentPart documentPart) { |
| this.relationship = relationship; |
| this.documentPart = documentPart; |
| } |
| |
| /** |
| * @return the cached relationship, which uniquely identifies this child document part within the parent |
| */ |
| public PackageRelationship getRelationship() { |
| return relationship; |
| } |
| |
| /** |
| * @param <T> the cast of the caller to a document sub class |
| * |
| * @return the child document part |
| */ |
| @SuppressWarnings("unchecked") |
| public <T extends POIXMLDocumentPart> T getDocumentPart() { |
| return (T)documentPart; |
| } |
| } |
| |
| /** |
| * Counter that provides the amount of incoming relations from other parts |
| * to this part. |
| */ |
| private int relationCounter = 0; |
| |
| int incrementRelationCounter() { |
| relationCounter++; |
| return relationCounter; |
| } |
| |
| int decrementRelationCounter() { |
| relationCounter--; |
| return relationCounter; |
| } |
| |
| int getRelationCounter() { |
| return relationCounter; |
| } |
| |
| /** |
| * Construct POIXMLDocumentPart representing a "core document" package part. |
| * |
| * @param pkg the OPCPackage containing this document |
| */ |
| public POIXMLDocumentPart(OPCPackage pkg) { |
| this(pkg, PackageRelationshipTypes.CORE_DOCUMENT); |
| } |
| |
| /** |
| * Construct POIXMLDocumentPart representing a custom "core document" package part. |
| * |
| * @param pkg the OPCPackage containing this document |
| * @param coreDocumentRel the relation type of this document |
| */ |
| public POIXMLDocumentPart(OPCPackage pkg, String coreDocumentRel) { |
| this(getPartFromOPCPackage(pkg, coreDocumentRel)); |
| this.coreDocumentRel = coreDocumentRel; |
| } |
| |
| /** |
| * Creates new POIXMLDocumentPart - called by client code to create new parts from scratch. |
| * |
| * @see #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean) |
| */ |
| public POIXMLDocumentPart() { |
| } |
| |
| /** |
| * Creates an POIXMLDocumentPart representing the given package part and relationship. |
| * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. |
| * |
| * @param part - The package part that holds xml data representing this sheet. |
| * @see #read(POIXMLFactory, java.util.Map) |
| * |
| * @since POI 3.14-Beta1 |
| */ |
| public POIXMLDocumentPart(PackagePart part) { |
| this(null, part); |
| } |
| |
| /** |
| * Creates an POIXMLDocumentPart representing the given package part, relationship and parent |
| * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. |
| * |
| * @param parent - Parent part |
| * @param part - The package part that holds xml data representing this sheet. |
| * @see #read(POIXMLFactory, java.util.Map) |
| * |
| * @since POI 3.14-Beta1 |
| */ |
| public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part) { |
| this.packagePart = part; |
| this.parent = parent; |
| } |
| |
| /** |
| * Creates an POIXMLDocumentPart representing the given package part and relationship. |
| * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an existing file. |
| * |
| * @param part - The package part that holds xml data representing this sheet. |
| * @param rel - the relationship of the given package part |
| * @see #read(POIXMLFactory, java.util.Map) |
| * |
| * @deprecated in POI 3.14, scheduled for removal in POI 3.16 |
| */ |
| @Deprecated |
| public POIXMLDocumentPart(PackagePart part, PackageRelationship rel){ |
| this(null, part); |
| } |
| |
| /** |
| * Creates an POIXMLDocumentPart representing the given package part, relationship and parent |
| * Called by {@link #read(POIXMLFactory, java.util.Map)} when reading in an exisiting file. |
| * |
| * @param parent - Parent part |
| * @param part - The package part that holds xml data represenring this sheet. |
| * @param rel - the relationship of the given package part |
| * @see #read(POIXMLFactory, java.util.Map) |
| * |
| * @deprecated in POI 3.14, scheduled for removal in POI 3.16 |
| */ |
| @Deprecated |
| public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel){ |
| this(parent, part); |
| } |
| |
| /** |
| * When you open something like a theme, call this to |
| * re-base the XML Document onto the core child of the |
| * current core document |
| * |
| * @param pkg the package to be rebased |
| * |
| * @throws InvalidFormatException if there was an error in the core document relation |
| * @throws IllegalStateException if there are more than one core document relations |
| */ |
| protected final void rebase(OPCPackage pkg) throws InvalidFormatException { |
| PackageRelationshipCollection cores = |
| packagePart.getRelationshipsByType(coreDocumentRel); |
| if(cores.size() != 1) { |
| throw new IllegalStateException( |
| "Tried to rebase using " + coreDocumentRel + |
| " but found " + cores.size() + " parts of the right type" |
| ); |
| } |
| packagePart = packagePart.getRelatedPart(cores.getRelationship(0)); |
| } |
| |
| /** |
| * Provides access to the underlying PackagePart |
| * |
| * @return the underlying PackagePart |
| */ |
| public final PackagePart getPackagePart(){ |
| return packagePart; |
| } |
| |
| /** |
| * Provides access to the PackageRelationship that identifies this POIXMLDocumentPart |
| * |
| * @return the PackageRelationship that identifies this POIXMLDocumentPart |
| * |
| * @deprecated in POI 3.14, scheduled for removal in POI 3.16 |
| */ |
| @Deprecated |
| @SuppressWarnings("resource") |
| public final PackageRelationship getPackageRelationship() { |
| if (this.parent != null) { |
| for (RelationPart rp : parent.getRelationParts()) { |
| if (rp.getDocumentPart() == this) { |
| return rp.getRelationship(); |
| } |
| } |
| } else { |
| OPCPackage pkg = getPackagePart().getPackage(); |
| String partName = getPackagePart().getPartName().getName(); |
| for (PackageRelationship rel : pkg.getRelationships()) { |
| if (rel.getTargetURI().toASCIIString().equals(partName)) { |
| return rel; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the list of child relations for this POIXMLDocumentPart |
| * |
| * @return child relations |
| */ |
| public final List<POIXMLDocumentPart> getRelations(){ |
| List<POIXMLDocumentPart> l = new ArrayList<POIXMLDocumentPart>(); |
| for (RelationPart rp : relations.values()) { |
| l.add(rp.getDocumentPart()); |
| } |
| return Collections.unmodifiableList(l); |
| } |
| |
| /** |
| * Returns the list of child relations for this POIXMLDocumentPart |
| * |
| * @return child relations |
| */ |
| public final List<RelationPart> getRelationParts() { |
| List<RelationPart> l = new ArrayList<RelationPart>(relations.values()); |
| return Collections.unmodifiableList(l); |
| } |
| |
| /** |
| * Returns the target {@link POIXMLDocumentPart}, where a |
| * {@link PackageRelationship} is set from the {@link PackagePart} of this |
| * {@link POIXMLDocumentPart} to the {@link PackagePart} of the target |
| * {@link POIXMLDocumentPart} with a {@link PackageRelationship#getId()} |
| * matching the given parameter value. |
| * |
| * @param id |
| * The relation id to look for |
| * @return the target part of the relation, or null, if none exists |
| */ |
| public final POIXMLDocumentPart getRelationById(String id) { |
| RelationPart rp = relations.get(id); |
| return (rp == null) ? null : rp.getDocumentPart(); |
| } |
| |
| /** |
| * Returns the {@link PackageRelationship#getId()} of the |
| * {@link PackageRelationship}, that sources from the {@link PackagePart} of |
| * this {@link POIXMLDocumentPart} to the {@link PackagePart} of the given |
| * parameter value. |
| * |
| * @param part |
| * The {@link POIXMLDocumentPart} for which the according |
| * relation-id shall be found. |
| * @return The value of the {@link PackageRelationship#getId()} or null, if |
| * parts are not related. |
| */ |
| public final String getRelationId(POIXMLDocumentPart part) { |
| for (RelationPart rp : relations.values()) { |
| if (rp.getDocumentPart() == part) { |
| return rp.getRelationship().getId(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Add a new child POIXMLDocumentPart |
| * |
| * @param id the id of an existing child to replace |
| * @param part the child to add |
| * |
| * @deprecated in POI 3.14, scheduled for removal in POI 3.16 |
| */ |
| @Deprecated |
| public final void addRelation(String id, POIXMLDocumentPart part) { |
| PackageRelationship pr = part.getPackagePart().getRelationship(id); |
| addRelation(pr, part); |
| } |
| |
| /** |
| * Add a new child POIXMLDocumentPart |
| * |
| * @param relId the preferred relation id, when null the next free relation id will be used |
| * @param relationshipType the package relationship type |
| * @param part the child to add |
| * |
| * @return the new RelationPart |
| * |
| * @since 3.14-Beta1 |
| */ |
| public final RelationPart addRelation(String relId, POIXMLRelation relationshipType, POIXMLDocumentPart part) { |
| PackageRelationship pr = findExistingRelation(part); |
| if (pr == null) { |
| PackagePartName ppn = part.getPackagePart().getPartName(); |
| String relType = relationshipType.getRelation(); |
| pr = packagePart.addRelationship(ppn, TargetMode.INTERNAL, relType, relId); |
| } |
| addRelation(pr, part); |
| return new RelationPart(pr, part); |
| } |
| |
| /** |
| * Add a new child POIXMLDocumentPart |
| * |
| * @param pr the relationship of the child |
| * @param part the child to add |
| */ |
| private void addRelation(PackageRelationship pr, POIXMLDocumentPart part) { |
| relations.put(pr.getId(), new RelationPart(pr,part)); |
| part.incrementRelationCounter(); |
| |
| } |
| |
| /** |
| * Check if the new part was already added before via PackagePart.addRelationship() |
| * |
| * @param part to find the relationship for |
| * @return The existing relationship, or null if there isn't yet one |
| */ |
| private PackageRelationship findExistingRelation(POIXMLDocumentPart part) { |
| String ppn = part.getPackagePart().getPartName().getName(); |
| try { |
| for (PackageRelationship pr : packagePart.getRelationships()) { |
| if (pr.getTargetMode() == TargetMode.EXTERNAL) { |
| continue; |
| } |
| PackagePart pp = packagePart.getRelatedPart(pr); |
| if (ppn.equals(pp.getPartName().getName())) { |
| return pr; |
| } |
| } |
| } catch (InvalidFormatException e) { |
| throw new POIXMLException("invalid package relationships", e); |
| } |
| return null; |
| } |
| |
| /** |
| * Remove the relation to the specified part in this package and remove the |
| * part, if it is no longer needed. |
| * |
| * @param part the part which relation is to be removed from this document |
| */ |
| protected final void removeRelation(POIXMLDocumentPart part){ |
| removeRelation(part,true); |
| } |
| |
| /** |
| * Remove the relation to the specified part in this package and remove the |
| * part, if it is no longer needed and flag is set to true. |
| * |
| * @param part |
| * The related part, to which the relation shall be removed. |
| * @param removeUnusedParts |
| * true, if the part shall be removed from the package if not |
| * needed any longer. |
| * @return true, if the relation was removed |
| */ |
| protected final boolean removeRelation(POIXMLDocumentPart part, boolean removeUnusedParts){ |
| String id = getRelationId(part); |
| if (id == null) { |
| // part is not related with this POIXMLDocumentPart |
| return false; |
| } |
| /* decrement usage counter */ |
| part.decrementRelationCounter(); |
| /* remove packagepart relationship */ |
| getPackagePart().removeRelationship(id); |
| /* remove POIXMLDocument from relations */ |
| relations.remove(id); |
| |
| if (removeUnusedParts) { |
| /* if last relation to target part was removed, delete according target part */ |
| if (part.getRelationCounter() == 0) { |
| try { |
| part.onDocumentRemove(); |
| } catch (IOException e) { |
| throw new POIXMLException(e); |
| } |
| getPackagePart().getPackage().removePart(part.getPackagePart()); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the parent POIXMLDocumentPart. All parts except root have not-null parent. |
| * |
| * @return the parent POIXMLDocumentPart or <code>null</code> for the root element. |
| */ |
| public final POIXMLDocumentPart getParent(){ |
| return parent; |
| } |
| |
| @Override |
| public String toString(){ |
| return packagePart == null ? "" : packagePart.toString(); |
| } |
| |
| /** |
| * Save the content in the underlying package part. |
| * Default implementation is empty meaning that the package part is left unmodified. |
| * |
| * Sub-classes should override and add logic to marshal the "model" into Ooxml4J. |
| * |
| * For example, the code saving a generic XML entry may look as follows: |
| * <pre> |
| * protected void commit() throws IOException { |
| * PackagePart part = getPackagePart(); |
| * OutputStream out = part.getOutputStream(); |
| * XmlObject bean = getXmlBean(); //the "model" which holds changes in memory |
| * bean.save(out, DEFAULT_XML_OPTIONS); |
| * out.close(); |
| * } |
| * </pre> |
| * |
| * @throws IOException a subclass may throw an IOException if the changes can't be committed |
| */ |
| protected void commit() throws IOException { |
| |
| } |
| |
| /** |
| * Save changes in the underlying OOXML package. |
| * Recursively fires {@link #commit()} for each package part |
| * |
| * @param alreadySaved context set containing already visited nodes |
| * |
| * @throws IOException a related part may throw an IOException if the changes can't be saved |
| */ |
| protected final void onSave(Set<PackagePart> alreadySaved) throws IOException{ |
| // this usually clears out previous content in the part... |
| prepareForCommit(); |
| |
| commit(); |
| alreadySaved.add(this.getPackagePart()); |
| for(RelationPart rp : relations.values()){ |
| POIXMLDocumentPart p = rp.getDocumentPart(); |
| if (!alreadySaved.contains(p.getPackagePart())) { |
| p.onSave(alreadySaved); |
| } |
| } |
| } |
| |
| /** |
| * Ensure that a memory based package part does not have lingering data from previous |
| * commit() calls. |
| * |
| * Note: This is overwritten for some objects, as *PictureData seem to store the actual content |
| * in the part directly without keeping a copy like all others therefore we need to handle them differently. |
| */ |
| protected void prepareForCommit() { |
| PackagePart part = this.getPackagePart(); |
| if(part != null) { |
| part.clear(); |
| } |
| } |
| |
| /** |
| * Create a new child POIXMLDocumentPart |
| * |
| * @param descriptor the part descriptor |
| * @param factory the factory that will create an instance of the requested relation |
| * @return the created child POIXMLDocumentPart |
| * @throws PartAlreadyExistsException |
| * If rule M1.12 is not verified : Packages shall not contain |
| * equivalent part names and package implementers shall neither |
| * create nor recognize packages with equivalent part names. |
| */ |
| public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory){ |
| return createRelationship(descriptor, factory, -1, false).getDocumentPart(); |
| } |
| |
| /** |
| * Create a new child POIXMLDocumentPart |
| * |
| * @param descriptor the part descriptor |
| * @param factory the factory that will create an instance of the requested relation |
| * @param idx part number |
| * @return the created child POIXMLDocumentPart |
| * @throws PartAlreadyExistsException |
| * If rule M1.12 is not verified : Packages shall not contain |
| * equivalent part names and package implementers shall neither |
| * create nor recognize packages with equivalent part names. |
| */ |
| public final POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx){ |
| return createRelationship(descriptor, factory, idx, false).getDocumentPart(); |
| } |
| |
| /** |
| * Identifies the next available part number for a part of the given type, |
| * if possible, otherwise -1 if none are available. |
| * The found (valid) index can then be safely given to |
| * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int)} or |
| * {@link #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean)} |
| * without naming clashes. |
| * If parts with other types are already claiming a name for this relationship |
| * type (eg a {@link XSSFRelation#CHART} using the drawing part namespace |
| * normally used by {@link XSSFRelation#DRAWINGS}), those will be considered |
| * when finding the next spare number. |
| * |
| * @param descriptor The relationship type to find the part number for |
| * @param minIdx The minimum free index to assign, use -1 for any |
| * @return The next free part number, or -1 if none available |
| */ |
| protected final int getNextPartNumber(POIXMLRelation descriptor, int minIdx) { |
| OPCPackage pkg = packagePart.getPackage(); |
| |
| try { |
| String name = descriptor.getDefaultFileName(); |
| if (name.equals(descriptor.getFileName(9999))) { |
| // Non-index based, check if default is free |
| PackagePartName ppName = PackagingURIHelper.createPartName(name); |
| if (pkg.containPart(ppName)) { |
| // Default name already taken, not index based, nothing free |
| return -1; |
| } else { |
| // Default name free |
| return 0; |
| } |
| } |
| |
| // Default to searching from 1, unless they asked for 0+ |
| int idx = (minIdx < 0) ? 1 : minIdx; |
| int maxIdx = minIdx + pkg.getParts().size(); |
| while (idx <= maxIdx) { |
| name = descriptor.getFileName(idx); |
| PackagePartName ppName = PackagingURIHelper.createPartName(name); |
| if (!pkg.containPart(ppName)) { |
| return idx; |
| } |
| idx++; |
| } |
| } catch (InvalidFormatException e) { |
| // Give a general wrapped exception for the problem |
| throw new POIXMLException(e); |
| } |
| return -1; |
| } |
| |
| /** |
| * Create a new child POIXMLDocumentPart |
| * |
| * @param descriptor the part descriptor |
| * @param factory the factory that will create an instance of the requested relation |
| * @param idx part number |
| * @param noRelation if true, then no relationship is added. |
| * @return the created child POIXMLDocumentPart |
| * @throws PartAlreadyExistsException |
| * If rule M1.12 is not verified : Packages shall not contain |
| * equivalent part names and package implementers shall neither |
| * create nor recognize packages with equivalent part names. |
| */ |
| protected final RelationPart createRelationship(POIXMLRelation descriptor, POIXMLFactory factory, int idx, boolean noRelation){ |
| try { |
| PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx)); |
| PackageRelationship rel = null; |
| PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); |
| if(!noRelation) { |
| /* only add to relations, if according relationship is being created. */ |
| rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); |
| } |
| POIXMLDocumentPart doc = factory.newDocumentPart(descriptor); |
| doc.packagePart = part; |
| doc.parent = this; |
| if (!noRelation) { |
| /* only add to relations, if according relationship is being created. */ |
| addRelation(rel,doc); |
| } |
| |
| return new RelationPart(rel,doc); |
| } catch (PartAlreadyExistsException pae) { |
| // Return the specific exception so the user knows |
| // that the name is already taken |
| throw pae; |
| } catch (Exception e){ |
| // Give a general wrapped exception for the problem |
| throw new POIXMLException(e); |
| } |
| } |
| |
| /** |
| * Iterate through the underlying PackagePart and create child POIXMLFactory instances |
| * using the specified factory |
| * |
| * @param factory the factory object that creates POIXMLFactory instances |
| * @param context context map containing already visited noted keyed by targetURI |
| * |
| * @throws OpenXML4JException thrown when a related part can't be read |
| */ |
| protected void read(POIXMLFactory factory, Map<PackagePart, POIXMLDocumentPart> context) throws OpenXML4JException { |
| PackagePart pp = getPackagePart(); |
| // add mapping a second time, in case of initial caller hasn't done so |
| POIXMLDocumentPart otherChild = context.put(pp, this); |
| if (otherChild != null && otherChild != this) { |
| throw new POIXMLException("Unique PackagePart-POIXMLDocumentPart relation broken!"); |
| } |
| |
| if (!pp.hasRelationships()) return; |
| |
| PackageRelationshipCollection rels = packagePart.getRelationships(); |
| List<POIXMLDocumentPart> readLater = new ArrayList<POIXMLDocumentPart>(); |
| |
| // scan breadth-first, so parent-relations are hopefully the shallowest element |
| for (PackageRelationship rel : rels) { |
| if(rel.getTargetMode() == TargetMode.INTERNAL){ |
| URI uri = rel.getTargetURI(); |
| |
| // check for internal references (e.g. '#Sheet1!A1') |
| PackagePartName relName; |
| if(uri.getRawFragment() != null) { |
| relName = PackagingURIHelper.createPartName(uri.getPath()); |
| } else { |
| relName = PackagingURIHelper.createPartName(uri); |
| } |
| |
| final PackagePart p = packagePart.getPackage().getPart(relName); |
| if (p == null) { |
| logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); |
| continue; |
| } |
| |
| POIXMLDocumentPart childPart = context.get(p); |
| if (childPart == null) { |
| childPart = factory.createDocumentPart(this, p); |
| childPart.parent = this; |
| // already add child to context, so other children can reference it |
| context.put(p, childPart); |
| readLater.add(childPart); |
| } |
| |
| addRelation(rel,childPart); |
| } |
| } |
| |
| for (POIXMLDocumentPart childPart : readLater) { |
| childPart.read(factory, context); |
| } |
| } |
| |
| /** |
| * Get the PackagePart that is the target of a relationship from this Part. |
| * |
| * @param rel The relationship |
| * @return The target part |
| * @throws InvalidFormatException thrown if the related part has is erroneous |
| */ |
| protected PackagePart getTargetPart(PackageRelationship rel) throws InvalidFormatException { |
| return getPackagePart().getRelatedPart(rel); |
| } |
| |
| |
| /** |
| * Fired when a new package part is created |
| * |
| * @throws IOException a subclass may throw an IOException on document creation |
| */ |
| protected void onDocumentCreate() throws IOException { |
| |
| } |
| |
| /** |
| * Fired when a package part is read |
| * |
| * @throws IOException a subclass may throw an IOException when a document is read |
| */ |
| protected void onDocumentRead() throws IOException { |
| |
| } |
| |
| /** |
| * Fired when a package part is about to be removed from the package |
| * |
| * @throws IOException a subclass may throw an IOException when a document is removed |
| */ |
| protected void onDocumentRemove() throws IOException { |
| |
| } |
| |
| /** |
| * Internal method, do not use! |
| * <p> |
| * This method only exists to allow access to protected {@link POIXMLDocumentPart#onDocumentRead()} |
| * from {@link org.apache.poi.xwpf.usermodel.XWPFDocument} without reflection. It should be removed. |
| * |
| * @param part the part which is to be read |
| * |
| * @throws IOException if the part can't be read |
| */ |
| @Internal @Deprecated |
| public static void _invokeOnDocumentRead(POIXMLDocumentPart part) throws IOException { |
| part.onDocumentRead(); |
| } |
| |
| /** |
| * Retrieves the core document part |
| * |
| * @since POI 3.14-Beta1 |
| */ |
| private static PackagePart getPartFromOPCPackage(OPCPackage pkg, String coreDocumentRel) { |
| PackageRelationship coreRel = pkg.getRelationshipsByType(coreDocumentRel).getRelationship(0); |
| |
| if (coreRel != null) { |
| PackagePart pp = pkg.getPart(coreRel); |
| if (pp == null) { |
| throw new POIXMLException("OOXML file structure broken/invalid - core document '"+coreRel.getTargetURI()+"' not found."); |
| } |
| return pp; |
| } |
| |
| coreRel = pkg.getRelationshipsByType(PackageRelationshipTypes.STRICT_CORE_DOCUMENT).getRelationship(0); |
| if (coreRel != null) { |
| throw new POIXMLException("Strict OOXML isn't currently supported, please see bug #57699"); |
| } |
| |
| throw new POIXMLException("OOXML file structure broken/invalid - no core document found!"); |
| } |
| } |