diff options
Diffstat (limited to 'src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java')
-rw-r--r-- | src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java | 283 |
1 files changed, 211 insertions, 72 deletions
diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java index ec10c638ba..d81391035d 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java +++ b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java @@ -17,15 +17,23 @@ package org.apache.poi; import java.io.IOException; -import java.util.*; import java.net.URI; +import java.util.*; +import java.util.Map.Entry; -import org.apache.xmlbeans.XmlOptions; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.POILogFactory; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; -import org.apache.poi.openxml4j.opc.*; +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.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.xmlbeans.XmlOptions; /** * Represents an entry of a OOXML package. @@ -47,19 +55,55 @@ public class POIXMLDocumentPart { DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces(); } + private PackagePart packagePart; private PackageRelationship packageRel; private POIXMLDocumentPart parent; - private List<POIXMLDocumentPart> relations; + private Map<String,POIXMLDocumentPart> relations = new LinkedHashMap<String,POIXMLDocumentPart>(); + + /** + * Get the PackagePart that is the target of a relationship. + * + * @param rel The relationship + * @param pkg The package to fetch from + * @return The target part + * @throws InvalidFormatException + */ + protected static PackagePart getTargetPart(OPCPackage pkg, PackageRelationship rel) + throws InvalidFormatException { + PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); + PackagePart part = pkg.getPart(relName); + if (part == null) { + throw new IllegalArgumentException("No part found for relationship " + rel); + } + return part; + } + /** + * 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. */ public POIXMLDocumentPart(OPCPackage pkg) { - PackageRelationship coreRel = pkg.getRelationshipsByType( - PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); + PackageRelationship coreRel = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); - this.relations = new LinkedList<POIXMLDocumentPart>(); this.packagePart = pkg.getPart(coreRel); this.packageRel = coreRel; } @@ -70,7 +114,6 @@ public class POIXMLDocumentPart { * @see #createRelationship(POIXMLRelation, POIXMLFactory, int, boolean) */ public POIXMLDocumentPart(){ - this.relations = new LinkedList<POIXMLDocumentPart>(); } /** @@ -82,7 +125,6 @@ public class POIXMLDocumentPart { * @see #read(POIXMLFactory, java.util.Map) */ public POIXMLDocumentPart(PackagePart part, PackageRelationship rel){ - this.relations = new LinkedList<POIXMLDocumentPart>(); this.packagePart = part; this.packageRel = rel; } @@ -97,7 +139,6 @@ public class POIXMLDocumentPart { * @see #read(POIXMLFactory, java.util.Map) */ public POIXMLDocumentPart(POIXMLDocumentPart parent, PackagePart part, PackageRelationship rel){ - this.relations = new LinkedList<POIXMLDocumentPart>(); this.packagePart = part; this.packageRel = rel; this.parent = parent; @@ -109,16 +150,16 @@ public class POIXMLDocumentPart { * current core document */ protected final void rebase(OPCPackage pkg) throws InvalidFormatException { - PackageRelationshipCollection cores = - packagePart.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); - if(cores.size() != 1) { - throw new IllegalStateException( + PackageRelationshipCollection cores = + packagePart.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT); + if(cores.size() != 1) { + throw new IllegalStateException( "Tried to rebase using " + PackageRelationshipTypes.CORE_DOCUMENT + " but found " + cores.size() + " parts of the right type" - ); - } - packageRel = cores.getRelationship(0); - packagePart = POIXMLDocument.getTargetPart(pkg, packageRel); + ); + } + packageRel = cores.getRelationship(0); + packagePart = POIXMLDocument.getTargetPart(pkg, packageRel); } /** @@ -145,7 +186,46 @@ public class POIXMLDocumentPart { * @return child relations */ public final List<POIXMLDocumentPart> getRelations(){ - return relations; + return Collections.unmodifiableList(new ArrayList<POIXMLDocumentPart>(relations.values())); + } + + /** + * 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) { + return relations.get(id); + } + + /** + * 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) { + Iterator<Entry<String, POIXMLDocumentPart>> iter = relations.entrySet().iterator(); + while (iter.hasNext()) + { + Entry<String, POIXMLDocumentPart> entry = iter.next(); + if (entry.getValue() == part) { + return entry.getKey(); + } + } + return null; } /** @@ -153,17 +233,54 @@ public class POIXMLDocumentPart { * * @param part the child to add */ - protected final void addRelation(POIXMLDocumentPart part){ - relations.add(part); + protected final void addRelation(String id,POIXMLDocumentPart part){ + relations.put(id,part); + part.incrementRelationCounter(); } /** - * Remove the specified part in this package. + * Remove the relation to the specified part in this package and remove the + * part, if it is no longer needed. */ - public final void removeRelation(POIXMLDocumentPart part){ - getPackagePart().removeRelationship(part.getPackageRelationship().getId()); - getPackagePart().getPackage().removePart(part.getPackagePart()); - relations.remove(part); + 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. + */ + 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; } /** @@ -209,13 +326,13 @@ public class POIXMLDocumentPart { * @param alreadySaved context set containing already visited nodes */ protected final void onSave(Set<PackagePart> alreadySaved) throws IOException{ - commit(); - alreadySaved.add(this.getPackagePart()); - for(POIXMLDocumentPart p : relations){ + commit(); + alreadySaved.add(this.getPackagePart()); + for(POIXMLDocumentPart p : relations.values()){ if (!alreadySaved.contains(p.getPackagePart())) { - p.onSave(alreadySaved); - } - } + p.onSave(alreadySaved); + } + } } /** @@ -246,15 +363,19 @@ public class POIXMLDocumentPart { try { PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx)); PackageRelationship rel = null; + PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); if(!noRelation) { - rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); + /* only add to relations, if according relationship is being created. */ + rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); } - PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); POIXMLDocumentPart doc = factory.newDocumentPart(descriptor); doc.packageRel = rel; doc.packagePart = part; doc.parent = this; - addRelation(doc); + if(!noRelation) { + /* only add to relations, if according relationship is being created. */ + addRelation(rel.getId(),doc); + } return doc; } catch (Exception e){ throw new POIXMLException(e); @@ -269,40 +390,40 @@ public class POIXMLDocumentPart { * @param context context map containing already visited noted keyed by targetURI */ protected void read(POIXMLFactory factory, Map<PackagePart, POIXMLDocumentPart> context) throws OpenXML4JException { - PackageRelationshipCollection rels = packagePart.getRelationships(); - for (PackageRelationship rel : rels) { - if(rel.getTargetMode() == TargetMode.INTERNAL){ - URI uri = rel.getTargetURI(); - - PackagePart p; - if(uri.getRawFragment() != null) { - /* - * For internal references (e.g. '#Sheet1!A1') the package part is null - */ - p = null; - } else { - PackagePartName relName = PackagingURIHelper.createPartName(uri); - p = packagePart.getPackage().getPart(relName); - if(p == null) { - logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); - continue; - } - } - - if (!context.containsKey(p)) { - POIXMLDocumentPart childPart = factory.createDocumentPart(this, rel, p); - childPart.parent = this; - addRelation(childPart); - if(p != null){ - context.put(p, childPart); - if(p.hasRelationships()) childPart.read(factory, context); - } - } - else { - addRelation(context.get(p)); - } - } - } + PackageRelationshipCollection rels = packagePart.getRelationships(); + for (PackageRelationship rel : rels) { + if(rel.getTargetMode() == TargetMode.INTERNAL){ + URI uri = rel.getTargetURI(); + + PackagePart p; + if(uri.getRawFragment() != null) { + /* + * For internal references (e.g. '#Sheet1!A1') the package part is null + */ + p = null; + } else { + PackagePartName relName = PackagingURIHelper.createPartName(uri); + p = packagePart.getPackage().getPart(relName); + if(p == null) { + logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); + continue; + } + } + + if (!context.containsKey(p)) { + POIXMLDocumentPart childPart = factory.createDocumentPart(this, rel, p); + childPart.parent = this; + addRelation(rel.getId(),childPart); + if(p != null){ + context.put(p, childPart); + if(p.hasRelationships()) childPart.read(factory, context); + } + } + else { + addRelation(rel.getId(),context.get(p)); + } + } + } } /** @@ -315,7 +436,25 @@ public class POIXMLDocumentPart { /** * Fired when a package part is read */ - protected void onDocumentRead() throws IOException{ + protected void onDocumentRead() throws IOException { + + } + + /** + * Get the PackagePart that is the target of a relationship. + * + * @param rel The relationship + * @return The target part + * @throws InvalidFormatException + */ + protected PackagePart getTargetPart(PackageRelationship rel) throws InvalidFormatException { + return getTargetPart(getPackagePart().getPackage(), rel); + } + + /** + * Fired when a package part is about to be removed from the package + */ + protected void onDocumentRemove() throws IOException { } } |