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