1 package org.simantics.graph.representation;
3 import java.io.BufferedInputStream;
4 import java.io.DataInput;
5 import java.io.DataInputStream;
6 import java.io.InputStream;
7 import java.math.BigInteger;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.nio.file.Paths;
11 import java.security.MessageDigest;
12 import java.security.NoSuchAlgorithmException;
13 import java.util.HashMap;
16 import java.util.TreeMap;
17 import java.util.TreeSet;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.Datatypes;
23 import org.simantics.databoard.binding.Binding;
24 import org.simantics.databoard.binding.mutable.Variant;
25 import org.simantics.databoard.container.DataContainers;
26 import org.simantics.databoard.type.Datatype;
28 import gnu.trove.list.array.TIntArrayList;
29 import gnu.trove.map.hash.TIntObjectHashMap;
30 import gnu.trove.set.hash.TLongHashSet;
33 * @author Antti Villberg
36 public class PrettyPrintTG extends TransferableGraphUtils {
41 private final Pattern versionExtractPattern = Pattern.compile("^.*-(\\d+\\.\\d+)");
43 final StringBuilder output;
44 final Map<String,String> ontologies = new HashMap<>(knownOntologies);
45 final Set<String> referencedOntologies = new TreeSet<>();
47 static class ResourceInfo {
51 boolean newResource = false;
53 // -1 = no owner, -2 = multiple owners
55 int ownerPredicate = 0;
56 String aliasURI = null;
57 TIntArrayList owned = new TIntArrayList();
58 //TIntObjectHashMap<TIntHashSet> statements = new TIntObjectHashMap<TIntHashSet>();
59 public ResourceInfo(boolean hasURI, String name, int resource) {
62 this.resource = resource;
66 public PrettyPrintTG(StringBuilder b) throws NoSuchAlgorithmException {
68 m = MessageDigest.getInstance("SHA-256");
71 public PrettyPrintTG() throws NoSuchAlgorithmException {
72 this(new StringBuilder());
75 TIntObjectHashMap<ResourceInfo> infos = new TIntObjectHashMap<>();
77 String tgNodeName(String name) {
78 if(name.contains(" ")) return "\"" + name + "\"";
82 ResourceInfo recurseURI(TransferableGraph1 graph, Identity parent, String parentName) {
83 String name = parentName + "." + tgNodeName(getName(parent));
84 ResourceInfo info = new ResourceInfo(true, name, parent.resource);
85 infos.put(parent.resource, info);
86 for(Identity child : getChildren(graph, parent)) {
87 recurseURI(graph, child, name);
92 void discoverBlank(TransferableGraph1 graph, int resource, TIntArrayList todo) throws Exception {
93 TIntArrayList statements = getStatements(graph, resource);
94 for(int i=0;i<statements.size();i+=2) {
95 int object = statements.get(i+1);
96 Identity objectId = getIdentity(graph, object);
97 if(objectId != null) {
98 if(objectId.definition instanceof External) continue;
100 Value value = TransferableGraphUtils.findValue(graph, object);
102 infos.put(object, new ResourceInfo(false, printValue(value), object));
105 ResourceInfo existing = infos.get(object);
106 if(existing == null) {
107 existing = new ResourceInfo(false, "blank" + blankCounter++, object);
108 infos.put(object, existing);
114 void discoverOwners(TransferableGraph1 graph, ResourceInfo info) {
115 int resource = info.resource;
116 TIntArrayList statements = getStatements(graph, resource);
117 for(int i=0;i<statements.size();i+=2) {
118 int predicate = statements.get(i);
119 int object = statements.get(i+1);
120 ResourceInfo existing = infos.get(object);
121 if(existing == null) continue;
122 if(existing.owner == -1) {
123 existing.owner = resource;
124 existing.ownerPredicate = predicate;
125 //System.err.println("First owner " + info.name + " => " + predicateURI + " " + existing.name);
128 //System.err.println("Multiple owners " + info.name + " => " + predicateURI + " " + existing.name);
134 String printValue(Value value) throws Exception {
135 Variant variant = value.value;
136 Datatype dt = variant.getBinding().type();
137 if(Datatypes.STRING.equals(dt)) {
138 String s = (String)variant.getValue(Bindings.STRING);
139 if(s.contains("\n")) {
140 return "\"\"\"" + s + "\"\"\"";
142 return "\"" + s + "\"";
144 } else if(Datatypes.BOOLEAN.equals(dt)) {
145 Boolean i = (Boolean)variant.getValue(Bindings.BOOLEAN);
146 return i ? "true" : "false";
147 } else if(Datatypes.INTEGER.equals(dt)) {
148 Integer i = (Integer)variant.getValue(Bindings.INTEGER);
150 } else if(Datatypes.LONG.equals(dt)) {
151 Long i = (Long)variant.getValue(Bindings.LONG);
154 byte[] data = variant.getBinding().serializer().serialize(variant.getValue());
156 m.update(data, 0, data.length);
157 return "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
162 // void fixInstanceOf(TransferableGraph1 graph, ResourceInfo info) {
163 // Identity id = getIdentity(graph, info.resource);
164 // if(id == null) return;
165 // if(id.definition instanceof Internal) {
166 // Identity instanceOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/InstanceOf");
167 // Identity library = findExternal(graph, "http://www.simantics.org/Layer0-1.1/Library");
168 // info.statements.add(instanceOf.resource);
169 // info.statements.add(library.resource);
173 public static String getExternalURI(TransferableGraph1 tg, External ext) {
174 String name = ext.name;
175 if(name.contains(" ")) name = name.replace(" ", "_").replaceAll("@", "_");//name = "\"" + name + "\"";
176 int parentId = ext.parent;
177 //if(parentId == 0) return ext.name;
179 Identity id = getIdentity(tg, parentId);
180 if(id.definition instanceof External) {
181 return getExternalURI(tg, (External)id.definition) + "/" + name;
182 } else if(id.definition instanceof Root) {
183 Root root = (Root)id.definition;
184 return "http:/" + root.name + "/" + name;
191 public static String getExternalURI(TransferableGraph1 tg, int resource) {
192 Identity id = getIdentity(tg, resource);
193 if(id == null) return null;
194 if(id.definition instanceof External) {
195 External ext = (External)id.definition;
196 return getExternalURI(tg, ext);
201 String rewritePredicateURI(TransferableGraph1 graph, int predicate) {
203 String uri = getExternalURI(graph, predicate);
205 ResourceInfo info = infos.get(predicate);
206 if(info != null) return info.name;
210 for(String ontology : ontologies.keySet()) {
211 if(uri.contains(ontology)) {
212 String key = ontologies.get(ontology);
213 uri = uri.replace(ontology, key);
214 referencedOntologies.add(ontology);
218 uri = uri.replace("/", ".");
224 void indent(int indent) {
225 for(int i=0;i<indent;i++) output.append(" ");
228 void printBlank(TransferableGraph1 graph, String predicateURI2, ResourceInfo info, int indent) {
230 if(info.hasURI) return;
232 output.append(predicateURI2 + " " + info.name + "\n");
235 printURI(graph, info, false, indent);
240 long longStm(int predicate, int object) {
241 return (predicate<<32) | (object & 0xffffffffL);
244 void addStatement(Map<String,Set<String>> statements, String predicate, String object) {
245 Set<String> objects = statements.get(predicate);
246 if(objects == null) {
247 objects = new TreeSet<>();
248 statements.put(predicate, objects);
253 void printURI(TransferableGraph1 graph, ResourceInfo info, boolean requireURI, int indent) {
255 if(requireURI && !info.hasURI) return;
257 Map<String,Set<String>> statements = new TreeMap<>();
258 Identity consistsOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/ConsistsOf");
259 TLongHashSet processed = new TLongHashSet();
260 for(int i=0;i<info.owned.size();i+=2) {
261 long stmId = longStm(info.owned.get(i), info.owned.get(i+1));
262 processed.add(stmId);
265 TIntArrayList rawStatements = getStatements(graph, info.resource);
266 for(int i=0;i<rawStatements.size();i+=2) {
267 long stmId = longStm(rawStatements.get(i), rawStatements.get(i+1));
268 if(!processed.add(stmId)) continue;
269 if(consistsOf.resource == rawStatements.get(i)) continue;
270 String predicateURI = rewritePredicateURI(graph, rawStatements.get(i));
271 ResourceInfo objectInfo = infos.get(rawStatements.get(i+1));
272 if(objectInfo == null) {
273 String objectURI = rewritePredicateURI(graph, rawStatements.get(i+1));
274 addStatement(statements, predicateURI, objectURI);
276 addStatement(statements, predicateURI, objectInfo.name);
281 if("ROOT".equals(info.name)) {
282 output.append("ROOT=<http:/>");
283 } else if (info.aliasURI != null) {
284 output.append(info.name + " = <" + info.aliasURI + ">");
286 output.append(info.name);
288 Set<String> instanceOfs = statements.get("L0.InstanceOf");
289 if(instanceOfs != null) {
290 for(String instanceOf : instanceOfs) {
291 output.append(" : " + instanceOf);
294 Set<String> subrelationOfs = statements.get("L0.SubrelationOf");
295 if(subrelationOfs != null) {
296 for(String subrelationOf : subrelationOfs) {
297 output.append(" <R " + subrelationOf);
300 Set<String> inherits = statements.get("L0.Inherits");
301 if(inherits != null) {
302 for(String inherit : inherits) {
303 output.append(" <T " + inherit);
310 output.append(" @L0.new\n");
312 for(Map.Entry<String, Set<String>> entry : statements.entrySet()) {
313 String predicate = entry.getKey();
314 if("L0.InstanceOf".equals(predicate)) continue;
315 if("L0.SubrelationOf".equals(predicate)) continue;
316 if("L0.Inherits".equals(predicate)) continue;
317 if("L0.PartOf".equals(predicate)) continue;
318 Set<String> objects = entry.getValue();
319 if(objects.size() == 1) {
321 output.append(predicate + " " + objects.iterator().next() + "\n");
324 output.append(predicate + "\n");
325 for(String object : objects) {
327 output.append(" " + object + "\n");
332 for(int i=0;i<info.owned.size();i+=2) {
333 String predicateURI = rewritePredicateURI(graph, info.owned.get(i));
334 ResourceInfo ownedInfo = infos.get(info.owned.get(i+1));
335 printBlank(graph, predicateURI, ownedInfo, indent+1);
340 void prettyPrint(Path input, Path output) throws Exception {
342 System.out.format("Converting exported shared ontology%n\t" + input.toString() + "%nto bundle-compatible ontology%n\t" + output.toString());
343 try (InputStream is = new BufferedInputStream(Files.newInputStream(input), 128*1024)) {
344 DataInput dis = new DataInputStream(is);
345 org.simantics.databoard.container.DataContainer container =
346 DataContainers.readFile(dis);
347 Binding binding = TransferableGraph1.BINDING;
348 TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
350 Files.write(output, this.output.toString().getBytes());
356 static Map<String,String> knownOntologies = new HashMap<>();
359 knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0");
360 knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X");
361 knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD");
362 knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA");
363 knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR");
364 knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC");
365 knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU");
366 knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D");
367 knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL");
368 knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP");
369 knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2");
370 knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE");
371 knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT");
372 knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA");
373 knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH");
377 void prettyPrint(TransferableGraph1 graph) throws Exception {
379 for(Identity id : graph.identities) {
380 if(id.definition instanceof Internal) {
381 Internal internal = (Internal)id.definition;
382 Identity parent = TransferableGraphUtils.getIdentity(graph, internal.parent);
383 if(parent.definition instanceof External) {
384 String name = "BASE";
385 ResourceInfo info = new ResourceInfo(true, name, id.resource);
386 info.aliasURI = TransferableGraphUtils.getURI(graph, id.resource);
387 info.newResource = true;
388 infos.put(id.resource, info);
389 for(Identity child : getChildren(graph, id)) {
390 recurseURI(graph, child, name);
393 } else if (id.definition instanceof External) {
394 External ext = (External)id.definition;
395 // Try to detect shared libraries
396 if(ext.name.contains("@")) {
398 int index = ext.name.indexOf('@');
399 String prefix = ext.name.substring(0, index);
400 int index2 = ext.name.indexOf('/', index);
401 String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2);
402 String uri = TransferableGraphUtils.getURI(graph, id.resource);
403 ontologies.put(uri, prefix);
405 } else if (ext.name.contains("-")) {
407 String uri = TransferableGraphUtils.getURI(graph, id.resource);
408 Matcher m = versionExtractPattern.matcher(uri);
410 if(!ontologies.containsKey(uri)) {
411 int index = ext.name.indexOf('-');
412 String prefix = ext.name.substring(0, index);
413 ontologies.put(uri, prefix);
423 // Discover other resources
424 TIntArrayList todo = new TIntArrayList();
425 for(ResourceInfo info : infos.valueCollection())
426 todo.add(info.resource);
427 while(!todo.isEmpty()) {
428 int resource = todo.removeAt(todo.size()-1);
429 discoverBlank(graph, resource, todo);
431 for(ResourceInfo info : infos.valueCollection())
432 discoverOwners(graph, info);
433 // for(ResourceInfo info : infos.valueCollection())
434 // fixInstanceOf(graph, info);
435 for(ResourceInfo info : infos.valueCollection())
436 if(info.owner >= 0) {
437 ResourceInfo ownerInfo = infos.get(info.owner);
438 ownerInfo.owned.add(info.ownerPredicate);
439 ownerInfo.owned.add(info.resource);
442 TreeMap<String,ResourceInfo> order = new TreeMap<>();
443 for(ResourceInfo info : infos.valueCollection())
444 order.put(info.name, info);
446 for(ResourceInfo info : order.values())
447 printURI(graph, info, true, 0);
449 for(ResourceInfo info : order.values())
450 if(!info.hasURI && info.owner < 0)
451 printURI(graph, info, false, 0);
453 StringBuilder refs = new StringBuilder();
454 for(String ontology : referencedOntologies) {
455 String key = ontologies.get(ontology);
456 refs.append(key + " = <" + ontology + ">\n");
458 output.insert(0, refs.toString());
462 public static String print(TransferableGraph1 tg) throws Exception {
463 StringBuilder b = new StringBuilder();
464 new PrettyPrintTG(b).prettyPrint(tg);
468 public static void main(String[] args) throws Exception {
469 if (args.length < 1) {
470 System.out.println("Required arguments: <input .sharedOntology file> [<output .tg file>]");
471 } else if (args.length < 2) {
472 Path input = Paths.get(args[0]);
473 Path output = input.getParent().resolve(input.getName(input.getNameCount()-1) + ".fixed");
474 new PrettyPrintTG().prettyPrint(input, output);
476 new PrettyPrintTG().prettyPrint(Paths.get(args[0]), Paths.get(args[1]));