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