import java.io.InputStream;\r
import java.io.Reader;\r
import java.io.StringReader;\r
+import java.lang.ref.WeakReference;\r
import java.net.URL;\r
import java.util.Collections;\r
import java.util.HashMap;\r
\r
import org.apache.poi.util.DocumentHelper;\r
import org.apache.xmlbeans.SchemaType;\r
+import org.apache.xmlbeans.SchemaTypeLoader;\r
import org.apache.xmlbeans.XmlBeans;\r
import org.apache.xmlbeans.XmlException;\r
import org.apache.xmlbeans.XmlObject;\r
@SuppressWarnings("deprecation")\r
public class POIXMLTypeLoader {\r
\r
+ private static ThreadLocal<ClassLoader> classLoader = new ThreadLocal<ClassLoader>();\r
+ \r
public static final XmlOptions DEFAULT_XML_OPTIONS;\r
static {\r
DEFAULT_XML_OPTIONS = new XmlOptions();\r
return options == null ? DEFAULT_XML_OPTIONS : options;\r
}\r
\r
+ /**\r
+ * Sets the {@link ClassLoader} which is used, when XmlBeans are dynamically instantiated -\r
+ * opposed to being loaded by the factory class which is accompanied by each generated XmlBeans interface.\r
+ * <p>\r
+ * This is especially necessary in a context which doesn't guarantee that the current (thread) context\r
+ * cassloader has access to all XmlBeans schema definitions (*.xsb) - which is typically in OSGI the case.\r
+ * <p>\r
+ * The classloader will be only set for the current thread in a {@link ThreadLocal}. Although the\r
+ * ThreadLocal is implemented via a {@link WeakReference}, it's good style to {@code null} the classloader\r
+ * when the user code is finalized.\r
+ * \r
+ * @param cl the classloader to be used when XmlBeans classes and definitions are looked up\r
+ */\r
+ public static void setClassLoader(ClassLoader cl) {\r
+ classLoader.set(cl);\r
+ }\r
+ \r
+ private static SchemaTypeLoader getTypeLoader() {\r
+ ClassLoader cl = classLoader.get();\r
+ return (cl == null)\r
+ ? XmlBeans.getContextTypeLoader()\r
+ : XmlBeans.typeLoaderForClassLoader(cl);\r
+ }\r
+ \r
public static XmlObject newInstance(SchemaType type, XmlOptions options) {\r
- return XmlBeans.getContextTypeLoader().newInstance(type, getXmlOptions(options));\r
+ return getTypeLoader().newInstance(type, getXmlOptions(options));\r
}\r
\r
public static XmlObject parse(String xmlText, SchemaType type, XmlOptions options) throws XmlException {\r
public static XmlObject parse(InputStream jiois, SchemaType type, XmlOptions options) throws XmlException, IOException {\r
try {\r
Document doc = DocumentHelper.readDocument(jiois);\r
- return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
+ return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
} catch (SAXException e) {\r
throw new IOException("Unable to parse xml bean", e);\r
}\r
}\r
\r
public static XmlObject parse(XMLStreamReader xsr, SchemaType type, XmlOptions options) throws XmlException {\r
- return XmlBeans.getContextTypeLoader().parse(xsr, type, getXmlOptions(options));\r
+ return getTypeLoader().parse(xsr, type, getXmlOptions(options));\r
}\r
\r
public static XmlObject parse(Reader jior, SchemaType type, XmlOptions options) throws XmlException, IOException {\r
try {\r
Document doc = DocumentHelper.readDocument(new InputSource(jior));\r
- return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
+ return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
} catch (SAXException e) {\r
throw new XmlException("Unable to parse xml bean", e);\r
}\r
}\r
\r
public static XmlObject parse(Node node, SchemaType type, XmlOptions options) throws XmlException {\r
- return XmlBeans.getContextTypeLoader().parse(node, type, getXmlOptions(options));\r
+ return getTypeLoader().parse(node, type, getXmlOptions(options));\r
}\r
\r
public static XmlObject parse(XMLInputStream xis, SchemaType type, XmlOptions options) throws XmlException, XMLStreamException {\r
- return XmlBeans.getContextTypeLoader().parse(xis, type, getXmlOptions(options));\r
+ return getTypeLoader().parse(xis, type, getXmlOptions(options));\r
}\r
\r
public static XMLInputStream newValidatingXMLInputStream ( XMLInputStream xis, SchemaType type, XmlOptions options ) throws XmlException, XMLStreamException {\r
- return XmlBeans.getContextTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));\r
+ return getTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));\r
}\r
}\r
\r
package org.apache.poi.xslf.usermodel;\r
\r
-import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;\r
-\r
import java.awt.geom.Rectangle2D;\r
import java.util.ArrayList;\r
import java.util.Collections;\r
\r
import javax.xml.namespace.QName;\r
\r
-import org.apache.poi.POIXMLException;\r
import org.apache.poi.sl.draw.DrawFactory;\r
import org.apache.poi.sl.draw.DrawTableShape;\r
import org.apache.poi.sl.draw.DrawTextShape;\r
import org.apache.poi.util.Internal;\r
import org.apache.poi.util.Units;\r
import org.apache.xmlbeans.XmlCursor;\r
-import org.apache.xmlbeans.XmlException;\r
import org.apache.xmlbeans.XmlObject;\r
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;\r
public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>,\r
TableShape<XSLFShape,XSLFTextParagraph> {\r
/* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";\r
+ /* package */ static final String DRAWINGML_URI = "http://schemas.openxmlformats.org/drawingml/2006/main";\r
\r
private CTTable _table;\r
private List<XSLFTableRow> _rows;\r
/*package*/ XSLFTable(CTGraphicalObjectFrame shape, XSLFSheet sheet){\r
super(shape, sheet);\r
\r
- XmlObject[] rs = shape.getGraphic().getGraphicData()\r
- .selectPath("declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ./a:tbl");\r
- if (rs.length == 0) {\r
- throw new IllegalStateException("a:tbl element was not found in\n " + shape.getGraphic().getGraphicData());\r
+ CTGraphicalObjectData god = shape.getGraphic().getGraphicData();\r
+ XmlCursor xc = god.newCursor();\r
+ if (!xc.toChild(DRAWINGML_URI, "tbl")) {\r
+ throw new IllegalStateException("a:tbl element was not found in\n " + god);\r
}\r
\r
+ XmlObject xo = xc.getObject();\r
// Pesky XmlBeans bug - see Bugzilla #49934\r
// it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas\r
- if(rs[0] instanceof XmlAnyTypeImpl){\r
- try {\r
- rs[0] = CTTable.Factory.parse(rs[0].toString(), DEFAULT_XML_OPTIONS);\r
- }catch (XmlException e){\r
- throw new POIXMLException(e);\r
- }\r
+ if (xo instanceof XmlAnyTypeImpl){\r
+ String errStr =\r
+ "Schemas (*.xsb) for CTTable can't be loaded - usually this happens when OSGI " +\r
+ "loading is used and the thread context classloader has no reference to " +\r
+ "the xmlbeans classes - use POIXMLTypeLoader.setClassLoader() to set the loader, " +\r
+ "e.g. with CTTable.class.getClassLoader()"\r
+ ;\r
+ throw new IllegalStateException(errStr);\r
}\r
+ _table = (CTTable)xo;\r
+ xc.dispose();\r
\r
- _table = (CTTable) rs[0];\r
- CTTableRow[] trArray = _table.getTrArray();\r
- _rows = new ArrayList<XSLFTableRow>(trArray.length);\r
- for(CTTableRow row : trArray) {\r
- XSLFTableRow xr = new XSLFTableRow(row, this);\r
- _rows.add(xr);\r
+ _rows = new ArrayList<XSLFTableRow>(_table.sizeOfTrArray());\r
+ for(CTTableRow row : _table.getTrArray()) {\r
+ _rows.add(new XSLFTableRow(row, this));\r
}\r
updateRowColIndexes();\r
}\r
\r
frame.addNewXfrm();\r
CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData();\r
- XmlCursor cursor = gr.newCursor();\r
- cursor.toNextToken();\r
- cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tbl"));\r
- cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblPr"));\r
- cursor.toNextToken();\r
- cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblGrid"));\r
- cursor.dispose();\r
+ XmlCursor grCur = gr.newCursor();\r
+ grCur.toNextToken();\r
+ grCur.beginElement(new QName(DRAWINGML_URI, "tbl"));\r
+ \r
+ CTTable tbl = CTTable.Factory.newInstance();\r
+ tbl.addNewTblPr();\r
+ tbl.addNewTblGrid();\r
+ XmlCursor tblCur = tbl.newCursor();\r
+ \r
+ tblCur.moveXmlContents(grCur);\r
+ tblCur.dispose();\r
+ grCur.dispose();\r
gr.setUri(TABLE_URI);\r
return frame;\r
}\r
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.poi.util.NullOutputStream;
import org.apache.poi.util.PackageHelper;
import org.apache.poi.util.TempFile;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
+import org.apache.poi.xslf.usermodel.XSLFShape;
import org.junit.Test;
/**
open.close();
}
}
+
+ @Test(expected=IllegalStateException.class)
+ public void testOSGIClassLoadingAsIs() throws IOException {
+ Thread thread = Thread.currentThread();
+ ClassLoader cl = thread.getContextClassLoader();
+ InputStream is = POIDataSamples.getSlideShowInstance().openResourceAsStream("table_test.pptx");
+ try {
+ thread.setContextClassLoader(cl.getParent());
+ XMLSlideShow ppt = new XMLSlideShow(is);
+ ppt.getSlides().get(0).getShapes();
+ } finally {
+ thread.setContextClassLoader(cl);
+ is.close();
+ }
+ }
+
+
+ @Test
+ public void testOSGIClassLoadingFixed() throws IOException {
+ Thread thread = Thread.currentThread();
+ ClassLoader cl = thread.getContextClassLoader();
+ InputStream is = POIDataSamples.getSlideShowInstance().openResourceAsStream("table_test.pptx");
+ try {
+ thread.setContextClassLoader(cl.getParent());
+ POIXMLTypeLoader.setClassLoader(cl);
+ XMLSlideShow ppt = new XMLSlideShow(is);
+ ppt.getSlides().get(0).getShapes();
+ } finally {
+ thread.setContextClassLoader(cl);
+ POIXMLTypeLoader.setClassLoader(null);
+ is.close();
+ }
+ }
+
}