]> gerrit.simantics Code Review - simantics/interop.git/blobdiff - org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java
refs #3842
[simantics/interop.git] / org.simantics.interop.xmlio / src / org / simantics / interop / xmlio / SaveXML.java
diff --git a/org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java b/org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java
new file mode 100644 (file)
index 0000000..bcbd527
--- /dev/null
@@ -0,0 +1,646 @@
+package org.simantics.interop.xmlio;\r
+\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStreamWriter;\r
+import java.io.PrintWriter;\r
+import java.io.Writer;\r
+import java.util.Arrays;\r
+import java.util.Calendar;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.apache.xml.serialize.OutputFormat;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.layer0.DatabaseManagementResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.xml.sax.Attributes;\r
+import org.xml.sax.Locator;\r
+import org.xml.sax.SAXException;\r
+import org.xml.sax.SAXParseException;\r
+import org.xml.sax.ext.LexicalHandler;\r
+import org.xml.sax.ext.Locator2;\r
+import org.xml.sax.helpers.AttributesImpl;\r
+import org.xml.sax.helpers.DefaultHandler;\r
+\r
+public class SaveXML extends DefaultHandler implements LexicalHandler {\r
+       \r
+       private Session session;\r
+       private Resource root;\r
+       private SaveRule saveRule;\r
+       \r
+       \r
+       private File file;\r
+       \r
+       private PrintWriter fOut;\r
+       /** Canonical output. */\r
+       protected boolean fCanonical = true;\r
+         /** Element depth. */\r
+    protected int fElementDepth;\r
+    \r
+    /** Document locator. */\r
+    protected Locator fLocator;\r
+    \r
+    /** Processing XML 1.1 document. */\r
+    protected boolean fXML11 = true;\r
+    \r
+    /** In CDATA section. */\r
+    protected boolean fInCDATA;\r
+    \r
+       \r
+       public SaveXML(Session session, Resource root, File file) {\r
+               this.session = session;\r
+               this.root = root;\r
+               this.file = file;\r
+               this.saveRule = new DependsOnSaveRule();\r
+       }\r
+       \r
+       public void setSaveRule(SaveRule saveRule) {\r
+               this.saveRule = saveRule;\r
+       }\r
+       \r
+       public void save() throws DatabaseException, IOException, SAXException {\r
+               fCanonical = false;\r
+               FileOutputStream fos = new FileOutputStream(file);\r
+           fOut = new PrintWriter(new OutputStreamWriter(fos, "UTF8"));\r
+               \r
+           startDocument();\r
+           fXML11 = true;\r
+           AttributesImpl attrs = new AttributesImpl();\r
+           addDate(attrs);\r
+           startElement("", "", "graphexport", attrs);\r
+           saveGraphBundles();\r
+           saveData();\r
+           endElement("", "", "graphexport");\r
+           endDocument();\r
+           fOut.close();\r
+       }\r
+       \r
+       private void addDate(AttributesImpl attrs) {\r
+               Calendar calendar = Calendar.getInstance();\r
+               int year = calendar.get(Calendar.YEAR);\r
+               int month = calendar.get(Calendar.MONTH) + 1;\r
+               int day = calendar.get(Calendar.DAY_OF_MONTH);\r
+               int hour = calendar.get(Calendar.HOUR_OF_DAY);\r
+               int min = calendar.get(Calendar.MINUTE);\r
+               int sec = calendar.get(Calendar.SECOND);\r
+       String date = "";\r
+       date += year;\r
+       date += "-";\r
+       if (month < 10)\r
+               date+="0";\r
+       date += month;\r
+       date += "-";\r
+       if (day < 10)\r
+               date+="0";\r
+       date += day;\r
+       date += "T";\r
+       if (hour < 10)\r
+               date+="0";\r
+       date += hour;\r
+       date += ":";\r
+       if (min < 10)\r
+               date+="0";\r
+       date += min;\r
+       date += ":";\r
+       if (sec < 10)\r
+               date+="0";\r
+       date += sec;\r
+       attrs.addAttribute("", "", "date", "CDATA", date);\r
+       }\r
+       \r
+       \r
+       Map<Resource,Integer> idMap = new HashMap<Resource, Integer>();\r
+       \r
+       Set<Resource> processed = new HashSet<Resource>();\r
+       Stack<Resource> stack = new Stack<Resource>();\r
+       Layer0 l0;\r
+       int statements = 0;\r
+       \r
+       private void saveData() throws DatabaseException {\r
+       idMap.clear();\r
+       processed.clear();\r
+       stack.clear();\r
+       statements = 0;\r
+       session.syncRequest(new ReadRequest() {\r
+               \r
+               @Override\r
+               public void run(ReadGraph graph) throws DatabaseException {\r
+                       l0 = Layer0.getInstance(graph);\r
+                       saveRule.init(graph);\r
+                       stack.push(root);\r
+               }\r
+       });\r
+       try {\r
+               while (!stack.isEmpty()) {\r
+                       session.syncRequest(new ReadRequest() {\r
+                               \r
+                               @Override\r
+                               public void run(ReadGraph graph) throws DatabaseException {\r
+                                       \r
+                                       try {\r
+                                       \r
+                                       \r
+                                       AttributesImpl attrs = new AttributesImpl();\r
+                                       \r
+                                       \r
+                                       while (!stack.isEmpty()) {\r
+                                               Resource r = stack.pop();\r
+                                               if (processed.contains(r))\r
+                                                       continue;\r
+                                               processed.add(r);\r
+                                               Collection<Statement> statement = graph.getStatements(r, l0.IsWeaklyRelatedTo);\r
+                                               boolean split = false;\r
+                                               for (Statement s : statement) { \r
+                                                       if (s.isAsserted(r))\r
+                                                               continue;\r
+                                                       if (!saveRule.save(graph, s))\r
+                                                               continue;\r
+                                                       // System.out.println("Processing " + s.getSubject() + " " + s.getPredicate() + " " + s.getObject());\r
+                                                       \r
+                                                       statements++;\r
+                                                       int sId = getId(graph, s.getSubject(),false);\r
+                                                       int pId = getId(graph, s.getPredicate(),true);\r
+                                                       int oId = getId(graph, s.getObject(),false);\r
+                                                       attrs.clear();\r
+                                                       attrs.addAttribute("", "", "subject", "CDATA", Integer.toString(sId));\r
+                                                       attrs.addAttribute("", "", "predicate", "CDATA", Integer.toString(pId));\r
+                                                       attrs.addAttribute("", "", "object", "CDATA", Integer.toString(oId));\r
+                                                       startElement("", "", "statement", attrs);\r
+                                                       endElement("", "", "statement");\r
+                                                       if (!processed.contains(s.getObject()))\r
+                                                               stack.push(s.getObject());\r
+                                                       if (statements % 10000 == 0) {\r
+                                                               System.out.println("Processed " + statements + " statements...");\r
+                                                               split = true;\r
+                                                       }\r
+                                                       \r
+                                               }\r
+                                               if (split)\r
+                                                       return;\r
+                                               \r
+                                       }\r
+                                       System.out.println("Done. Processed " + statements + " statements.");\r
+                                       } catch (SAXException e) {\r
+                                               throw new DatabaseException(e); \r
+                                       }\r
+                               \r
+                               }\r
+                       });\r
+               \r
+               }\r
+       } finally {\r
+               idMap.clear();\r
+               processed.clear();\r
+               stack.clear();\r
+       }\r
+       \r
+}\r
+       \r
+       private int getId(ReadGraph g, Resource r, boolean rel) throws SAXException, DatabaseException {\r
+               Integer id = idMap.get(r);\r
+               if (id == null) {\r
+                       id = idMap.size();\r
+                       idMap.put(r, id);\r
+                       AttributesImpl attrs = new AttributesImpl();\r
+                       attrs.addAttribute("", "", "id", "CDATA", id.toString());\r
+                       if (rel) {\r
+                               attrs.addAttribute("", "", "rel", "CDATA", Boolean.TRUE.toString());\r
+                               String uri = g.getPossibleURI(r);\r
+                               if (uri != null)\r
+                                       attrs.addAttribute("", "", "uri", "CDATA", uri);\r
+                       }\r
+                       \r
+                       startElement("", "", "resource", attrs);\r
+                       if (!rel) {\r
+                               Layer0 l0 = Layer0.getInstance(g);\r
+                               //Collection<Resource> types = g.getPrincipalTypes(r);\r
+                               Collection<Resource> types = g.getObjects(r, l0.InstanceOf);\r
+                               attrs.clear();\r
+                               for (Resource type : types) {\r
+                                       String uri = g.getPossibleURI(type);\r
+                                       if (uri != null) {\r
+                                               attrs.addAttribute("", "", "uri", "CDATA", uri);\r
+                                               startElement("", "", "type", attrs);\r
+                                               endElement("", "", "type");\r
+                                       } else {\r
+                                               // TODO : handle URIless types.\r
+                                               throw new DatabaseException("Cannot resolve URI for type " + type + ", instance " + r); \r
+                                       }\r
+                               }\r
+                       }\r
+                       if (g.hasValue(r)) {\r
+                               // FIXME: does not handle arrays.\r
+                               Object value = g.getValue(r);\r
+                               \r
+                               if (value instanceof Object[]) {\r
+                                       Object[] valuearray = (Object[])value;\r
+                                       for (Object o : valuearray) {\r
+                                               attrs.clear();\r
+                                               attrs.addAttribute("", "", "value", "CDATA", o.toString());\r
+                                               startElement("", "", "value", attrs);\r
+                                               endElement("", "", "value");\r
+                                       }\r
+                               } else {\r
+                                       attrs.clear();\r
+                                       attrs.addAttribute("", "", "value", "CDATA", value.toString());\r
+                                       startElement("", "", "value", attrs);\r
+                                       endElement("", "", "value");\r
+                               }\r
+                       }\r
+                       endElement("", "", "resource");\r
+                       \r
+               }\r
+               return id;\r
+       }\r
+       \r
+       \r
+       private void saveGraphBundles() throws DatabaseException{\r
+               final Resource rootLibrary = session.getRootLibrary();\r
+               session.syncRequest(new ReadRequest() {\r
+                       \r
+                       @Override\r
+                       public void run(ReadGraph graph) throws DatabaseException {\r
+                               Layer0 l0 = Layer0.getInstance(graph);\r
+                               DatabaseManagementResource dm = DatabaseManagementResource.getInstance(graph);\r
+                               Collection<Resource> objs = graph.getObjects(rootLibrary, l0.ConsistsOf);\r
+                               Resource graphBundles = null;\r
+                               for (Resource o : objs) {\r
+                                       if (isGraphBundleLib(graph, rootLibrary)) {\r
+                                               graphBundles = o;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (graphBundles == null)\r
+                                       graphBundles = dm.InstalledGraphBundles;\r
+                                       \r
+                               objs = graph.getObjects(graphBundles, l0.ConsistsOf);\r
+                               AttributesImpl attrs = new AttributesImpl();\r
+                               try {\r
+                               startElement("", "", "graphbundles", attrs);\r
+                               for (Resource graphBundle : objs) {\r
+                                       if (!graph.isInstanceOf(graphBundle, dm.GraphBundle))\r
+                                               continue;\r
+                                       String name = graph.getRelatedValue(graphBundle, l0.HasName);\r
+                                       String versionId = graph.getRelatedValue(graphBundle, dm.HasVersionedId);\r
+                                       attrs.clear();\r
+                                       attrs.addAttribute("", "", "name", "CDATA", name);\r
+                                       attrs.addAttribute("", "", "versionid", "CDATA", versionId);\r
+                                       startElement("", "", "bundle", attrs);\r
+                                       endElement("", "", "bundle");\r
+                               }\r
+                               \r
+                               endElement("", "", "graphbundles");\r
+                               } catch (SAXException e) {\r
+                                       throw new DatabaseException(e);\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+       \r
+       private boolean isGraphBundleLib(ReadGraph graph, Resource o) throws DatabaseException {\r
+               Layer0 l0 = Layer0.getInstance(graph);\r
+               String name = graph.getPossibleRelatedValue(o, l0.HasName);\r
+               return "InstalledGraphBundles".equals(name);\r
+       }\r
+       \r
+       \r
+       \r
+    /** Set Document Locator. */\r
+    public void setDocumentLocator(Locator locator) {\r
+       fLocator = locator;\r
+    } // setDocumentLocator(Locator)\r
+       \r
+    /** Start document. */\r
+    public void startDocument() throws SAXException {\r
+\r
+        fElementDepth = 0;\r
+        fXML11 = false;\r
+        fInCDATA = false;\r
+        \r
+    } // startDocument()\r
+\r
+    /** Processing instruction. */\r
+    public void processingInstruction(String target, String data)\r
+        throws SAXException {\r
+\r
+        if (fElementDepth > 0) {\r
+            fOut.print("<?");\r
+            fOut.print(target);\r
+            if (data != null && data.length() > 0) {\r
+                fOut.print(' ');\r
+                fOut.print(data);\r
+            }\r
+            fOut.print("?>");\r
+            fOut.flush();\r
+        }\r
+\r
+    } // processingInstruction(String,String)\r
+\r
+    /** Start element. */\r
+    public void startElement(String uri, String local, String raw,\r
+                             Attributes attrs) throws SAXException {\r
+\r
+        // Root Element\r
+        if (fElementDepth == 0) {\r
+            String encoding = "UTF-8";\r
+            if (fLocator != null) {\r
+                if (fLocator instanceof Locator2) {\r
+                    Locator2 locator2 = (Locator2) fLocator;\r
+                    fXML11 = "1.1".equals(locator2.getXMLVersion());\r
+                    encoding = locator2.getEncoding();\r
+                    if (encoding == null) {\r
+                        encoding = "UTF-8";\r
+                    }\r
+                }\r
+                fLocator = null;\r
+            }\r
+\r
+            // The XML declaration cannot be printed in startDocument because\r
+            // the version and encoding information reported by the Locator \r
+            // cannot be relied on until the next event after startDocument.\r
+            if (!fCanonical) {\r
+                fOut.print("<?xml version=\"");\r
+                fOut.print(fXML11 ? "1.1" : "1.0");\r
+                fOut.print("\" encoding=\"");\r
+                fOut.print(encoding);\r
+                fOut.println("\"?>");\r
+                fOut.flush();\r
+            }\r
+        }\r
+        \r
+        fElementDepth++;\r
+        fOut.print('<');\r
+        fOut.print(raw);\r
+        if (attrs != null) {\r
+            attrs = sortAttributes(attrs);\r
+            int len = attrs.getLength();\r
+            for (int i = 0; i < len; i++) {\r
+                fOut.print(' ');\r
+                fOut.print(attrs.getQName(i));\r
+                fOut.print("=\"");\r
+                normalizeAndPrint(attrs.getValue(i), true);\r
+                fOut.print('"');\r
+            }\r
+        }\r
+        //fOut.print('>');\r
+        fOut.print(">\n");\r
+        fOut.flush();\r
+\r
+    } // startElement(String,String,String,Attributes)\r
+\r
+    /** Characters. */\r
+    public void characters(char ch[], int start, int length)\r
+        throws SAXException {\r
+\r
+        if (!fInCDATA) {\r
+            normalizeAndPrint(ch, start, length, false);\r
+        }\r
+        else {\r
+            for (int i = 0; i < length; ++i) {\r
+               fOut.print(ch[start+i]);\r
+            }\r
+        }\r
+        fOut.flush();\r
+\r
+    } // characters(char[],int,int);\r
+\r
+    /** Ignorable whitespace. */\r
+    public void ignorableWhitespace(char ch[], int start, int length)\r
+        throws SAXException {\r
+\r
+        characters(ch, start, length);\r
+        fOut.flush();\r
+\r
+    } // ignorableWhitespace(char[],int,int);\r
+\r
+    /** End element. */\r
+    public void endElement(String uri, String local, String raw)\r
+        throws SAXException {\r
+\r
+        fElementDepth--;\r
+        fOut.print("</");\r
+        fOut.print(raw);\r
+        //fOut.print('>');\r
+        fOut.print(">\n");\r
+        fOut.flush();\r
+\r
+    } // endElement(String)\r
+\r
+    //\r
+    // ErrorHandler methods\r
+    //\r
+\r
+    /** Warning. */\r
+    public void warning(SAXParseException ex) throws SAXException {\r
+        printError("Warning", ex);\r
+    } // warning(SAXParseException)\r
+\r
+    /** Error. */\r
+    public void error(SAXParseException ex) throws SAXException {\r
+        printError("Error", ex);\r
+    } // error(SAXParseException)\r
+\r
+    /** Fatal error. */\r
+    public void fatalError(SAXParseException ex) throws SAXException {\r
+        printError("Fatal Error", ex);\r
+        throw ex;\r
+    } // fatalError(SAXParseException)\r
+\r
+    //\r
+    // LexicalHandler methods\r
+    //\r
+\r
+    /** Start DTD. */\r
+    public void startDTD(String name, String publicId, String systemId)\r
+        throws SAXException {\r
+    } // startDTD(String,String,String)\r
+\r
+    /** End DTD. */\r
+    public void endDTD() throws SAXException {\r
+    } // endDTD()\r
+\r
+    /** Start entity. */\r
+    public void startEntity(String name) throws SAXException {\r
+    } // startEntity(String)\r
+\r
+    /** End entity. */\r
+    public void endEntity(String name) throws SAXException {\r
+    } // endEntity(String)\r
+\r
+    /** Start CDATA section. */\r
+    public void startCDATA() throws SAXException {\r
+        if (!fCanonical) {\r
+            fOut.print("<![CDATA[");\r
+            fInCDATA = true;\r
+        }\r
+    } // startCDATA()\r
+\r
+    /** End CDATA section. */\r
+    public void endCDATA() throws SAXException {\r
+        if (!fCanonical) {\r
+            fInCDATA = false;\r
+            fOut.print("]]>");\r
+        }\r
+    } // endCDATA()\r
+\r
+    /** Comment. */\r
+    public void comment(char ch[], int start, int length) throws SAXException {\r
+        if (!fCanonical && fElementDepth > 0) {\r
+            fOut.print("<!--");\r
+            for (int i = 0; i < length; ++i) {\r
+                fOut.print(ch[start+i]);\r
+            }\r
+            //fOut.print("-->");\r
+            fOut.print("-->\n");\r
+            fOut.flush();\r
+        }\r
+    } // comment(char[],int,int)\r
+\r
+    //\r
+    // Protected methods\r
+    //\r
+\r
+    /** Returns a sorted list of attributes. */\r
+    protected Attributes sortAttributes(Attributes attrs) {\r
+\r
+       return attrs;\r
+//        AttributesImpl attributes = new AttributesImpl();\r
+//\r
+//        int len = (attrs != null) ? attrs.getLength() : 0;\r
+//        for (int i = 0; i < len; i++) {\r
+//            String name = attrs.getQName(i);\r
+//            int count = attributes.getLength();\r
+//            int j = 0;\r
+//            while (j < count) {\r
+//                if (name.compareTo(attributes.getQName(j)) < 0) {\r
+//                    break;\r
+//                }\r
+//                j++;\r
+//            }\r
+//            //attributes.insertAttributeAt(j, name, attrs.getType(i),attrs.getValue(i));\r
+//            attributes.setAttribute(j, attrs.getURI(i), attrs.getLocalName(i), name, attrs.getType(i), attrs.getValue(i));\r
+//        }\r
+//\r
+//        return attributes;\r
+\r
+    } // sortAttributes(AttributeList):AttributeList\r
+\r
+    /** Normalizes and prints the given string. */\r
+    protected void normalizeAndPrint(String s, boolean isAttValue) {\r
+\r
+        int len = (s != null) ? s.length() : 0;\r
+        for (int i = 0; i < len; i++) {\r
+            char c = s.charAt(i);\r
+            normalizeAndPrint(c, isAttValue);\r
+        }\r
+\r
+    } // normalizeAndPrint(String,boolean)\r
+\r
+    /** Normalizes and prints the given array of characters. */\r
+    protected void normalizeAndPrint(char[] ch, int offset, int length, boolean isAttValue) {\r
+        for (int i = 0; i < length; i++) {\r
+            normalizeAndPrint(ch[offset + i], isAttValue);\r
+        }\r
+    } // normalizeAndPrint(char[],int,int,boolean)\r
+\r
+    /** Normalizes and print the given character. */\r
+    protected void normalizeAndPrint(char c, boolean isAttValue) {\r
+\r
+        switch (c) {\r
+            case '<': {\r
+                fOut.print("&lt;");\r
+                break;\r
+            }\r
+            case '>': {\r
+                fOut.print("&gt;");\r
+                break;\r
+            }\r
+            case '&': {\r
+                fOut.print("&amp;");\r
+                break;\r
+            }\r
+            case '"': {\r
+                // A '"' that appears in character data \r
+                // does not need to be escaped.\r
+                if (isAttValue) {\r
+                    fOut.print("&quot;");\r
+                }\r
+                else {\r
+                    fOut.print("\"");\r
+                }\r
+                break;\r
+            }\r
+            case '\r': {\r
+               // If CR is part of the document's content, it\r
+               // must not be printed as a literal otherwise\r
+               // it would be normalized to LF when the document\r
+               // is reparsed.\r
+               fOut.print("&#xD;");\r
+               break;\r
+            }\r
+            case '\n': {\r
+                if (fCanonical) {\r
+                    fOut.print("&#xA;");\r
+                    break;\r
+                }\r
+                // else, default print char\r
+            }\r
+            default: {\r
+                // In XML 1.1, control chars in the ranges [#x1-#x1F, #x7F-#x9F] must be escaped.\r
+               //\r
+               // Escape space characters that would be normalized to #x20 in attribute values\r
+               // when the document is reparsed.\r
+               //\r
+               // Escape NEL (0x85) and LSEP (0x2028) that appear in content \r
+               // if the document is XML 1.1, since they would be normalized to LF \r
+               // when the document is reparsed.\r
+               if (fXML11 && ((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A) \r
+                   || (c >= 0x7F && c <= 0x9F) || c == 0x2028)\r
+                   || isAttValue && (c == 0x09 || c == 0x0A)) {\r
+                   fOut.print("&#x");\r
+                   fOut.print(Integer.toHexString(c).toUpperCase());\r
+                   fOut.print(";");\r
+                }\r
+                else {\r
+                    fOut.print(c);\r
+                }        \r
+            }\r
+        }\r
+    } // normalizeAndPrint(char,boolean)\r
+\r
+    /** Prints the error message. */\r
+    protected void printError(String type, SAXParseException ex) {\r
+\r
+        System.err.print("[");\r
+        System.err.print(type);\r
+        System.err.print("] ");\r
+        String systemId = ex.getSystemId();\r
+        if (systemId != null) {\r
+            int index = systemId.lastIndexOf('/');\r
+            if (index != -1)\r
+                systemId = systemId.substring(index + 1);\r
+            System.err.print(systemId);\r
+        }\r
+        System.err.print(':');\r
+        System.err.print(ex.getLineNumber());\r
+        System.err.print(':');\r
+        System.err.print(ex.getColumnNumber());\r
+        System.err.print(": ");\r
+        System.err.print(ex.getMessage());\r
+        System.err.println();\r
+        System.err.flush();\r
+\r
+    } // printError(String,SAXParseException)\r
+\r
+}\r