]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java
bcbd52728e396df77ff1ae5075a31dac483f0694
[simantics/interop.git] / org.simantics.interop.xmlio / src / org / simantics / interop / xmlio / SaveXML.java
1 package org.simantics.interop.xmlio;\r
2 \r
3 import java.io.File;\r
4 import java.io.FileOutputStream;\r
5 import java.io.IOException;\r
6 import java.io.OutputStreamWriter;\r
7 import java.io.PrintWriter;\r
8 import java.io.Writer;\r
9 import java.util.Arrays;\r
10 import java.util.Calendar;\r
11 import java.util.Collection;\r
12 import java.util.HashMap;\r
13 import java.util.HashSet;\r
14 import java.util.Map;\r
15 import java.util.Set;\r
16 import java.util.Stack;\r
17 \r
18 import org.apache.xml.serialize.OutputFormat;\r
19 import org.simantics.db.ReadGraph;\r
20 import org.simantics.db.Resource;\r
21 import org.simantics.db.Session;\r
22 import org.simantics.db.Statement;\r
23 import org.simantics.db.common.request.ReadRequest;\r
24 import org.simantics.db.exception.DatabaseException;\r
25 import org.simantics.layer0.DatabaseManagementResource;\r
26 import org.simantics.layer0.Layer0;\r
27 import org.xml.sax.Attributes;\r
28 import org.xml.sax.Locator;\r
29 import org.xml.sax.SAXException;\r
30 import org.xml.sax.SAXParseException;\r
31 import org.xml.sax.ext.LexicalHandler;\r
32 import org.xml.sax.ext.Locator2;\r
33 import org.xml.sax.helpers.AttributesImpl;\r
34 import org.xml.sax.helpers.DefaultHandler;\r
35 \r
36 public class SaveXML extends DefaultHandler implements LexicalHandler {\r
37         \r
38         private Session session;\r
39         private Resource root;\r
40         private SaveRule saveRule;\r
41         \r
42         \r
43         private File file;\r
44         \r
45         private PrintWriter fOut;\r
46         /** Canonical output. */\r
47         protected boolean fCanonical = true;\r
48           /** Element depth. */\r
49     protected int fElementDepth;\r
50     \r
51     /** Document locator. */\r
52     protected Locator fLocator;\r
53     \r
54     /** Processing XML 1.1 document. */\r
55     protected boolean fXML11 = true;\r
56     \r
57     /** In CDATA section. */\r
58     protected boolean fInCDATA;\r
59     \r
60         \r
61         public SaveXML(Session session, Resource root, File file) {\r
62                 this.session = session;\r
63                 this.root = root;\r
64                 this.file = file;\r
65                 this.saveRule = new DependsOnSaveRule();\r
66         }\r
67         \r
68         public void setSaveRule(SaveRule saveRule) {\r
69                 this.saveRule = saveRule;\r
70         }\r
71         \r
72         public void save() throws DatabaseException, IOException, SAXException {\r
73                 fCanonical = false;\r
74                 FileOutputStream fos = new FileOutputStream(file);\r
75             fOut = new PrintWriter(new OutputStreamWriter(fos, "UTF8"));\r
76                 \r
77             startDocument();\r
78             fXML11 = true;\r
79             AttributesImpl attrs = new AttributesImpl();\r
80             addDate(attrs);\r
81             startElement("", "", "graphexport", attrs);\r
82             saveGraphBundles();\r
83             saveData();\r
84             endElement("", "", "graphexport");\r
85             endDocument();\r
86             fOut.close();\r
87         }\r
88         \r
89         private void addDate(AttributesImpl attrs) {\r
90                 Calendar calendar = Calendar.getInstance();\r
91                 int year = calendar.get(Calendar.YEAR);\r
92                 int month = calendar.get(Calendar.MONTH) + 1;\r
93                 int day = calendar.get(Calendar.DAY_OF_MONTH);\r
94                 int hour = calendar.get(Calendar.HOUR_OF_DAY);\r
95                 int min = calendar.get(Calendar.MINUTE);\r
96                 int sec = calendar.get(Calendar.SECOND);\r
97         String date = "";\r
98         date += year;\r
99         date += "-";\r
100         if (month < 10)\r
101                 date+="0";\r
102         date += month;\r
103         date += "-";\r
104         if (day < 10)\r
105                 date+="0";\r
106         date += day;\r
107         date += "T";\r
108         if (hour < 10)\r
109                 date+="0";\r
110         date += hour;\r
111         date += ":";\r
112         if (min < 10)\r
113                 date+="0";\r
114         date += min;\r
115         date += ":";\r
116         if (sec < 10)\r
117                 date+="0";\r
118         date += sec;\r
119         attrs.addAttribute("", "", "date", "CDATA", date);\r
120         }\r
121         \r
122         \r
123         Map<Resource,Integer> idMap = new HashMap<Resource, Integer>();\r
124         \r
125         Set<Resource> processed = new HashSet<Resource>();\r
126         Stack<Resource> stack = new Stack<Resource>();\r
127         Layer0 l0;\r
128         int statements = 0;\r
129         \r
130         private void saveData() throws DatabaseException {\r
131         idMap.clear();\r
132         processed.clear();\r
133         stack.clear();\r
134         statements = 0;\r
135         session.syncRequest(new ReadRequest() {\r
136                 \r
137                 @Override\r
138                 public void run(ReadGraph graph) throws DatabaseException {\r
139                         l0 = Layer0.getInstance(graph);\r
140                         saveRule.init(graph);\r
141                         stack.push(root);\r
142                 }\r
143         });\r
144         try {\r
145                 while (!stack.isEmpty()) {\r
146                         session.syncRequest(new ReadRequest() {\r
147                                 \r
148                                 @Override\r
149                                 public void run(ReadGraph graph) throws DatabaseException {\r
150                                         \r
151                                         try {\r
152                                         \r
153                                         \r
154                                         AttributesImpl attrs = new AttributesImpl();\r
155                                         \r
156                                         \r
157                                         while (!stack.isEmpty()) {\r
158                                                 Resource r = stack.pop();\r
159                                                 if (processed.contains(r))\r
160                                                         continue;\r
161                                                 processed.add(r);\r
162                                                 Collection<Statement> statement = graph.getStatements(r, l0.IsWeaklyRelatedTo);\r
163                                                 boolean split = false;\r
164                                                 for (Statement s : statement) { \r
165                                                         if (s.isAsserted(r))\r
166                                                                 continue;\r
167                                                         if (!saveRule.save(graph, s))\r
168                                                                 continue;\r
169                                                         // System.out.println("Processing " + s.getSubject() + " " + s.getPredicate() + " " + s.getObject());\r
170                                                         \r
171                                                         statements++;\r
172                                                         int sId = getId(graph, s.getSubject(),false);\r
173                                                         int pId = getId(graph, s.getPredicate(),true);\r
174                                                         int oId = getId(graph, s.getObject(),false);\r
175                                                         attrs.clear();\r
176                                                         attrs.addAttribute("", "", "subject", "CDATA", Integer.toString(sId));\r
177                                                         attrs.addAttribute("", "", "predicate", "CDATA", Integer.toString(pId));\r
178                                                         attrs.addAttribute("", "", "object", "CDATA", Integer.toString(oId));\r
179                                                         startElement("", "", "statement", attrs);\r
180                                                         endElement("", "", "statement");\r
181                                                         if (!processed.contains(s.getObject()))\r
182                                                                 stack.push(s.getObject());\r
183                                                         if (statements % 10000 == 0) {\r
184                                                                 System.out.println("Processed " + statements + " statements...");\r
185                                                                 split = true;\r
186                                                         }\r
187                                                         \r
188                                                 }\r
189                                                 if (split)\r
190                                                         return;\r
191                                                 \r
192                                         }\r
193                                         System.out.println("Done. Processed " + statements + " statements.");\r
194                                         } catch (SAXException e) {\r
195                                                 throw new DatabaseException(e); \r
196                                         }\r
197                                 \r
198                                 }\r
199                         });\r
200                 \r
201                 }\r
202         } finally {\r
203                 idMap.clear();\r
204                 processed.clear();\r
205                 stack.clear();\r
206         }\r
207         \r
208 }\r
209         \r
210         private int getId(ReadGraph g, Resource r, boolean rel) throws SAXException, DatabaseException {\r
211                 Integer id = idMap.get(r);\r
212                 if (id == null) {\r
213                         id = idMap.size();\r
214                         idMap.put(r, id);\r
215                         AttributesImpl attrs = new AttributesImpl();\r
216                         attrs.addAttribute("", "", "id", "CDATA", id.toString());\r
217                         if (rel) {\r
218                                 attrs.addAttribute("", "", "rel", "CDATA", Boolean.TRUE.toString());\r
219                                 String uri = g.getPossibleURI(r);\r
220                                 if (uri != null)\r
221                                         attrs.addAttribute("", "", "uri", "CDATA", uri);\r
222                         }\r
223                         \r
224                         startElement("", "", "resource", attrs);\r
225                         if (!rel) {\r
226                                 Layer0 l0 = Layer0.getInstance(g);\r
227                                 //Collection<Resource> types = g.getPrincipalTypes(r);\r
228                                 Collection<Resource> types = g.getObjects(r, l0.InstanceOf);\r
229                                 attrs.clear();\r
230                                 for (Resource type : types) {\r
231                                         String uri = g.getPossibleURI(type);\r
232                                         if (uri != null) {\r
233                                                 attrs.addAttribute("", "", "uri", "CDATA", uri);\r
234                                                 startElement("", "", "type", attrs);\r
235                                                 endElement("", "", "type");\r
236                                         } else {\r
237                                                 // TODO : handle URIless types.\r
238                                                 throw new DatabaseException("Cannot resolve URI for type " + type + ", instance " + r); \r
239                                         }\r
240                                 }\r
241                         }\r
242                         if (g.hasValue(r)) {\r
243                                 // FIXME: does not handle arrays.\r
244                                 Object value = g.getValue(r);\r
245                                 \r
246                                 if (value instanceof Object[]) {\r
247                                         Object[] valuearray = (Object[])value;\r
248                                         for (Object o : valuearray) {\r
249                                                 attrs.clear();\r
250                                                 attrs.addAttribute("", "", "value", "CDATA", o.toString());\r
251                                                 startElement("", "", "value", attrs);\r
252                                                 endElement("", "", "value");\r
253                                         }\r
254                                 } else {\r
255                                         attrs.clear();\r
256                                         attrs.addAttribute("", "", "value", "CDATA", value.toString());\r
257                                         startElement("", "", "value", attrs);\r
258                                         endElement("", "", "value");\r
259                                 }\r
260                         }\r
261                         endElement("", "", "resource");\r
262                         \r
263                 }\r
264                 return id;\r
265         }\r
266         \r
267         \r
268         private void saveGraphBundles() throws DatabaseException{\r
269                 final Resource rootLibrary = session.getRootLibrary();\r
270                 session.syncRequest(new ReadRequest() {\r
271                         \r
272                         @Override\r
273                         public void run(ReadGraph graph) throws DatabaseException {\r
274                                 Layer0 l0 = Layer0.getInstance(graph);\r
275                                 DatabaseManagementResource dm = DatabaseManagementResource.getInstance(graph);\r
276                                 Collection<Resource> objs = graph.getObjects(rootLibrary, l0.ConsistsOf);\r
277                                 Resource graphBundles = null;\r
278                                 for (Resource o : objs) {\r
279                                         if (isGraphBundleLib(graph, rootLibrary)) {\r
280                                                 graphBundles = o;\r
281                                                 break;\r
282                                         }\r
283                                 }\r
284                                 if (graphBundles == null)\r
285                                         graphBundles = dm.InstalledGraphBundles;\r
286                                         \r
287                                 objs = graph.getObjects(graphBundles, l0.ConsistsOf);\r
288                                 AttributesImpl attrs = new AttributesImpl();\r
289                                 try {\r
290                                 startElement("", "", "graphbundles", attrs);\r
291                                 for (Resource graphBundle : objs) {\r
292                                         if (!graph.isInstanceOf(graphBundle, dm.GraphBundle))\r
293                                                 continue;\r
294                                         String name = graph.getRelatedValue(graphBundle, l0.HasName);\r
295                                         String versionId = graph.getRelatedValue(graphBundle, dm.HasVersionedId);\r
296                                         attrs.clear();\r
297                                         attrs.addAttribute("", "", "name", "CDATA", name);\r
298                                         attrs.addAttribute("", "", "versionid", "CDATA", versionId);\r
299                                         startElement("", "", "bundle", attrs);\r
300                                         endElement("", "", "bundle");\r
301                                 }\r
302                                 \r
303                                 endElement("", "", "graphbundles");\r
304                                 } catch (SAXException e) {\r
305                                         throw new DatabaseException(e);\r
306                                 }\r
307                         }\r
308                 });\r
309         }\r
310         \r
311         private boolean isGraphBundleLib(ReadGraph graph, Resource o) throws DatabaseException {\r
312                 Layer0 l0 = Layer0.getInstance(graph);\r
313                 String name = graph.getPossibleRelatedValue(o, l0.HasName);\r
314                 return "InstalledGraphBundles".equals(name);\r
315         }\r
316         \r
317         \r
318         \r
319     /** Set Document Locator. */\r
320     public void setDocumentLocator(Locator locator) {\r
321         fLocator = locator;\r
322     } // setDocumentLocator(Locator)\r
323         \r
324     /** Start document. */\r
325     public void startDocument() throws SAXException {\r
326 \r
327         fElementDepth = 0;\r
328         fXML11 = false;\r
329         fInCDATA = false;\r
330         \r
331     } // startDocument()\r
332 \r
333     /** Processing instruction. */\r
334     public void processingInstruction(String target, String data)\r
335         throws SAXException {\r
336 \r
337         if (fElementDepth > 0) {\r
338             fOut.print("<?");\r
339             fOut.print(target);\r
340             if (data != null && data.length() > 0) {\r
341                 fOut.print(' ');\r
342                 fOut.print(data);\r
343             }\r
344             fOut.print("?>");\r
345             fOut.flush();\r
346         }\r
347 \r
348     } // processingInstruction(String,String)\r
349 \r
350     /** Start element. */\r
351     public void startElement(String uri, String local, String raw,\r
352                              Attributes attrs) throws SAXException {\r
353 \r
354         // Root Element\r
355         if (fElementDepth == 0) {\r
356             String encoding = "UTF-8";\r
357             if (fLocator != null) {\r
358                 if (fLocator instanceof Locator2) {\r
359                     Locator2 locator2 = (Locator2) fLocator;\r
360                     fXML11 = "1.1".equals(locator2.getXMLVersion());\r
361                     encoding = locator2.getEncoding();\r
362                     if (encoding == null) {\r
363                         encoding = "UTF-8";\r
364                     }\r
365                 }\r
366                 fLocator = null;\r
367             }\r
368 \r
369             // The XML declaration cannot be printed in startDocument because\r
370             // the version and encoding information reported by the Locator \r
371             // cannot be relied on until the next event after startDocument.\r
372             if (!fCanonical) {\r
373                 fOut.print("<?xml version=\"");\r
374                 fOut.print(fXML11 ? "1.1" : "1.0");\r
375                 fOut.print("\" encoding=\"");\r
376                 fOut.print(encoding);\r
377                 fOut.println("\"?>");\r
378                 fOut.flush();\r
379             }\r
380         }\r
381         \r
382         fElementDepth++;\r
383         fOut.print('<');\r
384         fOut.print(raw);\r
385         if (attrs != null) {\r
386             attrs = sortAttributes(attrs);\r
387             int len = attrs.getLength();\r
388             for (int i = 0; i < len; i++) {\r
389                 fOut.print(' ');\r
390                 fOut.print(attrs.getQName(i));\r
391                 fOut.print("=\"");\r
392                 normalizeAndPrint(attrs.getValue(i), true);\r
393                 fOut.print('"');\r
394             }\r
395         }\r
396         //fOut.print('>');\r
397         fOut.print(">\n");\r
398         fOut.flush();\r
399 \r
400     } // startElement(String,String,String,Attributes)\r
401 \r
402     /** Characters. */\r
403     public void characters(char ch[], int start, int length)\r
404         throws SAXException {\r
405 \r
406         if (!fInCDATA) {\r
407             normalizeAndPrint(ch, start, length, false);\r
408         }\r
409         else {\r
410             for (int i = 0; i < length; ++i) {\r
411                 fOut.print(ch[start+i]);\r
412             }\r
413         }\r
414         fOut.flush();\r
415 \r
416     } // characters(char[],int,int);\r
417 \r
418     /** Ignorable whitespace. */\r
419     public void ignorableWhitespace(char ch[], int start, int length)\r
420         throws SAXException {\r
421 \r
422         characters(ch, start, length);\r
423         fOut.flush();\r
424 \r
425     } // ignorableWhitespace(char[],int,int);\r
426 \r
427     /** End element. */\r
428     public void endElement(String uri, String local, String raw)\r
429         throws SAXException {\r
430 \r
431         fElementDepth--;\r
432         fOut.print("</");\r
433         fOut.print(raw);\r
434         //fOut.print('>');\r
435         fOut.print(">\n");\r
436         fOut.flush();\r
437 \r
438     } // endElement(String)\r
439 \r
440     //\r
441     // ErrorHandler methods\r
442     //\r
443 \r
444     /** Warning. */\r
445     public void warning(SAXParseException ex) throws SAXException {\r
446         printError("Warning", ex);\r
447     } // warning(SAXParseException)\r
448 \r
449     /** Error. */\r
450     public void error(SAXParseException ex) throws SAXException {\r
451         printError("Error", ex);\r
452     } // error(SAXParseException)\r
453 \r
454     /** Fatal error. */\r
455     public void fatalError(SAXParseException ex) throws SAXException {\r
456         printError("Fatal Error", ex);\r
457         throw ex;\r
458     } // fatalError(SAXParseException)\r
459 \r
460     //\r
461     // LexicalHandler methods\r
462     //\r
463 \r
464     /** Start DTD. */\r
465     public void startDTD(String name, String publicId, String systemId)\r
466         throws SAXException {\r
467     } // startDTD(String,String,String)\r
468 \r
469     /** End DTD. */\r
470     public void endDTD() throws SAXException {\r
471     } // endDTD()\r
472 \r
473     /** Start entity. */\r
474     public void startEntity(String name) throws SAXException {\r
475     } // startEntity(String)\r
476 \r
477     /** End entity. */\r
478     public void endEntity(String name) throws SAXException {\r
479     } // endEntity(String)\r
480 \r
481     /** Start CDATA section. */\r
482     public void startCDATA() throws SAXException {\r
483         if (!fCanonical) {\r
484             fOut.print("<![CDATA[");\r
485             fInCDATA = true;\r
486         }\r
487     } // startCDATA()\r
488 \r
489     /** End CDATA section. */\r
490     public void endCDATA() throws SAXException {\r
491         if (!fCanonical) {\r
492             fInCDATA = false;\r
493             fOut.print("]]>");\r
494         }\r
495     } // endCDATA()\r
496 \r
497     /** Comment. */\r
498     public void comment(char ch[], int start, int length) throws SAXException {\r
499         if (!fCanonical && fElementDepth > 0) {\r
500             fOut.print("<!--");\r
501             for (int i = 0; i < length; ++i) {\r
502                 fOut.print(ch[start+i]);\r
503             }\r
504             //fOut.print("-->");\r
505             fOut.print("-->\n");\r
506             fOut.flush();\r
507         }\r
508     } // comment(char[],int,int)\r
509 \r
510     //\r
511     // Protected methods\r
512     //\r
513 \r
514     /** Returns a sorted list of attributes. */\r
515     protected Attributes sortAttributes(Attributes attrs) {\r
516 \r
517         return attrs;\r
518 //        AttributesImpl attributes = new AttributesImpl();\r
519 //\r
520 //        int len = (attrs != null) ? attrs.getLength() : 0;\r
521 //        for (int i = 0; i < len; i++) {\r
522 //            String name = attrs.getQName(i);\r
523 //            int count = attributes.getLength();\r
524 //            int j = 0;\r
525 //            while (j < count) {\r
526 //                if (name.compareTo(attributes.getQName(j)) < 0) {\r
527 //                    break;\r
528 //                }\r
529 //                j++;\r
530 //            }\r
531 //            //attributes.insertAttributeAt(j, name, attrs.getType(i),attrs.getValue(i));\r
532 //            attributes.setAttribute(j, attrs.getURI(i), attrs.getLocalName(i), name, attrs.getType(i), attrs.getValue(i));\r
533 //        }\r
534 //\r
535 //        return attributes;\r
536 \r
537     } // sortAttributes(AttributeList):AttributeList\r
538 \r
539     /** Normalizes and prints the given string. */\r
540     protected void normalizeAndPrint(String s, boolean isAttValue) {\r
541 \r
542         int len = (s != null) ? s.length() : 0;\r
543         for (int i = 0; i < len; i++) {\r
544             char c = s.charAt(i);\r
545             normalizeAndPrint(c, isAttValue);\r
546         }\r
547 \r
548     } // normalizeAndPrint(String,boolean)\r
549 \r
550     /** Normalizes and prints the given array of characters. */\r
551     protected void normalizeAndPrint(char[] ch, int offset, int length, boolean isAttValue) {\r
552         for (int i = 0; i < length; i++) {\r
553             normalizeAndPrint(ch[offset + i], isAttValue);\r
554         }\r
555     } // normalizeAndPrint(char[],int,int,boolean)\r
556 \r
557     /** Normalizes and print the given character. */\r
558     protected void normalizeAndPrint(char c, boolean isAttValue) {\r
559 \r
560         switch (c) {\r
561             case '<': {\r
562                 fOut.print("&lt;");\r
563                 break;\r
564             }\r
565             case '>': {\r
566                 fOut.print("&gt;");\r
567                 break;\r
568             }\r
569             case '&': {\r
570                 fOut.print("&amp;");\r
571                 break;\r
572             }\r
573             case '"': {\r
574                 // A '"' that appears in character data \r
575                 // does not need to be escaped.\r
576                 if (isAttValue) {\r
577                     fOut.print("&quot;");\r
578                 }\r
579                 else {\r
580                     fOut.print("\"");\r
581                 }\r
582                 break;\r
583             }\r
584             case '\r': {\r
585                 // If CR is part of the document's content, it\r
586                 // must not be printed as a literal otherwise\r
587                 // it would be normalized to LF when the document\r
588                 // is reparsed.\r
589                 fOut.print("&#xD;");\r
590                 break;\r
591             }\r
592             case '\n': {\r
593                 if (fCanonical) {\r
594                     fOut.print("&#xA;");\r
595                     break;\r
596                 }\r
597                 // else, default print char\r
598             }\r
599             default: {\r
600                 // In XML 1.1, control chars in the ranges [#x1-#x1F, #x7F-#x9F] must be escaped.\r
601                 //\r
602                 // Escape space characters that would be normalized to #x20 in attribute values\r
603                 // when the document is reparsed.\r
604                 //\r
605                 // Escape NEL (0x85) and LSEP (0x2028) that appear in content \r
606                 // if the document is XML 1.1, since they would be normalized to LF \r
607                 // when the document is reparsed.\r
608                 if (fXML11 && ((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A) \r
609                     || (c >= 0x7F && c <= 0x9F) || c == 0x2028)\r
610                     || isAttValue && (c == 0x09 || c == 0x0A)) {\r
611                     fOut.print("&#x");\r
612                     fOut.print(Integer.toHexString(c).toUpperCase());\r
613                     fOut.print(";");\r
614                 }\r
615                 else {\r
616                     fOut.print(c);\r
617                 }        \r
618             }\r
619         }\r
620     } // normalizeAndPrint(char,boolean)\r
621 \r
622     /** Prints the error message. */\r
623     protected void printError(String type, SAXParseException ex) {\r
624 \r
625         System.err.print("[");\r
626         System.err.print(type);\r
627         System.err.print("] ");\r
628         String systemId = ex.getSystemId();\r
629         if (systemId != null) {\r
630             int index = systemId.lastIndexOf('/');\r
631             if (index != -1)\r
632                 systemId = systemId.substring(index + 1);\r
633             System.err.print(systemId);\r
634         }\r
635         System.err.print(':');\r
636         System.err.print(ex.getLineNumber());\r
637         System.err.print(':');\r
638         System.err.print(ex.getColumnNumber());\r
639         System.err.print(": ");\r
640         System.err.print(ex.getMessage());\r
641         System.err.println();\r
642         System.err.flush();\r
643 \r
644     } // printError(String,SAXParseException)\r
645 \r
646 }\r