Also, performed major cleanup of core XSSF classes and test cases git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@700472 13f79535-47bb-0310-9956-ffa450edef68tags/ooxml_20081107
@@ -29,7 +29,7 @@ public class CreateNewSpreadsheet { | |||
CreationHelper createHelper = wb.getCreationHelper(); | |||
XSSFSheet s1 = wb.createSheet("Sheet One"); | |||
XSSFSheet s2 = wb.createSheet("Sheet One"); | |||
XSSFSheet s2 = wb.createSheet("Sheet Two"); | |||
// Create a few cells | |||
s1.createRow(0); |
@@ -376,9 +376,6 @@ public interface Workbook { | |||
Palette getCustomPalette(); | |||
/** Test only. Do not use */ | |||
void insertChartRecord(); | |||
/** | |||
* Adds a picture to the workbook. | |||
* | |||
@@ -392,7 +389,7 @@ public interface Workbook { | |||
/** | |||
* Gets all pictures from the Workbook. | |||
* | |||
* @return the list of pictures (a list of {@link HSSFPictureData} objects.) | |||
* @return the list of pictures (a list of {@link PictureData} objects.) | |||
*/ | |||
List getAllPictures(); | |||
@@ -16,78 +16,61 @@ | |||
==================================================================== */ | |||
package org.apache.poi; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.PushbackInputStream; | |||
import java.io.*; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.apache.poi.poifs.common.POIFSConstants; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.util.PackageHelper; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.openxml4j.exceptions.InvalidFormatException; | |||
import org.openxml4j.exceptions.OpenXML4JException; | |||
import org.openxml4j.opc.*; | |||
import org.openxml4j.opc.Package; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackagePartName; | |||
import org.openxml4j.opc.PackageRelationship; | |||
import org.openxml4j.opc.PackageRelationshipCollection; | |||
import org.openxml4j.opc.PackageRelationshipTypes; | |||
import org.openxml4j.opc.PackagingURIHelper; | |||
public abstract class POIXMLDocument { | |||
public class POIXMLDocument extends POIXMLDocumentPart{ | |||
public static final String CORE_PROPERTIES_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; | |||
public static final String EXTENDED_PROPERTIES_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"; | |||
public static final String CUSTOM_PROPERTIES_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"; | |||
// OLE embeddings relation name | |||
public static final String OLE_OBJECT_REL_TYPE="http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject"; | |||
// Embedded OPC documents relation name | |||
public static final String PACK_OBJECT_REL_TYPE="http://schemas.openxmlformats.org/officeDocument/2006/relationships/package"; | |||
/** The OPC Package */ | |||
private Package pkg; | |||
/** The OPC core Package Part */ | |||
private PackagePart corePart; | |||
/** | |||
* The properties of the OPC package, opened as needed | |||
*/ | |||
private POIXMLProperties properties; | |||
/** | |||
* The embedded OLE2 files in the OPC package | |||
*/ | |||
protected List<PackagePart> embedds = new LinkedList<PackagePart>(); | |||
protected POIXMLDocument() {} | |||
/** | |||
* The embedded OLE2 files in the OPC package | |||
*/ | |||
protected List<PackagePart> embedds; | |||
protected POIXMLDocument() { | |||
super(null, null); | |||
embedds = new LinkedList<PackagePart>(); | |||
} | |||
protected POIXMLDocument(Package pkg) throws IOException { | |||
try { | |||
this.pkg = pkg; | |||
PackageRelationship coreDocRelationship = this.pkg.getRelationshipsByType( | |||
PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); | |||
// Get core part | |||
this.corePart = this.pkg.getPart(coreDocRelationship); | |||
// Verify it's there | |||
if(corePart == null) { | |||
throw new IllegalArgumentException("No core part found for this document! Nothing with " + coreDocRelationship.getRelationshipType() + " present as a relation."); | |||
} | |||
} catch (OpenXML4JException e) { | |||
throw new IOException(e.toString()); | |||
} | |||
this(); | |||
initialize(pkg); | |||
} | |||
protected POIXMLDocument(String path) throws IOException { | |||
this(openPackage(path)); | |||
this(openPackage(path)); | |||
} | |||
/** | |||
* Wrapper to open a package, returning an IOException | |||
* in the event of a problem. | |||
@@ -100,25 +83,37 @@ public abstract class POIXMLDocument { | |||
throw new IOException(e.toString()); | |||
} | |||
} | |||
public static Package openPackage(InputStream is) throws IOException { | |||
protected void initialize(Package pkg) throws IOException { | |||
try { | |||
return Package.open(is); | |||
} catch (InvalidFormatException e) { | |||
this.pkg = pkg; | |||
PackageRelationship coreDocRelationship = this.pkg.getRelationshipsByType( | |||
PackageRelationshipTypes.CORE_DOCUMENT).getRelationship(0); | |||
// Get core part | |||
this.corePart = super.packagePart = this.pkg.getPart(coreDocRelationship); | |||
// Verify it's there | |||
if(corePart == null) { | |||
throw new IllegalArgumentException("No core part found for this document! Nothing with " + coreDocRelationship.getRelationshipType() + " present as a relation."); | |||
} | |||
} catch (OpenXML4JException e) { | |||
throw new IOException(e.toString()); | |||
} | |||
} | |||
protected Package getPackage() { | |||
public Package getPackage() { | |||
return this.pkg; | |||
} | |||
protected PackagePart getCorePart() { | |||
return this.corePart; | |||
} | |||
/** | |||
* Get the PackagePart that is the target of a relationship. | |||
* | |||
* | |||
* @param rel The relationship | |||
* @return The target part | |||
* @throws InvalidFormatException | |||
@@ -128,7 +123,7 @@ public abstract class POIXMLDocument { | |||
} | |||
/** | |||
* 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 | |||
@@ -143,90 +138,90 @@ public abstract class POIXMLDocument { | |||
return part; | |||
} | |||
/** | |||
* Fetches the (single) PackagePart which is defined as | |||
* the supplied relation content type of the base | |||
* package/container, or null if none found. | |||
* @param relationType The relation content type to search for | |||
* @throws IllegalArgumentException If we find more than one part of that type | |||
*/ | |||
protected PackagePart getSinglePartByRelationType(String relationType) throws IllegalArgumentException, OpenXML4JException { | |||
PackageRelationshipCollection rels = | |||
pkg.getRelationshipsByType(relationType); | |||
if(rels.size() == 0) { | |||
return null; | |||
} | |||
if(rels.size() > 1) { | |||
throw new IllegalArgumentException("Found " + rels.size() + " relations for the type " + relationType + ", should only ever be one!"); | |||
} | |||
PackageRelationship rel = rels.getRelationship(0); | |||
return getTargetPart(rel); | |||
} | |||
/** | |||
* Retrieves all the PackageParts which are defined as | |||
* relationships of the base document with the | |||
* specified content type. | |||
*/ | |||
protected PackagePart[] getRelatedByType(String contentType) throws InvalidFormatException { | |||
PackageRelationshipCollection partsC = | |||
getCorePart().getRelationshipsByType(contentType); | |||
PackagePart[] parts = new PackagePart[partsC.size()]; | |||
int count = 0; | |||
for (PackageRelationship rel : partsC) { | |||
parts[count] = getTargetPart(rel); | |||
count++; | |||
} | |||
return parts; | |||
} | |||
/** | |||
* Fetches the (single) PackagePart which is defined as | |||
* the supplied relation content type of the base | |||
* package/container, or null if none found. | |||
* @param relationType The relation content type to search for | |||
* @throws IllegalArgumentException If we find more than one part of that type | |||
*/ | |||
protected PackagePart getSinglePartByRelationType(String relationType) throws IllegalArgumentException, OpenXML4JException { | |||
PackageRelationshipCollection rels = | |||
pkg.getRelationshipsByType(relationType); | |||
if(rels.size() == 0) { | |||
return null; | |||
} | |||
if(rels.size() > 1) { | |||
throw new IllegalArgumentException("Found " + rels.size() + " relations for the type " + relationType + ", should only ever be one!"); | |||
} | |||
PackageRelationship rel = rels.getRelationship(0); | |||
return getTargetPart(rel); | |||
} | |||
/** | |||
* Retrieves all the PackageParts which are defined as | |||
* relationships of the base document with the | |||
* specified content type. | |||
*/ | |||
protected PackagePart[] getRelatedByType(String contentType) throws InvalidFormatException { | |||
PackageRelationshipCollection partsC = | |||
getCorePart().getRelationshipsByType(contentType); | |||
PackagePart[] parts = new PackagePart[partsC.size()]; | |||
int count = 0; | |||
for (PackageRelationship rel : partsC) { | |||
parts[count] = getTargetPart(rel); | |||
count++; | |||
} | |||
return parts; | |||
} | |||
/** | |||
* Checks that the supplied InputStream (which MUST | |||
* support mark and reset, or be a PushbackInputStream) | |||
* support mark and reset, or be a PushbackInputStream) | |||
* has a OOXML (zip) header at the start of it. | |||
* If your InputStream does not support mark / reset, | |||
* then wrap it in a PushBackInputStream, then be | |||
* sure to always use that, and not the original! | |||
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream | |||
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream | |||
*/ | |||
public static boolean hasOOXMLHeader(InputStream inp) throws IOException { | |||
// We want to peek at the first 4 bytes | |||
inp.mark(4); | |||
// We want to peek at the first 4 bytes | |||
inp.mark(4); | |||
byte[] header = new byte[4]; | |||
IOUtils.readFully(inp, header); | |||
byte[] header = new byte[4]; | |||
IOUtils.readFully(inp, header); | |||
// Wind back those 4 bytes | |||
if(inp instanceof PushbackInputStream) { | |||
PushbackInputStream pin = (PushbackInputStream)inp; | |||
pin.unread(header); | |||
PushbackInputStream pin = (PushbackInputStream)inp; | |||
pin.unread(header); | |||
} else { | |||
inp.reset(); | |||
inp.reset(); | |||
} | |||
// Did it match the ooxml zip signature? | |||
// Did it match the ooxml zip signature? | |||
return ( | |||
header[0] == POIFSConstants.OOXML_FILE_HEADER[0] && | |||
header[1] == POIFSConstants.OOXML_FILE_HEADER[1] && | |||
header[2] == POIFSConstants.OOXML_FILE_HEADER[2] && | |||
header[3] == POIFSConstants.OOXML_FILE_HEADER[3] | |||
); | |||
header[0] == POIFSConstants.OOXML_FILE_HEADER[0] && | |||
header[1] == POIFSConstants.OOXML_FILE_HEADER[1] && | |||
header[2] == POIFSConstants.OOXML_FILE_HEADER[2] && | |||
header[3] == POIFSConstants.OOXML_FILE_HEADER[3] | |||
); | |||
} | |||
/** | |||
* Get the document properties. This gives you access to the | |||
* core ooxml properties, and the extended ooxml properties. | |||
*/ | |||
public POIXMLProperties getProperties() throws OpenXML4JException, IOException, XmlException { | |||
if(properties == null) { | |||
properties = new POIXMLProperties(pkg); | |||
} | |||
return properties; | |||
} | |||
/** | |||
* Get the document properties. This gives you access to the | |||
* core ooxml properties, and the extended ooxml properties. | |||
*/ | |||
public POIXMLProperties getProperties() throws OpenXML4JException, IOException, XmlException { | |||
if(properties == null) { | |||
properties = new POIXMLProperties(pkg); | |||
} | |||
return properties; | |||
} | |||
/** | |||
* Get the document's embedded files. | |||
*/ |
@@ -0,0 +1,203 @@ | |||
/* ==================================================================== | |||
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; | |||
import java.io.IOException; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.openxml4j.exceptions.OpenXML4JException; | |||
import org.openxml4j.opc.*; | |||
/** | |||
* Represents an entry of a OOXML package. | |||
* | |||
* <p> | |||
* Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.openxml4j.opc.PackagePart}. | |||
* </p> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class POIXMLDocumentPart { | |||
private static POILogger logger = POILogFactory.getLogger(POIXMLDocumentPart.class); | |||
public static XmlOptions DEFAULT_XML_OPTIONS; | |||
static { | |||
DEFAULT_XML_OPTIONS = new XmlOptions(); | |||
DEFAULT_XML_OPTIONS.setSaveOuter(); | |||
DEFAULT_XML_OPTIONS.setUseDefaultNamespace(); | |||
} | |||
protected PackagePart packagePart; | |||
protected PackageRelationship packageRel; | |||
protected POIXMLDocumentPart parent; | |||
protected List<POIXMLDocumentPart> relations; | |||
public POIXMLDocumentPart(PackagePart part, PackageRelationship rel){ | |||
relations = new LinkedList<POIXMLDocumentPart>(); | |||
this.packagePart = part; | |||
this.packageRel = rel; | |||
} | |||
/** | |||
* Provides access to the underlying PackagePart | |||
* | |||
* @return the underlying PackagePart | |||
*/ | |||
public PackagePart getPackagePart(){ | |||
return packagePart; | |||
} | |||
/** | |||
* Provides access to the PackageRelationship that identifies this POIXMLDocumentPart | |||
* | |||
* @return the PackageRelationship that identifies this POIXMLDocumentPart | |||
*/ | |||
public PackageRelationship getPackageRelationship(){ | |||
return packageRel; | |||
} | |||
/** | |||
* Returns the list of child relations for this POIXMLDocumentPart | |||
* | |||
* @return child relations | |||
*/ | |||
public List<POIXMLDocumentPart> getRelations(){ | |||
return relations; | |||
} | |||
/** | |||
* Add a new child POIXMLDocumentPart | |||
* | |||
* @param part the child to add | |||
*/ | |||
protected void addRelation(POIXMLDocumentPart part){ | |||
relations.add(part); | |||
} | |||
/** | |||
* Returns the parent POIXMLDocumentPart. All parts except root have not-null parent. | |||
* | |||
* @return the parent POIXMLDocumentPart or <code>null</code> for the root element. | |||
*/ | |||
public POIXMLDocumentPart getParent(){ | |||
return parent; | |||
} | |||
@Override | |||
public String toString(){ | |||
return packagePart.toString(); | |||
} | |||
/** | |||
* Save the content in the underlying package part. | |||
* Default implemenation is empty meaning that the package part is left unmodified. | |||
* | |||
* Sub-classes should override and add logic to marshal the "model" into Ooxml4J. | |||
* | |||
* For example, the code saving a generic XML entry may look as follows: | |||
* <pre><code> | |||
* protected void commit() throws IOException { | |||
* PackagePart part = getPackagePart(); | |||
* OutputStream out = part.getOutputStream(); | |||
* XmlObject bean = getXmlBean(); //the "model" which holds changes in memory | |||
* bean.save(out, DEFAULT_XML_OPTIONS); | |||
* out.close(); | |||
* </code></pre> | |||
* | |||
*/ | |||
protected void commit() throws IOException { | |||
} | |||
/** | |||
* Save changes in the underlying OOXML package. | |||
*/ | |||
protected void save() throws IOException{ | |||
commit(); | |||
for(POIXMLDocumentPart p : relations){ | |||
p.save(); | |||
} | |||
} | |||
/** | |||
* Create a new child POIXMLDocumentPart | |||
* | |||
* @param descriptor the part descriptor | |||
* @param cls the Class object identifying the type of instance to create | |||
* @return the created child POIXMLDocumentPart | |||
*/ | |||
protected POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, Class<? extends POIXMLDocumentPart> cls){ | |||
return createRelationship(descriptor, cls, -1); | |||
} | |||
/** | |||
* Create a new child POIXMLDocumentPart | |||
* | |||
* @param descriptor the part descriptor | |||
* @param cls the Class object identifying the type of instance to create | |||
* @param idx part number | |||
* @return the created child POIXMLDocumentPart | |||
*/ | |||
protected POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, Class<? extends POIXMLDocumentPart> cls, int idx){ | |||
try { | |||
PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx)); | |||
PackageRelationship rel = | |||
packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation()); | |||
PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType()); | |||
POIXMLDocumentPart doc = cls.newInstance(); | |||
doc.packageRel = rel; | |||
doc.packagePart = part; | |||
addRelation(doc); | |||
return doc; | |||
} catch (Exception e){ | |||
throw new POIXMLException(e); | |||
} | |||
} | |||
/** | |||
* Iterate through the underlying PackagePart and create child POIXMLFactory instances | |||
* using the specified factory | |||
* | |||
* @param factory the factory object that creates POIXMLFactory instances | |||
*/ | |||
protected void read(POIXMLFactory factory) throws OpenXML4JException { | |||
PackageRelationshipCollection rels = packagePart.getRelationships(); | |||
for (PackageRelationship rel : rels) { | |||
if(rel.getTargetMode() == TargetMode.INTERNAL){ | |||
PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); | |||
PackagePart p = packagePart.getPackage().getPart(relName); | |||
if(p == null) { | |||
logger.log(POILogger.ERROR, "Skipped invalid entry " + rel.getTargetURI()); | |||
continue; | |||
} | |||
POIXMLDocumentPart childPart = factory.create(rel, p); | |||
childPart.parent = this; | |||
addRelation(childPart); | |||
if(p.hasRelationships()) childPart.read(factory); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* ==================================================================== | |||
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; | |||
/** | |||
* Indicates a generic OOXML error. | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class POIXMLException extends RuntimeException{ | |||
/** | |||
* Create a new <code>POIXMLException</code> with no | |||
* detail mesage. | |||
*/ | |||
public POIXMLException() { | |||
super(); | |||
} | |||
/** | |||
* Create a new <code>POIXMLException</code> with | |||
* the <code>String</code> specified as an error message. | |||
* | |||
* @param msg The error message for the exception. | |||
*/ | |||
public POIXMLException(String msg) { | |||
super(msg); | |||
} | |||
/** | |||
* Create a new <code>POIXMLException</code> with | |||
* the <code>String</code> specified as an error message and the cause. | |||
* | |||
* @param msg The error message for the exception. | |||
* @param cause the cause (which is saved for later retrieval by the | |||
* {@link #getCause()} method). (A <tt>null</tt> value is | |||
* permitted, and indicates that the cause is nonexistent or | |||
* unknown.) | |||
*/ | |||
public POIXMLException(String msg, Throwable cause) { | |||
super(msg, cause); | |||
} | |||
/** | |||
* Create a new <code>POIXMLException</code> with | |||
* the specified cause. | |||
* | |||
* @param cause the cause (which is saved for later retrieval by the | |||
* {@link #getCause()} method). (A <tt>null</tt> value is | |||
* permitted, and indicates that the cause is nonexistent or | |||
* unknown.) | |||
*/ | |||
public POIXMLException(Throwable cause) { | |||
super(cause); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* ==================================================================== | |||
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; | |||
import org.openxml4j.opc.PackageRelationship; | |||
import org.openxml4j.opc.PackagePart; | |||
/** | |||
* Defines a factory API that enables sub-classes to create instances of <code>POIXMLDocumentPart</code> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class POIXMLFactory { | |||
/** | |||
* Creates a new instance of a {@link POIXMLDocumentPart} | |||
* | |||
* @param rel the package part relationship | |||
* @param part the PackagePart representing the created instance | |||
* @return A new instance of a POIXMLDocumentPart. | |||
*/ | |||
public POIXMLDocumentPart create(PackageRelationship rel, PackagePart part){ | |||
return new POIXMLDocumentPart(part, rel); | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
/* ==================================================================== | |||
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; | |||
/** | |||
* Represents a descriptor of a OOXML relation. | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class POIXMLRelation { | |||
protected String _type; | |||
protected String _relation; | |||
protected String _defaultName; | |||
/** | |||
* Instantiates a POIXMLRelation. | |||
*/ | |||
protected POIXMLRelation(String type, String rel, String defaultName) { | |||
_type = type; | |||
_relation = rel; | |||
_defaultName = defaultName; | |||
} | |||
public String getContentType() { return _type; } | |||
public String getRelation() { return _relation; } | |||
public String getDefaultFileName() { return _defaultName; } | |||
/** | |||
* Returns the filename for the nth one of these, | |||
* eg /xl/comments4.xml | |||
*/ | |||
public String getFileName(int index) { | |||
if(_defaultName.indexOf("#") == -1) { | |||
// Generic filename in all cases | |||
return getDefaultFileName(); | |||
} | |||
return _defaultName.replace("#", Integer.toString(index)); | |||
} | |||
} |
@@ -0,0 +1,150 @@ | |||
/* ==================================================================== | |||
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.util; | |||
import org.openxml4j.opc.*; | |||
import org.openxml4j.opc.Package; | |||
import org.openxml4j.opc.internal.PackagePropertiesPart; | |||
import org.openxml4j.opc.internal.marshallers.PackagePropertiesMarshaller; | |||
import org.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.poi.util.IOUtils; | |||
import java.io.*; | |||
import java.util.ArrayList; | |||
import java.lang.reflect.Method; | |||
/** | |||
* Provides handy methods to work with OOXML packages | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class PackageHelper { | |||
/** | |||
* Clone the specified package. | |||
* | |||
* @param pkg the package to clone | |||
* @return the cloned package | |||
*/ | |||
public static Package clone(Package pkg) throws OpenXML4JException, IOException { | |||
return clone(pkg, createTempFile()); | |||
} | |||
/** | |||
* Clone the specified package. | |||
* | |||
* @param pkg the package to clone | |||
* @param file the destination file | |||
* @return the cloned package | |||
*/ | |||
public static Package clone(Package pkg, File file) throws OpenXML4JException, IOException { | |||
String path = file.getAbsolutePath(); | |||
Package dest = Package.create(path); | |||
PackageRelationshipCollection rels = pkg.getRelationships(); | |||
for (PackageRelationship rel : rels) { | |||
PackagePart part = pkg.getPart(rel); | |||
PackagePart part_tgt; | |||
if (rel.getRelationshipType().equals(PackageRelationshipTypes.CORE_PROPERTIES)) { | |||
copyProperties(pkg.getPackageProperties(), dest.getPackageProperties()); | |||
continue; | |||
} else { | |||
dest.addRelationship(part.getPartName(), rel.getTargetMode(), rel.getRelationshipType()); | |||
part_tgt = dest.createPart(part.getPartName(), part.getContentType()); | |||
} | |||
OutputStream out = part_tgt.getOutputStream(); | |||
IOUtils.copy(part.getInputStream(), out); | |||
out.close(); | |||
if(part.hasRelationships()) { | |||
copy(pkg, part, dest, part_tgt); | |||
} | |||
} | |||
dest.close(); | |||
//the temp file will be deleted when JVM terminates | |||
new File(path).deleteOnExit(); | |||
return Package.open(path); | |||
} | |||
/** | |||
* | |||
* @return | |||
* @throws IOException | |||
*/ | |||
public static File createTempFile() throws IOException { | |||
File file = File.createTempFile("poi-ooxml-", ".tmp"); | |||
//there is no way to pass an existing file to Package.create(file), | |||
//delete first, the file will be re-created in Packe.create(file) | |||
file.delete(); | |||
file.deleteOnExit(); | |||
return file; | |||
} | |||
/** | |||
* Recursively copy package parts to the destination package | |||
*/ | |||
private static void copy(Package pkg, PackagePart part, Package tgt, PackagePart part_tgt) throws OpenXML4JException, IOException { | |||
PackageRelationshipCollection rels = part.getRelationships(); | |||
if(rels != null) for (PackageRelationship rel : rels) { | |||
PackagePart p; | |||
if(rel.getTargetMode() == TargetMode.EXTERNAL){ | |||
part_tgt.addExternalRelationship(rel.getTargetURI().toString(), rel.getRelationshipType(), rel.getId()); | |||
//external relations don't have associated package parts | |||
continue; | |||
} else { | |||
PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); | |||
p = pkg.getPart(relName); | |||
} | |||
part_tgt.addRelationship(p.getPartName(), rel.getTargetMode(), rel.getRelationshipType(), rel.getId()); | |||
PackagePart dest; | |||
if(!tgt.containPart(p.getPartName())){ | |||
dest = tgt.createPart(p.getPartName(), p.getContentType()); | |||
OutputStream out = dest.getOutputStream(); | |||
IOUtils.copy(p.getInputStream(), out); | |||
out.close(); | |||
copy(pkg, p, tgt, dest); | |||
} | |||
} | |||
} | |||
/** | |||
* Copy core package properties | |||
* | |||
* @param src source properties | |||
* @param tgt target properties | |||
*/ | |||
private static void copyProperties(PackageProperties src, PackageProperties tgt){ | |||
tgt.setCategoryProperty(src.getCategoryProperty().getValue()); | |||
tgt.setContentStatusProperty(src.getContentStatusProperty().getValue()); | |||
tgt.setContentTypeProperty(src.getContentTypeProperty().getValue()); | |||
tgt.setCreatorProperty(src.getCreatorProperty().getValue()); | |||
tgt.setDescriptionProperty(src.getDescriptionProperty().getValue()); | |||
tgt.setIdentifierProperty(src.getIdentifierProperty().getValue()); | |||
tgt.setKeywordsProperty(src.getKeywordsProperty().getValue()); | |||
tgt.setLanguageProperty(src.getLanguageProperty().getValue()); | |||
tgt.setRevisionProperty(src.getRevisionProperty().getValue()); | |||
tgt.setSubjectProperty(src.getSubjectProperty().getValue()); | |||
tgt.setTitleProperty(src.getTitleProperty().getValue()); | |||
tgt.setVersionProperty(src.getVersionProperty().getValue()); | |||
} | |||
} |
@@ -21,10 +21,7 @@ import org.w3c.dom.Document; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import java.io.FileOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.*; | |||
import java.util.zip.ZipFile; | |||
import java.util.zip.ZipEntry; | |||
import java.util.Enumeration; | |||
@@ -70,24 +67,32 @@ public class XSSFDump { | |||
FileOutputStream out = new FileOutputStream(f); | |||
if(entry.getName().endsWith(".xml") || entry.getName().endsWith(".vml") || entry.getName().endsWith(".rels")){ | |||
//pass the xml through the Xerces serializer to produce nicely formatted output | |||
Document doc = builder.parse(zip.getInputStream(entry)); | |||
OutputFormat format = new OutputFormat( doc ); | |||
format.setIndenting(true); | |||
XMLSerializer serial = new XMLSerializer( out, format ); | |||
serial.asDOMSerializer(); | |||
serial.serialize( doc.getDocumentElement() ); | |||
try { | |||
//pass the xml through the Xerces serializer to produce nicely formatted output | |||
Document doc = builder.parse(zip.getInputStream(entry)); | |||
OutputFormat format = new OutputFormat( doc ); | |||
format.setIndenting(true); | |||
XMLSerializer serial = new XMLSerializer( out, format ); | |||
serial.asDOMSerializer(); | |||
serial.serialize( doc.getDocumentElement() ); | |||
} catch (Exception e){ | |||
System.err.println("Failed to parse " + entry.getName() + ", dumping raw content"); | |||
dump(zip.getInputStream(entry), out); | |||
} | |||
} else { | |||
int pos; | |||
byte[] chunk = new byte[2048]; | |||
InputStream is = zip.getInputStream(entry); | |||
while((pos = is.read(chunk)) > 0) out.write(chunk, 0, pos); | |||
dump(zip.getInputStream(entry), out); | |||
} | |||
out.close(); | |||
} | |||
} | |||
protected static void dump(InputStream is, OutputStream out) throws IOException{ | |||
int pos; | |||
byte[] chunk = new byte[2048]; | |||
while((pos = is.read(chunk)) > 0) out.write(chunk, 0, pos); | |||
} | |||
} |
@@ -31,6 +31,7 @@ public class XSSFSave { | |||
for (int i = 0; i < args.length; i++) { | |||
XSSFWorkbook wb = new XSSFWorkbook(args[i]); | |||
System.out.println("wb.getNumberOfSheets(): " + wb.getNumberOfSheets()); | |||
int sep = args[i].lastIndexOf('.'); | |||
String outfile = args[i].substring(0, sep) + "-save.xlsx"; | |||
FileOutputStream out = new FileOutputStream(outfile); |
@@ -23,6 +23,7 @@ import java.io.OutputStream; | |||
import org.apache.poi.ss.usermodel.CommentsSource; | |||
import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.xssf.usermodel.XSSFComment; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAuthors; | |||
@@ -30,103 +31,121 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackageRelationship; | |||
public class CommentsTable implements CommentsSource, XSSFModel { | |||
private CTComments comments; | |||
public CommentsTable(InputStream is) throws IOException { | |||
readFrom(is); | |||
} | |||
public CommentsTable() { | |||
comments = CTComments.Factory.newInstance(); | |||
} | |||
/** | |||
* For unit testing only! | |||
*/ | |||
public CommentsTable(CTComments comments) { | |||
this.comments = comments; | |||
} | |||
public void readFrom(InputStream is) throws IOException { | |||
try { | |||
CommentsDocument doc = CommentsDocument.Factory.parse(is); | |||
comments = doc.getComments(); | |||
public class CommentsTable extends POIXMLDocumentPart implements CommentsSource, XSSFModel { | |||
private CTComments comments; | |||
public CommentsTable(InputStream is) throws IOException { | |||
super(null, null); | |||
readFrom(is); | |||
} | |||
public CommentsTable() { | |||
super(null, null); | |||
comments = CTComments.Factory.newInstance(); | |||
} | |||
/** | |||
* For unit testing only! | |||
*/ | |||
public CommentsTable(CTComments comments) { | |||
super(null, null); | |||
this.comments = comments; | |||
} | |||
public CommentsTable(PackagePart part, PackageRelationship rel) throws IOException { | |||
super(part, rel); | |||
readFrom(part.getInputStream()); | |||
} | |||
public void readFrom(InputStream is) throws IOException { | |||
try { | |||
CommentsDocument doc = CommentsDocument.Factory.parse(is); | |||
comments = doc.getComments(); | |||
} catch (XmlException e) { | |||
throw new IOException(e.getLocalizedMessage()); | |||
} | |||
} | |||
public void writeTo(OutputStream out) throws IOException { | |||
} | |||
public void writeTo(OutputStream out) throws IOException { | |||
XmlOptions options = new XmlOptions(); | |||
options.setSaveOuter(); | |||
options.setUseDefaultNamespace(); | |||
// Requests use of whitespace for easier reading | |||
//options.setSavePrettyPrint(); | |||
CommentsDocument doc = CommentsDocument.Factory.newInstance(options); | |||
doc.setComments(comments); | |||
doc.save(out, options); | |||
} | |||
public int getNumberOfComments() { | |||
return comments.getCommentList().sizeOfCommentArray(); | |||
} | |||
public int getNumberOfAuthors() { | |||
return getCommentsAuthors().sizeOfAuthorArray(); | |||
} | |||
public String getAuthor(long authorId) { | |||
return getCommentsAuthors().getAuthorArray((int)authorId); | |||
} | |||
public int findAuthor(String author) { | |||
for (int i = 0 ; i < getCommentsAuthors().sizeOfAuthorArray() ; i++) { | |||
if (getCommentsAuthors().getAuthorArray(i).equals(author)) { | |||
return i; | |||
} | |||
} | |||
return addNewAuthor(author); | |||
} | |||
public XSSFComment findCellComment(int row, int column) { | |||
return findCellComment( | |||
(new CellReference(row, column)).formatAsString() ); | |||
} | |||
public XSSFComment findCellComment(String cellRef) { | |||
for (CTComment comment : getCommentsList().getCommentArray()) { | |||
if (cellRef.equals(comment.getRef())) { | |||
return new XSSFComment(this, comment); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Generates a new XSSFComment, associated with the | |||
* current comments list. | |||
*/ | |||
public XSSFComment addComment() { | |||
return new XSSFComment(this, getCommentsList().addNewComment()); | |||
} | |||
private CTCommentList getCommentsList() { | |||
if (comments.getCommentList() == null) { | |||
comments.addNewCommentList(); | |||
} | |||
return comments.getCommentList(); | |||
} | |||
private CTAuthors getCommentsAuthors() { | |||
if (comments.getAuthors() == null) { | |||
comments.addNewAuthors(); | |||
} | |||
return comments.getAuthors(); | |||
} | |||
private int addNewAuthor(String author) { | |||
int index = getCommentsAuthors().sizeOfAuthorArray(); | |||
getCommentsAuthors().insertAuthor(index, author); | |||
return index; | |||
} | |||
} | |||
@Override | |||
protected void commit() throws IOException { | |||
PackagePart part = getPackagePart(); | |||
OutputStream out = part.getOutputStream(); | |||
writeTo(out); | |||
out.close(); | |||
} | |||
public int getNumberOfComments() { | |||
return comments.getCommentList().sizeOfCommentArray(); | |||
} | |||
public int getNumberOfAuthors() { | |||
return getCommentsAuthors().sizeOfAuthorArray(); | |||
} | |||
public String getAuthor(long authorId) { | |||
return getCommentsAuthors().getAuthorArray((int)authorId); | |||
} | |||
public int findAuthor(String author) { | |||
for (int i = 0 ; i < getCommentsAuthors().sizeOfAuthorArray() ; i++) { | |||
if (getCommentsAuthors().getAuthorArray(i).equals(author)) { | |||
return i; | |||
} | |||
} | |||
return addNewAuthor(author); | |||
} | |||
public XSSFComment findCellComment(int row, int column) { | |||
return findCellComment( | |||
(new CellReference(row, column)).formatAsString() ); | |||
} | |||
public XSSFComment findCellComment(String cellRef) { | |||
for (CTComment comment : getCommentsList().getCommentArray()) { | |||
if (cellRef.equals(comment.getRef())) { | |||
return new XSSFComment(this, comment); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Generates a new XSSFComment, associated with the | |||
* current comments list. | |||
*/ | |||
public XSSFComment addComment() { | |||
return new XSSFComment(this, getCommentsList().addNewComment()); | |||
} | |||
private CTCommentList getCommentsList() { | |||
if (comments.getCommentList() == null) { | |||
comments.addNewCommentList(); | |||
} | |||
return comments.getCommentList(); | |||
} | |||
private CTAuthors getCommentsAuthors() { | |||
if (comments.getAuthors() == null) { | |||
comments.addNewAuthors(); | |||
} | |||
return comments.getAuthors(); | |||
} | |||
private int addNewAuthor(String author) { | |||
int index = getCommentsAuthors().sizeOfAuthorArray(); | |||
getCommentsAuthors().insertAuthor(index, author); | |||
return index; | |||
} | |||
} |
@@ -27,9 +27,13 @@ import java.util.Map; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSst; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackageRelationship; | |||
/** | |||
@@ -56,7 +60,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument; | |||
* @author Nick Birch | |||
* @author Yegor Kozlov | |||
*/ | |||
public class SharedStringsTable implements SharedStringSource, XSSFModel { | |||
public class SharedStringsTable extends POIXMLDocumentPart implements XSSFModel, SharedStringSource { | |||
/** | |||
* Array of individual string items in the Shared String table. | |||
@@ -89,13 +93,17 @@ public class SharedStringsTable implements SharedStringSource, XSSFModel { | |||
* @throws IOException if an error occurs while reading. | |||
*/ | |||
public SharedStringsTable(InputStream is) throws IOException { | |||
super(null, null); | |||
readFrom(is); | |||
} | |||
/** | |||
* Create a new, empty SharedStringsTable | |||
*/ | |||
public SharedStringsTable() { | |||
count = uniqueCount = 0; | |||
super(null, null); | |||
} | |||
public SharedStringsTable(PackagePart part, PackageRelationship rel) throws IOException { | |||
super(part, rel); | |||
readFrom(part.getInputStream()); | |||
} | |||
/** | |||
@@ -204,4 +212,12 @@ public class SharedStringsTable implements SharedStringSource, XSSFModel { | |||
sst.setSiArray(ctr); | |||
doc.save(out, options); | |||
} | |||
@Override | |||
protected void commit() throws IOException { | |||
PackagePart part = getPackagePart(); | |||
OutputStream out = part.getOutputStream(); | |||
writeTo(out); | |||
out.close(); | |||
} | |||
} |
@@ -36,6 +36,7 @@ import org.apache.poi.xssf.usermodel.XSSFCellStyle; | |||
import org.apache.poi.xssf.usermodel.XSSFFont; | |||
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder; | |||
import org.apache.poi.xssf.usermodel.extensions.XSSFCellFill; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBorder; | |||
@@ -54,13 +55,16 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackageRelationship; | |||
/** | |||
* Table of styles shared across all sheets in a workbook. | |||
* | |||
* @author ugo | |||
*/ | |||
public class StylesTable implements StylesSource, XSSFModel { | |||
public class StylesTable extends POIXMLDocumentPart implements StylesSource, XSSFModel { | |||
private final Hashtable<Long,String> numberFormats = new Hashtable<Long,String>(); | |||
private final List<CTFont> fonts = new ArrayList<CTFont>(); | |||
private final List<CTFill> fills = new LinkedList<CTFill>(); | |||
@@ -85,18 +89,25 @@ public class StylesTable implements StylesSource, XSSFModel { | |||
* @throws IOException if an error occurs while reading. | |||
*/ | |||
public StylesTable(InputStream is) throws IOException { | |||
super(null, null); | |||
readFrom(is); | |||
} | |||
/** | |||
* Create a new, empty StylesTable | |||
*/ | |||
public StylesTable() { | |||
super(null, null); | |||
doc = StyleSheetDocument.Factory.newInstance(); | |||
doc.addNewStyleSheet(); | |||
// Initialization required in order to make the document readable by MSExcel | |||
initialize(); | |||
} | |||
public StylesTable(PackagePart part, PackageRelationship rel) throws IOException { | |||
super(part, rel); | |||
readFrom(part.getInputStream()); | |||
} | |||
/** | |||
* Read this shared styles table from an XML file. | |||
* | |||
@@ -365,6 +376,14 @@ public class StylesTable implements StylesSource, XSSFModel { | |||
doc.save(out, options); | |||
} | |||
@Override | |||
protected void commit() throws IOException { | |||
PackagePart part = getPackagePart(); | |||
OutputStream out = part.getOutputStream(); | |||
writeTo(out); | |||
out.close(); | |||
} | |||
private long putBorder(XSSFCellBorder border, List<CTBorder> borders) { | |||
return border.putBorder((LinkedList<CTBorder>) borders); // TODO - use List instead of LinkedList | |||
} |
@@ -23,26 +23,22 @@ import org.apache.poi.ss.usermodel.RichTextString; | |||
public class XSSFCreationHelper implements CreationHelper { | |||
private XSSFWorkbook workbook; | |||
private XSSFDataFormat dataFormat; | |||
XSSFCreationHelper(XSSFWorkbook wb) { | |||
workbook = wb; | |||
// Create the things we only ever need one of | |||
dataFormat = new XSSFDataFormat(workbook.getStylesSource()); | |||
} | |||
/** | |||
* Creates a new XSSFRichTextString for you. | |||
*/ | |||
public RichTextString createRichTextString(String text) { | |||
public XSSFRichTextString createRichTextString(String text) { | |||
return new XSSFRichTextString(text); | |||
} | |||
public DataFormat createDataFormat() { | |||
return dataFormat; | |||
public XSSFDataFormat createDataFormat() { | |||
return workbook.createDataFormat(); | |||
} | |||
public Hyperlink createHyperlink(int type) { | |||
public XSSFHyperlink createHyperlink(int type) { | |||
return new XSSFHyperlink(type); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
/* ==================================================================== | |||
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.usermodel; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.POIXMLFactory; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.poi.xssf.model.SharedStringsTable; | |||
import org.apache.poi.xssf.model.StylesTable; | |||
import org.apache.poi.xssf.model.CommentsTable; | |||
import org.openxml4j.opc.PackageRelationship; | |||
import org.openxml4j.opc.PackagePart; | |||
import java.util.Map; | |||
import java.util.HashMap; | |||
import java.lang.reflect.Constructor; | |||
/** | |||
* Instantiates sub-classes of POIXMLDocumentPart depending on their relationship type | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class XSSFFactory extends POIXMLFactory { | |||
protected static Map<String, Class> parts = new HashMap<String, Class>(); | |||
static { | |||
parts.put(XSSFRelation.WORKSHEET.getRelation(), XSSFSheet.class); | |||
parts.put(XSSFRelation.SHARED_STRINGS.getRelation(), SharedStringsTable.class); | |||
parts.put(XSSFRelation.STYLES.getRelation(), StylesTable.class); | |||
parts.put(XSSFRelation.SHEET_COMMENTS.getRelation(), CommentsTable.class); | |||
} | |||
public POIXMLDocumentPart create(PackageRelationship rel, PackagePart p){ | |||
Class cls = parts.get(rel.getRelationshipType()); | |||
if(cls == null) return super.create(rel, p); | |||
try { | |||
Constructor<? extends POIXMLDocumentPart> constructor = cls.getConstructor(PackagePart.class, PackageRelationship.class); | |||
return constructor.newInstance(p, rel); | |||
} catch (Exception e){ | |||
throw new POIXMLException(e); | |||
} | |||
} | |||
} |
@@ -26,6 +26,7 @@ import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.POIXMLRelation; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.xssf.model.BinaryPart; | |||
@@ -49,7 +50,7 @@ import org.openxml4j.opc.TargetMode; | |||
/** | |||
* | |||
*/ | |||
public final class XSSFRelation<W extends XSSFModel> { | |||
public final class XSSFRelation<W extends XSSFModel> extends POIXMLRelation { | |||
public static final XSSFRelation WORKBOOK = new XSSFRelation( | |||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", | |||
@@ -156,16 +157,12 @@ public final class XSSFRelation<W extends XSSFModel> { | |||
return new XSSFRelation<R>(type, rel, defaultName, cls); | |||
} | |||
private String _type; | |||
private String _relation; | |||
private String _defaultName; | |||
private Constructor<W> _constructor; | |||
private final boolean _constructorTakesTwoArgs; | |||
private XSSFRelation(String type, String rel, String defaultName, Class<W> cls) { | |||
_type = type; | |||
_relation = rel; | |||
_defaultName = defaultName; | |||
super(type, rel, defaultName); | |||
if (cls == null) { | |||
_constructor = null; | |||
_constructorTakesTwoArgs = false; | |||
@@ -189,10 +186,7 @@ public final class XSSFRelation<W extends XSSFModel> { | |||
_constructorTakesTwoArgs = twoArg; | |||
} | |||
} | |||
public String getContentType() { return _type; } | |||
public String getRelation() { return _relation; } | |||
public String getDefaultFileName() { return _defaultName; } | |||
/** | |||
* Does one of these exist for the given core | |||
* package part? |
@@ -75,10 +75,22 @@ public class XSSFCellBorder { | |||
private CTBorderPr getBorder(BorderSide side) { | |||
switch (side) { | |||
case TOP: return border.getTop(); | |||
case RIGHT: return border.getRight(); | |||
case BOTTOM: return border.getBottom(); | |||
case LEFT: return border.getLeft(); | |||
case TOP: { | |||
CTBorderPr borderPr = border.isSetTop() ? border.getTop() : border.addNewTop(); | |||
return borderPr; | |||
} | |||
case RIGHT: { | |||
CTBorderPr borderPr = border.isSetRight() ? border.getRight() : border.addNewRight(); | |||
return borderPr; | |||
} | |||
case BOTTOM:{ | |||
CTBorderPr borderPr = border.isSetBottom() ? border.getBottom() : border.addNewBottom(); | |||
return borderPr; | |||
} | |||
case LEFT:{ | |||
CTBorderPr borderPr = border.isSetLeft() ? border.getLeft() : border.addNewLeft(); | |||
return borderPr; | |||
} | |||
default: throw new IllegalArgumentException("No suitable side specified for the border"); | |||
} | |||
} |
@@ -17,10 +17,7 @@ | |||
package org.apache.poi.xssf; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.*; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
@@ -47,15 +44,20 @@ public class XSSFTestDataSamples { | |||
} | |||
} | |||
public static <R extends Workbook> R writeOutAndReadBack(R wb) { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); | |||
Workbook result; | |||
try { | |||
wb.write(baos); | |||
InputStream is = new ByteArrayInputStream(baos.toByteArray()); | |||
if (wb instanceof HSSFWorkbook) { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); | |||
wb.write(baos); | |||
InputStream is = new ByteArrayInputStream(baos.toByteArray()); | |||
result = new HSSFWorkbook(is); | |||
} else if (wb instanceof XSSFWorkbook) { | |||
Package pkg = Package.open(is); | |||
File tmp = File.createTempFile("poi-ooxml-", ".xlsx"); | |||
tmp.deleteOnExit(); | |||
FileOutputStream out = new FileOutputStream(tmp); | |||
wb.write(out); | |||
out.close(); | |||
Package pkg = Package.open(tmp.getAbsolutePath()); | |||
result = new XSSFWorkbook(pkg); | |||
} else { | |||
throw new RuntimeException("Unexpected workbook type (" |
@@ -30,6 +30,7 @@ import org.apache.poi.xssf.usermodel.XSSFComment; | |||
import org.apache.poi.xssf.usermodel.XSSFRichTextString; | |||
import org.apache.poi.xssf.usermodel.XSSFSheet; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
import org.openxml4j.opc.Package; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackagingURIHelper; | |||
@@ -212,12 +213,8 @@ public class TestCommentsTable extends TestCase { | |||
// Save, and re-load the file | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
workbook.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
workbook = new XSSFWorkbook(Package.open(bais)); | |||
workbook = XSSFTestDataSamples.writeOutAndReadBack(workbook); | |||
// Check we still have comments where we should do | |||
sheet1 = workbook.getSheetAt(0); | |||
sheet2 = (XSSFSheet)workbook.getSheetAt(1); | |||
@@ -259,12 +256,8 @@ public class TestCommentsTable extends TestCase { | |||
sheet1.getRow(12).getCell(2).getCellComment().getAuthor()); | |||
// Save, and re-load the file | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
workbook.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
workbook = new XSSFWorkbook(Package.open(bais)); | |||
workbook = XSSFTestDataSamples.writeOutAndReadBack(workbook); | |||
// Check we still have comments where we should do | |||
sheet1 = workbook.getSheetAt(0); | |||
assertNotNull(sheet1.getRow(4).getCell(2).getCellComment()); |
@@ -17,120 +17,107 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import junit.framework.TestCase; | |||
import org.openxml4j.opc.Package; | |||
import org.openxml4j.opc.PackagePart; | |||
import org.openxml4j.opc.PackagingURIHelper; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
public class TestXSSFBugs extends TestCase { | |||
private String getFilePath(String file) { | |||
File xml = new File( | |||
System.getProperty("HSSF.testdata.path") + | |||
File.separator + file | |||
); | |||
assertTrue(xml.exists()); | |||
return xml.toString(); | |||
} | |||
private Package saveAndOpen(XSSFWorkbook wb) throws Exception { | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
wb.write(baos); | |||
ByteArrayInputStream inp = new ByteArrayInputStream( | |||
baos.toByteArray() | |||
); | |||
Package pkg = Package.open(inp); | |||
return pkg; | |||
} | |||
/** | |||
* Named ranges had the right reference, but | |||
* the wrong sheet name | |||
*/ | |||
public void test45430() throws Exception { | |||
XSSFWorkbook wb = new XSSFWorkbook(getFilePath("45430.xlsx")); | |||
assertFalse(wb.isMacroEnabled()); | |||
assertEquals(3, wb.getNumberOfNames()); | |||
assertEquals(0, wb.getNameAt(0).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(0).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetA!$A$1", wb.getNameAt(0).getReference()); | |||
assertEquals("SheetA", wb.getNameAt(0).getSheetName()); | |||
assertEquals(0, wb.getNameAt(1).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(1).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetB!$A$1", wb.getNameAt(1).getReference()); | |||
assertEquals("SheetB", wb.getNameAt(1).getSheetName()); | |||
assertEquals(0, wb.getNameAt(2).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(2).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetC!$A$1", wb.getNameAt(2).getReference()); | |||
assertEquals("SheetC", wb.getNameAt(2).getSheetName()); | |||
// Save and re-load, still there | |||
Package nPkg = saveAndOpen(wb); | |||
XSSFWorkbook nwb = new XSSFWorkbook(nPkg); | |||
assertEquals(3, nwb.getNumberOfNames()); | |||
assertEquals("SheetA!$A$1", nwb.getNameAt(0).getReference()); | |||
} | |||
/** | |||
* We should carry vba macros over after save | |||
*/ | |||
public void test45431() throws Exception { | |||
Package pkg = Package.open(getFilePath("45431.xlsm")); | |||
XSSFWorkbook wb = new XSSFWorkbook(pkg); | |||
assertTrue(wb.isMacroEnabled()); | |||
// Check the various macro related bits can be found | |||
PackagePart vba = pkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
// And the drawing bit | |||
PackagePart drw = pkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// Save and re-open, both still there | |||
Package nPkg = saveAndOpen(wb); | |||
XSSFWorkbook nwb = new XSSFWorkbook(nPkg); | |||
assertTrue(nwb.isMacroEnabled()); | |||
vba = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
drw = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// And again, just to be sure | |||
nPkg = saveAndOpen(nwb); | |||
nwb = new XSSFWorkbook(nPkg); | |||
assertTrue(nwb.isMacroEnabled()); | |||
vba = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
drw = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// For testing with excel | |||
private String getFilePath(String file) { | |||
File xml = new File( | |||
System.getProperty("HSSF.testdata.path") + | |||
File.separator + file | |||
); | |||
assertTrue(xml.exists()); | |||
return xml.toString(); | |||
} | |||
/** | |||
* Named ranges had the right reference, but | |||
* the wrong sheet name | |||
*/ | |||
public void test45430() throws Exception { | |||
XSSFWorkbook wb = new XSSFWorkbook(getFilePath("45430.xlsx")); | |||
assertFalse(wb.isMacroEnabled()); | |||
assertEquals(3, wb.getNumberOfNames()); | |||
assertEquals(0, wb.getNameAt(0).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(0).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetA!$A$1", wb.getNameAt(0).getReference()); | |||
assertEquals("SheetA", wb.getNameAt(0).getSheetName()); | |||
assertEquals(0, wb.getNameAt(1).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(1).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetB!$A$1", wb.getNameAt(1).getReference()); | |||
assertEquals("SheetB", wb.getNameAt(1).getSheetName()); | |||
assertEquals(0, wb.getNameAt(2).getCTName().getLocalSheetId()); | |||
assertFalse(wb.getNameAt(2).getCTName().isSetLocalSheetId()); | |||
assertEquals("SheetC!$A$1", wb.getNameAt(2).getReference()); | |||
assertEquals("SheetC", wb.getNameAt(2).getSheetName()); | |||
// Save and re-load, still there | |||
XSSFWorkbook nwb = XSSFTestDataSamples.writeOutAndReadBack(wb); | |||
assertEquals(3, nwb.getNumberOfNames()); | |||
assertEquals("SheetA!$A$1", nwb.getNameAt(0).getReference()); | |||
} | |||
/** | |||
* We should carry vba macros over after save | |||
*/ | |||
public void test45431() throws Exception { | |||
Package pkg = Package.open(getFilePath("45431.xlsm")); | |||
XSSFWorkbook wb = new XSSFWorkbook(pkg); | |||
assertTrue(wb.isMacroEnabled()); | |||
// Check the various macro related bits can be found | |||
PackagePart vba = pkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
// And the drawing bit | |||
PackagePart drw = pkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// Save and re-open, both still there | |||
XSSFWorkbook nwb = XSSFTestDataSamples.writeOutAndReadBack(wb); | |||
Package nPkg = nwb.getPackage(); | |||
assertTrue(nwb.isMacroEnabled()); | |||
vba = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
drw = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// And again, just to be sure | |||
nwb = XSSFTestDataSamples.writeOutAndReadBack(nwb); | |||
nPkg = nwb.getPackage(); | |||
assertTrue(nwb.isMacroEnabled()); | |||
vba = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/vbaProject.bin") | |||
); | |||
assertNotNull(vba); | |||
drw = nPkg.getPart( | |||
PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing1.vml") | |||
); | |||
assertNotNull(drw); | |||
// For testing with excel | |||
// FileOutputStream fout = new FileOutputStream("/tmp/foo.xlsm"); | |||
// nwb.write(fout); | |||
// fout.close(); | |||
} | |||
} | |||
} |
@@ -17,9 +17,6 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import org.apache.poi.hssf.usermodel.HSSFRichTextString; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.Comment; | |||
@@ -27,6 +24,7 @@ import org.apache.poi.ss.usermodel.RichTextString; | |||
import org.apache.poi.ss.usermodel.Row; | |||
import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.xssf.model.CommentsTable; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
import org.openxml4j.opc.Package; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTAuthors; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; | |||
@@ -135,7 +133,7 @@ public class TestXSSFComment extends TestCase { | |||
*/ | |||
public void testCreateSave() throws Exception { | |||
XSSFWorkbook wb = new XSSFWorkbook(); | |||
XSSFSheet s1 = (XSSFSheet)wb.createSheet(); | |||
XSSFSheet s1 = wb.createSheet(); | |||
Row r1 = s1.createRow(0); | |||
Cell r1c1 = r1.createCell(0); | |||
r1c1.setCellValue(2.2); | |||
@@ -150,12 +148,8 @@ public class TestXSSFComment extends TestCase { | |||
assertEquals(1, s1.getNumberOfComments()); | |||
// Save and re-load | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
wb.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
wb = new XSSFWorkbook(Package.open(bais)); | |||
s1 = (XSSFSheet)wb.getSheetAt(0); | |||
wb = XSSFTestDataSamples.writeOutAndReadBack(wb); | |||
s1 = wb.getSheetAt(0); | |||
assertEquals(1, s1.getNumberOfComments()); | |||
assertNotNull(s1.getRow(0).getCell(0).getCellComment()); | |||
@@ -171,12 +165,9 @@ public class TestXSSFComment extends TestCase { | |||
assertEquals(2, s1.getNumberOfComments()); | |||
// Save and re-load | |||
baos = new ByteArrayOutputStream(); | |||
wb.write(baos); | |||
bais = new ByteArrayInputStream(baos.toByteArray()); | |||
wb = new XSSFWorkbook(Package.open(bais)); | |||
s1 = (XSSFSheet)wb.getSheetAt(0); | |||
wb = XSSFTestDataSamples.writeOutAndReadBack(wb); | |||
s1 = wb.getSheetAt(0); | |||
assertEquals(2, s1.getNumberOfComments()); | |||
assertNotNull(s1.getCellComment(0, 0)); |
@@ -17,8 +17,6 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import junit.framework.TestCase; | |||
@@ -27,6 +25,7 @@ import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.CreationHelper; | |||
import org.apache.poi.ss.usermodel.Hyperlink; | |||
import org.apache.poi.ss.usermodel.Row; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
import org.openxml4j.opc.Package; | |||
public class TestXSSFHyperlink extends TestCase { | |||
@@ -79,12 +78,9 @@ public class TestXSSFHyperlink extends TestCase { | |||
// Write out, and check | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
workbook.write(baos); | |||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||
// Load up again, check all links still there | |||
XSSFWorkbook wb2 = new XSSFWorkbook(Package.open(bais)); | |||
XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(workbook); | |||
assertEquals(3, wb2.getNumberOfSheets()); | |||
assertNotNull(wb2.getSheetAt(0)); | |||
assertNotNull(wb2.getSheetAt(1)); | |||
@@ -119,18 +115,14 @@ public class TestXSSFHyperlink extends TestCase { | |||
// Save and re-load once more | |||
baos = new ByteArrayOutputStream(); | |||
wb2.write(baos); | |||
bais = new ByteArrayInputStream(baos.toByteArray()); | |||
XSSFWorkbook wb3 = new XSSFWorkbook(Package.open(bais)); | |||
XSSFWorkbook wb3 = XSSFTestDataSamples.writeOutAndReadBack(wb2); | |||
assertEquals(3, wb3.getNumberOfSheets()); | |||
assertNotNull(wb3.getSheetAt(0)); | |||
assertNotNull(wb3.getSheetAt(1)); | |||
assertNotNull(wb3.getSheetAt(2)); | |||
sheet = (XSSFSheet)wb3.getSheetAt(0); | |||
sheet = wb3.getSheetAt(0); | |||
assertEquals(5, sheet.getNumHyperlinks()); | |||
doTestHyperlinkContents(sheet); |
@@ -17,8 +17,6 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.util.Iterator; | |||
import junit.framework.TestCase; | |||
@@ -30,11 +28,7 @@ import org.apache.poi.ss.util.Region; | |||
import org.apache.poi.xssf.model.CommentsTable; | |||
import org.apache.poi.xssf.model.StylesTable; | |||
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper; | |||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.hssf.usermodel.HSSFRow; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.openxml4j.opc.Package; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; | |||
@@ -349,11 +343,7 @@ public class TestXSSFSheet extends TestCase { | |||
// Save and reload | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
workbook.write(baos); | |||
XSSFWorkbook wb = new XSSFWorkbook(Package.open( | |||
new ByteArrayInputStream(baos.toByteArray()) | |||
)); | |||
XSSFWorkbook wb = XSSFTestDataSamples.writeOutAndReadBack(workbook); | |||
hdr = (XSSFOddHeader)wb.getSheetAt(0).getHeader(); | |||
ftr = (XSSFOddFooter)wb.getSheetAt(0).getFooter(); |
@@ -441,7 +441,7 @@ public final class TestXSSFWorkbook extends TestCase { | |||
// Now, an existing file with named ranges | |||
workbook = XSSFTestDataSamples.openSampleWorkbook("WithVariousData.xlsx"); | |||
assertEquals(2, workbook.getNumberOfNames()); | |||
assertEquals("Sheet1!$A$2:$A$7", workbook.getNameAt(0).getReference()); | |||
assertEquals("AllANumbers", workbook.getNameAt(0).getNameName()); |