From 35906116edf297fbe512765cc4728acfca3b20eb Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Thu, 16 Jul 2009 05:46:14 +0000 Subject: [PATCH] support for custom XML mappings in XSSF, see Bugzilla 47520 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@794539 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../usermodel/examples/CustomXMLMapping.java | 42 ++ .../poi/xssf/extractor/XSSFExportToXml.java | 544 ++++++++++++++++++ .../org/apache/poi/xssf/model/MapInfo.java | 148 +++++ .../apache/poi/xssf/model/SingleXmlCells.java | 106 ++++ .../java/org/apache/poi/xssf/model/Table.java | 248 ++++++++ .../apache/poi/xssf/usermodel/XSSFMap.java | 120 ++++ .../poi/xssf/usermodel/XSSFRelation.java | 25 + .../poi/xssf/usermodel/XSSFWorkbook.java | 22 +- .../usermodel/helpers/XSSFSingleXmlCell.java | 84 +++ .../usermodel/helpers/XSSFXmlColumnPr.java | 65 +++ .../xssf/extractor/TestXSSFExportToXML.java | 220 +++++++ .../apache/poi/xssf/model/TestMapInfo.java | 84 +++ .../data/CustomXMLMappings-complex-type.xlsx | Bin 0 -> 13348 bytes .../poi/hssf/data/CustomXMLMappings.xlsx | Bin 0 -> 9906 bytes .../data/CustomXmlMappings-inverse-order.xlsx | Bin 0 -> 9915 bytes 16 files changed, 1703 insertions(+), 6 deletions(-) create mode 100755 src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/model/MapInfo.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/model/SingleXmlCells.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/model/Table.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFMap.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFSingleXmlCell.java create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFXmlColumnPr.java create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFExportToXML.java create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/model/TestMapInfo.java create mode 100755 src/testcases/org/apache/poi/hssf/data/CustomXMLMappings-complex-type.xlsx create mode 100755 src/testcases/org/apache/poi/hssf/data/CustomXMLMappings.xlsx create mode 100755 src/testcases/org/apache/poi/hssf/data/CustomXmlMappings-inverse-order.xlsx diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 79993d965f..415f5390ec 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,7 @@ + 47520 - Initial support for custom XML mappings in XSSF 47460 - Fixed NPE when retrieving core properties from a newly created workbook 47498 - Fixed HyperlinkRecord to properly handle URL monikers 47504 - Fixed XSSFWorkbook to read files with hyperlinks to document locations diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java new file mode 100755 index 0000000000..3e69df286c --- /dev/null +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/CustomXMLMapping.java @@ -0,0 +1,42 @@ +/* ==================================================================== + 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.examples; + +import org.apache.poi.xssf.extractor.XSSFExportToXml; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFMap; + +import java.io.ByteArrayOutputStream; + +/** + * Print all custom XML mappings registered in the given workbook + */ +public class CustomXMLMapping { + + public static void main(String[] args) throws Exception { + XSSFWorkbook wb = new XSSFWorkbook(args[0]); + + for (XSSFMap map : wb.getCustomXMLMappings()) { + XSSFExportToXml exporter = new XSSFExportToXml(map); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os, true); + String xml = os.toString("UTF-8"); + System.out.println(xml); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java new file mode 100755 index 0000000000..01d96832b2 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java @@ -0,0 +1,544 @@ +/* ==================================================================== + 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.extractor; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.xssf.model.Table; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFMap; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell; +import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * + * Maps an XLSX to an XML according to one of the mapping defined. + * + * + * The output XML Schema must respect this limitations: + * + *
    + *
  • all mandatory elements and attributes must be mapped
  • + * + *
  • no <any> in complex type/element declaration
  • + *
  • no <anyAttribute> attributes declaration
  • + *
  • no recursive structures: recursive structures can't be nested more than one level
  • + *
  • no abstract elements: abstract complex types can be declared but must not be used in elements.
  • + *
  • no mixed content: an element can't contain simple text and child element(s) together
  • + *
  • no <substitutionGroup> in complex type/element declaration
  • + *
+ * + * @author Roberto Manicardi + * + * + * + */ + + +public class XSSFExportToXml implements Comparator{ + + private XSSFMap map; + + /** + * Creates a new exporter and sets the mapping to be used when generating the XML output document + * + * @param map the mapping rule to be used + */ + public XSSFExportToXml(XSSFMap map){ + this.map = map; + } + + /** + * + * Exports the data in an XML stream + * + * @param os OutputStream in which will contain the output XML + * @param validate if true, validates the XML againts the XML Schema + * @throws SAXException + */ + public void exportToXML(OutputStream os, boolean validate) throws SAXException{ + exportToXML(os,"UTF-8", validate); + } + + private Document getEmptyDocument() throws ParserConfigurationException{ + + + DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); + Document doc = docBuilder.newDocument(); + + + + return doc; + } + + /** + * Exports the data in an XML stream + * + * @param os OutputStream in which will contain the output XML + * @param encoding the output charset encoding + * @param validate if true, validates the XML againts the XML Schema + * @throws SAXException + * @throws InvalidFormatException + */ + + public void exportToXML(OutputStream os, String encoding, boolean validate) throws SAXException{ + List singleXMLCells = map.getRelatedSingleXMLCell(); + List tables = map.getRelatedTables(); + + String rootElement = map.getCtMap().getRootElement(); + + try{ + + Document doc = getEmptyDocument(); + + Element root = null; + + if(isNamespaceDeclared()){ + root=doc.createElementNS(getNamespace(),rootElement); + }else{ + root=doc.createElement(rootElement); + } + doc.appendChild(root); + + + List xpaths = new Vector(); + Map singleXmlCellsMappings = new HashMap(); + Map tableMappings = new HashMap(); + + for(XSSFSingleXmlCell simpleXmlCell : singleXMLCells){ + xpaths.add(simpleXmlCell.getXpath()); + singleXmlCellsMappings.put(simpleXmlCell.getXpath(), simpleXmlCell); + } + for(Table table : tables){ + String commonXPath = table.getCommonXpath(); + xpaths.add(commonXPath); + tableMappings.put(commonXPath, table); + } + + + Collections.sort(xpaths,this); + + for(String xpath : xpaths){ + + XSSFSingleXmlCell simpleXmlCell = singleXmlCellsMappings.get(xpath); + Table table = tableMappings.get(xpath); + + if(!xpath.matches(".*\\[.*")){ + + // Exports elements and attributes mapped with simpleXmlCell + if(simpleXmlCell!=null){ + XSSFCell cell = simpleXmlCell.getReferencedCell(); + if(cell!=null){ + Node currentNode = getNodeByXPath(xpath,doc.getFirstChild(),doc,false); + STXmlDataType.Enum dataType = simpleXmlCell.getXmlDataType(); + mapCellOnNode(cell,currentNode,dataType); + } + } + + // Exports elements and attributes mapped with tables + if(table!=null){ + + List tableColumns = table.getXmlColumnPrs(); + + XSSFSheet sheet = table.getXSSFSheet(); + + int startRow = table.getStartCellReference().getRow(); + // In mappings created with Microsoft Excel the first row contains the table header and must be skipped + startRow +=1; + + int endRow = table.getEndCellReference().getRow(); + + for(int i = startRow; i<= endRow; i++){ + XSSFRow row = sheet.getRow(i); + + Node tableRootNode = getNodeByXPath(table.getCommonXpath(),doc.getFirstChild(),doc,true); + + short startColumnIndex = table.getStartCellReference().getCol(); + for(int j = startColumnIndex; j<= table.getEndCellReference().getCol();j++){ + XSSFCell cell = row.getCell(j); + if(cell!=null){ + XSSFXmlColumnPr pointer = tableColumns.get(j-startColumnIndex); + String localXPath = pointer.getLocalXPath(); + Node currentNode = getNodeByXPath(localXPath,tableRootNode,doc,false); + STXmlDataType.Enum dataType = pointer.getXmlDataType(); + + + mapCellOnNode(cell,currentNode,dataType); + } + + } + + } + + + + } + }else{ + // TODO: implement filtering management in xpath + } + } + + boolean isValid = true; + if(validate){ + isValid =isValid(doc); + } + + + + if(isValid){ + + ///////////////// + //Output the XML + + //set up a transformer + TransformerFactory transfac = TransformerFactory.newInstance(); + Transformer trans = transfac.newTransformer(); + trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + trans.setOutputProperty(OutputKeys.INDENT, "yes"); + trans.setOutputProperty(OutputKeys.ENCODING, encoding); + + //create string from xml tree + + StreamResult result = new StreamResult(os); + DOMSource source = new DOMSource(doc); + trans.transform(source, result); + + } + }catch(ParserConfigurationException e){ + e.printStackTrace(); + }catch(TransformerException e){ + e.printStackTrace(); + } + + + } + + /** + * Validate the generated XML against the XML Schema associated with the XSSFMap + * + * @param xml the XML to validate + * @return + */ + + private boolean isValid(Document xml) throws SAXException{ + boolean isValid = false; + try{ + String language = XMLConstants.W3C_XML_SCHEMA_NS_URI; + SchemaFactory factory = SchemaFactory.newInstance(language); + + Source source = new DOMSource(map.getSchema()); + Schema schema = factory.newSchema(source); + Validator validator = schema.newValidator(); + validator.validate(new DOMSource(xml)); + //if no exceptions where raised, the document is valid + isValid=true; + + + }catch(IOException e){ + e.printStackTrace(); + } + return isValid; + } + + + private void mapCellOnNode(XSSFCell cell, Node node, STXmlDataType.Enum outputDataType){ + + + String value =""; + switch (cell.getCellType()){ + + case XSSFCell.CELL_TYPE_STRING: value = cell.getStringCellValue(); break; + case XSSFCell.CELL_TYPE_BOOLEAN: value += cell.getBooleanCellValue(); break; + case XSSFCell.CELL_TYPE_ERROR: value = cell.getErrorCellString(); + case XSSFCell.CELL_TYPE_FORMULA: value = cell.getStringCellValue(); break; + case XSSFCell.CELL_TYPE_NUMERIC: value += cell.getRawValue(); break; + default: ; + + } + if(node instanceof Element){ + Element currentElement = (Element) node; + currentElement.setTextContent(value); + }else{ + node.setNodeValue(value); + } + + + } + + private String removeNamespace(String elementName){ + return elementName.matches(".*:.*")?elementName.split(":")[1]:elementName; + } + + + + private Node getNodeByXPath(String xpath,Node rootNode,Document doc,boolean createMultipleInstances){ + String[] xpathTokens = xpath.split("/"); + + + Node currentNode =rootNode; + // The first token is empty, the second is the root node + for(int i =2; i rightIndex){ + result = 1; + } + }else{ + // NOTE: the xpath doesn't match correctly in the schema + } + + + } + + } + + + + + return result; + } + + private int indexOfElementInComplexType(String elementName,Node complexType){ + + NodeList list = complexType.getChildNodes(); + int indexOf = -1; + + for(int i=0; i< list.getLength();i++){ + Node node = list.item(i); + if(node instanceof Element){ + if(node.getLocalName().equals("element")){ + Node nameAttribute = node.getAttributes().getNamedItem("name"); + if(nameAttribute.getNodeValue().equals(removeNamespace(elementName))){ + indexOf = i; + break; + } + + } + } + } + + return indexOf; + + } + + private Node getComplexTypeForElement(String elementName,Node xmlSchema,Node localComplexTypeRootNode){ + Node complexTypeNode = null; + + String elementNameWithoutNamespace = removeNamespace(elementName); + + + NodeList list = localComplexTypeRootNode.getChildNodes(); + String complexTypeName = ""; + + + + for(int i=0; i< list.getLength();i++){ + Node node = list.item(i); + if( node instanceof Element){ + if(node.getLocalName().equals("element")){ + Node nameAttribute = node.getAttributes().getNamedItem("name"); + if(nameAttribute.getNodeValue().equals(elementNameWithoutNamespace)){ + Node complexTypeAttribute = node.getAttributes().getNamedItem("type"); + if(complexTypeAttribute!=null){ + complexTypeName = complexTypeAttribute.getNodeValue(); + break; + } + } + } + } + } + // Note: we expect that all the complex types are defined at root level + if(!complexTypeName.equals("")){ + NodeList complexTypeList = xmlSchema.getChildNodes(); + for(int i=0; i< complexTypeList.getLength();i++){ + Node node = list.item(i); + if( node instanceof Element){ + if(node.getLocalName().equals("complexType")){ + Node nameAttribute = node.getAttributes().getNamedItem("name"); + if(nameAttribute.getNodeValue().equals(complexTypeName)){ + + NodeList complexTypeChildList =node.getChildNodes(); + for(int j=0; j maps ; + + public MapInfo() { + super(); + mapInfo = CTMapInfo.Factory.newInstance(); + + } + + public MapInfo(PackagePart part, PackageRelationship rel) + throws IOException { + super(part, rel); + readFrom(part.getInputStream()); + } + + public void readFrom(InputStream is) throws IOException { + try { + MapInfoDocument doc = MapInfoDocument.Factory.parse(is); + mapInfo = doc.getMapInfo(); + + maps= new HashMap(); + for(CTMap map :mapInfo.getMapArray()){ + maps.put((int)map.getID(), new XSSFMap(map,this)); + } + + } catch (XmlException e) { + throw new IOException(e.getLocalizedMessage()); + } + } + + /** + * Returns the parent XSSFWorkbook + * + * @return the parent XSSFWorkbook + */ + public XSSFWorkbook getWorkbook() { + return (XSSFWorkbook)getParent(); + } + + /** + * + * @return the internal data object + */ + public CTMapInfo getCTMapInfo(){ + return mapInfo; + + } + + /** + * Gets the CTSchema buy it's ID + * @param schemaId the schema ID + * @return + */ + public CTSchema getCTSchemaById(String schemaId){ + CTSchema xmlSchema = null; + + CTSchema[] schemas = mapInfo.getSchemaArray(); + for(CTSchema schema: schemas){ + if(schema.getID().equals(schemaId)){ + xmlSchema = schema; + break; + } + } + return xmlSchema; + } + + + public XSSFMap getXSSFMapById(int id){ + return maps.get(id); + } + + /** + * + * @return all the mappings configured in this document + */ + public Collection getAllXSSFMaps(){ + return maps.values(); + } + + protected void writeTo(OutputStream out) throws IOException { + MapInfoDocument doc = MapInfoDocument.Factory.newInstance(); + doc.setMapInfo(mapInfo); + doc.save(out, DEFAULT_XML_OPTIONS); + } + + @Override + protected void commit() throws IOException { + PackagePart part = getPackagePart(); + OutputStream out = part.getOutputStream(); + writeTo(out); + out.close(); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/model/SingleXmlCells.java b/src/ooxml/java/org/apache/poi/xssf/model/SingleXmlCells.java new file mode 100755 index 0000000000..9b118bfdf8 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/SingleXmlCells.java @@ -0,0 +1,106 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.xssf.model; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Vector; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell; +import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSingleXmlCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSingleXmlCells; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.SingleXmlCellsDocument; + + +/** + * + * This class implements the Single Cell Tables Part (Open Office XML Part 4: + * chapter 3.5.2) + * + * + * @author Roberto Manicardi + */ +public class SingleXmlCells extends POIXMLDocumentPart { + + + private CTSingleXmlCells singleXMLCells; + + public SingleXmlCells() { + super(); + singleXMLCells = CTSingleXmlCells.Factory.newInstance(); + + } + + public SingleXmlCells(PackagePart part, PackageRelationship rel) + throws IOException { + super(part, rel); + readFrom(part.getInputStream()); + } + + public void readFrom(InputStream is) throws IOException { + try { + SingleXmlCellsDocument doc = SingleXmlCellsDocument.Factory.parse(is); + singleXMLCells = doc.getSingleXmlCells(); + } catch (XmlException e) { + throw new IOException(e.getLocalizedMessage()); + } + } + + public XSSFSheet getXSSFSheet(){ + return (XSSFSheet) getParent(); + } + + protected void writeTo(OutputStream out) throws IOException { + SingleXmlCellsDocument doc = SingleXmlCellsDocument.Factory.newInstance(); + doc.setSingleXmlCells(singleXMLCells); + doc.save(out, DEFAULT_XML_OPTIONS); + } + + @Override + protected void commit() throws IOException { + PackagePart part = getPackagePart(); + OutputStream out = part.getOutputStream(); + writeTo(out); + out.close(); + } + + public CTSingleXmlCells getCTSingleXMLCells(){ + return singleXMLCells; + } + + /** + * + * @return all the SimpleXmlCell contained in this SingleXmlCells element + */ + public List getAllSimpleXmlCell(){ + List list = new Vector(); + CTSingleXmlCell[] singleXMLCellArray = singleXMLCells.getSingleXmlCellArray(); + + for(CTSingleXmlCell singleXmlCell: singleXMLCellArray){ + list.add(new XSSFSingleXmlCell(singleXmlCell,this)); + } + return list; + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/model/Table.java b/src/ooxml/java/org/apache/poi/xssf/model/Table.java new file mode 100755 index 0000000000..9ab24ac57a --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/Table.java @@ -0,0 +1,248 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.xssf.model; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr; +import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument; + +/** + * + * This class implements the Table Part (Open Office XML Part 4: + * chapter 3.5.1) + * + * This implementation works under the assumption that a table contains mappings to a subtree of an XML. + * The root element of this subtree an occur multiple times (one for each row of the table). The child nodes + * of the root element can be only attributes or element with maxOccurs=1 property set + * + * + * @author Roberto Manicardi + */ +public class Table extends POIXMLDocumentPart { + + private CTTable ctTable; + private List xmlColumnPr; + private CellReference startCellReference; + private CellReference endCellReference; + private String commonXPath; + + + public Table() { + super(); + ctTable = CTTable.Factory.newInstance(); + + } + + public Table(PackagePart part, PackageRelationship rel) + throws IOException { + super(part, rel); + readFrom(part.getInputStream()); + } + + public void readFrom(InputStream is) throws IOException { + try { + TableDocument doc = TableDocument.Factory.parse(is); + ctTable = doc.getTable(); + } catch (XmlException e) { + throw new IOException(e.getLocalizedMessage()); + } + } + + public XSSFSheet getXSSFSheet(){ + return (XSSFSheet) getParent(); + } + + public void writeTo(OutputStream out) throws IOException { + TableDocument doc = TableDocument.Factory.newInstance(); + doc.setTable(ctTable); + doc.save(out, DEFAULT_XML_OPTIONS); + } + + @Override + protected void commit() throws IOException { + PackagePart part = getPackagePart(); + OutputStream out = part.getOutputStream(); + writeTo(out); + out.close(); + } + + public CTTable getCTTable(){ + return ctTable; + } + + /** + * Checks if this Table element contains even a single mapping to the map identified by id + * @param id the XSSFMap ID + * @return true if the Table element contain mappings + */ + public boolean mapsTo(long id){ + boolean maps =false; + + List pointers = getXmlColumnPrs(); + + for(XSSFXmlColumnPr pointer: pointers){ + if(pointer.getMapId()==id){ + maps=true; + break; + } + } + + return maps; + } + + + /** + * + * Calculates the xpath of the root element for the table. This will be the common part + * of all the mapping's xpaths + * + * @return the xpath of the table's root element + */ + public String getCommonXpath() { + + if(commonXPath == null){ + + String[] commonTokens ={}; + + for(CTTableColumn column :ctTable.getTableColumns().getTableColumnArray()){ + if(column.getXmlColumnPr()!=null){ + String xpath = column.getXmlColumnPr().getXpath(); + String[] tokens = xpath.split("/"); + if(commonTokens.length==0){ + commonTokens = tokens; + + }else{ + int maxLenght = commonTokens.length>tokens.length? tokens.length:commonTokens.length; + for(int i =0; i subCommonTokens = Arrays.asList(commonTokens).subList(0, i); + + String[] container = {}; + + commonTokens = subCommonTokens.toArray(container); + break; + + + } + } + } + + } + } + + + commonXPath =""; + + for(int i = 1 ; i< commonTokens.length;i++){ + commonXPath +="/"+commonTokens[i]; + + } + } + + return commonXPath; + } + + + public List getXmlColumnPrs() { + + if(xmlColumnPr==null){ + xmlColumnPr = new Vector(); + for(CTTableColumn column:ctTable.getTableColumns().getTableColumnArray()){ + if(column.getXmlColumnPr()!=null){ + XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this,column,column.getXmlColumnPr()); + xmlColumnPr.add(columnPr); + } + } + } + return xmlColumnPr; + } + + /** + * the number of mapped table columns (see Open Office XML Part 4: chapter 3.5.1.4) + * @return + */ + public long getNumerOfMappedColumns(){ + return ctTable.getTableColumns().getCount(); + } + + + /** + * The reference for the cell in the top-left part of the table + * (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) + * @return + */ + public CellReference getStartCellReference() { + + if(startCellReference==null){ + String ref = ctTable.getRef(); + String[] boundaries = ref.split(":"); + String from = boundaries[0]; + startCellReference = new CellReference(from); + } + return startCellReference; + } + + /** + * The reference for the cell in the bottom-right part of the table + * (see Open Office XML Part 4: chapter 3.5.1.2, attribute ref) + * @return + */ + public CellReference getEndCellReference() { + + if(endCellReference==null){ + + String ref = ctTable.getRef(); + String[] boundaries = ref.split(":"); + String from = boundaries[1]; + endCellReference = new CellReference(from); + } + return endCellReference; + } + + + /** + * Gets the total number of rows in the selection. (Note: in this version autofiltering is ignored) + * @return + */ + public int getRowCount(){ + + + CellReference from = getStartCellReference(); + CellReference to = getEndCellReference(); + + int rowCount = -1; + if (from!=null && to!=null){ + rowCount = to.getRow()-from.getRow(); + } + return rowCount; + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFMap.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFMap.java new file mode 100755 index 0000000000..33c742f6e7 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFMap.java @@ -0,0 +1,120 @@ +/* ==================================================================== + 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 java.util.List; +import java.util.Vector; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.xssf.model.MapInfo; +import org.apache.poi.xssf.model.SingleXmlCells; +import org.apache.poi.xssf.model.Table; +import org.apache.poi.xssf.usermodel.helpers.XSSFSingleXmlCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMap; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSchema; +import org.w3c.dom.Node; + +/** + * This class implements the Map element (Open Office XML Part 4: + * chapter 3.16.2) + *

+ * This element contains all of the properties related to the XML map, + * and the behaviors expected during data refresh operations. + * + * @author Roberto Manicardi + */ + + +public class XSSFMap { + + private CTMap ctMap; + + private MapInfo mapInfo; + + + public XSSFMap(CTMap ctMap, MapInfo mapInfo) { + this.ctMap = ctMap; + this.mapInfo = mapInfo; + } + + + public CTMap getCtMap() { + return ctMap; + } + + + public CTSchema getCTSchema() { + String schemaId = ctMap.getSchemaID(); + return mapInfo.getCTSchemaById(schemaId); + } + + public Node getSchema() { + Node xmlSchema = null; + + CTSchema schema = getCTSchema(); + xmlSchema = schema.getDomNode().getFirstChild(); + + return xmlSchema; + } + + /** + * @return the list of Single Xml Cells that provide a map rule to this mapping. + */ + public List getRelatedSingleXMLCell() { + List relatedSimpleXmlCells = new Vector(); + + int sheetNumber = mapInfo.getWorkbook().getNumberOfSheets(); + for (int i = 0; i < sheetNumber; i++) { + XSSFSheet sheet = mapInfo.getWorkbook().getSheetAt(i); + for (POIXMLDocumentPart p : sheet.getRelations()) { + if (p instanceof SingleXmlCells) { + SingleXmlCells singleXMLCells = (SingleXmlCells) p; + for (XSSFSingleXmlCell cell : singleXMLCells.getAllSimpleXmlCell()) { + if (cell.getMapId() == ctMap.getID()) { + relatedSimpleXmlCells.add(cell); + } + } + } + } + } + return relatedSimpleXmlCells; + } + + /** + * @return the list of all Tables that provide a map rule to this mapping + */ + public List

getRelatedTables() { + + List
tables = new Vector
(); + int sheetNumber = mapInfo.getWorkbook().getNumberOfSheets(); + + for (int i = 0; i < sheetNumber; i++) { + XSSFSheet sheet = mapInfo.getWorkbook().getSheetAt(i); + for (POIXMLDocumentPart p : sheet.getRelations()) { + if (p.getPackageRelationship().getRelationshipType().equals(XSSFRelation.TABLE.getRelation())) { + Table table = (Table) p; + if (table.mapsTo(ctMap.getID())) { + tables.add(table); + } + } + } + } + + return tables; + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java index 73fe263a26..c3b97c6c75 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java @@ -25,10 +25,13 @@ import java.util.HashMap; import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLRelation; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.xssf.model.MapInfo; +import org.apache.poi.xssf.model.SingleXmlCells; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.CalculationChain; +import org.apache.poi.xssf.model.Table; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; @@ -93,6 +96,28 @@ public final class XSSFRelation extends POIXMLRelation { "/xl/drawings/vmlDrawing#.vml", null ); + + public static final XSSFRelation CUSTOM_XML_MAPPINGS = new XSSFRelation( + "application/xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/xmlMaps", + "/xl/xmlMaps.xml", + MapInfo.class + ); + + public static final XSSFRelation SINGLE_XML_CELLS = new XSSFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableSingleCells", + "/tables/tableSingleCells#.xml", + SingleXmlCells.class + ); + + public static final XSSFRelation TABLE = new XSSFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table", + "/tables/table#.xml", + Table.class + ); + public static final XSSFRelation IMAGES = new XSSFRelation( null, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 5d509260e3..5453b33b14 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -22,12 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Pattern; import javax.xml.namespace.QName; @@ -56,6 +51,8 @@ import org.apache.poi.util.PackageHelper; import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.model.MapInfo; +import org.apache.poi.xssf.extractor.XSSFExportToXml; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; @@ -106,6 +103,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable getCustomXMLMappings(){ + return mapInfo == null ? new ArrayList() : mapInfo.getAllXSSFMaps(); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFSingleXmlCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFSingleXmlCell.java new file mode 100755 index 0000000000..055f2b8145 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFSingleXmlCell.java @@ -0,0 +1,84 @@ +/* ==================================================================== + 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.helpers; + +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.model.SingleXmlCells; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSingleXmlCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlCellPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType.Enum; + +/** + * + * This class is a wrapper around the CTSingleXmlCell (Open Office XML Part 4: + * chapter 3.5.2.1) + * + + * + * @author Roberto Manicardi + * + */ +public class XSSFSingleXmlCell { + + private CTSingleXmlCell singleXmlCell; + private SingleXmlCells parent; + + + public XSSFSingleXmlCell(CTSingleXmlCell singleXmlCell, SingleXmlCells parent){ + this.singleXmlCell = singleXmlCell; + this.parent = parent; + } + + /** + * Gets the XSSFCell referenced by the R attribute + * @return the referenced XSSFCell, null if the cell reference is invalid + */ + public XSSFCell getReferencedCell(){ + XSSFCell cell = null; + + + CellReference cellReference = new CellReference(singleXmlCell.getR()); + + XSSFRow row = parent.getXSSFSheet().getRow(cellReference.getRow()); + cell = row.getCell(cellReference.getCol()); + + + return cell; + } + + public String getXpath(){ + CTXmlCellPr xmlCellPr = singleXmlCell.getXmlCellPr(); + CTXmlPr xmlPr = xmlCellPr.getXmlPr(); + String xpath = xmlPr.getXpath(); + return xpath; + } + + public long getMapId(){ + return singleXmlCell.getXmlCellPr().getXmlPr().getMapId(); + } + + public Enum getXmlDataType() { + CTXmlCellPr xmlCellPr = singleXmlCell.getXmlCellPr(); + CTXmlPr xmlPr = xmlCellPr.getXmlPr(); + return xmlPr.getXmlDataType(); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFXmlColumnPr.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFXmlColumnPr.java new file mode 100755 index 0000000000..c03b56e250 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFXmlColumnPr.java @@ -0,0 +1,65 @@ +/* ==================================================================== + 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.helpers; + +import org.apache.poi.xssf.model.Table; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXmlColumnPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXmlDataType.Enum; + + +/** + * + * This class is a wrapper around the CTXmlColumnPr (Open Office XML Part 4: + * chapter 3.5.1.7) + * + * + * @author Roberto Manicardi + */ +public class XSSFXmlColumnPr { + + private Table table; + private CTTableColumn ctTableColumn; + private CTXmlColumnPr ctXmlColumnPr; + + public XSSFXmlColumnPr(Table table ,CTTableColumn ctTableColum,CTXmlColumnPr ctXmlColumnPr){ + this.table = table; + this.ctTableColumn = ctTableColum; + this.ctXmlColumnPr = ctXmlColumnPr; + } + + public long getMapId(){ + return ctXmlColumnPr.getMapId(); + } + + public String getLocalXPath(){ + String localXPath = ""; + int numberOfCommonXPathAxis = table.getCommonXpath().split("/").length-1; + + String[] xPathTokens = ctXmlColumnPr.getXpath().split("/"); + for(int i=numberOfCommonXPathAxis; i")[1].split("")[0].trim(); + String nome = xml.split("")[1].split("")[0].trim(); + String tutor = xml.split("")[1].split("")[0].trim(); + String cdl = xml.split("")[1].split("")[0].trim(); + String durata = xml.split("")[1].split("")[0].trim(); + String argomento = xml.split("")[1].split("")[0].trim(); + String progetto = xml.split("")[1].split("")[0].trim(); + String crediti = xml.split("")[1].split("")[0].trim(); + + assertEquals("ro",docente); + assertEquals("ro",nome); + assertEquals("ds",tutor); + assertEquals("gs",cdl); + assertEquals("g",durata); + assertEquals("gvvv",argomento); + assertEquals("aaaa",progetto); + assertEquals("aa",crediti); + + } + + } + + } + + + public void testExportToXMLInverseOrder() throws Exception{ + + + + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXmlMappings-inverse-order.xlsx"); + + MapInfo mapInfo = null; + + for(POIXMLDocumentPart p : wb.getRelations()){ + + + if(p instanceof MapInfo){ + mapInfo = (MapInfo) p; + + + XSSFMap map = mapInfo.getXSSFMapById(1); + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os,true); + String xml = os.toString("UTF-8"); + + assertNotNull(xml); + assertTrue(!xml.equals("")); + + String docente = xml.split("")[1].split("")[0].trim(); + String nome = xml.split("")[1].split("")[0].trim(); + String tutor = xml.split("")[1].split("")[0].trim(); + String cdl = xml.split("")[1].split("")[0].trim(); + String durata = xml.split("")[1].split("")[0].trim(); + String argomento = xml.split("")[1].split("")[0].trim(); + String progetto = xml.split("")[1].split("")[0].trim(); + String crediti = xml.split("")[1].split("")[0].trim(); + + assertEquals("aa",nome); + assertEquals("aaaa",docente); + assertEquals("gvvv",tutor); + assertEquals("g",cdl); + assertEquals("gs",durata); + assertEquals("ds",argomento); + assertEquals("ro",progetto); + assertEquals("ro",crediti); + + } + + } + + + + + + } + + public void testXPathOrdering() throws Exception{ + + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXmlMappings-inverse-order.xlsx"); + + MapInfo mapInfo = null; + + for(POIXMLDocumentPart p : wb.getRelations()){ + + + if(p instanceof MapInfo){ + mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(1); + XSSFExportToXml exporter = new XSSFExportToXml(map); + + assertEquals(1,exporter.compare("/CORSO/DOCENTE", "/CORSO/NOME")); + + assertEquals(-1,exporter.compare("/CORSO/NOME", "/CORSO/DOCENTE")); + + + } + } + } + + + + + public void testMultiTable() throws Exception{ + + + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXMLMappings-complex-type.xlsx"); + + for(POIXMLDocumentPart p : wb.getRelations()){ + + + if(p instanceof MapInfo){ + MapInfo mapInfo = (MapInfo) p; + + XSSFMap map = mapInfo.getXSSFMapById(2); + + assertNotNull(map); + + XSSFExportToXml exporter = new XSSFExportToXml(map); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + exporter.exportToXML(os,true); + String xml = os.toString("UTF-8"); + + assertNotNull(xml); + + String[] regexConditions = { "", + "", + "", + "DataBinding", + "Map Append=\"false\" AutoFit=\"false\" ID=\"1\"", + "Map Append=\"false\" AutoFit=\"false\" ID=\"5\"" + + }; + + for(String condition : regexConditions){ + Pattern pattern = Pattern.compile(condition); + Matcher matcher = pattern.matcher(xml); + assertTrue(matcher.find()); + } + + + } + } + + + } + + + +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestMapInfo.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestMapInfo.java new file mode 100755 index 0000000000..35c80f06d7 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestMapInfo.java @@ -0,0 +1,84 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.xssf.model; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.xssf.XSSFTestDataSamples; +import org.apache.poi.xssf.usermodel.XSSFMap; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSchema; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTMapInfo; +import org.w3c.dom.Node; + +import junit.framework.TestCase; + +/** + * @author Roberto Manicardi + */ +public class TestMapInfo extends TestCase { + + + public void testMapInfoExists() throws Exception { + + + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("CustomXmlMappings.xlsx"); + + MapInfo mapInfo = null; + SingleXmlCells singleXMLCells = null; + + for (POIXMLDocumentPart p : wb.getRelations()) { + + + if (p instanceof MapInfo) { + mapInfo = (MapInfo) p; + + + CTMapInfo ctMapInfo = mapInfo.getCTMapInfo(); + + assertNotNull(ctMapInfo); + + CTSchema[] schemas = ctMapInfo.getSchemaArray(); + assertEquals(1, schemas.length); + + + for (XSSFMap map : mapInfo) { + Node xmlSchema = map.getSchema(); + assertNotNull(xmlSchema); + } + } + } + + XSSFSheet sheet1 = wb.getSheetAt(0); + + for (POIXMLDocumentPart p : sheet1.getRelations()) { + + if (p instanceof SingleXmlCells) { + singleXMLCells = (SingleXmlCells) p; + } + + } + + + assertNotNull(mapInfo); + assertNotNull(singleXMLCells); + + + } + + +} diff --git a/src/testcases/org/apache/poi/hssf/data/CustomXMLMappings-complex-type.xlsx b/src/testcases/org/apache/poi/hssf/data/CustomXMLMappings-complex-type.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..0d6d8d00990b979ddb0a7092c2320c2a3c9486a2 GIT binary patch literal 13348 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 Ql`Rfn0~Ut$cfVi#59?LEUH||9 literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/data/CustomXmlMappings-inverse-order.xlsx b/src/testcases/org/apache/poi/hssf/data/CustomXmlMappings-inverse-order.xlsx new file mode 100755 index 0000000000000000000000000000000000000000..f3acef335919956fdd65c4f0063f11698d3e34b0 GIT binary patch literal 9915 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 a_@A{$Q5FsoL*I9|7=SHE7`9V