package org.simantics.graph.representation; import java.io.BufferedInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.InputStream; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.container.DataContainers; import org.simantics.databoard.parser.DataValuePrinter; import org.simantics.databoard.parser.PrintFormat; import org.simantics.databoard.parser.repository.DataValueRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.procedure.TIntIntProcedure; import gnu.trove.set.hash.TLongHashSet; /** * @author Antti Villberg * @since 1.24.0 */ public class PrettyPrintTG { private static final Logger LOGGER = LoggerFactory.getLogger(PrettyPrintTG.class); int blankCounter = 0; MessageDigest m; private final Pattern versionExtractPattern = Pattern.compile("^.*-(\\d+\\.\\d+)"); final StringBuilder output; final Map ontologies = new HashMap<>(knownOntologies); final Set referencedOntologies = new TreeSet<>(); static class ResourceInfo { final boolean hasURI; final String name; final int resource; boolean newResource = false; int parent; boolean inlined = false; // -1 = no owner, -2 = multiple owners // Set ownedBy Set ownedBy = new HashSet<>(); // A Map containing information about resource that this resource owns and what are the predicates for forming this ownership TIntIntHashMap ownedResourcesWithPredicates = new TIntIntHashMap(); // int owner = -1; // int ownerPredicate = 0; String aliasURI = null; TIntArrayList owned = new TIntArrayList(); //TIntObjectHashMap statements = new TIntObjectHashMap(); public ResourceInfo(boolean hasURI, String name, int resource, int parent) { this.hasURI = hasURI; this.name = name; this.resource = resource; this.parent = parent; } @Override public String toString() { return name + (aliasURI != null ? " = <" + aliasURI + ">" : ""); } } public PrettyPrintTG(StringBuilder b) throws NoSuchAlgorithmException { output = b; m = MessageDigest.getInstance("SHA-256"); } public PrettyPrintTG() throws NoSuchAlgorithmException { this(new StringBuilder()); } TIntObjectHashMap infos = new TIntObjectHashMap<>(); String tgNodeName(String name) { if(name.contains(" ")) return "\"" + name + "\""; else return name; } ResourceInfo recurseURI(TransferableGraph1 graph, Identity parent, String parentName, int parentId) { String name = parentName + "." + tgNodeName(TransferableGraphUtils.getName(parent)); ResourceInfo info = new ResourceInfo(true, name, parent.resource, parentId); infos.put(parent.resource, info); for(Identity child : TransferableGraphUtils.getChildren(graph, parent)) { recurseURI(graph, child, name, info.resource); } return info; } void discoverBlank(TransferableGraph1 graph, int resource, TIntArrayList todo) throws Exception { TIntArrayList statements = TransferableGraphUtils.getStatements(graph, resource); for(int i=0;i 100) { // Ok, value too long, lets calculate a hash for it and store first 100 chars as comment byte[] data = Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue()); m.reset(); m.update(data, 0, data.length); String hash = "\"" + new BigInteger(1, m.digest()).toString(16) + "\""; return hash + " // " + formattedOutput.substring(0, 100) + ".."; } else { return formattedOutput; } // // Variant variant = value.value; // Datatype dt = variant.getBinding().type(); // if (Datatypes.STRING.equals(dt)) { // String s = (String) variant.getValue(Bindings.STRING); // if (s.contains("\n")) { // return "\"\"\"" + s + "\"\"\""; // } else { // return "\"" + s + "\""; // } // } else if (Datatypes.BOOLEAN.equals(dt)) { // Boolean i = (Boolean) variant.getValue(Bindings.BOOLEAN); // return i ? "true" : "false"; // } else if (Datatypes.INTEGER.equals(dt)) { // Integer i = (Integer) variant.getValue(Bindings.INTEGER); // return i.toString(); // } else if (Datatypes.LONG.equals(dt)) { // Long i = (Long) variant.getValue(Bindings.LONG); // return i.toString(); // } else if (Datatypes.DOUBLE.equals(dt)) { // Double d = (Double) variant.getValue(); // return d.toString(); // } else if (Datatypes.FLOAT.equals(dt)) { // Float f = (Float) variant.getValue(); // return f.toString(); // } else if (Datatypes.STRING_ARRAY.equals(dt)) { // return Arrays.toString((String []) variant.getValue()); // } else if (Datatypes.BOOLEAN_ARRAY.equals(dt)) { // return Arrays.toString((boolean []) variant.getValue()); // } else if (Datatypes.INTEGER_ARRAY.equals(dt)) { // return Arrays.toString((int []) variant.getValue()); // } else if (Datatypes.LONG_ARRAY.equals(dt)) { // return Arrays.toString((long []) variant.getValue()); // } else if (Datatypes.DOUBLE_ARRAY.equals(dt)) { // return Arrays.toString((double []) variant.getValue()); // } else if (Datatypes.FLOAT_ARRAY.equals(dt)) { // return Arrays.toString((float []) variant.getValue()); // } else if (Datatypes.BYTE_ARRAY.equals(dt)) { // return Arrays.toString((byte []) variant.getValue()); //// } else if (dt instanceof ArrayType) { //// return Arrays.toString((Object []) variant.getValue()); // } else { // byte[] data = Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue()); // m.reset(); // m.update(data, 0, data.length); // return "\"" + new BigInteger(1, m.digest()).toString(16) + "\""; // } } // void fixInstanceOf(TransferableGraph1 graph, ResourceInfo info) { // Identity id = getIdentity(graph, info.resource); // if(id == null) return; // if(id.definition instanceof Internal) { // Identity instanceOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/InstanceOf"); // Identity library = findExternal(graph, "http://www.simantics.org/Layer0-1.1/Library"); // info.statements.add(instanceOf.resource); // info.statements.add(library.resource); // } // } public static String getExternalURI(TransferableGraph1 tg, External ext) { String name = ext.name; if(name.contains(" ")) name = name.replace(" ", "_").replaceAll("@", "_");//name = "\"" + name + "\""; int parentId = ext.parent; //if(parentId == 0) return ext.name; // else { Identity id = TransferableGraphUtils.getIdentity(tg, parentId); if(id.definition instanceof External) { return getExternalURI(tg, (External)id.definition) + "/" + name; } else if(id.definition instanceof Root) { Root root = (Root)id.definition; return "http:/" + root.name + "/" + name; } else { return null; } // } } public static String getExternalURI(TransferableGraph1 tg, int resource) { Identity id = TransferableGraphUtils.getIdentity(tg, resource); if(id == null) return null; if(id.definition instanceof External) { External ext = (External)id.definition; return getExternalURI(tg, ext); } return null; } String rewritePredicateURI(TransferableGraph1 graph, int predicate) { String uri = getExternalURI(graph, predicate); if(uri == null) { ResourceInfo info = infos.get(predicate); if(info != null) return info.name; return "_"; } for(String ontology : ontologies.keySet()) { if(uri.contains(ontology)) { String key = ontologies.get(ontology); uri = uri.replace(ontology, key); referencedOntologies.add(ontology); } } uri = uri.replace("/", "."); return uri; } static void indent(StringBuilder output, int indent) { for(int i=0;i> statements, String predicate, ResourceInfo objectInfo, int indent) { Set objects = statements.get(predicate); if(objects == null) { objects = new TreeSet<>(); statements.put(predicate, objects); } String uri = printURI(graph, objectInfo, false, indent + 1, true); if (uri != null) { // TODO: this is not the right place to remove trailing newline uri = uri.endsWith("\n") ? uri.substring(0, uri.length() - 2) : uri; objects.add(uri); } objectInfo.inlined = true; } void addStatement(Map> statements, String predicate, String object) { // TODO: fix this if (predicate.endsWith("Inverse")) return; Set objects = statements.get(predicate); if(objects == null) { objects = new TreeSet<>(); statements.put(predicate, objects); } objects.add(object); } String printURI(TransferableGraph1 graph, ResourceInfo info, boolean requireURI, int indent, boolean inline) { if(requireURI && !info.hasURI) return null; // Check if this ResourceInfo is already inlined with some other ResourceInfo if (info.inlined) return null; Map> statements = new TreeMap<>(); Identity consistsOf = TransferableGraphUtils.findExternal(graph, "http://www.simantics.org/Layer0-1.1/ConsistsOf"); TLongHashSet processed = new TLongHashSet(); for(int i=0;i debug = new HashSet<>(); info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() { @Override public boolean execute(int owner, int predicate) { ResourceInfo ownerInfo = infos.get(owner); debug.add(ownerInfo); // ResourceInfo predicateInfo = infos.get(predicate); // debug.add(predicateInfo); return true; } }); StringBuilder output = new StringBuilder(); if(indent == 0 || inline) { if("ROOT".equals(info.name)) { output.append("ROOT="); } else if (info.aliasURI != null) { output.append(info.name + " = <" + info.aliasURI + ">"); } else { if (inline) System.out.println("asdasd"); output.append(inline ? "_" : info.name); } Set instanceOfs = statements.get("L0.InstanceOf"); if(instanceOfs != null) { for(String instanceOf : instanceOfs) { output.append(" : " + instanceOf); } } Set subrelationOfs = statements.get("L0.SubrelationOf"); if(subrelationOfs != null) { for(String subrelationOf : subrelationOfs) { output.append(" inherits = statements.get("L0.Inherits"); if(inherits != null) { for(String inherit : inherits) { output.append(" > entry : statements.entrySet()) { String predicate = entry.getKey(); if("L0.InstanceOf".equals(predicate)) continue; if("L0.SubrelationOf".equals(predicate)) continue; if("L0.Inherits".equals(predicate)) continue; if("L0.PartOf".equals(predicate)) continue; Set objects = entry.getValue(); indent(output, indent+1); if(objects.size() == 1) { output.append(predicate + " " + objects.iterator().next() + "\n"); } else{ output.append(predicate + "\n"); for(String object : objects) { indent(output, indent+1); output.append(" " + object + "\n"); } } } for(int i=0;i knownOntologies = new HashMap<>(); static { knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0"); knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X"); knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD"); knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA"); knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR"); knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC"); knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU"); knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D"); knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL"); knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP"); knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2"); knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE"); knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT"); knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA"); knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH"); } void prettyPrint(TransferableGraph1 graph) throws Exception { if (LOGGER.isDebugEnabled()) LOGGER.debug("Starting prettyPrint for TransferableGraph with " + graph.resourceCount + " resources, " + graph.identities + " identities, " + graph.statements.length + " statements and " + graph.values.length + " values"); for(Identity id : graph.identities) { if(id.definition instanceof Internal) { Internal internal = (Internal)id.definition; Identity parent = TransferableGraphUtils.getIdentity(graph, internal.parent); if(parent.definition instanceof External) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Resolving internal identity " + id); String name = "BASE"; ResourceInfo info = new ResourceInfo(true, name, id.resource, -1); info.aliasURI = TransferableGraphUtils.getURI(graph, id.resource); info.newResource = true; infos.put(id.resource, info); if (LOGGER.isDebugEnabled()) LOGGER.debug(" which parent is external " + parent + " and has an aliasURI " + info.aliasURI) ; for(Identity child : TransferableGraphUtils.getChildren(graph, id)) { recurseURI(graph, child, name, info.resource); } if (LOGGER.isDebugEnabled()) LOGGER.debug(" and has " + (infos.size() - 1) + " children"); } } else if (id.definition instanceof External) { External ext = (External)id.definition; // Try to detect shared libraries if(ext.name.contains("@")) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Detected an external shared library " + ext); int index = ext.name.indexOf('@'); String prefix = ext.name.substring(0, index); int index2 = ext.name.indexOf('/', index); String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2); String uri = TransferableGraphUtils.getURI(graph, id.resource); if (LOGGER.isDebugEnabled()) LOGGER.debug(" which was resolved as URI=" + uri + " and prefix " + prefix); ontologies.put(uri, prefix); } else if (ext.name.contains("-")) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Resolving possible ontology " + ext); String uri = TransferableGraphUtils.getURI(graph, id.resource); Matcher m = versionExtractPattern.matcher(uri); if (m.matches()) { if(!ontologies.containsKey(uri)) { int index = ext.name.indexOf('-'); String prefix = ext.name.substring(0, index); if (LOGGER.isDebugEnabled()) LOGGER.debug(" and it was resolved as URI=" + uri + " and prefix " + prefix); ontologies.put(uri, prefix); } } } } } // Discover other resources if (LOGGER.isDebugEnabled()) LOGGER.debug("Discovering other resources.."); TIntArrayList todo = new TIntArrayList(); for(ResourceInfo info : infos.valueCollection()) todo.add(info.resource); while(!todo.isEmpty()) { int resource = todo.removeAt(todo.size()-1); discoverBlank(graph, resource, todo); } for(ResourceInfo info : infos.valueCollection()) discoverOwners(graph, info); // for(ResourceInfo info : infos.valueCollection()) // fixInstanceOf(graph, info); for (ResourceInfo info : infos.valueCollection()) { // Old implementation // if (info.owner >= 0) { // ResourceInfo ownerInfo = infos.get(info.owner); // System.out.println("originalOwner : " + info.owner + " originalPredicate: " + info.ownerPredicate); // ownerInfo.owned.add(info.ownerPredicate); // ownerInfo.owned.add(info.resource); // } if (!info.ownedResourcesWithPredicates.isEmpty() && info.ownedResourcesWithPredicates.size() == 1) { info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() { @Override public boolean execute(int owner, int predicate) { ResourceInfo ownerInfo = infos.get(owner); ownerInfo.owned.add(predicate); ownerInfo.owned.add(info.resource); return false; } }); } } // Resolve inverses from ownedBy list for (ResourceInfo info : infos.valueCollection()) { for (int i = 0; i < info.owned.size(); i+=2) { int object = info.owned.get(i+1); ResourceInfo inf = infos.get(object); if (inf != null) { info.ownedBy.remove(inf); } } } TreeMap order = new TreeMap<>(); for(ResourceInfo info : infos.valueCollection()) order.put(info.name, info); for(ResourceInfo info : order.values()) { String uri = printURI(graph, info, true, 0, false); if (uri != null) output.append(uri); } for (ResourceInfo info : order.values()) { if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) { String uri = printURI(graph, info, false, 0, false); if (uri != null) output.append(uri); } } // for(ResourceInfo info : order.values()) // if(!info.hasURI && info.owner < 0) // printURI(graph, info, false, 0); StringBuilder refs = new StringBuilder(); for(String ontology : referencedOntologies) { String key = ontologies.get(ontology); refs.append(key + " = <" + ontology + ">\n"); } if (!referencedOntologies.isEmpty()) refs.append("\n"); output.insert(0, refs.toString()); } public static String print(TransferableGraph1 tg) throws Exception { StringBuilder b = new StringBuilder(); new PrettyPrintTG(b).prettyPrint(tg); return b.toString(); } public static void main(String[] args) throws Exception { if (args.length < 1) { System.out.println("Required arguments: []"); } else if (args.length < 2) { Path input = Paths.get(args[0]); Path output = input.getParent().resolve(input.getName(input.getNameCount()-1) + ".fixed"); new PrettyPrintTG().prettyPrint(input, output); } else { new PrettyPrintTG().prettyPrint(Paths.get(args[0]), Paths.get(args[1])); } } }