1 package org.simantics.interop.xmlio;
\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
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
36 public class SaveXML extends DefaultHandler implements LexicalHandler {
\r
38 private Session session;
\r
39 private Resource root;
\r
40 private SaveRule saveRule;
\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
51 /** Document locator. */
\r
52 protected Locator fLocator;
\r
54 /** Processing XML 1.1 document. */
\r
55 protected boolean fXML11 = true;
\r
57 /** In CDATA section. */
\r
58 protected boolean fInCDATA;
\r
61 public SaveXML(Session session, Resource root, File file) {
\r
62 this.session = session;
\r
65 this.saveRule = new DependsOnSaveRule();
\r
68 public void setSaveRule(SaveRule saveRule) {
\r
69 this.saveRule = saveRule;
\r
72 public void save() throws DatabaseException, IOException, SAXException {
\r
74 FileOutputStream fos = new FileOutputStream(file);
\r
75 fOut = new PrintWriter(new OutputStreamWriter(fos, "UTF8"));
\r
79 AttributesImpl attrs = new AttributesImpl();
\r
81 startElement("", "", "graphexport", attrs);
\r
84 endElement("", "", "graphexport");
\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
119 attrs.addAttribute("", "", "date", "CDATA", date);
\r
123 Map<Resource,Integer> idMap = new HashMap<Resource, Integer>();
\r
125 Set<Resource> processed = new HashSet<Resource>();
\r
126 Stack<Resource> stack = new Stack<Resource>();
\r
128 int statements = 0;
\r
130 private void saveData() throws DatabaseException {
\r
135 session.syncRequest(new ReadRequest() {
\r
138 public void run(ReadGraph graph) throws DatabaseException {
\r
139 l0 = Layer0.getInstance(graph);
\r
140 saveRule.init(graph);
\r
145 while (!stack.isEmpty()) {
\r
146 session.syncRequest(new ReadRequest() {
\r
149 public void run(ReadGraph graph) throws DatabaseException {
\r
154 AttributesImpl attrs = new AttributesImpl();
\r
157 while (!stack.isEmpty()) {
\r
158 Resource r = stack.pop();
\r
159 if (processed.contains(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
167 if (!saveRule.save(graph, s))
\r
169 // System.out.println("Processing " + s.getSubject() + " " + s.getPredicate() + " " + s.getObject());
\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
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
193 System.out.println("Done. Processed " + statements + " statements.");
\r
194 } catch (SAXException e) {
\r
195 throw new DatabaseException(e);
\r
210 private int getId(ReadGraph g, Resource r, boolean rel) throws SAXException, DatabaseException {
\r
211 Integer id = idMap.get(r);
\r
215 AttributesImpl attrs = new AttributesImpl();
\r
216 attrs.addAttribute("", "", "id", "CDATA", id.toString());
\r
218 attrs.addAttribute("", "", "rel", "CDATA", Boolean.TRUE.toString());
\r
219 String uri = g.getPossibleURI(r);
\r
221 attrs.addAttribute("", "", "uri", "CDATA", uri);
\r
224 startElement("", "", "resource", attrs);
\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
230 for (Resource type : types) {
\r
231 String uri = g.getPossibleURI(type);
\r
233 attrs.addAttribute("", "", "uri", "CDATA", uri);
\r
234 startElement("", "", "type", attrs);
\r
235 endElement("", "", "type");
\r
237 // TODO : handle URIless types.
\r
238 throw new DatabaseException("Cannot resolve URI for type " + type + ", instance " + r);
\r
242 if (g.hasValue(r)) {
\r
243 // FIXME: does not handle arrays.
\r
244 Object value = g.getValue(r);
\r
246 if (value instanceof Object[]) {
\r
247 Object[] valuearray = (Object[])value;
\r
248 for (Object o : valuearray) {
\r
250 attrs.addAttribute("", "", "value", "CDATA", o.toString());
\r
251 startElement("", "", "value", attrs);
\r
252 endElement("", "", "value");
\r
256 attrs.addAttribute("", "", "value", "CDATA", value.toString());
\r
257 startElement("", "", "value", attrs);
\r
258 endElement("", "", "value");
\r
261 endElement("", "", "resource");
\r
268 private void saveGraphBundles() throws DatabaseException{
\r
269 final Resource rootLibrary = session.getRootLibrary();
\r
270 session.syncRequest(new ReadRequest() {
\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
284 if (graphBundles == null)
\r
285 graphBundles = dm.InstalledGraphBundles;
\r
287 objs = graph.getObjects(graphBundles, l0.ConsistsOf);
\r
288 AttributesImpl attrs = new AttributesImpl();
\r
290 startElement("", "", "graphbundles", attrs);
\r
291 for (Resource graphBundle : objs) {
\r
292 if (!graph.isInstanceOf(graphBundle, dm.GraphBundle))
\r
294 String name = graph.getRelatedValue(graphBundle, l0.HasName);
\r
295 String versionId = graph.getRelatedValue(graphBundle, dm.HasVersionedId);
\r
297 attrs.addAttribute("", "", "name", "CDATA", name);
\r
298 attrs.addAttribute("", "", "versionid", "CDATA", versionId);
\r
299 startElement("", "", "bundle", attrs);
\r
300 endElement("", "", "bundle");
\r
303 endElement("", "", "graphbundles");
\r
304 } catch (SAXException e) {
\r
305 throw new DatabaseException(e);
\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
319 /** Set Document Locator. */
\r
320 public void setDocumentLocator(Locator locator) {
\r
321 fLocator = locator;
\r
322 } // setDocumentLocator(Locator)
\r
324 /** Start document. */
\r
325 public void startDocument() throws SAXException {
\r
331 } // startDocument()
\r
333 /** Processing instruction. */
\r
334 public void processingInstruction(String target, String data)
\r
335 throws SAXException {
\r
337 if (fElementDepth > 0) {
\r
339 fOut.print(target);
\r
340 if (data != null && data.length() > 0) {
\r
348 } // processingInstruction(String,String)
\r
350 /** Start element. */
\r
351 public void startElement(String uri, String local, String raw,
\r
352 Attributes attrs) throws SAXException {
\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
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
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
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
390 fOut.print(attrs.getQName(i));
\r
392 normalizeAndPrint(attrs.getValue(i), true);
\r
400 } // startElement(String,String,String,Attributes)
\r
403 public void characters(char ch[], int start, int length)
\r
404 throws SAXException {
\r
407 normalizeAndPrint(ch, start, length, false);
\r
410 for (int i = 0; i < length; ++i) {
\r
411 fOut.print(ch[start+i]);
\r
416 } // characters(char[],int,int);
\r
418 /** Ignorable whitespace. */
\r
419 public void ignorableWhitespace(char ch[], int start, int length)
\r
420 throws SAXException {
\r
422 characters(ch, start, length);
\r
425 } // ignorableWhitespace(char[],int,int);
\r
427 /** End element. */
\r
428 public void endElement(String uri, String local, String raw)
\r
429 throws SAXException {
\r
438 } // endElement(String)
\r
441 // ErrorHandler methods
\r
445 public void warning(SAXParseException ex) throws SAXException {
\r
446 printError("Warning", ex);
\r
447 } // warning(SAXParseException)
\r
450 public void error(SAXParseException ex) throws SAXException {
\r
451 printError("Error", ex);
\r
452 } // error(SAXParseException)
\r
454 /** Fatal error. */
\r
455 public void fatalError(SAXParseException ex) throws SAXException {
\r
456 printError("Fatal Error", ex);
\r
458 } // fatalError(SAXParseException)
\r
461 // LexicalHandler methods
\r
465 public void startDTD(String name, String publicId, String systemId)
\r
466 throws SAXException {
\r
467 } // startDTD(String,String,String)
\r
470 public void endDTD() throws SAXException {
\r
473 /** Start entity. */
\r
474 public void startEntity(String name) throws SAXException {
\r
475 } // startEntity(String)
\r
478 public void endEntity(String name) throws SAXException {
\r
479 } // endEntity(String)
\r
481 /** Start CDATA section. */
\r
482 public void startCDATA() throws SAXException {
\r
484 fOut.print("<![CDATA[");
\r
489 /** End CDATA section. */
\r
490 public void endCDATA() throws SAXException {
\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
504 //fOut.print("-->");
\r
505 fOut.print("-->\n");
\r
508 } // comment(char[],int,int)
\r
511 // Protected methods
\r
514 /** Returns a sorted list of attributes. */
\r
515 protected Attributes sortAttributes(Attributes attrs) {
\r
518 // AttributesImpl attributes = new AttributesImpl();
\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
525 // while (j < count) {
\r
526 // if (name.compareTo(attributes.getQName(j)) < 0) {
\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
535 // return attributes;
\r
537 } // sortAttributes(AttributeList):AttributeList
\r
539 /** Normalizes and prints the given string. */
\r
540 protected void normalizeAndPrint(String s, boolean isAttValue) {
\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
548 } // normalizeAndPrint(String,boolean)
\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
555 } // normalizeAndPrint(char[],int,int,boolean)
\r
557 /** Normalizes and print the given character. */
\r
558 protected void normalizeAndPrint(char c, boolean isAttValue) {
\r
562 fOut.print("<");
\r
566 fOut.print(">");
\r
570 fOut.print("&");
\r
574 // A '"' that appears in character data
\r
575 // does not need to be escaped.
\r
577 fOut.print(""");
\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
589 fOut.print("
");
\r
594 fOut.print("
");
\r
597 // else, default print char
\r
600 // In XML 1.1, control chars in the ranges [#x1-#x1F, #x7F-#x9F] must be escaped.
\r
602 // Escape space characters that would be normalized to #x20 in attribute values
\r
603 // when the document is reparsed.
\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
612 fOut.print(Integer.toHexString(c).toUpperCase());
\r
620 } // normalizeAndPrint(char,boolean)
\r
622 /** Prints the error message. */
\r
623 protected void printError(String type, SAXParseException ex) {
\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
632 systemId = systemId.substring(index + 1);
\r
633 System.err.print(systemId);
\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
644 } // printError(String,SAXParseException)
\r