X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.interop.xmlio%2Fsrc%2Forg%2Fsimantics%2Finterop%2Fxmlio%2FSaveXML.java;fp=org.simantics.interop.xmlio%2Fsrc%2Forg%2Fsimantics%2Finterop%2Fxmlio%2FSaveXML.java;h=bcbd52728e396df77ff1ae5075a31dac483f0694;hb=348b97fc6c67013c0918cb425798fb78996964a9;hp=0000000000000000000000000000000000000000;hpb=cde15da71bdb1ccba37aa890fd9fbc2ba99bac58;p=simantics%2Finterop.git diff --git a/org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java b/org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java new file mode 100644 index 0000000..bcbd527 --- /dev/null +++ b/org.simantics.interop.xmlio/src/org/simantics/interop/xmlio/SaveXML.java @@ -0,0 +1,646 @@ +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.io.Writer; +import java.util.Arrays; +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.apache.xml.serialize.OutputFormat; +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) + +}