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
This commit is contained in:
Nick Burch 2008-07-30 00:29:55 +00:00
parent 613f361d61
commit 647537c626
9 changed files with 190 additions and 108 deletions

View File

@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFActiveXData;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
import org.openxml4j.exceptions.InvalidFormatException;
import org.openxml4j.opc.PackagePart; 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; 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 * Generates and adds XSSFActiveXData children
*/ */
@ -79,22 +79,14 @@ public class Control implements XSSFChildContainingModel {
activexBins.add(actX); activexBins.add(actX);
} }
/** public WritableChild getChildForWriting(int index) {
* Writes back out our XSSFPictureData children if(index >= activexBins.size()) {
*/ throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren());
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++;
} }
return new WritableChild(
activexBins.get(index),
XSSFWorkbook.ACTIVEX_BINS
);
} }
public ArrayList<XSSFActiveXData> getData() { public ArrayList<XSSFActiveXData> getData() {

View File

@ -9,11 +9,7 @@ import org.apache.poi.xssf.usermodel.XSSFPictureData;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
import org.openxml4j.exceptions.InvalidFormatException;
import org.openxml4j.opc.PackagePart; 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; 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 * Generates and adds XSSFActiveXData children
*/ */
@ -81,22 +81,14 @@ public class Drawing implements XSSFChildContainingModel {
pictures.add(pd); pictures.add(pd);
} }
/** public WritableChild getChildForWriting(int index) {
* Writes back out our XSSFPictureData children if(index >= pictures.size()) {
*/ throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren());
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++;
} }
return new WritableChild(
pictures.get(index),
XSSFWorkbook.IMAGES
);
} }
public ArrayList<XSSFPictureData> getPictures() public ArrayList<XSSFPictureData> getPictures()

View File

@ -18,6 +18,7 @@ package org.apache.poi.xssf.model;
import java.io.IOException; import java.io.IOException;
import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation;
import org.openxml4j.exceptions.InvalidFormatException; import org.openxml4j.exceptions.InvalidFormatException;
import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackagePart;
@ -43,9 +44,27 @@ public interface XSSFChildContainingModel extends XSSFModel {
public void generateChild(PackagePart childPart, String childRelId); public void generateChild(PackagePart childPart, String childRelId);
/** /**
* Writes out any children associated with the {@link XSSFModel}, * Returns the number of children contained, which
* along with the required relationship stuff. * will need to be written out.
* @param modelPart The new PackagePart of this model
*/ */
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; }
}
} }

View File

@ -18,7 +18,6 @@ package org.apache.poi.xssf.model;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.xssf.usermodel.XSSFWorkbook.XSSFRelation; 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 * (InputStream is), so they can be used with
* {@link XSSFRelation} * {@link XSSFRelation}
*/ */
public interface XSSFModel { public interface XSSFModel extends XSSFWritableModel {
/** Read from the given InputStream */ /** Read from the given InputStream */
public void readFrom(InputStream is) throws IOException; public void readFrom(InputStream is) throws IOException;
/** Write to the supplied OutputStream, with default options */
public void writeTo(OutputStream out) throws IOException;
} }

View File

@ -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;
}

View File

@ -5,9 +5,10 @@ import java.io.OutputStream;
import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.model.XSSFWritableModel;
import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackagePart;
public class XSSFActiveXData implements PictureData { public class XSSFActiveXData implements PictureData, XSSFWritableModel {
private PackagePart packagePart; private PackagePart packagePart;
private String originalId; private String originalId;

View File

@ -22,13 +22,14 @@ import java.io.OutputStream;
import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.model.XSSFWritableModel;
import org.openxml4j.opc.PackagePart; import org.openxml4j.opc.PackagePart;
/** /**
* Raw picture data, normally attached to a * Raw picture data, normally attached to a
* vmlDrawing * vmlDrawing
*/ */
public class XSSFPictureData implements PictureData { public class XSSFPictureData implements PictureData, XSSFWritableModel {
private PackagePart packagePart; private PackagePart packagePart;
private String originalId; private String originalId;

View File

@ -53,6 +53,7 @@ import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.model.XSSFChildContainingModel; import org.apache.poi.xssf.model.XSSFChildContainingModel;
import org.apache.poi.xssf.model.XSSFModel; import org.apache.poi.xssf.model.XSSFModel;
import org.apache.poi.xssf.model.XSSFWritableModel;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
@ -118,7 +119,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
"application/vnd.openxmlformats-officedocument.vmlDrawing", "application/vnd.openxmlformats-officedocument.vmlDrawing",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
"/xl/drawings/vmlDrawing#.vml", "/xl/drawings/vmlDrawing#.vml",
null Drawing.class
); );
public static final XSSFRelation IMAGES = new XSSFRelation( public static final XSSFRelation IMAGES = new XSSFRelation(
"image/x-emf", // TODO "image/x-emf", // TODO
@ -161,7 +162,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
"application/vnd.ms-office.activeX+xml", "application/vnd.ms-office.activeX+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/control", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/control",
"/xl/activeX/activeX#.xml", "/xl/activeX/activeX#.xml",
null Control.class
); );
public static final XSSFRelation ACTIVEX_BINS = new XSSFRelation( public static final XSSFRelation ACTIVEX_BINS = new XSSFRelation(
"application/vnd.ms-office.activeX", "application/vnd.ms-office.activeX",
@ -220,7 +221,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
/** /**
* Fetches the InputStream to read the contents, based * 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 { public InputStream getContents(PackagePart corePart) throws IOException, InvalidFormatException {
PackageRelationshipCollection prc = PackageRelationshipCollection prc =
@ -238,32 +240,66 @@ 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 * defined as relationships of the given parent part
*/ */
public ArrayList<? extends XSSFModel> findAll(PackagePart parentPart) throws Exception { public ArrayList<? extends XSSFModel> loadAll(PackagePart parentPart) throws Exception {
ArrayList<XSSFModel> found = new ArrayList<XSSFModel>(); ArrayList<XSSFModel> found = new ArrayList<XSSFModel>();
for(PackageRelationship rel : parentPart.getRelationshipsByType(REL)) { for(PackageRelationship rel : parentPart.getRelationshipsByType(REL)) {
PackagePart part = getTargetPart(parentPart.getPackage(), rel); PackagePart part = getTargetPart(parentPart.getPackage(), rel);
found.add(load(part)); found.add(create(part, rel));
} }
return found; 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 { public XSSFModel load(PackagePart corePart) throws Exception {
Constructor<? extends XSSFModel> c = CLASS.getConstructor(InputStream.class); PackageRelationshipCollection prc =
corePart.getRelationshipsByType(REL);
Iterator<PackageRelationship> 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; XSSFModel model = null;
InputStream inp = getContents(corePart); Constructor<? extends XSSFModel> 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) { if(inp != null) {
try { try {
if(withString) {
model = c.newInstance(inp, rel.getId());
} else {
model = c.newInstance(inp); model = c.newInstance(inp);
}
} finally { } finally {
inp.close(); inp.close();
}
} }
// Do children, if required // Do children, if required
@ -271,12 +307,14 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
XSSFChildContainingModel ccm = XSSFChildContainingModel ccm =
(XSSFChildContainingModel)model; (XSSFChildContainingModel)model;
for(String relType : ccm.getChildrenRelationshipTypes()) { for(String relType : ccm.getChildrenRelationshipTypes()) {
for(PackageRelationship rel : corePart.getRelationshipsByType(relType)) { for(PackageRelationship cRel : thisPart.getRelationshipsByType(relType)) {
PackagePart childPart = getTargetPart(corePart.getPackage(), rel); PackagePart childPart = getTargetPart(thisPart.getPackage(), cRel);
ccm.generateChild(childPart, rel.getId()); ccm.generateChild(childPart, cRel.getId());
} }
} }
} }
}
return model; return model;
} }
@ -285,19 +323,26 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
* Save, with the default name * Save, with the default name
* @return The internal reference ID it was saved at, normally then used as an r:id * @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); 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 * Save, with the specified name
* @return The internal reference ID it was saved at, normally then used as an r:id * @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; PackagePartName ppName = null;
try { try {
ppName = PackagingURIHelper.createPartName(name); ppName = PackagingURIHelper.createPartName(name);
} catch(InvalidFormatException e) { } catch(InvalidFormatException e) {
throw new IllegalStateException(e); throw new IllegalStateException("Can't create part with name " + name + " for " + model, e);
} }
PackageRelationship rel = PackageRelationship rel =
corePart.addRelationship(ppName, TargetMode.INTERNAL, REL); corePart.addRelationship(ppName, TargetMode.INTERNAL, REL);
@ -307,6 +352,23 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
model.writeTo(out); model.writeTo(out);
out.close(); 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<numChildren; i++) {
XSSFChildContainingModel.WritableChild child =
ccm.getChildForWriting(i);
child.getRelation().save(
child.getModel(),
part,
(i+1)
);
}
}
return rel.getId(); return rel.getId();
} }
} }
@ -382,15 +444,15 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
ArrayList<Control> controls; ArrayList<Control> controls;
try { try {
// Get the comments for the sheet, if there are any // Get the comments for the sheet, if there are any
childModels = SHEET_COMMENTS.findAll(part); childModels = SHEET_COMMENTS.loadAll(part);
if(childModels.size() > 0) { if(childModels.size() > 0) {
comments = (CommentsSource)childModels.get(0); comments = (CommentsSource)childModels.get(0);
} }
// Get the drawings for the sheet, if there are any // Get the drawings for the sheet, if there are any
drawings = (ArrayList<Drawing>)VML_DRAWINGS.findAll(part); drawings = (ArrayList<Drawing>)VML_DRAWINGS.loadAll(part);
// Get the activeX controls for the sheet, if there are any // Get the activeX controls for the sheet, if there are any
controls = (ArrayList<Control>)ACTIVEX_CONTROLS.findAll(part); controls = (ArrayList<Control>)ACTIVEX_CONTROLS.loadAll(part);
} catch(Exception e) { } catch(Exception e) {
throw new RuntimeException("Unable to construct child part",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 our sheet has comments, then write out those
if(sheet.hasComments()) { if(sheet.hasComments()) {
CommentsTable ct = (CommentsTable)sheet.getCommentsSourceIfExists(); CommentsTable ct = (CommentsTable)sheet.getCommentsSourceIfExists();
PackagePartName ctName = PackagingURIHelper.createPartName( SHEET_COMMENTS.save(ct, part, sheetNumber);
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();
} }
// If our sheet has drawings, then write out those // If our sheet has drawings, then write out those
if(sheet.getDrawings() != null) { if(sheet.getDrawings() != null) {
int drawingIndex = 1; int drawingIndex = 1;
for(Drawing drawing : sheet.getDrawings()) { for(Drawing drawing : sheet.getDrawings()) {
PackagePartName drName = PackagingURIHelper.createPartName( VML_DRAWINGS.save(
VML_DRAWINGS.getFileName(drawingIndex)); drawing,
part.addRelationship(drName, TargetMode.INTERNAL, VML_DRAWINGS.getRelation(), drawing.getOriginalId()); part,
PackagePart drPart = pkg.createPart(drName, VML_DRAWINGS.getContentType()); drawingIndex
);
drawing.writeChildren(drPart);
out = drPart.getOutputStream();
drawing.writeTo(out);
out.close();
drawingIndex++; drawingIndex++;
} }
} }
@ -921,15 +972,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
if(sheet.getControls() != null) { if(sheet.getControls() != null) {
int controlIndex = 1; int controlIndex = 1;
for(Control control : sheet.getControls()) { for(Control control : sheet.getControls()) {
PackagePartName crName = PackagingURIHelper.createPartName( ACTIVEX_CONTROLS.save(
ACTIVEX_CONTROLS.getFileName(controlIndex)); control,
part.addRelationship(crName, TargetMode.INTERNAL, ACTIVEX_CONTROLS.getRelation(), control.getOriginalId()); part,
PackagePart crPart = pkg.createPart(crName, ACTIVEX_CONTROLS.getContentType()); controlIndex
);
control.writeChildren(crPart);
out = crPart.getOutputStream();
control.writeTo(out);
out.close();
controlIndex++; controlIndex++;
} }
} }

View File

@ -128,10 +128,6 @@ public class TestXSSFBugs extends TestCase {
); );
assertNotNull(drw); assertNotNull(drw);
FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm");
nwb.write(fout);
fout.close();
// For testing with excel // For testing with excel
// FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm"); // FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm");
// nwb.write(fout); // nwb.write(fout);