]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.graph/src/org/simantics/graph/representation/PrettyPrintTG.java
Escape/unescape names of the externals when converting to/from URIs
[simantics/platform.git] / bundles / org.simantics.graph / src / org / simantics / graph / representation / PrettyPrintTG.java
1 package org.simantics.graph.representation;
2
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.ArrayList;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20 import java.util.TreeMap;
21 import java.util.TreeSet;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
25
26 import org.simantics.databoard.Bindings;
27 import org.simantics.databoard.binding.Binding;
28 import org.simantics.databoard.binding.mutable.Variant;
29 import org.simantics.databoard.container.DataContainers;
30 import org.simantics.databoard.parser.DataValuePrinter;
31 import org.simantics.databoard.parser.PrintFormat;
32 import org.simantics.databoard.parser.repository.DataValueRepository;
33 import org.simantics.databoard.util.URIStringUtils;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import gnu.trove.list.array.TIntArrayList;
38 import gnu.trove.map.hash.THashMap;
39 import gnu.trove.map.hash.TIntIntHashMap;
40 import gnu.trove.map.hash.TIntObjectHashMap;
41 import gnu.trove.procedure.TIntIntProcedure;
42 import gnu.trove.set.hash.TLongHashSet;
43
44 /**
45  * @author Antti Villberg
46  * @since 1.24.0
47  */
48 public class PrettyPrintTG {
49
50     private static final Logger LOGGER = LoggerFactory.getLogger(PrettyPrintTG.class);
51
52     private static final boolean DEBUG = false;
53
54     int blankCounter = 0;
55     int newBlankCounter = 0;
56     Map<String, String> blankRewrites = new HashMap<>();
57     MessageDigest m;
58
59     private final Pattern versionExtractPattern = Pattern.compile("^.*-(\\d+\\.\\d+)");
60
61     final StringBuilder output;
62     final Map<String, String> ontologies = new HashMap<>(knownOntologies);
63     final Set<String> referencedOntologies = new TreeSet<>();
64
65     private boolean ignoreIdentifiers;
66
67     private TransferableGraphQueries query;
68
69     static class ResourceInfo {
70         final boolean hasURI;
71         String name;
72         final int resource;
73         boolean newResource = false;
74
75         int parent;
76
77         boolean inlined = false;
78
79         // -1 = no owner, -2 = multiple owners
80
81         // Set<ResourceInfo> ownedBy
82         Set<ResourceInfo> ownedBy = new HashSet<>();
83
84         // A Map<Integer, Integer> containing information about resource that
85         // this resource owns and what are the predicates for forming this
86         // ownership
87         TIntIntHashMap ownedResourcesWithPredicates = new TIntIntHashMap();
88
89         // int owner = -1;
90         // int ownerPredicate = 0;
91         String aliasURI = null;
92         TIntArrayList owned = new TIntArrayList();
93
94         // TIntObjectHashMap<TIntHashSet> statements = new
95         // TIntObjectHashMap<TIntHashSet>();
96         public ResourceInfo(boolean hasURI, String name, int resource, int parent) {
97             this.hasURI = hasURI;
98             this.name = name;
99             this.resource = resource;
100             this.parent = parent;
101         }
102
103         @Override
104         public String toString() {
105             return name + (aliasURI != null ? " = <" + aliasURI + ">" : "");
106         }
107     }
108
109     public PrettyPrintTG(TransferableGraph1 tg, StringBuilder b, boolean ignoreIdentifiers) throws NoSuchAlgorithmException {
110         output = b;
111         m = MessageDigest.getInstance("SHA-256");
112         this.ignoreIdentifiers = ignoreIdentifiers;
113         
114         this.query = new TransferableGraphQueries(tg);
115     }
116
117     public PrettyPrintTG(TransferableGraph1 tg) throws NoSuchAlgorithmException {
118         this(tg, new StringBuilder(), false);
119     }
120
121     TreeMap<String, ResourceInfo> orderedInfos = new TreeMap<>();
122     TIntObjectHashMap<ResourceInfo> infos = new TIntObjectHashMap<>();
123
124     private static String tgNodeName(String name) {
125         if (name.contains(" "))
126             return "\"" + name + "\"";
127         else
128             return name;
129     }
130
131     private ResourceInfo recurseURI(Map<String, ResourceInfo> infos, Identity identity, String parentName, int parentId) {
132         String name = parentName + "." + tgNodeName(TransferableGraphUtils.getName(identity));
133         int identityResource = identity.resource;
134         ResourceInfo info = new ResourceInfo(true, name, identityResource, parentId);
135         infos.put(name, info);
136         for (Identity child : query.getChildren(identity))
137             recurseURI(infos, child, name, identityResource);
138         return info;
139     }
140
141     /**
142      * Sorts statements by predicateURI in natural order
143      * 
144      * @param graph
145      * @param resource
146      * @return
147      */
148     private TreeMap<String, TreeSet<Integer>> sortByPredicateUniqueStatements(int resource) {
149         TreeMap<String, TreeSet<Integer>> results = new TreeMap<>();
150         TIntArrayList statements = query.getStatements(resource);
151         for (int i = 0; i < statements.size(); i += 2) {
152             int predicate = statements.get(i);
153             String predicateURI = query.getURI(predicate);
154             TreeSet<Integer> objects = results.get(predicateURI);
155             if (objects == null) {
156                 objects = new TreeSet<>();
157             }
158             objects.add(statements.get(i + 1));
159             results.put(predicateURI, objects);
160         }
161         return results;
162     }
163
164     void discoverBlank(int resource, TIntArrayList todo) throws Exception {
165         // TIntArrayList statements =
166         // TransferableGraphUtils.getStatements(graph, resource);
167         // for(int i=0;i<statements.size();i+=2) {
168         for (TreeSet<Integer> objects : sortByPredicateUniqueStatements(resource).values()) {
169             for (int object : objects) {
170                 // int object = statements.get(i+1);
171                 Identity objectId = query.getIdentity(object);
172                 if (objectId != null) {
173                     if (objectId.definition instanceof External)
174                         continue;
175                 }
176                 Value value = query.findValue(object);
177                 if (value != null) {
178                     infos.put(object, new ResourceInfo(false, printValue(value), object, resource));
179                 } else {
180                     ResourceInfo existing = infos.get(object);
181                     if (existing == null) {
182     
183                         existing = new ResourceInfo(false, "blank" + blankCounter++, object, resource);
184     
185                         // System.out.println("created blank" + blankCounter + "
186                         // with object " + object + " resource " + resource);
187                         infos.put(object, existing);
188                         todo.add(object);
189                     }
190                 }
191             }
192         }
193     }
194
195     private String makeHash(byte[] data) {
196         m.reset();
197         m.update(data, 0, data.length);
198         return new BigInteger(1, m.digest()).toString(16);
199     }
200
201     void discoverOwners(ResourceInfo info) {
202         log("Discovering owners for {}", info);
203         int resource = info.resource;
204         TIntArrayList statements = query.getStatements(resource);
205         for (int i = 0; i < statements.size(); i += 2) {
206             int predicate = statements.get(i);
207             int object = statements.get(i + 1);
208             ResourceInfo existing = infos.get(object);
209             if (existing != null) {
210                 // Add all owners here for now and resolve the best owner later
211
212                 // Check if predicate is inverse, this just resolves all
213                 // predicates to be inverse with ending "Inverse"..
214                 String predicateUri = rewritePredicateURI(predicate);
215                 if (!predicateUri.endsWith("Inverse") && !predicateUri.endsWith("Of")) {
216                     existing.ownedResourcesWithPredicates.put(resource, predicate);
217                     // if (predicateUri.endsWith("Of")) {
218                     // System.out.println("asd");
219                     // } else {
220                     existing.ownedBy.add(info);
221                     log("    {} owns {} with {}", existing, info, predicateUri);
222                     // }
223                 } else {
224                     // System.out.println("asd");
225                 }
226             }
227         }
228     }
229
230     DataValueRepository repo = new DataValueRepository();
231     DataValuePrinter printer = new DataValuePrinter(null, repo);
232
233     String printValue(Value value) throws Exception {
234         StringBuilder sb = new StringBuilder();
235         printer.setFormat(PrintFormat.SINGLE_LINE);
236         printer.setOutput(sb);
237
238         Variant variant = value.value;
239         printer.print(variant.getBinding(), variant.getValue());
240         String formattedOutput = sb.toString();
241         if (formattedOutput.length() > 100) {
242             // Ok, value too long, lets calculate a hash for it and store first
243             // 100 chars as comment
244             byte[] data = Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue());
245             m.reset();
246             m.update(data, 0, data.length);
247             String hash = "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
248             return hash + " // " + formattedOutput.substring(0, 100) + "..";
249         } else {
250             return formattedOutput;
251         }
252     }
253
254     public String getExternalURI(External ext) {
255         String name = ext.name;
256         if (name.contains(" "))
257             name = name.replace(" ", "_").replaceAll("@", "_");// name = "\"" +
258                                                                // name + "\"";
259         int parentId = ext.parent;
260         // if(parentId == 0) return ext.name;
261         // else {
262         Identity id = query.getIdentity(parentId);
263         if (id.definition instanceof External) {
264             return getExternalURI((External) id.definition) + "/" + URIStringUtils.escape(name);
265         } else if (id.definition instanceof Root) {
266             Root root = (Root) id.definition;
267             return "http:/" + URIStringUtils.escape(root.name) + "/" + URIStringUtils.escape(name);
268         } else {
269             return null;
270         }
271         // }
272     }
273
274     public String getExternalURI(int resource) {
275         Identity id = query.getIdentity(resource);
276         if (id == null)
277             return null;
278         if (id.definition instanceof External) {
279             External ext = (External) id.definition;
280             return getExternalURI(ext);
281         }
282         return null;
283     }
284
285     String rewritePredicateURI(int predicate) {
286
287         String uri = getExternalURI(predicate);
288         if (uri == null) {
289             ResourceInfo info = infos.get(predicate);
290             if (info != null)
291                 return info.name;
292             return "_";
293         }
294
295         for (String ontology : ontologies.keySet()) {
296             if (uri.contains(ontology)) {
297                 String key = ontologies.get(ontology);
298                 uri = uri.replace(ontology, key);
299                 referencedOntologies.add(ontology);
300             }
301         }
302
303         uri = uri.replace("/", ".");
304
305         return uri;
306
307     }
308
309     static void indent(StringBuilder output, int indent) {
310         for (int i = 0; i < indent; i++)
311             output.append("  ");
312     }
313
314     String printBlank(String predicateURI2, ResourceInfo info, int indent) {
315
316         if (info.hasURI)
317             return null;
318
319         StringBuilder output = new StringBuilder();
320
321         String infoName = info.name;
322         if (infoName.startsWith("blank")) {
323             infoName = getBlankRewrite(infoName);
324         }
325
326         if (ignoreIdentifiers) {
327             if (predicateURI2.contains("L0.identifier")) {
328                 // Do nothing
329             } else {
330                 indent(output, indent);
331                 output.append(predicateURI2 + " " + infoName + "\n");
332             }
333         } else {
334             indent(output, indent);
335             output.append(predicateURI2 + " " + infoName + "\n");
336         }
337
338         if (info.ownedResourcesWithPredicates.isEmpty()) {
339             if (DEBUG)
340                 System.out.print("printBlank");
341             String uri = printURI(info, false, indent, false);
342             if (uri != null)
343                 output.append(uri);
344         }
345         // if(info.owner < 0) {
346         // printURI(graph, info, false, indent);
347         // }
348         return output.toString();
349     }
350
351     private String getBlankRewrite(String infoName) {
352         String rewrite = blankRewrites.get(infoName);
353         if (rewrite == null) {
354             rewrite = "rblank" + newBlankCounter++;
355             if (DEBUG)
356                 System.out.println("rewrote " + infoName + " to " + rewrite);
357             blankRewrites.put(infoName, rewrite);
358         }
359         return rewrite;
360         // return infoName;
361     }
362
363     static long longStm(int predicate, int object) {
364         return ((predicate & 0xffffffffL) << 32) | (object & 0xffffffffL);
365     }
366
367     private void addInlineStatement(Map<String, Set<String>> statements, String predicate,
368             ResourceInfo objectInfo, int indent) {
369         Set<String> objects = statements.get(predicate);
370         if (objects == null) {
371             objects = new TreeSet<>();
372             statements.put(predicate, objects);
373         }
374         String uri = printURI(objectInfo, false, indent + 1, true);
375         if (uri != null) {
376             // TODO: this is not the right place to remove trailing newline
377             uri = uri.endsWith("\n") ? uri.substring(0, uri.length() - 2) : uri;
378             objects.add(uri);
379         }
380         objectInfo.inlined = true;
381     }
382
383     void addStatement(Map<String, Set<String>> statements, String predicate, String object) {
384         // TODO: fix this
385         if (predicate.endsWith("Inverse"))
386             return;
387         Set<String> objects = statements.get(predicate);
388         if (objects == null) {
389             objects = new TreeSet<>();
390             statements.put(predicate, objects);
391         }
392         objects.add(object);
393     }
394
395     String printURI(ResourceInfo info, boolean requireURI, int indent, boolean inline) {
396
397         if (requireURI && !info.hasURI)
398             return null;
399
400         // Check if this ResourceInfo is already inlined with some other
401         // ResourceInfo
402         if (info.inlined)
403             return null;
404
405         Map<String, Set<String>> statements = new TreeMap<>();
406         Identity consistsOf = query.findExternalByURI("http://www.simantics.org/Layer0-1.1/ConsistsOf");
407         // Identity partOf = TransferableGraphUtils.findExternal(graph,
408         // "http://www.simantics.org/Layer0-1.1/PartOf");
409         TLongHashSet processed = new TLongHashSet();
410         if (DEBUG)
411             System.out.println("info.owned.size " + info.owned.size() + info.owned);
412         for (int i = 0; i < info.owned.size(); i += 2) {
413             int predicate = info.owned.get(i);
414             int object = info.owned.get(i + 1);
415             long stmId = longStm(predicate, object);
416             if (DEBUG)
417                 System.out.println(
418                         "  " + stmId + " is already processed as it is owned (" + predicate + " " + object + ")");
419             processed.add(stmId);
420         }
421
422         TreeMap<String, List<Integer>> predicateURIs = new TreeMap<>();
423
424         TIntArrayList rawStatements = query.getStatements(info.resource);
425         if (DEBUG)
426             System.out.println(
427                     "rawStatements size for " + info.name + " : " + rawStatements.size() + " " + rawStatements);
428         for (int i = 0; i < rawStatements.size(); i += 2) {
429             int predicate = rawStatements.get(i);
430             int object = rawStatements.get(i + 1);
431             long stmId = longStm(predicate, object);
432             if (!processed.add(stmId)) {
433                 if (DEBUG)
434                     System.out.println("  " + stmId + " is already processed (" + (predicate & 0xffffffffL) + " "
435                             + (object & 0xffffffffL) + ")");
436                 continue;
437             }
438             if (DEBUG)
439                 System.out.println("   " + stmId + " is currently being processed (" + (predicate & 0xffffffffL) + " "
440                         + (object & 0xffffffffL) + ")");
441             // if (partOf.resource == rawStatements.get(i))
442             // continue;
443             if (consistsOf.resource == predicate) {
444                 // if (!info.owned.isEmpty() && !info.name.startsWith("blank"))
445                 // {
446                 if (DEBUG)
447                     System.out.println("  is consistsof " + predicate + " (" + consistsOf.resource + ")");
448                 continue;
449                 // } else {
450                 // // this will be inlined so lets indent
451                 // indent++;
452                 // }
453             }
454             String predicateURI = rewritePredicateURI(predicate);
455             List<Integer> objects = predicateURIs.get(predicateURI);
456             if (objects == null) {
457                 objects = new ArrayList<>();
458                 predicateURIs.put(predicateURI, objects);
459             }
460             objects.add(object);
461         }
462         for (Entry<String, List<Integer>> entry : predicateURIs.entrySet()) {
463             String predicateURI = entry.getKey();
464             List<Integer> objects = entry.getValue();
465             for (int object : objects) {
466                 ResourceInfo objectInfo = infos.get(object);
467                 if (objectInfo == null) {
468                     String objectURI = rewritePredicateURI(object);
469                     if (DEBUG)
470                         System.out.println("  adding statement " + predicateURI + " " + objectURI);
471                     addStatement(statements, predicateURI, objectURI);
472                 } else if (objectInfo.ownedBy.size() == 1 && objectInfo.ownedBy.contains(info)) {
473                     // inline printing with _
474                     if (DEBUG)
475                         System.out.println("  adding inline statement " + predicateURI + " " + objectInfo.name);
476                     addInlineStatement(statements, predicateURI, objectInfo, indent);
477                 } else {
478                     String objectName = objectInfo.name;
479                     if (objectName.startsWith("blank")) {
480                         objectName = getBlankRewrite(objectName);
481                     }
482                     if (DEBUG)
483                         System.out.println("  adding statement " + predicateURI + " " + objectName);
484                     addStatement(statements, predicateURI, objectName);
485                 }
486             }
487         }
488
489         if (DEBUG)
490             System.out.println(
491                     "statements size for " + info.name + " : " + statements.size() + " " + statements.keySet());
492
493         StringBuilder output = new StringBuilder();
494
495         if (indent == 0 || inline) {
496             if ("ROOT".equals(info.name)) {
497                 output.append("ROOT=<http:/>");
498             } else if (info.aliasURI != null) {
499                 output.append(info.name + " = <" + info.aliasURI + ">");
500             } else {
501                 String infoName = info.name;
502                 if (infoName.startsWith("blank")) {
503                     infoName = getBlankRewrite(infoName);
504                 }
505                 output.append(inline ? "_" : infoName);
506             }
507             Set<String> instanceOfs = statements.get("L0.InstanceOf");
508             if (instanceOfs != null) {
509                 for (String instanceOf : instanceOfs) {
510                     output.append(" : " + instanceOf);
511                 }
512             }
513             Set<String> subrelationOfs = statements.get("L0.SubrelationOf");
514             if (subrelationOfs != null) {
515                 for (String subrelationOf : subrelationOfs) {
516                     output.append(" <R " + subrelationOf);
517                 }
518             }
519             Set<String> inherits = statements.get("L0.Inherits");
520             if (inherits != null) {
521                 for (String inherit : inherits) {
522                     output.append(" <T " + inherit);
523                 }
524             }
525             output.append("\n");
526         }
527
528         if (info.newResource)
529             output.append("  @L0.new\n");
530
531         for (Map.Entry<String, Set<String>> entry : statements.entrySet()) {
532             String predicate = entry.getKey();
533             if (ignoreIdentifiers) {
534                 if (predicate.equals("L0.identifier")) {
535                     continue;
536                 }
537             }
538             if ("L0.InstanceOf".equals(predicate))
539                 continue;
540             if ("L0.SubrelationOf".equals(predicate))
541                 continue;
542             if ("L0.Inherits".equals(predicate))
543                 continue;
544             if ("L0.PartOf".equals(predicate))
545                 continue;
546
547             // predicates can be blank
548             if (predicate.startsWith("blan")) {
549                 predicate = getBlankRewrite(predicate);
550             }
551
552             Set<String> objects = entry.getValue();
553             indent(output, indent + 1);
554             if (objects.size() == 1) {
555                 output.append(predicate + " " + objects.iterator().next() + "\n");
556             } else {
557                 output.append(predicate + "\n");
558                 for (String object : objects) {
559                     indent(output, indent + 1);
560                     output.append("  " + object + "\n");
561                 }
562             }
563         }
564
565         TreeMap<String, Set<Integer>> ownedOrdered = new TreeMap<>();
566
567         for (int i = 0; i < info.owned.size(); i += 2) {
568             String predicateURI = rewritePredicateURI(info.owned.get(i));
569             Set<Integer> owneds = ownedOrdered.get(predicateURI);
570             if (owneds == null) {
571                 owneds = new TreeSet<>();
572                 ownedOrdered.put(predicateURI, owneds);
573             }
574             owneds.add(info.owned.get(i + 1));
575         }
576
577         if (DEBUG)
578             System.out.println(info.name + " : " + ownedOrdered.keySet());
579
580         for (Entry<String, Set<Integer>> entry : ownedOrdered.entrySet()) {
581             String predicateURI = entry.getKey();
582             Set<Integer> owneds = entry.getValue();
583             for (int owned : owneds) {
584                 ResourceInfo ownedInfo = infos.get(owned);
585     
586                 String blank = printBlank(predicateURI, ownedInfo, indent + 1);
587                 if (blank != null) {
588                     output.append(blank);
589                 }
590             }
591         }
592
593         return output.toString();
594     }
595
596     static Map<String, String> knownOntologies = new HashMap<>();
597
598     static {
599         knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0");
600         knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X");
601         knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD");
602         knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA");
603         knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR");
604         knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC");
605         knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU");
606         knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D");
607         knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL");
608         knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP");
609         knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2");
610         knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE");
611         knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT");
612         knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA");
613         knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH");
614     }
615
616     void prettyPrint() throws Exception {
617         log("Starting prettyPrint for TransferableGraph with {} resources, {} identities, {} statements and {} values",
618                 query.getGraph().resourceCount, query.getGraph().identities, query.getGraph().statements.length, query.getGraph().values.length);
619
620         query.forIdentities(id -> {
621             int identityResource = id.resource;
622             if (id.definition instanceof Internal) {
623                 Internal internal = (Internal) id.definition;
624                 Identity parent = query.getIdentity(internal.parent);
625                 if (parent.definition instanceof External || parent.definition instanceof Root) {
626                     log("Resolving internal identity {}", id);
627                     String name = "BASE";
628                     ResourceInfo info = new ResourceInfo(true, name, identityResource, -1);
629                     info.aliasURI = query.getURI(identityResource);
630                     info.newResource = true;
631                     orderedInfos.put(name, info);
632                     // infos.put(id.resource, info);
633                     log("    which parent is external {} and has an aliasURI {}", parent, info.aliasURI);
634                     for (Identity child : query.getChildren(id))
635                         recurseURI(orderedInfos, child, name, identityResource);
636                     log("    and has {} children", infos.size());
637                 }
638             } else if (id.definition instanceof External) {
639                 External ext = (External) id.definition;
640                 // Try to detect shared libraries
641                 if (ext.name.contains("@")) {
642
643                     log("Detected an external shared library {}", ext);
644
645                     int index = ext.name.indexOf('@');
646                     String prefix = ext.name.substring(0, index);
647                     int index2 = ext.name.indexOf('/', index);
648                     String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2);
649                     String uri = query.getURI(identityResource);
650
651                     log("    which was resolved as URI={} and prefix={}", uri, prefix);
652
653                     ontologies.put(uri, prefix);
654
655                 } else if (ext.name.contains("-")) {
656                     log("Resolving possible ontology {}", ext);
657                     String uri = query.getURI(identityResource);
658                     Matcher m = versionExtractPattern.matcher(uri);
659                     if (m.matches()) {
660                         if (!ontologies.containsKey(uri)) {
661                             int index = ext.name.indexOf('-');
662                             String prefix = ext.name.substring(0, index);
663                             log("    and it was resolved as URI={} and prefix {}", uri, prefix);
664                             ontologies.put(uri, prefix);
665                         }
666                     }
667                 }
668             }
669             return true;
670         });
671
672         // Discover other resources
673         log("Discovering other resources..");
674
675         TIntArrayList todo = new TIntArrayList();
676         for (ResourceInfo info : orderedInfos.values()) {
677             todo.add(info.resource);
678
679             // put orderedInfos to infos
680             infos.put(info.resource, info);
681         }
682
683         while (!todo.isEmpty()) {
684             int resource = todo.removeAt(todo.size() - 1);
685             discoverBlank(resource, todo);
686         }
687         for (ResourceInfo info : infos.valueCollection())
688             discoverOwners(info);
689         // for(ResourceInfo info : infos.valueCollection())
690         // fixInstanceOf(graph, info);
691
692         for (ResourceInfo info : infos.valueCollection()) {
693             // Old implementation
694             // if (info.owner >= 0) {
695             // ResourceInfo ownerInfo = infos.get(info.owner);
696             // System.out.println("originalOwner : " + info.owner + "
697             // originalPredicate: " + info.ownerPredicate);
698             // ownerInfo.owned.add(info.ownerPredicate);
699             // ownerInfo.owned.add(info.resource);
700             // }
701
702             if (!info.ownedResourcesWithPredicates.isEmpty() && info.ownedResourcesWithPredicates.size() == 1) {
703                 info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() {
704
705                     @Override
706                     public boolean execute(int owner, int predicate) {
707                        
708                         ResourceInfo ownerInfo = infos.get(owner);
709                         ownerInfo.owned.add(predicate);
710                         ownerInfo.owned.add(info.resource);
711                         return false;
712                     }
713                 });
714             } else {
715                 System.err.println("Here we are with " + info);
716             }
717         }
718
719         // Resolve inverses from ownedBy list
720         for (ResourceInfo info : infos.valueCollection()) {
721             for (int i = 0; i < info.owned.size(); i += 2) {
722                 int object = info.owned.get(i + 1);
723                 ResourceInfo inf = infos.get(object);
724                 if (inf != null) {
725                     info.ownedBy.remove(inf);
726                 }
727             }
728         }
729
730         Identity routeGraphConn = query.findExternalByURI("http://www.simantics.org/Diagram-2.2/RouteGraphConnection");
731         Identity instanceOf = query.findExternalByURI("http://www.simantics.org/Layer0-1.1/InstanceOf");
732         Identity diagramConnetionToConnection = query.findExternalByURI("http://www.simantics.org/Modeling-1.2/DiagramConnectionToConnection");
733
734         Identity elemTo = query.findExternalByURI("http://www.simantics.org/Modeling-1.2/ElementToComponent");
735         for (ResourceInfo infoo : infos.valueCollection()) {
736             if (elemTo != null) {
737                 int elemToComponent = query.getPossibleObject(infoo.resource, elemTo);
738                 if (elemToComponent != TransferableGraphUtils.NOT_FOUND) {
739
740                     Identity component = query.getIdentity(elemToComponent);
741                     Identity internal = query.getIdentity(infoo.resource);
742                     if (internal.definition instanceof Internal && component.definition instanceof Internal) {
743                         Internal iComponent = (Internal) component.definition;
744                         infoo.name = infoo.name.substring(0, infoo.name.lastIndexOf(".") + 1) + iComponent.name;
745                     }
746                 }
747             }
748
749             if (instanceOf != null) {
750                 int instOf = query.getPossibleObject( infoo.resource, instanceOf);
751                 if (instOf != TransferableGraphUtils.NOT_FOUND && routeGraphConn != null) {
752                     if (instOf == routeGraphConn.resource) {
753                         // Found routegraphconnection, change name
754                         // Lets go to configuration
755
756                         int connection = query.getPossibleObject( infoo.resource,
757                                 diagramConnetionToConnection);
758                         if (connection != TransferableGraphUtils.NOT_FOUND) {
759                             // Gather all inverse statements to construct unique
760                             // name
761                             List<String> nameParts = new ArrayList<>();
762                             TIntArrayList statements = query.getStatements(connection);
763                             for (int i = 0; i < statements.size(); i += 2) {
764                                 int predicate = statements.get(i);
765                                 Identity possibleInverse = query.getIdentity(predicate);
766                                 if (possibleInverse != null) {
767                                     int inverseRelation = TransferableGraphUtils.NOT_FOUND;
768                                     int parentId = TransferableGraphUtils.NOT_FOUND;
769                                     if (possibleInverse.definition instanceof Internal) {
770                                         Internal iPossibleInverse = (Internal) possibleInverse.definition;
771                                         if (iPossibleInverse.name.equals("Inverse")) {
772                                             inverseRelation = query.getPossibleObject(
773                                                     connection, possibleInverse);
774                                             parentId = iPossibleInverse.parent;
775                                         } else {
776                                             log("Unsupported inverse relation found for {} {}", infoo, iPossibleInverse);
777                                         }
778                                     } else if (possibleInverse.definition instanceof External) {
779                                         External ePossibleInverse = (External) possibleInverse.definition;
780                                         if (ePossibleInverse.name.equals("Inverse")) {
781                                             inverseRelation = query.getPossibleObject(
782                                                     connection, possibleInverse);
783                                             parentId = ePossibleInverse.parent;
784                                         } else {
785                                             log("This external inverse is unsupported for {} {}", infoo, ePossibleInverse);
786                                         }
787                                     } else {
788                                         log("This type of definition is not supported {}", infoo);
789                                     }
790                                     if (inverseRelation != TransferableGraphUtils.NOT_FOUND) {
791                                         // Ok found something
792                                         Identity object = query.getIdentity(inverseRelation);
793                                         Identity parent = query.getIdentity(parentId);
794                                         String objectName, parentName;
795                                         if (object.definition instanceof Internal) {
796                                             objectName = ((Internal) object.definition).name;
797                                         } else if (object.definition instanceof External) {
798                                             objectName = ((External) object.definition).name;
799                                         } else {
800                                             log("This type of definition is not supported {}", infoo);
801                                             throw new Error("UNSUPPORTED " + infoo);
802                                         }
803                                         if (parent.definition instanceof Internal) {
804                                             parentName = ((Internal) parent.definition).name;
805                                         } else if (parent.definition instanceof External) {
806                                             parentName = ((External) parent.definition).name;
807                                         } else {
808                                             log("This type of definition is not supported {}", infoo);
809                                             throw new Error("UNSUPPORTED " + infoo);
810                                         }
811                                         String fullName = parentName + "_" + objectName;
812                                         nameParts.add(fullName);
813                                     } else {
814                                         log("No inverse relation found for {}", infoo);
815                                     }
816                                 } else {
817                                     log("Did not find possible inverse relation for {}", infoo);
818                                 }
819                             }
820                             nameParts.sort((o1, o2) -> o1.compareTo(o2));
821                             String name = "";
822                             for (String namep : nameParts) {
823                                 name += namep;
824                             }
825                             infoo.name = infoo.name.substring(0, infoo.name.lastIndexOf(".") + 1) + name;
826                         } else {
827                             LOGGER.error("Could not find connection for " + infoo + ". Statements of graph below");
828                             LOGGER.error("Subject -> Predicate : " + infoo.resource + " -> "
829                                     + diagramConnetionToConnection.resource);
830                         }
831                     }
832                 }
833             }
834         }
835         for (ResourceInfo info : infos.valueCollection()) {
836             if (info.name.startsWith("blank")) {
837                 info.name = "blank" + findHash(info);
838             }
839         }
840
841         TreeMap<String, ResourceInfo> order = new TreeMap<>();
842         for (ResourceInfo info : infos.valueCollection())
843             order.put(info.name, info);
844
845         for (ResourceInfo info : order.values()) {
846             if (DEBUG)
847                 System.out.print("info ");
848             String uri = printURI(info, true, 0, false);
849             if (uri != null)
850                 output.append(uri);
851         }
852
853         TreeMap<String, ResourceInfo> rblanks = new TreeMap<>();
854
855         for (ResourceInfo info : order.values()) {
856             if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) {
857                 if (DEBUG)
858                     System.out.print("ownedResources ");
859                 if (info.name.startsWith("blank")) {
860                     // These will be printed later
861                     rblanks.put(getBlankRewrite(info.name), info);
862                 } else {
863                     String uri = printURI(info, false, 0, false);
864                     if (uri != null)
865                         output.append(uri);
866                 }
867             }
868         }
869         // Now print blanks in order
870         for (ResourceInfo info : rblanks.values()) {
871             if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) {
872                 if (DEBUG)
873                     System.out.print("ownedResources ");
874                 String uri = printURI(info, false, 0, false);
875                 if (uri != null)
876                     output.append(uri);
877             }
878         }
879
880         // for(ResourceInfo info : order.values())
881         // if(!info.hasURI && info.owner < 0)
882         // printURI(graph, info, false, 0);
883
884         StringBuilder refs = new StringBuilder();
885         for (String ontology : referencedOntologies) {
886             String key = ontologies.get(ontology);
887             refs.append(key + " = <" + ontology + ">\n");
888         }
889         if (!referencedOntologies.isEmpty())
890             refs.append("\n");
891         output.insert(0, refs.toString());
892
893     }
894
895     private String calculateHash(ResourceInfo info) {
896         StringBuilder statementHash = new StringBuilder();
897         TreeSet<String> parts = new TreeSet<>();
898         for (int i = 0; i < info.owned.size(); i += 2) {
899             int predicate = info.owned.get(i);
900             int object = info.owned.get(i + 1);
901             // Lets resolve a unique name for this based on the statements this
902             // one has
903
904             String predicatee = rewritePredicateURI(predicate);
905             ResourceInfo objInfo = infos.get(object);
906             parts.add(predicatee + "->" + objInfo.name + ";;;");
907         }
908         // Remove this from the list
909         List<ResourceInfo> filtered = info.ownedBy.stream().filter(ri -> !ri.name.startsWith("blank"))
910                 .collect(Collectors.toList());
911         for (ResourceInfo ownedBy : filtered) {
912             parts.add(ownedBy.name);
913         }
914         // check parent
915         ResourceInfo parentInfo = infos.get(info.parent);
916         if (parentInfo != null && !parentInfo.name.startsWith("blank")) {
917             parts.add("parent" + parentInfo.name);
918         } else {
919             // LOGGER.error("This should not happen");
920         }
921         for (String s : parts) {
922             statementHash.append(s);
923         }
924         String hash = makeHash(statementHash.toString().getBytes());
925         if (DEBUG)
926             System.out.println(statementHash + " -> " + hash);
927         return hash;
928     }
929
930     private String findHash(ResourceInfo info) {
931         if (info.name.startsWith("blank")) {
932             String hash = hashes.get(info.name);
933             if (hash == null) {
934                 String oldName = info.name;
935                 if (DEBUG)
936                     System.out.print("calculating hash for " + oldName + " ");
937                 hash = calculateHash(info);
938                 if (hashes.put(oldName, hash) != null) {
939                     System.err.println("!!!!A clash occured for " + info + " with hash " + hash);
940                 }
941             }
942             return hash;
943         } else {
944             return info.name;
945         }
946     }
947
948     private THashMap<String, String> hashes = new THashMap<>();
949
950     public static String print(TransferableGraph1 tg, boolean ignoreIdentifiers) throws Exception {
951         StringBuilder b = new StringBuilder();
952         new PrettyPrintTG(tg, b, ignoreIdentifiers).prettyPrint();
953         return b.toString();
954     }
955
956     public static void main(String[] args) throws Exception {
957         if (args.length < 1) {
958             System.out.println("Required arguments: <input .sharedOntology file> [<output .tg file>]");
959         }
960         Path input;
961         Path output;
962         if (args.length < 2) {
963             input = Paths.get(args[0]);
964             output = input.getParent().resolve(input.getName(input.getNameCount() - 1) + ".fixed");
965         } else {
966             input = Paths.get(args[0]);
967             output = Paths.get(args[1]);
968         }
969         System.out.format("Converting exported shared ontology%n\t" + input.toString()
970         + "%nto bundle-compatible ontology%n\t" + output.toString());
971         try (InputStream is = new BufferedInputStream(Files.newInputStream(input), 128 * 1024)) {
972             DataInput dis = new DataInputStream(is);
973             org.simantics.databoard.container.DataContainer container = DataContainers.readFile(dis);
974             Binding binding = TransferableGraph1.BINDING;
975             TransferableGraph1 graph = (TransferableGraph1) container.content.getValue(binding);
976             new PrettyPrintTG(graph).prettyPrint();
977         }
978     }
979
980     private static void log(String string, Object... args) {
981         if (LOGGER.isDebugEnabled() && DEBUG)
982             LOGGER.debug(string, args);
983     }
984
985 }