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.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<XSSFActiveXData> getData() {
return this.activexBins;
}

View File

@ -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<XSSFPictureData> getPictures()

View File

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

View File

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

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.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;

View File

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

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.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<? extends XSSFModel> findAll(PackagePart parentPart) throws Exception {
public ArrayList<? extends XSSFModel> loadAll(PackagePart parentPart) throws Exception {
ArrayList<XSSFModel> found = new ArrayList<XSSFModel>();
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<? 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;
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) {
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<numChildren; i++) {
XSSFChildContainingModel.WritableChild child =
ccm.getChildForWriting(i);
child.getRelation().save(
child.getModel(),
part,
(i+1)
);
}
}
return rel.getId();
}
}
@ -382,15 +444,15 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
ArrayList<Control> 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<Drawing>)VML_DRAWINGS.findAll(part);
drawings = (ArrayList<Drawing>)VML_DRAWINGS.loadAll(part);
// 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) {
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++;
}
}

View File

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