You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by dn...@apache.org on 2015/07/13 11:05:17 UTC

svn commit: r1690593 - in /poi: site/src/documentation/content/xdocs/ trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/ trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/

Author: dnorth
Date: Mon Jul 13 09:05:17 2015
New Revision: 1690593

URL: http://svn.apache.org/r1690593
Log:
Patch from Chris Boyle to add basic support for .xlsm (macro-enabled) workbooks. The binary blob containing the VBA macros may be copied from one such workbook into another.

Fixes #58036

Added:
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVBAPart.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Mon Jul 13 09:05:17 2015
@@ -39,6 +39,7 @@
     </devs>
 
     <release version="3.13-beta1" date="2015-??-??">
+        <action dev="DN" type="add" fixes-bug="58036">Add basic support for VBA macro-enabled workbooks (xlsm)</action>
         <action dev="PD" type="fix" fixes-bug="57744">Fix parsing the email submission data when id contains a hyphen</action>
         <action dev="PD" type="fix" fixes-bug="57678">Better handle years in mail-messages between 1980 and 1999</action>
         <action dev="PD" type="fix" fixes-bug="54332">WMF extraction failing in Tika for older PowerPoint Files</action>

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java Mon Jul 13 09:05:17 2015
@@ -1551,4 +1551,31 @@ public abstract class OPCPackage impleme
         }
         return success;
     }
+
+    /**
+    * Add the specified part, and register its content type with the content
+    * type manager.
+    *
+    * @param part
+    *            The part to add.
+    */
+    public void registerPartAndContentType(PackagePart part) {
+        addPackagePart(part);
+        this.contentTypeManager.addContentType(part.getPartName(), part.getContentType());
+        this.isDirty = true;
+    }
+
+    /**
+     * Remove the specified part, and clear its content type from the content
+     * type manager.
+     *
+     * @param partName
+     *            The part name of the part to remove.
+     */
+    public void unregisterPartAndContentType(PackagePartName partName) {
+        removePart(partName);
+        this.contentTypeManager.removeContentType(partName);
+        this.isDirty = true;
+    }
+
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java Mon Jul 13 09:05:17 2015
@@ -601,11 +601,14 @@ public abstract class PackagePart implem
 	 */
 	public void setContentType(String contentType)
 			throws InvalidFormatException {
-		if (_container == null)
-			this._contentType = new ContentType(contentType);
-		else
-			throw new InvalidOperationException(
-					"You can't change the content type of a part.");
+		if (_container == null) {
+			_contentType = new ContentType(contentType);
+		}
+		else {
+		    _container.unregisterPartAndContentType(_partName);
+		    _contentType = new ContentType(contentType);
+		    _container.registerPartAndContentType(this);
+		}
 	}
 
 	public OPCPackage getPackage() {

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java Mon Jul 13 09:05:17 2015
@@ -281,7 +281,7 @@ public final class XSSFRelation extends
         "application/vnd.ms-office.vbaProject",
         "http://schemas.microsoft.com/office/2006/relationships/vbaProject",
         "/xl/vbaProject.bin",
-        null
+        XSSFVBAPart.class
     );
 
     public static final XSSFRelation ACTIVEX_CONTROLS = new XSSFRelation(

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVBAPart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVBAPart.java?rev=1690593&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVBAPart.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVBAPart.java Mon Jul 13 09:05:17 2015
@@ -0,0 +1,52 @@
+/* ====================================================================
+   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.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+
+public class XSSFVBAPart extends POIXMLDocumentPart {
+
+    /**
+     * Create a new XSSFVBAPart node
+     */
+    protected XSSFVBAPart() {
+        super();
+    }
+
+    /**
+     * Construct XSSFVBAPart from a package part
+     *
+     * @param part the package part holding the VBA data,
+     * @param rel  the package relationship holding this part
+     */
+    protected XSSFVBAPart(PackagePart part, PackageRelationship rel) {
+        super(part, rel);
+    }
+
+    /**
+     * Like *PictureData, VBA objects store the actual content in the part
+     * directly without keeping a copy like all others therefore we need to
+     * handle them differently.
+     */
+    protected void prepareForCommit() {
+        // do not clear the part here
+    }
+
+}

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java Mon Jul 13 09:05:17 2015
@@ -215,7 +215,15 @@ public class XSSFWorkbook extends POIXML
      * Create a new SpreadsheetML workbook.
      */
     public XSSFWorkbook() {
-        super(newPackage());
+        this(XSSFWorkbookType.XLSX);
+    }
+
+    /**
+     * Create a new SpreadsheetML workbook.
+     * @param workbookType The type of workbook to make (.xlsx or .xlsm).
+     */
+    public XSSFWorkbook(XSSFWorkbookType workbookType) {
+        super(newPackage(workbookType));
         onWorkbookCreate();
     }
 
@@ -429,7 +437,7 @@ public class XSSFWorkbook extends POIXML
     /**
      * Create a new SpreadsheetML package and setup the default minimal content
      */
-    protected static OPCPackage newPackage() {
+    protected static OPCPackage newPackage(XSSFWorkbookType workbookType) {
         try {
             OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
             // Main part
@@ -437,7 +445,7 @@ public class XSSFWorkbook extends POIXML
             // Create main part relationship
             pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
             // Create main document part
-            pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());
+            pkg.createPart(corePartName, workbookType.getContentType());
 
             pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);
 
@@ -2044,4 +2052,67 @@ public class XSSFWorkbook extends POIXML
     protected void setPivotTables(List<XSSFPivotTable> pivotTables) {
         this.pivotTables = pivotTables;
     }
+
+    public XSSFWorkbookType getWorkbookType() {
+        return isMacroEnabled() ? XSSFWorkbookType.XLSM : XSSFWorkbookType.XLSX;
+    }
+
+    /**
+     * Sets whether the workbook will be an .xlsx or .xlsm (macro-enabled) file.
+     */
+    public void setWorkbookType(XSSFWorkbookType type) {
+        try {
+            getPackagePart().setContentType(type.getContentType());
+        } catch (InvalidFormatException e) {
+            throw new POIXMLException(e);
+        }
+    }
+
+    /**
+     * Adds a vbaProject.bin file to the workbook.  This will change the workbook
+     * type if necessary.
+     *
+     * @throws IOException
+     */
+    public void setVBAProject(InputStream vbaProjectStream) throws IOException {
+        if (!isMacroEnabled()) {
+            setWorkbookType(XSSFWorkbookType.XLSM);
+        }
+
+        PackagePartName ppName;
+        try {
+            ppName = PackagingURIHelper.createPartName(XSSFRelation.VBA_MACROS.getDefaultFileName());
+        } catch (InvalidFormatException e) {
+            throw new POIXMLException(e);
+        }
+        OPCPackage opc = getPackage();
+        OutputStream outputStream;
+        if (!opc.containPart(ppName)) {
+            POIXMLDocumentPart relationship = createRelationship(XSSFRelation.VBA_MACROS, XSSFFactory.getInstance());
+            outputStream = relationship.getPackagePart().getOutputStream();
+        } else {
+            PackagePart part = opc.getPart(ppName);
+            outputStream = part.getOutputStream();
+        }
+        try {
+            IOUtils.copy(vbaProjectStream, outputStream);
+        } finally {
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
+    /**
+     * Adds a vbaProject.bin file taken from another, given workbook to this one.
+     * @throws IOException
+     * @throws InvalidFormatException
+     */
+    public void setVBAProject(XSSFWorkbook macroWorkbook) throws IOException, InvalidFormatException {
+        if (!macroWorkbook.isMacroEnabled()) {
+            return;
+        }
+        InputStream vbaProjectStream = XSSFRelation.VBA_MACROS.getContents(macroWorkbook.getCorePart());
+        if (vbaProjectStream != null) {
+            setVBAProject(vbaProjectStream);
+        }
+    }
 }

Added: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java?rev=1690593&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookType.java Mon Jul 13 09:05:17 2015
@@ -0,0 +1,45 @@
+/* ====================================================================
+   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;
+
+
+/**
+ * Represents the two different kinds of XML-based OOXML document.
+ */
+public enum XSSFWorkbookType {
+
+    XLSX(XSSFRelation.WORKBOOK.getContentType(), "xlsx"),
+    XLSM(XSSFRelation.MACROS_WORKBOOK.getContentType(), "xlsm");
+
+    private final String _contentType;
+    private final String _extension;
+
+    private XSSFWorkbookType(String contentType, String extension) {
+        _contentType = contentType;
+        _extension = extension;
+    }
+
+    public String getContentType() {
+        return _contentType;
+    }
+
+    public String getExtension() {
+        return _extension;
+    }
+
+}

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java?rev=1690593&r1=1690592&r2=1690593&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java Mon Jul 13 09:05:17 2015
@@ -17,6 +17,7 @@
 
 package org.apache.poi.xssf.usermodel;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -25,6 +26,7 @@ import static org.junit.Assert.assertSam
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -39,10 +41,11 @@ import org.apache.poi.openxml4j.opc.Cont
 import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.openxml4j.opc.internal.MemoryPackagePart;
 import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
-
+import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.usermodel.BaseTestWorkbook;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellStyle;
@@ -78,7 +81,8 @@ public final class TestXSSFWorkbook exte
 	 */
 	@Test
 	public void saveLoadNew() throws Exception {
-		XSSFWorkbook workbook = new XSSFWorkbook();
+		@SuppressWarnings("resource")
+        XSSFWorkbook workbook = new XSSFWorkbook();
 
 		//check that the default date system is set to 1900
 		CTWorkbookPr pr = workbook.getCTWorkbook().getWorkbookPr();
@@ -121,7 +125,8 @@ public final class TestXSSFWorkbook exte
 		// Links to the three sheets, shared strings and styles
 		assertTrue(wbPart.hasRelationships());
 		assertEquals(5, wbPart.getRelationships().size());
-
+		workbook.close();
+		
 		// Load back the XSSFWorkbook
 		workbook = new XSSFWorkbook(pkg);
 		assertEquals(3, workbook.getNumberOfSheets());
@@ -777,7 +782,7 @@ public final class TestXSSFWorkbook exte
         Cell cell9 = row3.createCell(2);
         cell9.setCellValue("Bepa");
 
-        AreaReference source = new AreaReference("A1:B2");
+        AreaReference source = new AreaReference("A1:B2", SpreadsheetVersion.EXCEL2007);
         sheet.createPivotTable(source, new CellReference("H5"));
     }
 
@@ -869,4 +874,54 @@ public final class TestXSSFWorkbook exte
             wb.close();
         }
     }
+
+    /**
+     * Tests that we can save a workbook with macros and reload it.
+     */
+    @Test
+    public void testSetVBAProject() throws Exception {
+        XSSFWorkbook workbook = null;
+        OutputStream out = null;
+        File file;
+        final byte[] allBytes = new byte[256];
+        for (int i = 0; i < 256; i++) {
+            allBytes[i] = (byte) (i - 128);
+        }
+        try {
+            workbook = new XSSFWorkbook();
+            workbook.createSheet();
+            workbook.setVBAProject(new ByteArrayInputStream(allBytes));
+            file = TempFile.createTempFile("poi-", ".xlsm");
+            out = new FileOutputStream(file);
+            workbook.write(out);
+        }
+        finally {
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(workbook);
+        }
+
+        try {
+            // Check the package contains what we'd expect it to
+            OPCPackage pkg = OPCPackage.open(file.toString());
+            PackagePart wbPart = pkg.getPart(PackagingURIHelper.createPartName("/xl/workbook.xml"));
+            assertTrue(wbPart.hasRelationships());
+            final PackageRelationshipCollection relationships = wbPart.getRelationships().getRelationships(XSSFRelation.VBA_MACROS.getRelation());
+            assertEquals(1, relationships.size());
+            assertEquals(XSSFRelation.VBA_MACROS.getDefaultFileName(), relationships.getRelationship(0).getTargetURI().toString());
+            PackagePart vbaPart = pkg.getPart(PackagingURIHelper.createPartName(XSSFRelation.VBA_MACROS.getDefaultFileName()));
+            assertNotNull(vbaPart);
+            assertFalse(vbaPart.isRelationshipPart());
+            assertEquals(XSSFRelation.VBA_MACROS.getContentType(), vbaPart.getContentType());
+            final byte[] fromFile = IOUtils.toByteArray(vbaPart.getInputStream());
+            assertArrayEquals(allBytes, fromFile);
+
+            // Load back the XSSFWorkbook just to check nothing explodes
+            workbook = new XSSFWorkbook(pkg);
+            assertEquals(1, workbook.getNumberOfSheets());
+            assertEquals(XSSFWorkbookType.XLSM, workbook.getWorkbookType());
+        }
+        finally {
+            IOUtils.closeQuietly(workbook);
+        }
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org