1 package org.simantics.interop.xmlio;
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;
14 import java.util.Stack;
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;
33 public class SaveXML extends DefaultHandler implements LexicalHandler {
35 private Session session;
36 private Resource root;
37 private SaveRule saveRule;
42 private PrintWriter fOut;
43 /** Canonical output. */
44 protected boolean fCanonical = true;
46 protected int fElementDepth;
48 /** Document locator. */
49 protected Locator fLocator;
51 /** Processing XML 1.1 document. */
52 protected boolean fXML11 = true;
54 /** In CDATA section. */
55 protected boolean fInCDATA;
58 public SaveXML(Session session, Resource root, File file) {
59 this.session = session;
62 this.saveRule = new DependsOnSaveRule();
65 public void setSaveRule(SaveRule saveRule) {
66 this.saveRule = saveRule;
69 public void save() throws DatabaseException, IOException, SAXException {
71 FileOutputStream fos = new FileOutputStream(file);
72 fOut = new PrintWriter(new OutputStreamWriter(fos, "UTF8"));
76 AttributesImpl attrs = new AttributesImpl();
78 startElement("", "", "graphexport", attrs);
81 endElement("", "", "graphexport");
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);
116 attrs.addAttribute("", "", "date", "CDATA", date);
120 Map<Resource,Integer> idMap = new HashMap<Resource, Integer>();
122 Set<Resource> processed = new HashSet<Resource>();
123 Stack<Resource> stack = new Stack<Resource>();
127 private void saveData() throws DatabaseException {
132 session.syncRequest(new ReadRequest() {
135 public void run(ReadGraph graph) throws DatabaseException {
136 l0 = Layer0.getInstance(graph);
137 saveRule.init(graph);
142 while (!stack.isEmpty()) {
143 session.syncRequest(new ReadRequest() {
146 public void run(ReadGraph graph) throws DatabaseException {
151 AttributesImpl attrs = new AttributesImpl();
154 while (!stack.isEmpty()) {
155 Resource r = stack.pop();
156 if (processed.contains(r))
159 Collection<Statement> statement = graph.getStatements(r, l0.IsWeaklyRelatedTo);
160 boolean split = false;
161 for (Statement s : statement) {
164 if (!saveRule.save(graph, s))
166 // System.out.println("Processing " + s.getSubject() + " " + s.getPredicate() + " " + s.getObject());
169 int sId = getId(graph, s.getSubject(),false);
170 int pId = getId(graph, s.getPredicate(),true);
171 int oId = getId(graph, s.getObject(),false);
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...");
190 System.out.println("Done. Processed " + statements + " statements.");
191 } catch (SAXException e) {
192 throw new DatabaseException(e);
207 private int getId(ReadGraph g, Resource r, boolean rel) throws SAXException, DatabaseException {
208 Integer id = idMap.get(r);
212 AttributesImpl attrs = new AttributesImpl();
213 attrs.addAttribute("", "", "id", "CDATA", id.toString());
215 attrs.addAttribute("", "", "rel", "CDATA", Boolean.TRUE.toString());
216 String uri = g.getPossibleURI(r);
218 attrs.addAttribute("", "", "uri", "CDATA", uri);
221 startElement("", "", "resource", attrs);
223 Layer0 l0 = Layer0.getInstance(g);
224 //Collection<Resource> types = g.getPrincipalTypes(r);
225 Collection<Resource> types = g.getObjects(r, l0.InstanceOf);
227 for (Resource type : types) {
228 String uri = g.getPossibleURI(type);
230 attrs.addAttribute("", "", "uri", "CDATA", uri);
231 startElement("", "", "type", attrs);
232 endElement("", "", "type");
234 // TODO : handle URIless types.
235 throw new DatabaseException("Cannot resolve URI for type " + type + ", instance " + r);
240 // FIXME: does not handle arrays.
241 Object value = g.getValue(r);
243 if (value instanceof Object[]) {
244 Object[] valuearray = (Object[])value;
245 for (Object o : valuearray) {
247 attrs.addAttribute("", "", "value", "CDATA", o.toString());
248 startElement("", "", "value", attrs);
249 endElement("", "", "value");
253 attrs.addAttribute("", "", "value", "CDATA", value.toString());
254 startElement("", "", "value", attrs);
255 endElement("", "", "value");
258 endElement("", "", "resource");
265 private void saveGraphBundles() throws DatabaseException{
266 final Resource rootLibrary = session.getRootLibrary();
267 session.syncRequest(new ReadRequest() {
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)) {
281 if (graphBundles == null)
282 graphBundles = dm.InstalledGraphBundles;
284 objs = graph.getObjects(graphBundles, l0.ConsistsOf);
285 AttributesImpl attrs = new AttributesImpl();
287 startElement("", "", "graphbundles", attrs);
288 for (Resource graphBundle : objs) {
289 if (!graph.isInstanceOf(graphBundle, dm.GraphBundle))
291 String name = graph.getRelatedValue(graphBundle, l0.HasName);
292 String versionId = graph.getRelatedValue(graphBundle, dm.HasVersionedId);
294 attrs.addAttribute("", "", "name", "CDATA", name);
295 attrs.addAttribute("", "", "versionid", "CDATA", versionId);
296 startElement("", "", "bundle", attrs);
297 endElement("", "", "bundle");
300 endElement("", "", "graphbundles");
301 } catch (SAXException e) {
302 throw new DatabaseException(e);
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);
316 /** Set Document Locator. */
317 public void setDocumentLocator(Locator locator) {
319 } // setDocumentLocator(Locator)
321 /** Start document. */
322 public void startDocument() throws SAXException {
330 /** Processing instruction. */
331 public void processingInstruction(String target, String data)
332 throws SAXException {
334 if (fElementDepth > 0) {
337 if (data != null && data.length() > 0) {
345 } // processingInstruction(String,String)
347 /** Start element. */
348 public void startElement(String uri, String local, String raw,
349 Attributes attrs) throws SAXException {
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) {
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.
370 fOut.print("<?xml version=\"");
371 fOut.print(fXML11 ? "1.1" : "1.0");
372 fOut.print("\" encoding=\"");
373 fOut.print(encoding);
374 fOut.println("\"?>");
383 attrs = sortAttributes(attrs);
384 int len = attrs.getLength();
385 for (int i = 0; i < len; i++) {
387 fOut.print(attrs.getQName(i));
389 normalizeAndPrint(attrs.getValue(i), true);
397 } // startElement(String,String,String,Attributes)
400 public void characters(char ch[], int start, int length)
401 throws SAXException {
404 normalizeAndPrint(ch, start, length, false);
407 for (int i = 0; i < length; ++i) {
408 fOut.print(ch[start+i]);
413 } // characters(char[],int,int);
415 /** Ignorable whitespace. */
416 public void ignorableWhitespace(char ch[], int start, int length)
417 throws SAXException {
419 characters(ch, start, length);
422 } // ignorableWhitespace(char[],int,int);
425 public void endElement(String uri, String local, String raw)
426 throws SAXException {
435 } // endElement(String)
438 // ErrorHandler methods
442 public void warning(SAXParseException ex) throws SAXException {
443 printError("Warning", ex);
444 } // warning(SAXParseException)
447 public void error(SAXParseException ex) throws SAXException {
448 printError("Error", ex);
449 } // error(SAXParseException)
452 public void fatalError(SAXParseException ex) throws SAXException {
453 printError("Fatal Error", ex);
455 } // fatalError(SAXParseException)
458 // LexicalHandler methods
462 public void startDTD(String name, String publicId, String systemId)
463 throws SAXException {
464 } // startDTD(String,String,String)
467 public void endDTD() throws SAXException {
471 public void startEntity(String name) throws SAXException {
472 } // startEntity(String)
475 public void endEntity(String name) throws SAXException {
476 } // endEntity(String)
478 /** Start CDATA section. */
479 public void startCDATA() throws SAXException {
481 fOut.print("<![CDATA[");
486 /** End CDATA section. */
487 public void endCDATA() throws SAXException {
495 public void comment(char ch[], int start, int length) throws SAXException {
496 if (!fCanonical && fElementDepth > 0) {
498 for (int i = 0; i < length; ++i) {
499 fOut.print(ch[start+i]);
505 } // comment(char[],int,int)
511 /** Returns a sorted list of attributes. */
512 protected Attributes sortAttributes(Attributes attrs) {
515 // AttributesImpl attributes = new AttributesImpl();
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();
522 // while (j < count) {
523 // if (name.compareTo(attributes.getQName(j)) < 0) {
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));
532 // return attributes;
534 } // sortAttributes(AttributeList):AttributeList
536 /** Normalizes and prints the given string. */
537 protected void normalizeAndPrint(String s, boolean isAttValue) {
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);
545 } // normalizeAndPrint(String,boolean)
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);
552 } // normalizeAndPrint(char[],int,int,boolean)
554 /** Normalizes and print the given character. */
555 protected void normalizeAndPrint(char c, boolean isAttValue) {
571 // A '"' that appears in character data
572 // does not need to be escaped.
574 fOut.print(""");
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
594 // else, default print char
597 // In XML 1.1, control chars in the ranges [#x1-#x1F, #x7F-#x9F] must be escaped.
599 // Escape space characters that would be normalized to #x20 in attribute values
600 // when the document is reparsed.
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)) {
609 fOut.print(Integer.toHexString(c).toUpperCase());
617 } // normalizeAndPrint(char,boolean)
619 /** Prints the error message. */
620 protected void printError(String type, SAXParseException ex) {
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('/');
629 systemId = systemId.substring(index + 1);
630 System.err.print(systemId);
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();
641 } // printError(String,SAXParseException)