aboutsummaryrefslogtreecommitdiffstats
path: root/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java')
-rw-r--r--src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java283
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 {
}
}