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