Bladeren bron

#64036 - remove reflective calls in Workbook- and SlideShowFactory

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1872066 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_4_1_2
Andreas Beeker 4 jaren geleden
bovenliggende
commit
a0770034fc

+ 6
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java Bestand weergeven

@@ -31,6 +31,12 @@ import org.apache.poi.util.Internal;
@SuppressWarnings("unused")
@Internal
public class HSSFWorkbookFactory extends WorkbookFactory {

static {
WorkbookFactory.createHssfFromScratch = HSSFWorkbookFactory::createWorkbook;
WorkbookFactory.createHssfByNode = HSSFWorkbookFactory::createWorkbook;
}

/**
* Create a new empty Workbook
*

+ 47
- 61
src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java Bestand weergeven

@@ -20,21 +20,40 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.OldFileFormatException;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentFactoryHelper;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;

public class SlideShowFactory {
@SuppressWarnings("unchecked")
public abstract class SlideShowFactory {

protected interface CreateSlideShow1<T> {
SlideShow<?, ?> apply(T t) throws IOException;
}

protected interface CreateSlideShow2<T, U> {
SlideShow<?, ?> apply(T t, U u) throws IOException;
}

// XMLSlideShow createSlideShow(InputStream stream)
protected static CreateSlideShow1<InputStream> createXslfByStream;

// XMLSlideShow createSlideShow(File file, boolean readOnly)
protected static CreateSlideShow2<File, Boolean> createXslfByFile;

// HSLFSlideShow createSlideShow(final POIFSFileSystem fs)
protected static CreateSlideShow1<POIFSFileSystem> createHslfByPoifs;

// HSLFSlideShow createSlideShow(final DirectoryNode root)
protected static CreateSlideShow1<DirectoryNode> createHslfByNode;

/**
* Creates a SlideShow from the given POIFSFileSystem.
*
@@ -106,8 +125,8 @@ public class SlideShowFactory {
InputStream stream = null;
try {
stream = DocumentFactoryHelper.getDecryptedStream(root, password);
return createXSLFSlideShow(stream);
initXslf();
return (SlideShow<S, P>) createXslfByStream.apply(stream);
} finally {
IOUtils.closeQuietly(stream);

@@ -125,7 +144,8 @@ public class SlideShowFactory {
passwordSet = true;
}
try {
return createHSLFSlideShow(root);
initHslf();
return (SlideShow<S, P>) createHslfByNode.apply(root);
} finally {
if (passwordSet) {
Biff8EncryptionKey.setCurrentUserPassword(null);
@@ -162,7 +182,7 @@ public class SlideShowFactory {
/**
* Creates the appropriate HSLFSlideShow / XMLSlideShow from
* the given InputStream, which may be password protected.
*
*
* <p>Note that using an {@link InputStream} has a higher memory footprint
* than using a {@link File}.</p>
*
@@ -185,13 +205,14 @@ public class SlideShowFactory {
> SlideShow<S,P> create(InputStream inp, String password) throws IOException, EncryptedDocumentException {
InputStream is = FileMagic.prepareToCheckMagic(inp);
FileMagic fm = FileMagic.valueOf(is);
switch (fm) {
case OLE2:
POIFSFileSystem fs = new POIFSFileSystem(is);
return create(fs, password);
case OOXML:
return createXSLFSlideShow(is);
initXslf();
return (SlideShow<S, P>) createXslfByStream.apply(is);
default:
throw new IOException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
@@ -270,66 +291,31 @@ public class SlideShowFactory {
return create(fs, password);
} catch(OfficeXmlFileException e) {
IOUtils.closeQuietly(fs);
return createXSLFSlideShow(file, readOnly);
initXslf();
return (SlideShow<S, P>) createXslfByFile.apply(file, readOnly);
} catch(RuntimeException e) {
IOUtils.closeQuietly(fs);
throw e;
}
}
private static <
S extends Shape<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
> SlideShow<S,P> createHSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
return createSlideShow("org.apache.poi.hslf.usermodel.HSLFSlideShowFactory", args);

private static void initXslf() throws IOException {
if (createXslfByFile == null) {
initFactory("org.apache.poi.xslf.usermodel.XSLFSlideShowFactory", "poi-ooxml-*.jar");
}
}
private static <
S extends Shape<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
> SlideShow<S,P> createXSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
return createSlideShow("org.apache.poi.xslf.usermodel.XSLFSlideShowFactory", args);

private static void initHslf() throws IOException {
if (createHslfByPoifs == null) {
initFactory("org.apache.poi.hslf.usermodel.HSLFSlideShowFactory", "poi-scratchpad-*.jar");
}
}

private static <
S extends Shape<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
> SlideShow<S,P> createSlideShow(String factoryClass, Object[] args) throws IOException, EncryptedDocumentException {
final Class<?> clazz;
private static void initFactory(String factoryClass, String jar) throws IOException {
try {
clazz = SlideShowFactory.class.getClassLoader().loadClass(factoryClass);
Class.forName(factoryClass, true, SlideShowFactory.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IOException(factoryClass+" not found - check if poi-scratchpad.jar is on the classpath.");
}
try {
Class<?>[] argsClz = new Class<?>[args.length];
int i=0;
for (Object o : args) {
Class<?> c = o.getClass();
if (Boolean.class.isAssignableFrom(c)) {
c = boolean.class;
} else if (InputStream.class.isAssignableFrom(c)) {
c = InputStream.class;
}
argsClz[i++] = c;
}
Method m = clazz.getMethod("createSlideShow", argsClz);
return (SlideShow<S,P>)m.invoke(null, args);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof IOException) {
throw (IOException)t;
} else if (t instanceof EncryptedDocumentException) {
throw (EncryptedDocumentException)t;
} else if (t instanceof OldFileFormatException) {
throw (OldFileFormatException)t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new IOException(t);
}
} catch (Exception e) {
throw new IOException(e);
throw new IOException(factoryClass+" not found - check if " + jar + " is on the classpath.");
}
}
}

+ 50
- 56
src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java Bestand weergeven

@@ -21,20 +21,16 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.OldFileFormatException;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentFactoryHelper;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Removal;

@@ -43,7 +39,28 @@ import org.apache.poi.util.Removal;
* (be it {@link HSSFWorkbook} or XSSFWorkbook),
* by auto-detecting from the supplied input.
*/
public class WorkbookFactory {
public abstract class WorkbookFactory {

protected interface CreateWorkbook0 {
Workbook apply() throws IOException;
}

protected interface CreateWorkbook1<T> {
Workbook apply(T t) throws IOException;
}

protected interface CreateWorkbook2<T, U> {
Workbook apply(T t, U u) throws IOException;
}

protected static CreateWorkbook0 createHssfFromScratch;
protected static CreateWorkbook1<DirectoryNode> createHssfByNode;

protected static CreateWorkbook0 createXssfFromScratch;
protected static CreateWorkbook1<InputStream> createXssfByStream;
protected static CreateWorkbook1<Object> createXssfByPackage;
protected static CreateWorkbook2<File,Boolean> createXssfByFile;

/**
* Create a new empty Workbook, either XSSF or HSSF depending
* on the parameter
@@ -56,9 +73,11 @@ public class WorkbookFactory {
*/
public static Workbook create(boolean xssf) throws IOException {
if(xssf) {
return createXSSFWorkbook();
initXssf();
return createXssfFromScratch.apply();
} else {
return createHSSFWorkbook();
initHssf();
return createHssfFromScratch.apply();
}
}

@@ -125,8 +144,8 @@ public class WorkbookFactory {
InputStream stream = null;
try {
stream = DocumentFactoryHelper.getDecryptedStream(root, password);
return createXSSFWorkbook(stream);
initXssf();
return createXssfByStream.apply(stream);
} finally {
IOUtils.closeQuietly(stream);

@@ -144,7 +163,8 @@ public class WorkbookFactory {
passwordSet = true;
}
try {
return createHSSFWorkbook(root);
initHssf();
return createHssfByNode.apply(root);
} finally {
if (passwordSet) {
Biff8EncryptionKey.setCurrentUserPassword(null);
@@ -172,7 +192,8 @@ public class WorkbookFactory {
@Deprecated
@Removal(version = "4.2.0")
public static Workbook create(Object pkg) throws IOException {
return createXSSFWorkbook(pkg);
initXssf();
return createXssfByPackage.apply(pkg);
}

/**
@@ -231,7 +252,8 @@ public class WorkbookFactory {
POIFSFileSystem fs = new POIFSFileSystem(is);
return create(fs, password);
case OOXML:
return createXSSFWorkbook(is);
initXssf();
return createXssfByStream.apply(is);
default:
throw new IOException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
@@ -301,60 +323,32 @@ public class WorkbookFactory {
return create(fs, password);
} catch(OfficeXmlFileException e) {
IOUtils.closeQuietly(fs);
return createXSSFWorkbook(file, readOnly);
initXssf();
return createXssfByFile.apply(file, readOnly);
} catch(RuntimeException e) {
IOUtils.closeQuietly(fs);
throw e;
}
}

private static Workbook createHSSFWorkbook(Object... args) throws IOException, EncryptedDocumentException {
return createWorkbook("org.apache.poi.hssf.usermodel.HSSFWorkbookFactory", args);
private static void initXssf() throws IOException {
if (createXssfFromScratch == null) {
initFactory("org.apache.poi.xssf.usermodel.XSSFWorkbookFactory", "poi-ooxml-*.jar");
}
}

private static Workbook createXSSFWorkbook(Object... args) throws IOException, EncryptedDocumentException {
return createWorkbook("org.apache.poi.xssf.usermodel.XSSFWorkbookFactory", args);
private static void initHssf() throws IOException {
if (createHssfFromScratch == null) {
// HSSF is part of the main jar, so this shouldn't fail ...
initFactory("org.apache.poi.hssf.usermodel.HSSFWorkbookFactory", "poi-*.jar");
}
}

/**
* Does the actual call to HSSF or XSSF to do the creation.
* Uses reflection, so that this class can be in the Core non-OOXML
* POI jar without errors / broken references to the OOXML / XSSF code.
*/
private static Workbook createWorkbook(String factoryClass, Object[] args) throws IOException, EncryptedDocumentException {
private static void initFactory(String factoryClass, String jar) throws IOException {
try {
Class<?> clazz = WorkbookFactory.class.getClassLoader().loadClass(factoryClass);
Class<?>[] argsClz = new Class<?>[args.length];
int i=0;
for (Object o : args) {
Class<?> c = o.getClass();
if (Boolean.class.isAssignableFrom(c)) {
c = boolean.class;
} else if (InputStream.class.isAssignableFrom(c)) {
c = InputStream.class;
} else if (File.class.isAssignableFrom(c)) {
c = File.class;
}
argsClz[i++] = c;
}
Method m = clazz.getMethod("createWorkbook", argsClz);
return (Workbook)m.invoke(null, args);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof IOException) {
throw (IOException)t;
} else if (t instanceof EncryptedDocumentException) {
throw (EncryptedDocumentException)t;
} else if (t instanceof OldFileFormatException) {
throw (OldFileFormatException)t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new IOException(t.getMessage(), t);
}
} catch (Exception e) {
throw new IOException("While trying to invoke 'createWorkbook' on factory " + factoryClass +
" and arguments " + Arrays.toString(args), e);
Class.forName(factoryClass, true, WorkbookFactory.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IOException(factoryClass+" not found - check if " + jar + " is on the classpath.");
}
}
}

+ 20
- 10
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideShowFactory.java Bestand weergeven

@@ -31,6 +31,11 @@ import org.apache.poi.util.Internal;
@Internal
public class XSLFSlideShowFactory extends SlideShowFactory {

static {
SlideShowFactory.createXslfByFile = XSLFSlideShowFactory::createSlideShow;
SlideShowFactory.createXslfByStream = XSLFSlideShowFactory::createSlideShow;
}

/**
* Creates a XMLSlideShow from the given OOXML Package.
* This is a convenience method to go along the create-methods of the super class.
@@ -43,7 +48,6 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
* @return The created SlideShow
*
* @throws IOException if an error occurs while reading the data
* @throws InvalidFormatException
*/
public static XMLSlideShow create(OPCPackage pkg) throws IOException {
try {
@@ -69,7 +73,6 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
* @return The created SlideShow
*
* @throws IOException if an error occurs while reading the data
* @throws InvalidFormatException
*/
public static XMLSlideShow createSlideShow(OPCPackage pkg) throws IOException {
try {
@@ -83,7 +86,7 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
throw ioe;
}
}
/**
* Creates the XMLSlideShow from the given File, which must exist and be readable.
* <p>Note that in order to properly release resources theSlideShow should be closed after use.
@@ -99,9 +102,13 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
*/
@SuppressWarnings("resource")
public static XMLSlideShow createSlideShow(File file, boolean readOnly)
throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE);
return createSlideShow(pkg);
throws IOException {
try {
OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE);
return createSlideShow(pkg);
} catch (InvalidFormatException e) {
throw new IOException(e);
}
}

/**
@@ -115,12 +122,15 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
* @return The created SlideShow
*
* @throws IOException if an error occurs while reading the data
* @throws InvalidFormatException
*/
@SuppressWarnings("resource")
public static XMLSlideShow createSlideShow(InputStream stream) throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(stream);
return createSlideShow(pkg);
public static XMLSlideShow createSlideShow(InputStream stream) throws IOException {
try {
OPCPackage pkg = OPCPackage.open(stream);
return createSlideShow(pkg);
} catch (InvalidFormatException e) {
throw new IOException(e);
}
}

}

+ 23
- 8
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookFactory.java Bestand weergeven

@@ -29,6 +29,15 @@ import org.apache.poi.openxml4j.opc.ZipPackage;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class XSSFWorkbookFactory extends WorkbookFactory {

static {
WorkbookFactory.createXssfFromScratch = XSSFWorkbookFactory::createWorkbook;
WorkbookFactory.createXssfByStream = XSSFWorkbookFactory::createWorkbook;
WorkbookFactory.createXssfByPackage = o -> XSSFWorkbookFactory.createWorkbook((OPCPackage)o);
WorkbookFactory.createXssfByFile = XSSFWorkbookFactory::createWorkbook;
}


/**
* Create a new empty Workbook
*
@@ -110,10 +119,13 @@ public class XSSFWorkbookFactory extends WorkbookFactory {
* @throws EncryptedDocumentException If the wrong password is given for a protected file
*/
@SuppressWarnings("resource")
public static XSSFWorkbook createWorkbook(File file, boolean readOnly)
throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE);
return createWorkbook(pkg);
public static XSSFWorkbook createWorkbook(File file, boolean readOnly) throws IOException {
try {
OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE);
return createWorkbook(pkg);
} catch (InvalidFormatException e) {
throw new IOException(e);
}
}

/**
@@ -127,11 +139,14 @@ public class XSSFWorkbookFactory extends WorkbookFactory {
* @return The created Workbook
*
* @throws IOException if an error occurs while reading the data
* @throws InvalidFormatException if the package is not valid.
*/
@SuppressWarnings("resource")
public static XSSFWorkbook createWorkbook(InputStream stream) throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(stream);
return createWorkbook(pkg);
public static XSSFWorkbook createWorkbook(InputStream stream) throws IOException {
try {
OPCPackage pkg = OPCPackage.open(stream);
return createWorkbook(pkg);
} catch (InvalidFormatException e) {
throw new IOException(e);
}
}
}

+ 20
- 40
src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java Bestand weergeven

@@ -52,9 +52,9 @@ public final class TestWorkbookFactory {
private static final String[] xls_protected = new String[] {"password.xls", "password"};
private static final String[] xlsx_protected = new String[]{"protected_passtika.xlsx", "tika"};
private static final String txt = "SampleSS.txt";
private static final POILogger LOGGER = POILogFactory.getLogger(TestWorkbookFactory.class);
/**
* Closes the sample workbook read in from filename.
* Throws an exception if closing the workbook results in the file on disk getting modified.
@@ -70,7 +70,7 @@ public final class TestWorkbookFactory {
assertArrayEquals(filename + " sample file was modified as a result of closing the workbook",
before, after);
}
/**
* bug 58779: Closing an XSSFWorkbook that was created with WorkbookFactory modifies the file
* FIXME: replace this method with wb.close() when bug 58779 is resolved.
@@ -353,56 +353,45 @@ public final class TestWorkbookFactory {
// expected here
}
}
/**
* Check that a helpful exception is given on an empty input stream
*/
@Test
@Test(expected = EmptyFileException.class)
public void testEmptyInputStream() throws Exception {
InputStream emptyStream = new ByteArrayInputStream(new byte[0]);
try {
WorkbookFactory.create(emptyStream);
fail("Shouldn't be able to create for an empty stream");
} catch (final EmptyFileException expected) {}
WorkbookFactory.create(emptyStream);
}

/**
* Check that a helpful exception is given on an empty file
*/
@Test
@Test(expected = EmptyFileException.class)
public void testEmptyFile() throws Exception {
File emptyFile = TempFile.createTempFile("empty", ".poi");
try {
WorkbookFactory.create(emptyFile);
fail("Shouldn't be able to create for an empty file");
} catch (final EmptyFileException expected) {
// expected here
} finally {
assertTrue(emptyFile.delete());
}

assertTrue(emptyFile.delete());
}

/**
* Check that a helpful exception is raised on a non-existing file
*/
@Test
@Test(expected = FileNotFoundException.class)
public void testNonExistingFile() throws Exception {
File nonExistingFile = new File("notExistingFile");
assertFalse(nonExistingFile.exists());

try {
WorkbookFactory.create(nonExistingFile, "password", true);
fail("Should not be able to create for a non-existing file");
} catch (final FileNotFoundException e) {
// expected
}
WorkbookFactory.create(nonExistingFile, "password", true);
}

/**
* See Bugzilla bug #62831 - #WorkbookFactory.create(File) needs
* to work for sub-classes of File too, eg JFileChooser
*/
@Test
@Test(expected = ClassCastException.class)
public void testFileSubclass() throws Exception {
File normalXLS = HSSFTestDataSamples.getSampleFile(xls);
File normalXLSX = HSSFTestDataSamples.getSampleFile(xlsx);
@@ -423,15 +412,11 @@ public final class TestWorkbookFactory {

// check what happens if the file is passed as "Object"

try {
//noinspection deprecation
WorkbookFactory.create((Object)altXLSX);
fail("Will throw an exception");
} catch(IOException e) {
// expected here because create() in this case expects an object of type "OPCPackage"
}
//noinspection deprecation
WorkbookFactory.create((Object)altXLSX);
// expected a ClassCastException here because create() in this case expects an object of type "OPCPackage"
}

private static class TestFile extends File {
public TestFile(String file) {
super(file);
@@ -452,15 +437,10 @@ public final class TestWorkbookFactory {
closeOrRevert(wb);
}

@Test
public void testInvalidFormatException() {
@Test(expected = IOException.class)
public void testInvalidFormatException() throws IOException {
String filename = "OPCCompliance_DerivedPartNameFAIL.docx";
try {
WorkbookFactory.create(POIDataSamples.getOpenXML4JInstance().openResourceAsStream(filename));
fail("Expecting an Exception for this document");
} catch (IOException e) {
// expected here
}
WorkbookFactory.create(POIDataSamples.getOpenXML4JInstance().openResourceAsStream(filename));
}

}

+ 6
- 0
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowFactory.java Bestand weergeven

@@ -31,6 +31,12 @@ import org.apache.poi.util.Internal;
@SuppressWarnings("unused")
@Internal
public class HSLFSlideShowFactory extends SlideShowFactory {

static {
SlideShowFactory.createHslfByNode = HSLFSlideShowFactory::createSlideShow;
SlideShowFactory.createHslfByPoifs = HSLFSlideShowFactory::createSlideShow;
}

/**
* Creates a HSLFSlideShow from the given {@link POIFSFileSystem}<p>
* Note that in order to properly release resources the

Laden…
Annuleren
Opslaan