]> source.dussan.org Git - poi.git/commitdiff
Custom XML import features; tests and implementation
authorPaolo Mottadelli <paolo@apache.org>
Thu, 30 Jul 2009 13:24:57 +0000 (13:24 +0000)
committerPaolo Mottadelli <paolo@apache.org>
Thu, 30 Jul 2009 13:24:57 +0000 (13:24 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@799258 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExportToXml.java
src/ooxml/java/org/apache/poi/xssf/extractor/XSSFImportFromXML.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/model/MapInfo.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFSingleXmlCell.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFXmlColumnPr.java
src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFImportFromXML.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/CustomXMLMapping-singleattributenamespace.xlsx [new file with mode: 0644]

index 01d96832b2fc43de1b227db257e168ab2f71bd78..6ca8ba6da91574235942d27ecd9d91aabc9fcd28 100755 (executable)
@@ -65,7 +65,7 @@ import org.xml.sax.SAXException;
  * The output XML Schema must respect this limitations:
  * 
  * <ul>
- * <li> all mandatory elements and attributes must be mapped </li>
+ * <li> all mandatory elements and attributes must be mapped (enable validation to check this)</li>
  * 
  * <li> no &lt;any&gt; in complex type/element declaration </li>
  * <li> no &lt;anyAttribute&gt; attributes declaration </li>
diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFImportFromXML.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFImportFromXML.java
new file mode 100644 (file)
index 0000000..fc1166b
--- /dev/null
@@ -0,0 +1,209 @@
+/* ====================================================================
+   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.StringReader;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+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.helpers.XSSFSingleXmlCell;
+import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import com.sun.org.apache.xml.internal.utils.PrefixResolver;
+import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
+
+
+/**
+ * 
+ *  Imports data from an external XML to an XLSX to an XML according to one of the mapping defined.
+ *  
+ *  The output XML Schema must respect this limitations:
+ *  
+ *  - the input XML must be valid according to the XML Schema used in the mapping
+ *  - denormalized table mapping is not supported (see OpenOffice part 4: chapter 3.5.1.7)
+ *  - all the namespaces used in the document must be declared in the root node 
+ *  
+ * 
+ * @author Roberto Manicardi
+ *
+ */
+public class XSSFImportFromXML {
+       
+       private XSSFMap map;
+       
+       private static POILogger logger = POILogFactory.getLogger(XSSFImportFromXML.class);
+       
+       public XSSFImportFromXML(XSSFMap map){
+               
+               this.map = map;
+               
+               
+       }
+       
+       /**
+        * Imports an XML into the XLSX using the Custom XML mapping defined
+        * 
+        * @param xmlInputString the XML to import
+        * @throws SAXException raised if error occurs during XML parsing
+        * @throws XPathExpressionException raised if error occurs during XML navigation
+        */
+       public void importFromXML(String xmlInputString) throws  SAXException, XPathExpressionException{
+               
+               try{
+                       
+               DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+               factory.setNamespaceAware(true); 
+               DocumentBuilder builder = factory.newDocumentBuilder();
+               
+               Document doc = builder.parse(new InputSource(new StringReader(xmlInputString.trim())));
+               
+               List<XSSFSingleXmlCell> singleXmlCells = map.getRelatedSingleXMLCell();
+               
+               List<Table> tables = map.getRelatedTables();
+               
+               XPathFactory xpathFactory = XPathFactory.newInstance();
+               XPath xpath = xpathFactory.newXPath();
+               
+               
+               
+               // Setting namespace context to XPath
+               // Assuming that the namespace prefix in the mapping xpath is the same as the one used in the document
+               final PrefixResolver resolver = new PrefixResolverDefault(doc.getDocumentElement());
+               
+               NamespaceContext ctx = new NamespaceContext() {
+               
+                      public String getNamespaceURI(String prefix) {
+                             return resolver.getNamespaceForPrefix(prefix);
+                      }
+                      // Dummy implementation - not used!
+                      public Iterator getPrefixes(String val) {
+                          return null;
+                      }
+                      // Dummy implemenation - not used!
+                      public String getPrefix(String uri) {
+                          return null;
+                      }
+               };
+               xpath.setNamespaceContext(ctx);
+
+
+               
+               
+               for(XSSFSingleXmlCell singleXmlCell :singleXmlCells ){
+               
+                       
+                       
+                       String xpathString = singleXmlCell.getXpath();
+                       
+                       Node result =  (Node) xpath.evaluate(xpathString,doc, XPathConstants.NODE);             
+                       String textContent = result.getTextContent();
+                       logger.log(POILogger.DEBUG,"Extracting with xpath "+xpathString+" : value is '"+textContent+"'");
+                       XSSFCell cell = singleXmlCell.getReferencedCell();
+                       logger.log(POILogger.DEBUG,"Setting '"+textContent+"' to cell "+cell.getColumnIndex()+"-"+cell.getRowIndex()+" in sheet "+cell.getSheet().getSheetName());
+                       cell.setCellValue(textContent);
+                       
+                       
+                       
+                       
+               }
+               
+               for(Table table : tables){
+                       
+                       String commonXPath = table.getCommonXpath();
+               
+                       NodeList result =  (NodeList) xpath.evaluate(commonXPath,doc, XPathConstants.NODESET);
+                       
+                       int rowOffset = table.getStartCellReference().getRow()+1;//the first row contains the table header
+                       int columnOffset = table.getStartCellReference().getCol()-1;
+                       
+                       for(int i = 0; i< result.getLength();i++){
+                               
+                               // TODO: implement support for denormalized XMLs (see OpenOffice part 4: chapter 3.5.1.7)
+                               
+                               for(XSSFXmlColumnPr xmlColumnPr: table.getXmlColumnPrs()){
+                                       
+                                       int localColumnId = (int)xmlColumnPr.getId();
+                                       
+                                       int rowId = rowOffset+i;
+                                       int columnId = columnOffset+localColumnId;
+                                       
+                                       
+                                       String localXPath = xmlColumnPr.getLocalXPath();
+                                       localXPath = localXPath.substring(localXPath.substring(1).indexOf('/')+1);
+                                       
+                                       // Build an XPath to select the right node (assuming that the commonXPath != "/")
+                                       String nodeXPath = commonXPath+"["+(i+1)+"]"+localXPath;
+                                       
+                                       
+                                       
+                                       // TODO: convert the data to the cell format    
+                                       String value = (String) xpath.evaluate(nodeXPath,result.item(i), XPathConstants.STRING);
+                                       logger.log(POILogger.DEBUG,"Extracting with xpath "+nodeXPath+" : value is '"+value+"'");
+                                       XSSFRow row = table.getXSSFSheet().getRow(rowId);
+                                       if(row==null){
+                                               row = table.getXSSFSheet().createRow(rowId);
+                                       }
+                                       
+                                       XSSFCell cell = row.getCell(columnId);
+                                       if(cell==null){
+                                               cell = row.createCell(columnId);
+                                       }
+                                       logger.log(POILogger.DEBUG,"Setting '"+value+"' to cell "+cell.getColumnIndex()+"-"+cell.getRowIndex()+" in sheet "+table.getXSSFSheet().getSheetName());
+                                       cell.setCellValue(value.trim());
+                                       
+                               }
+                                       
+                               
+                               
+                       }
+                       
+                       
+                       
+               }
+               }catch(IOException e){
+                       //Thrown by StringReader
+                       e.printStackTrace();
+               }catch(ParserConfigurationException e){
+                       //Thrown by DocumentBuilderFactory
+                       e.printStackTrace();
+               }
+               
+       }
+       
+
+}
index d301f18a16451b65b36eae6341326d8692186c88..f1243d3e1c51c97cbb51e8454f8e9734e4602077 100755 (executable)
@@ -23,7 +23,6 @@ import java.io.OutputStream;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Iterator;
 
 
 import org.apache.poi.POIXMLDocumentPart;
@@ -123,6 +122,19 @@ public class MapInfo extends POIXMLDocumentPart {
                return maps.get(id);
        }
        
+       public XSSFMap getXSSFMapByName(String name){
+               
+               XSSFMap matchedMap = null;
+               
+               for(XSSFMap map :maps.values()){
+                       if(map.getCtMap().getName()!=null && map.getCtMap().getName().equals(name)){
+                               matchedMap = map;
+                       }
+               }               
+               
+               return matchedMap;
+       }
+       
        /**
         * 
         * @return all the mappings configured in this document
index 3ff17e456496c20ae8c46b98817174c896e85aaf..e3961a51d8851e675e6784155b89685436c0fe4e 100644 (file)
@@ -1297,4 +1297,13 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
     public Collection<XSSFMap> getCustomXMLMappings(){
         return mapInfo == null ? new ArrayList<XSSFMap>() : mapInfo.getAllXSSFMaps();
     }
+    
+    /**
+     * 
+     * @return the helper class used to query the custom XML mapping defined in this workbook
+     */
+    public MapInfo getMapInfo(){
+       return mapInfo;
+    }
+    
 }
index 055f2b8145d5e08ca1e022a707db6184fcee181c..b91b20b34e83ad47b09bbaf164c81fb3f009fbd4 100755 (executable)
@@ -48,7 +48,7 @@ public class XSSFSingleXmlCell {
        }
        
        /**
-        * Gets the XSSFCell referenced by the R attribute
+        * Gets the XSSFCell referenced by the R attribute or creates a new one if cell doesn't exists
         * @return the referenced XSSFCell, null if the cell reference is invalid
         */
        public XSSFCell getReferencedCell(){
@@ -58,7 +58,14 @@ public class XSSFSingleXmlCell {
                CellReference cellReference =  new CellReference(singleXmlCell.getR()); 
                
                XSSFRow row = parent.getXSSFSheet().getRow(cellReference.getRow());
+               if(row==null){
+                       row = parent.getXSSFSheet().createRow(cellReference.getRow());
+               }
+               
                cell = row.getCell(cellReference.getCol());  
+               if(cell==null){
+                       cell = row.createCell(cellReference.getCol());
+               }
                
                
                return cell;
index c03b56e250777dfc6d39988f2176b146afa7936c..42b67471b716b45e72a2fba5d795d435d8954944 100755 (executable)
@@ -47,6 +47,23 @@ public class XSSFXmlColumnPr {
                return ctXmlColumnPr.getMapId();
        }
        
+       public String getXPath(){
+               return ctXmlColumnPr.getXpath();
+       }
+       /**
+        * (see Open Office XML Part 4: chapter 3.5.1.3)
+        * @return An integer representing the unique identifier of this column. 
+        */
+       public long getId(){
+               return ctTableColumn.getId();
+       }
+       
+       
+       /**
+        * If the XPath is, for example, /Node1/Node2/Node3 and /Node1/Node2 is the common XPath for the table, the local XPath is /Node3
+        *      
+        * @return the local XPath 
+        */
        public String getLocalXPath(){
                String localXPath = "";
                int numberOfCommonXPathAxis = table.getCommonXpath().split("/").length-1;
@@ -59,7 +76,12 @@ public class XSSFXmlColumnPr {
        }
 
        public Enum getXmlDataType() {
+               
                return ctXmlColumnPr.getXmlDataType();
        }
+       
+       
+       
+       
 
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFImportFromXML.java b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFImportFromXML.java
new file mode 100644 (file)
index 0000000..04c63fb
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFImportFromXML.java differ
diff --git a/src/testcases/org/apache/poi/hssf/data/CustomXMLMapping-singleattributenamespace.xlsx b/src/testcases/org/apache/poi/hssf/data/CustomXMLMapping-singleattributenamespace.xlsx
new file mode 100644 (file)
index 0000000..635dea7
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/CustomXMLMapping-singleattributenamespace.xlsx differ