From 647537c626cfbfcb681119e10218bd1b446fb283 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 30 Jul 2008 00:29:55 +0000 Subject: [PATCH] Change how we do the control and drawing write out, so that XSSFRelation can do much more of the work for us git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@680889 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/xssf/model/Control.java | 32 ++-- .../org/apache/poi/xssf/model/Drawing.java | 30 ++-- .../xssf/model/XSSFChildContainingModel.java | 31 +++- .../org/apache/poi/xssf/model/XSSFModel.java | 5 +- .../poi/xssf/model/XSSFWritableModel.java | 37 +++++ .../poi/xssf/usermodel/XSSFActiveXData.java | 3 +- .../poi/xssf/usermodel/XSSFPictureData.java | 3 +- .../poi/xssf/usermodel/XSSFWorkbook.java | 153 ++++++++++++------ .../poi/xssf/usermodel/TestXSSFBugs.java | 4 - 9 files changed, 190 insertions(+), 108 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/xssf/model/XSSFWritableModel.java diff --git a/src/ooxml/java/org/apache/poi/xssf/model/Control.java b/src/ooxml/java/org/apache/poi/xssf/model/Control.java index 0dc55a63f..9f2be120f 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/Control.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/Control.java @@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFActiveXData; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; -import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagePartName; -import org.openxml4j.opc.PackagingURIHelper; -import org.openxml4j.opc.TargetMode; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTControl; /** @@ -71,6 +67,10 @@ public class Control implements XSSFChildContainingModel { }; } + public int getNumberOfChildren() { + return activexBins.size(); + } + /** * Generates and adds XSSFActiveXData children */ @@ -79,24 +79,16 @@ public class Control implements XSSFChildContainingModel { activexBins.add(actX); } - /** - * Writes back out our XSSFPictureData children - */ - public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException { - int binIndex = 1; - OutputStream out; - - for(XSSFActiveXData actX : activexBins) { - PackagePartName binPartName = PackagingURIHelper.createPartName(XSSFWorkbook.ACTIVEX_BINS.getFileName(binIndex)); - modelPart.addRelationship(binPartName, TargetMode.INTERNAL, XSSFWorkbook.ACTIVEX_BINS.getRelation(), getOriginalId()); - PackagePart imagePart = modelPart.getPackage().createPart(binPartName, XSSFWorkbook.ACTIVEX_BINS.getContentType()); - out = imagePart.getOutputStream(); - actX.writeTo(out); - out.close(); - binIndex++; + public WritableChild getChildForWriting(int index) { + if(index >= activexBins.size()) { + throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren()); } + return new WritableChild( + activexBins.get(index), + XSSFWorkbook.ACTIVEX_BINS + ); } - + public ArrayList getData() { return this.activexBins; } diff --git a/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java b/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java index df4ed853e..6db4fcd92 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java @@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFPictureData; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; -import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagePartName; -import org.openxml4j.opc.PackagingURIHelper; -import org.openxml4j.opc.TargetMode; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing; /** @@ -73,6 +69,10 @@ public class Drawing implements XSSFChildContainingModel { }; } + public int getNumberOfChildren() { + return pictures.size(); + } + /** * Generates and adds XSSFActiveXData children */ @@ -81,22 +81,14 @@ public class Drawing implements XSSFChildContainingModel { pictures.add(pd); } - /** - * Writes back out our XSSFPictureData children - */ - public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException { - int pictureIndex = 1; - OutputStream out; - - for(XSSFPictureData picture : pictures) { - PackagePartName imagePartName = PackagingURIHelper.createPartName(XSSFWorkbook.IMAGES.getFileName(pictureIndex)); - modelPart.addRelationship(imagePartName, TargetMode.INTERNAL, XSSFWorkbook.IMAGES.getRelation(), getOriginalId()); - PackagePart imagePart = modelPart.getPackage().createPart(imagePartName, XSSFWorkbook.IMAGES.getContentType()); - out = imagePart.getOutputStream(); - picture.writeTo(out); - out.close(); - pictureIndex++; + public WritableChild getChildForWriting(int index) { + if(index >= pictures.size()) { + throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren()); } + return new WritableChild( + pictures.get(index), + XSSFWorkbook.IMAGES + ); } public ArrayList getPictures() diff --git a/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java b/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java index d50a8eba0..888dba4a9 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java @@ -18,6 +18,7 @@ package org.apache.poi.xssf.model; import java.io.IOException; +import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.opc.PackagePart; @@ -41,11 +42,29 @@ public interface XSSFChildContainingModel extends XSSFModel { * @param childId the ID of the relationship the child comes from */ public void generateChild(PackagePart childPart, String childRelId); - - /** - * Writes out any children associated with the {@link XSSFModel}, - * along with the required relationship stuff. - * @param modelPart The new PackagePart of this model + + /** + * Returns the number of children contained, which + * will need to be written out. */ - public void writeChildren(PackagePart modelPart) throws IOException, InvalidFormatException; + public int getNumberOfChildren(); + /** + * Called for each child in turn when writing out, + * which returns a WritableChild for the child in + * that position. The WritableChild has enough + * information to be able to write it out. + * @param index A zero based index of this child + */ + public WritableChild getChildForWriting(int index); + + static class WritableChild { + private XSSFWritableModel model; + private XSSFRelation relation; + public WritableChild(XSSFWritableModel model, XSSFRelation rel) { + this.model = model; + this.relation = rel; + } + public XSSFWritableModel getModel() { return model; } + public XSSFRelation getRelation() { return relation; } + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java b/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java index 03d08c030..06e97d4ca 100644 --- a/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java +++ b/src/ooxml/java/org/apache/poi/xssf/model/XSSFModel.java @@ -18,7 +18,6 @@ package org.apache.poi.xssf.model; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; @@ -29,9 +28,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; * (InputStream is), so they can be used with * {@link XSSFRelation} */ -public interface XSSFModel { +public interface XSSFModel extends XSSFWritableModel { /** Read from the given InputStream */ public void readFrom(InputStream is) throws IOException; - /** Write to the supplied OutputStream, with default options */ - public void writeTo(OutputStream out) throws IOException; } diff --git a/src/ooxml/java/org/apache/poi/xssf/model/XSSFWritableModel.java b/src/ooxml/java/org/apache/poi/xssf/model/XSSFWritableModel.java new file mode 100644 index 000000000..09f7cdf18 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/XSSFWritableModel.java @@ -0,0 +1,37 @@ +/* ==================================================================== + 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.xssf.model; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; + +/** + * A very stripped down XSSF models interface, + * which only allows writing out. Normally only + * used with the children of a + * {@link XSSFChildContainingModel}. + * Most proper models will go for {@link XSSFModel} + * instead of this one. + * {@link XSSFRelation} needs classes to implement + * this, so that it can write them out. + */ +public interface XSSFWritableModel { + /** Write to the supplied OutputStream, with default options */ + public void writeTo(OutputStream out) throws IOException; +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java index 84ef04972..5faed67e9 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java @@ -5,9 +5,10 @@ import java.io.OutputStream; import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.model.XSSFWritableModel; import org.openxml4j.opc.PackagePart; -public class XSSFActiveXData implements PictureData { +public class XSSFActiveXData implements PictureData, XSSFWritableModel { private PackagePart packagePart; private String originalId; diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java index eb153ce09..f7fb5b27a 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java @@ -22,13 +22,14 @@ import java.io.OutputStream; import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.model.XSSFWritableModel; import org.openxml4j.opc.PackagePart; /** * Raw picture data, normally attached to a * vmlDrawing */ -public class XSSFPictureData implements PictureData { +public class XSSFPictureData implements PictureData, XSSFWritableModel { private PackagePart packagePart; private String originalId; diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index cb953dfbe..9bbdd86d9 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -53,6 +53,7 @@ import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.XSSFChildContainingModel; import org.apache.poi.xssf.model.XSSFModel; +import org.apache.poi.xssf.model.XSSFWritableModel; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; @@ -118,7 +119,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { "application/vnd.openxmlformats-officedocument.vmlDrawing", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", "/xl/drawings/vmlDrawing#.vml", - null + Drawing.class ); public static final XSSFRelation IMAGES = new XSSFRelation( "image/x-emf", // TODO @@ -161,7 +162,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { "application/vnd.ms-office.activeX+xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/control", "/xl/activeX/activeX#.xml", - null + Control.class ); public static final XSSFRelation ACTIVEX_BINS = new XSSFRelation( "application/vnd.ms-office.activeX", @@ -220,7 +221,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { /** * Fetches the InputStream to read the contents, based - * of the specified core part + * of the specified core part, for which we are defined + * as a suitable relationship */ public InputStream getContents(PackagePart corePart) throws IOException, InvalidFormatException { PackageRelationshipCollection prc = @@ -238,45 +240,81 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { } /** - * Finds all the XSSFModels of this type which are + * Loads all the XSSFModels of this type which are * defined as relationships of the given parent part */ - public ArrayList findAll(PackagePart parentPart) throws Exception { + public ArrayList loadAll(PackagePart parentPart) throws Exception { ArrayList found = new ArrayList(); for(PackageRelationship rel : parentPart.getRelationshipsByType(REL)) { PackagePart part = getTargetPart(parentPart.getPackage(), rel); - found.add(load(part)); + found.add(create(part, rel)); } return found; } /** - * Load, off the specified core part + * Load a single Model, which is defined as a suitable + * relationship from the specified core (parent) + * package part. */ public XSSFModel load(PackagePart corePart) throws Exception { - Constructor c = CLASS.getConstructor(InputStream.class); + PackageRelationshipCollection prc = + corePart.getRelationshipsByType(REL); + Iterator it = prc.iterator(); + if(it.hasNext()) { + PackageRelationship rel = it.next(); + PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); + PackagePart part = corePart.getPackage().getPart(relName); + return create(part, rel); + } else { + log.log(POILogger.WARN, "No part " + DEFAULT_NAME + " found"); + return null; + } + } + + /** + * Does the actual Model creation + */ + private XSSFModel create(PackagePart thisPart, PackageRelationship rel) throws Exception { XSSFModel model = null; - InputStream inp = getContents(corePart); + Constructor c; + boolean withString = false; + + // Find the right constructor + try { + c = CLASS.getConstructor(InputStream.class, String.class); + withString = true; + } catch(NoSuchMethodException e) { + c = CLASS.getConstructor(InputStream.class); + } + + // Instantiate, if we can + InputStream inp = thisPart.getInputStream(); if(inp != null) { try { - model = c.newInstance(inp); + if(withString) { + model = c.newInstance(inp, rel.getId()); + } else { + model = c.newInstance(inp); + } } finally { inp.close(); } + + // Do children, if required + if(model instanceof XSSFChildContainingModel) { + XSSFChildContainingModel ccm = + (XSSFChildContainingModel)model; + for(String relType : ccm.getChildrenRelationshipTypes()) { + for(PackageRelationship cRel : thisPart.getRelationshipsByType(relType)) { + PackagePart childPart = getTargetPart(thisPart.getPackage(), cRel); + ccm.generateChild(childPart, cRel.getId()); + } + } + } } - // Do children, if required - if(model instanceof XSSFChildContainingModel) { - XSSFChildContainingModel ccm = - (XSSFChildContainingModel)model; - for(String relType : ccm.getChildrenRelationshipTypes()) { - for(PackageRelationship rel : corePart.getRelationshipsByType(relType)) { - PackagePart childPart = getTargetPart(corePart.getPackage(), rel); - ccm.generateChild(childPart, rel.getId()); - } - } - } return model; } @@ -285,19 +323,26 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { * Save, with the default name * @return The internal reference ID it was saved at, normally then used as an r:id */ - private String save(XSSFModel model, PackagePart corePart) throws IOException { + private String save(XSSFWritableModel model, PackagePart corePart) throws IOException { return save(model, corePart, DEFAULT_NAME); } + /** + * Save, with the name generated by the given index + * @return The internal reference ID it was saved at, normally then used as an r:id + */ + private String save(XSSFWritableModel model, PackagePart corePart, int index) throws IOException { + return save(model, corePart, getFileName(index)); + } /** * Save, with the specified name * @return The internal reference ID it was saved at, normally then used as an r:id */ - private String save(XSSFModel model, PackagePart corePart, String name) throws IOException { + private String save(XSSFWritableModel model, PackagePart corePart, String name) throws IOException { PackagePartName ppName = null; try { ppName = PackagingURIHelper.createPartName(name); } catch(InvalidFormatException e) { - throw new IllegalStateException(e); + throw new IllegalStateException("Can't create part with name " + name + " for " + model, e); } PackageRelationship rel = corePart.addRelationship(ppName, TargetMode.INTERNAL, REL); @@ -307,6 +352,23 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { model.writeTo(out); out.close(); + // Do children, if required + if(model instanceof XSSFChildContainingModel) { + XSSFChildContainingModel ccm = + (XSSFChildContainingModel)model; + // Loop over each child, writing it out + int numChildren = ccm.getNumberOfChildren(); + for(int i=0; i controls; try { // Get the comments for the sheet, if there are any - childModels = SHEET_COMMENTS.findAll(part); + childModels = SHEET_COMMENTS.loadAll(part); if(childModels.size() > 0) { comments = (CommentsSource)childModels.get(0); } // Get the drawings for the sheet, if there are any - drawings = (ArrayList)VML_DRAWINGS.findAll(part); + drawings = (ArrayList)VML_DRAWINGS.loadAll(part); // Get the activeX controls for the sheet, if there are any - controls = (ArrayList)ACTIVEX_CONTROLS.findAll(part); + controls = (ArrayList)ACTIVEX_CONTROLS.loadAll(part); } catch(Exception e) { throw new RuntimeException("Unable to construct child part",e); } @@ -890,29 +952,18 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { // If our sheet has comments, then write out those if(sheet.hasComments()) { CommentsTable ct = (CommentsTable)sheet.getCommentsSourceIfExists(); - PackagePartName ctName = PackagingURIHelper.createPartName( - SHEET_COMMENTS.getFileName(sheetNumber)); - part.addRelationship(ctName, TargetMode.INTERNAL, SHEET_COMMENTS.getRelation(), "rComments"); - PackagePart ctPart = pkg.createPart(ctName, SHEET_COMMENTS.getContentType()); - - out = ctPart.getOutputStream(); - ct.writeTo(out); - out.close(); + SHEET_COMMENTS.save(ct, part, sheetNumber); } // If our sheet has drawings, then write out those if(sheet.getDrawings() != null) { int drawingIndex = 1; for(Drawing drawing : sheet.getDrawings()) { - PackagePartName drName = PackagingURIHelper.createPartName( - VML_DRAWINGS.getFileName(drawingIndex)); - part.addRelationship(drName, TargetMode.INTERNAL, VML_DRAWINGS.getRelation(), drawing.getOriginalId()); - PackagePart drPart = pkg.createPart(drName, VML_DRAWINGS.getContentType()); - - drawing.writeChildren(drPart); - out = drPart.getOutputStream(); - drawing.writeTo(out); - out.close(); + VML_DRAWINGS.save( + drawing, + part, + drawingIndex + ); drawingIndex++; } } @@ -921,15 +972,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { if(sheet.getControls() != null) { int controlIndex = 1; for(Control control : sheet.getControls()) { - PackagePartName crName = PackagingURIHelper.createPartName( - ACTIVEX_CONTROLS.getFileName(controlIndex)); - part.addRelationship(crName, TargetMode.INTERNAL, ACTIVEX_CONTROLS.getRelation(), control.getOriginalId()); - PackagePart crPart = pkg.createPart(crName, ACTIVEX_CONTROLS.getContentType()); - - control.writeChildren(crPart); - out = crPart.getOutputStream(); - control.writeTo(out); - out.close(); + ACTIVEX_CONTROLS.save( + control, + part, + controlIndex + ); controlIndex++; } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index 9eb0da6b4..570cc077b 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -128,10 +128,6 @@ public class TestXSSFBugs extends TestCase { ); assertNotNull(drw); - FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm"); - nwb.write(fout); - fout.close(); - // For testing with excel // FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm"); // nwb.write(fout);