0a0a86f0be45be0c0aff59fe6927a989df675120
[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         // Variant variant = value.value;
244         // Datatype dt = variant.getBinding().type();
245         // if (Datatypes.STRING.equals(dt)) {
246         // String s = (String) variant.getValue(Bindings.STRING);
247         // if (s.contains("\n")) {
248         // return "\"\"\"" + s + "\"\"\"";
249         // } else {
250         // return "\"" + s + "\"";
251         // }
252         // } else if (Datatypes.BOOLEAN.equals(dt)) {
253         // Boolean i = (Boolean) variant.getValue(Bindings.BOOLEAN);
254         // return i ? "true" : "false";
255         // } else if (Datatypes.INTEGER.equals(dt)) {
256         // Integer i = (Integer) variant.getValue(Bindings.INTEGER);
257         // return i.toString();
258         // } else if (Datatypes.LONG.equals(dt)) {
259         // Long i = (Long) variant.getValue(Bindings.LONG);
260         // return i.toString();
261         // } else if (Datatypes.DOUBLE.equals(dt)) {
262         // Double d = (Double) variant.getValue();
263         // return d.toString();
264         // } else if (Datatypes.FLOAT.equals(dt)) {
265         // Float f = (Float) variant.getValue();
266         // return f.toString();
267         // } else if (Datatypes.STRING_ARRAY.equals(dt)) {
268         // return Arrays.toString((String []) variant.getValue());
269         // } else if (Datatypes.BOOLEAN_ARRAY.equals(dt)) {
270         // return Arrays.toString((boolean []) variant.getValue());
271         // } else if (Datatypes.INTEGER_ARRAY.equals(dt)) {
272         // return Arrays.toString((int []) variant.getValue());
273         // } else if (Datatypes.LONG_ARRAY.equals(dt)) {
274         // return Arrays.toString((long []) variant.getValue());
275         // } else if (Datatypes.DOUBLE_ARRAY.equals(dt)) {
276         // return Arrays.toString((double []) variant.getValue());
277         // } else if (Datatypes.FLOAT_ARRAY.equals(dt)) {
278         // return Arrays.toString((float []) variant.getValue());
279         // } else if (Datatypes.BYTE_ARRAY.equals(dt)) {
280         // return Arrays.toString((byte []) variant.getValue());
281         //// } else if (dt instanceof ArrayType) {
282         //// return Arrays.toString((Object []) variant.getValue());
283         // } else {
284         // byte[] data =
285         // Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue());
286         // m.reset();
287         // m.update(data, 0, data.length);
288         // return "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
289         // }
290
291     }
292
293     // void fixInstanceOf(TransferableGraph1 graph, ResourceInfo info) {
294     // Identity id = getIdentity(graph, info.resource);
295     // if(id == null) return;
296     // if(id.definition instanceof Internal) {
297     // Identity instanceOf = findExternal(graph,
298     // "http://www.simantics.org/Layer0-1.1/InstanceOf");
299     // Identity library = findExternal(graph,
300     // "http://www.simantics.org/Layer0-1.1/Library");
301     // info.statements.add(instanceOf.resource);
302     // info.statements.add(library.resource);
303     // }
304     // }
305
306     public static String getExternalURI(TransferableGraph1 tg, External ext) {
307         String name = ext.name;
308         if (name.contains(" "))
309             name = name.replace(" ", "_").replaceAll("@", "_");// name = "\"" +
310                                                                // name + "\"";
311         int parentId = ext.parent;
312         // if(parentId == 0) return ext.name;
313         // else {
314         Identity id = TransferableGraphUtils.getIdentity(tg, parentId);
315         if (id.definition instanceof External) {
316             return getExternalURI(tg, (External) id.definition) + "/" + name;
317         } else if (id.definition instanceof Root) {
318             Root root = (Root) id.definition;
319             return "http:/" + root.name + "/" + name;
320         } else {
321             return null;
322         }
323         // }
324     }
325
326     public static String getExternalURI(TransferableGraph1 tg, int resource) {
327         Identity id = TransferableGraphUtils.getIdentity(tg, resource);
328         if (id == null)
329             return null;
330         if (id.definition instanceof External) {
331             External ext = (External) id.definition;
332             return getExternalURI(tg, ext);
333         }
334         return null;
335     }
336
337     String rewritePredicateURI(TransferableGraph1 graph, int predicate) {
338
339         String uri = getExternalURI(graph, predicate);
340         if (uri == null) {
341             ResourceInfo info = infos.get(predicate);
342             if (info != null)
343                 return info.name;
344             return "_";
345         }
346
347         for (String ontology : ontologies.keySet()) {
348             if (uri.contains(ontology)) {
349                 String key = ontologies.get(ontology);
350                 uri = uri.replace(ontology, key);
351                 referencedOntologies.add(ontology);
352             }
353         }
354
355         uri = uri.replace("/", ".");
356
357         return uri;
358
359     }
360
361     static void indent(StringBuilder output, int indent) {
362         for (int i = 0; i < indent; i++)
363             output.append("  ");
364     }
365
366     String printBlank(TransferableGraph1 graph, String predicateURI2, ResourceInfo info, int indent) {
367
368         if (info.hasURI)
369             return null;
370
371         StringBuilder output = new StringBuilder();
372
373         String infoName = info.name;
374         if (infoName.startsWith("blank")) {
375             infoName = getBlankRewrite(infoName);
376         }
377
378         if (ignoreIdentifiers) {
379             if (predicateURI2.contains("L0.identifier")) {
380                 // Do nothing
381             } else {
382                 indent(output, indent);
383                 output.append(predicateURI2 + " " + infoName + "\n");
384             }
385         } else {
386             indent(output, indent);
387             output.append(predicateURI2 + " " + infoName + "\n");
388         }
389
390         if (info.ownedResourcesWithPredicates.isEmpty()) {
391             if (DEBUG)
392                 System.out.print("printBlank");
393             String uri = printURI(graph, info, false, indent, false);
394             if (uri != null)
395                 output.append(uri);
396         }
397         // if(info.owner < 0) {
398         // printURI(graph, info, false, indent);
399         // }
400         return output.toString();
401     }
402
403     private String getBlankRewrite(String infoName) {
404         String rewrite = blankRewrites.get(infoName);
405         if (rewrite == null) {
406             rewrite = "rblank" + newBlankCounter++;
407             if (DEBUG)
408                 System.out.println("rewrote " + infoName + " to " + rewrite);
409             blankRewrites.put(infoName, rewrite);
410         }
411         return rewrite;
412         // return infoName;
413     }
414
415     static long longStm(int predicate, int object) {
416         return ((predicate & 0xffffffffL) << 32) | (object & 0xffffffffL);
417     }
418
419     private void addInlineStatement(TransferableGraph1 graph, Map<String, Set<String>> statements, String predicate,
420             ResourceInfo objectInfo, int indent) {
421         Set<String> objects = statements.get(predicate);
422         if (objects == null) {
423             objects = new TreeSet<>();
424             statements.put(predicate, objects);
425         }
426         String uri = printURI(graph, objectInfo, false, indent + 1, true);
427         if (uri != null) {
428             // TODO: this is not the right place to remove trailing newline
429             uri = uri.endsWith("\n") ? uri.substring(0, uri.length() - 2) : uri;
430             objects.add(uri);
431         }
432         objectInfo.inlined = true;
433     }
434
435     void addStatement(Map<String, Set<String>> statements, String predicate, String object) {
436         // TODO: fix this
437         if (predicate.endsWith("Inverse"))
438             return;
439         Set<String> objects = statements.get(predicate);
440         if (objects == null) {
441             objects = new TreeSet<>();
442             statements.put(predicate, objects);
443         }
444         objects.add(object);
445     }
446
447     String printURI(TransferableGraph1 graph, ResourceInfo info, boolean requireURI, int indent, boolean inline) {
448
449         if (requireURI && !info.hasURI)
450             return null;
451
452         // Check if this ResourceInfo is already inlined with some other
453         // ResourceInfo
454         if (info.inlined)
455             return null;
456
457         Map<String, Set<String>> statements = new TreeMap<>();
458         Identity consistsOf = TransferableGraphUtils.findExternal(graph,
459                 "http://www.simantics.org/Layer0-1.1/ConsistsOf");
460         // Identity partOf = TransferableGraphUtils.findExternal(graph,
461         // "http://www.simantics.org/Layer0-1.1/PartOf");
462         TLongHashSet processed = new TLongHashSet();
463         if (DEBUG)
464             System.out.println("info.owned.size " + info.owned.size() + info.owned);
465         for (int i = 0; i < info.owned.size(); i += 2) {
466             int predicate = info.owned.get(i);
467             int object = info.owned.get(i + 1);
468             long stmId = longStm(predicate, object);
469             if (DEBUG)
470                 System.out.println(
471                         "  " + stmId + " is already processed as it is owned (" + predicate + " " + object + ")");
472             processed.add(stmId);
473         }
474
475         TreeMap<String, Integer> predicateURIs = new TreeMap<>();
476
477         TIntArrayList rawStatements = TransferableGraphUtils.getStatements(graph, info.resource);
478         if (DEBUG)
479             System.out.println(
480                     "rawStatements size for " + info.name + " : " + rawStatements.size() + " " + rawStatements);
481         for (int i = 0; i < rawStatements.size(); i += 2) {
482             int predicate = rawStatements.get(i);
483             int object = rawStatements.get(i + 1);
484             long stmId = longStm(predicate, object);
485             if (!processed.add(stmId)) {
486                 if (DEBUG)
487                     System.out.println("  " + stmId + " is already processed (" + (predicate & 0xffffffffL) + " "
488                             + (object & 0xffffffffL) + ")");
489                 continue;
490             }
491             if (DEBUG)
492                 System.out.println("   " + stmId + " is currently being processed (" + (predicate & 0xffffffffL) + " "
493                         + (object & 0xffffffffL) + ")");
494             // if (partOf.resource == rawStatements.get(i))
495             // continue;
496             if (consistsOf.resource == predicate) {
497                 // if (!info.owned.isEmpty() && !info.name.startsWith("blank"))
498                 // {
499                 if (DEBUG)
500                     System.out.println("  is consistsof " + predicate + " (" + consistsOf.resource + ")");
501                 continue;
502                 // } else {
503                 // // this will be inlined so lets indent
504                 // indent++;
505                 // }
506             }
507             String predicateURI = rewritePredicateURI(graph, predicate);
508             predicateURIs.put(predicateURI, object);
509         }
510         for (Entry<String, Integer> entry : predicateURIs.entrySet()) {
511             String predicateURI = entry.getKey();
512             int object = entry.getValue();
513
514             ResourceInfo objectInfo = infos.get(object);
515             if (objectInfo == null) {
516                 String objectURI = rewritePredicateURI(graph, object);
517                 if (DEBUG)
518                     System.out.println("  adding statement " + predicateURI + " " + objectURI);
519                 addStatement(statements, predicateURI, objectURI);
520             } else if (objectInfo.ownedBy.size() == 1 && objectInfo.ownedBy.contains(info)) {
521                 // inline printing with _
522                 if (DEBUG)
523                     System.out.println("  adding inline statement " + predicateURI + " " + objectInfo.name);
524                 addInlineStatement(graph, statements, predicateURI, objectInfo, indent);
525             } else {
526                 String objectName = objectInfo.name;
527                 if (objectName.startsWith("blank")) {
528                     objectName = getBlankRewrite(objectName);
529                 }
530                 if (DEBUG)
531                     System.out.println("  adding statement " + predicateURI + " " + objectName);
532                 addStatement(statements, predicateURI, objectName);
533             }
534         }
535
536         if (DEBUG)
537             System.out.println(
538                     "statements size for " + info.name + " : " + statements.size() + " " + statements.keySet());
539
540         StringBuilder output = new StringBuilder();
541
542         if (indent == 0 || inline) {
543             if ("ROOT".equals(info.name)) {
544                 output.append("ROOT=<http:/>");
545             } else if (info.aliasURI != null) {
546                 output.append(info.name + " = <" + info.aliasURI + ">");
547             } else {
548                 String infoName = info.name;
549                 if (infoName.startsWith("blank")) {
550                     infoName = getBlankRewrite(infoName);
551                 }
552                 output.append(inline ? "_" : infoName);
553             }
554             Set<String> instanceOfs = statements.get("L0.InstanceOf");
555             if (instanceOfs != null) {
556                 for (String instanceOf : instanceOfs) {
557                     output.append(" : " + instanceOf);
558                 }
559             }
560             Set<String> subrelationOfs = statements.get("L0.SubrelationOf");
561             if (subrelationOfs != null) {
562                 for (String subrelationOf : subrelationOfs) {
563                     output.append(" <R " + subrelationOf);
564                 }
565             }
566             Set<String> inherits = statements.get("L0.Inherits");
567             if (inherits != null) {
568                 for (String inherit : inherits) {
569                     output.append(" <T " + inherit);
570                 }
571             }
572             output.append("\n");
573         }
574
575         if (info.newResource)
576             output.append("  @L0.new\n");
577
578         for (Map.Entry<String, Set<String>> entry : statements.entrySet()) {
579             String predicate = entry.getKey();
580             if (ignoreIdentifiers) {
581                 if (predicate.equals("L0.identifier")) {
582                     continue;
583                 }
584             }
585             if ("L0.InstanceOf".equals(predicate))
586                 continue;
587             if ("L0.SubrelationOf".equals(predicate))
588                 continue;
589             if ("L0.Inherits".equals(predicate))
590                 continue;
591             if ("L0.PartOf".equals(predicate))
592                 continue;
593
594             // predicates can be blank
595             if (predicate.startsWith("blan")) {
596                 predicate = getBlankRewrite(predicate);
597             }
598
599             Set<String> objects = entry.getValue();
600             indent(output, indent + 1);
601             if (objects.size() == 1) {
602                 output.append(predicate + " " + objects.iterator().next() + "\n");
603             } else {
604                 output.append(predicate + "\n");
605                 for (String object : objects) {
606                     indent(output, indent + 1);
607                     output.append("  " + object + "\n");
608                 }
609             }
610         }
611
612         TreeMap<String, Integer> ownedOrdered = new TreeMap<>();
613
614         for (int i = 0; i < info.owned.size(); i += 2) {
615             String predicateURI = rewritePredicateURI(graph, info.owned.get(i));
616             ownedOrdered.put(predicateURI, info.owned.get(i + 1));
617         }
618
619         if (DEBUG)
620             System.out.println(info.name + " : " + ownedOrdered.keySet());
621
622         for (Entry<String, Integer> entry : ownedOrdered.entrySet()) {
623             String predicateURI = entry.getKey();
624             int owned = entry.getValue();
625             ResourceInfo ownedInfo = infos.get(owned);
626
627             String blank = printBlank(graph, predicateURI, ownedInfo, indent + 1);
628             if (blank != null) {
629                 output.append(blank);
630             }
631         }
632
633         return output.toString();
634     }
635
636     void prettyPrint(Path input, Path output) throws Exception {
637
638         System.out.format("Converting exported shared ontology%n\t" + input.toString()
639                 + "%nto bundle-compatible ontology%n\t" + output.toString());
640         try (InputStream is = new BufferedInputStream(Files.newInputStream(input), 128 * 1024)) {
641             DataInput dis = new DataInputStream(is);
642             org.simantics.databoard.container.DataContainer container = DataContainers.readFile(dis);
643             Binding binding = TransferableGraph1.BINDING;
644             TransferableGraph1 graph = (TransferableGraph1) container.content.getValue(binding);
645             prettyPrint(graph);
646             Files.write(output, this.output.toString().getBytes());
647
648         }
649
650     }
651
652     static Map<String, String> knownOntologies = new HashMap<>();
653
654     static {
655         knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0");
656         knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X");
657         knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD");
658         knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA");
659         knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR");
660         knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC");
661         knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU");
662         knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D");
663         knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL");
664         knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP");
665         knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2");
666         knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE");
667         knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT");
668         knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA");
669         knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH");
670     }
671
672     void prettyPrint(TransferableGraph1 graph) throws Exception {
673         log("Starting prettyPrint for TransferableGraph with {} resources, {} identities, {} statements and {} values",
674                 graph.resourceCount, graph.identities, graph.statements.length, graph.values.length);
675
676         for (Identity id : graph.identities) {
677             if (id.definition instanceof Internal) {
678                 Internal internal = (Internal) id.definition;
679                 Identity parent = TransferableGraphUtils.getIdentity(graph, internal.parent);
680                 if (parent.definition instanceof External) {
681                     log("Resolving internal identity {}", id);
682                     String name = "BASE";
683                     ResourceInfo info = new ResourceInfo(true, name, id.resource, -1);
684                     info.aliasURI = TransferableGraphUtils.getURI(graph, id.resource);
685                     info.newResource = true;
686                     orderedInfos.put(name, info);
687                     // infos.put(id.resource, info);
688                     log("    which parent is external {} and has an aliasURI {}", parent, info.aliasURI);
689                     for (Identity child : TransferableGraphUtils.getChildren(graph, id)) {
690                         recurseURI(graph, child, name, info.resource);
691                     }
692                     log("    and has {} children", infos.size());
693                 }
694             } else if (id.definition instanceof External) {
695                 External ext = (External) id.definition;
696                 // Try to detect shared libraries
697                 if (ext.name.contains("@")) {
698
699                     log("Detected an external shared library {}", ext);
700
701                     int index = ext.name.indexOf('@');
702                     String prefix = ext.name.substring(0, index);
703                     int index2 = ext.name.indexOf('/', index);
704                     String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2);
705                     String uri = TransferableGraphUtils.getURI(graph, id.resource);
706
707                     log("    which was resolved as URI={} and prefix={}", uri, prefix);
708
709                     ontologies.put(uri, prefix);
710
711                 } else if (ext.name.contains("-")) {
712                     log("Resolving possible ontology {}", ext);
713                     String uri = TransferableGraphUtils.getURI(graph, id.resource);
714                     Matcher m = versionExtractPattern.matcher(uri);
715                     if (m.matches()) {
716                         if (!ontologies.containsKey(uri)) {
717                             int index = ext.name.indexOf('-');
718                             String prefix = ext.name.substring(0, index);
719                             log("    and it was resolved as URI={} and prefix {}", uri, prefix);
720                             ontologies.put(uri, prefix);
721                         }
722                     }
723                 }
724             }
725         }
726
727         // Discover other resources
728         log("Discovering other resources..");
729
730         TIntArrayList todo = new TIntArrayList();
731         for (ResourceInfo info : orderedInfos.values()) {
732             todo.add(info.resource);
733
734             // put orderedInfos to infos
735             infos.put(info.resource, info);
736         }
737
738         while (!todo.isEmpty()) {
739             int resource = todo.removeAt(todo.size() - 1);
740             discoverBlank(graph, resource, todo);
741         }
742         for (ResourceInfo info : infos.valueCollection())
743             discoverOwners(graph, info);
744         // for(ResourceInfo info : infos.valueCollection())
745         // fixInstanceOf(graph, info);
746
747         for (ResourceInfo info : infos.valueCollection()) {
748             // Old implementation
749             // if (info.owner >= 0) {
750             // ResourceInfo ownerInfo = infos.get(info.owner);
751             // System.out.println("originalOwner : " + info.owner + "
752             // originalPredicate: " + info.ownerPredicate);
753             // ownerInfo.owned.add(info.ownerPredicate);
754             // ownerInfo.owned.add(info.resource);
755             // }
756
757             if (!info.ownedResourcesWithPredicates.isEmpty() && info.ownedResourcesWithPredicates.size() == 1) {
758                 info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() {
759
760                     @Override
761                     public boolean execute(int owner, int predicate) {
762                         ResourceInfo ownerInfo = infos.get(owner);
763                         ownerInfo.owned.add(predicate);
764                         ownerInfo.owned.add(info.resource);
765                         return false;
766                     }
767                 });
768             }
769         }
770
771         // Resolve inverses from ownedBy list
772         for (ResourceInfo info : infos.valueCollection()) {
773             for (int i = 0; i < info.owned.size(); i += 2) {
774                 int object = info.owned.get(i + 1);
775                 ResourceInfo inf = infos.get(object);
776                 if (inf != null) {
777                     info.ownedBy.remove(inf);
778                 }
779             }
780         }
781
782         Identity routeGraphConn = TransferableGraphUtils.findExternal(graph,
783                 "http://www.simantics.org/Diagram-2.2/RouteGraphConnection");
784         Identity instanceOf = TransferableGraphUtils.findExternal(graph,
785                 "http://www.simantics.org/Layer0-1.1/InstanceOf");
786         Identity diagramConnetionToConnection = TransferableGraphUtils.findExternal(graph,
787                 "http://www.simantics.org/Modeling-1.2/DiagramConnectionToConnection");
788
789         for (ResourceInfo infoo : infos.valueCollection()) {
790             Identity elemTo = TransferableGraphUtils.findExternal(graph,
791                     "http://www.simantics.org/Modeling-1.2/ElementToComponent");
792             if (elemTo != null) {
793                 int elemToComponent = TransferableGraphUtils.getPossibleObject2(graph, infoo.resource, elemTo);
794                 if (elemToComponent != TransferableGraphUtils.NOT_FOUND) {
795
796                     Identity component = TransferableGraphUtils.getIdentity(graph, elemToComponent);
797                     Identity internal = TransferableGraphUtils.getIdentity(graph, infoo.resource);
798                     if (internal.definition instanceof Internal && component.definition instanceof Internal) {
799                         Internal iCOmponent = (Internal) component.definition;
800                         infoo.name = infoo.name.substring(0, infoo.name.lastIndexOf(".") + 1) + iCOmponent.name;
801                     }
802                 }
803             }
804
805             if (instanceOf != null) {
806                 int instOf = TransferableGraphUtils.getPossibleObject2(graph, infoo.resource, instanceOf);
807                 if (instOf != TransferableGraphUtils.NOT_FOUND && routeGraphConn != null) {
808                     if (instOf == routeGraphConn.resource) {
809                         // Found routegraphconnection, change name
810                         // Lets go to configuration
811
812                         int connection = TransferableGraphUtils.getPossibleObject2(graph, infoo.resource,
813                                 diagramConnetionToConnection);
814                         if (connection != TransferableGraphUtils.NOT_FOUND) {
815                             // Gather all inverse statements to construct unique
816                             // name
817                             List<String> nameParts = new ArrayList<>();
818                             TIntArrayList statements = TransferableGraphUtils.getStatements(graph, connection);
819                             for (int i = 0; i < statements.size(); i += 2) {
820                                 int predicate = statements.get(i);
821                                 Identity possibleInverse = TransferableGraphUtils.getIdentity(graph, predicate);
822                                 if (possibleInverse != null) {
823                                     int inverseRelation = TransferableGraphUtils.NOT_FOUND;
824                                     int parentId = TransferableGraphUtils.NOT_FOUND;
825                                     if (possibleInverse.definition instanceof Internal) {
826                                         Internal iPossibleInverse = (Internal) possibleInverse.definition;
827                                         if (iPossibleInverse.name.equals("Inverse")) {
828                                             inverseRelation = TransferableGraphUtils.getPossibleObject2(graph,
829                                                     connection, possibleInverse);
830                                             parentId = iPossibleInverse.parent;
831                                         } else {
832                                             LOGGER.error("THIS UNSUPPORTED for " + infoo + " " + iPossibleInverse);
833                                         }
834                                     } else if (possibleInverse.definition instanceof External) {
835                                         External ePossibleInverse = (External) possibleInverse.definition;
836                                         if (ePossibleInverse.name.equals("Inverse")) {
837                                             inverseRelation = TransferableGraphUtils.getPossibleObject2(graph,
838                                                     connection, possibleInverse);
839                                             parentId = ePossibleInverse.parent;
840                                         } else {
841                                             // This is not an inverse
842                                             // LOGGER.error("THIS UNSUPPORTED
843                                             // TOO");
844                                         }
845                                     } else {
846                                         LOGGER.error("UNSUPPORTED for " + infoo + " ");
847                                     }
848                                     if (inverseRelation != TransferableGraphUtils.NOT_FOUND) {
849                                         // Ok found something
850                                         Identity object = TransferableGraphUtils.getIdentity(graph, inverseRelation);
851                                         Identity parent = TransferableGraphUtils.getIdentity(graph, parentId);
852                                         String objectName, parentName;
853                                         if (object.definition instanceof Internal) {
854                                             objectName = ((Internal) object.definition).name;
855                                         } else if (object.definition instanceof External) {
856                                             objectName = ((External) object.definition).name;
857                                         } else {
858                                             LOGGER.error("UNSUPPORTED " + infoo);
859                                             throw new Error("UNSUPPORTED " + infoo);
860                                         }
861                                         if (parent.definition instanceof Internal) {
862                                             parentName = ((Internal) parent.definition).name;
863                                         } else if (parent.definition instanceof External) {
864                                             parentName = ((External) parent.definition).name;
865                                         } else {
866                                             LOGGER.error("UNSUPPORTED " + infoo);
867                                             throw new Error("UNSUPPORTED " + infoo);
868                                         }
869                                         String fullName = parentName + "_" + objectName;
870                                         nameParts.add(fullName);
871                                     } else {
872                                         LOGGER.error("THIS IS ALSO UNSupported");
873                                     }
874                                 } else {
875                                     LOGGER.error("HERE");
876                                 }
877                             }
878                             nameParts.sort((o1, o2) -> o1.compareTo(o2));
879                             String name = "";
880                             for (String namep : nameParts) {
881                                 name += namep;
882                             }
883                             infoo.name = infoo.name.substring(0, infoo.name.lastIndexOf(".") + 1) + name;
884                         } else {
885                             LOGGER.error("Could not find connection for " + infoo + ". Statements of graph below");
886                             LOGGER.error(Arrays.toString(graph.statements));
887                             LOGGER.error("Subject -> Predicate : " + infoo.resource + " -> "
888                                     + diagramConnetionToConnection.resource);
889                         }
890                     }
891                 }
892             }
893         }
894         for (ResourceInfo info : infos.valueCollection()) {
895             if (info.name.startsWith("blank")) {
896                 info.name = "blank" + findHash(graph, info);
897             }
898         }
899
900         TreeMap<String, ResourceInfo> order = new TreeMap<>();
901         for (ResourceInfo info : infos.valueCollection())
902             order.put(info.name, info);
903
904         for (ResourceInfo info : order.values()) {
905             if (DEBUG)
906                 System.out.print("info ");
907             String uri = printURI(graph, info, true, 0, false);
908             if (uri != null)
909                 output.append(uri);
910         }
911
912         TreeMap<String, ResourceInfo> rblanks = new TreeMap<>();
913
914         for (ResourceInfo info : order.values()) {
915             if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) {
916                 if (DEBUG)
917                     System.out.print("ownedResources ");
918                 if (info.name.startsWith("blank")) {
919                     // These will be printed later
920                     rblanks.put(getBlankRewrite(info.name), info);
921                 } else {
922                     String uri = printURI(graph, info, false, 0, false);
923                     if (uri != null)
924                         output.append(uri);
925                 }
926             }
927         }
928         // Now print blanks in order
929         for (ResourceInfo info : rblanks.values()) {
930             if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) {
931                 if (DEBUG)
932                     System.out.print("ownedResources ");
933                 String uri = printURI(graph, info, false, 0, false);
934                 if (uri != null)
935                     output.append(uri);
936             }
937         }
938
939         // for(ResourceInfo info : order.values())
940         // if(!info.hasURI && info.owner < 0)
941         // printURI(graph, info, false, 0);
942
943         StringBuilder refs = new StringBuilder();
944         for (String ontology : referencedOntologies) {
945             String key = ontologies.get(ontology);
946             refs.append(key + " = <" + ontology + ">\n");
947         }
948         if (!referencedOntologies.isEmpty())
949             refs.append("\n");
950         output.insert(0, refs.toString());
951
952     }
953
954     private String calculateHash(TransferableGraph1 graph, ResourceInfo info) {
955         StringBuilder statementHash = new StringBuilder();
956         TreeSet<String> parts = new TreeSet<>();
957         for (int i = 0; i < info.owned.size(); i += 2) {
958             int predicate = info.owned.get(i);
959             int object = info.owned.get(i + 1);
960             // Lets resolve a unique name for this based on the statements this
961             // one has
962
963             String predicatee = rewritePredicateURI(graph, predicate);
964             ResourceInfo objInfo = infos.get(object);
965             parts.add(predicatee + "->" + objInfo.name + ";;;");
966         }
967         // Remove this from the list
968         List<ResourceInfo> filtered = info.ownedBy.stream().filter(ri -> !ri.name.startsWith("blank"))
969                 .collect(Collectors.toList());
970         for (ResourceInfo ownedBy : filtered) {
971             parts.add(ownedBy.name);
972         }
973         // check parent
974         ResourceInfo parentInfo = infos.get(info.parent);
975         if (parentInfo != null && !parentInfo.name.startsWith("blank")) {
976             parts.add("parent" + parentInfo.name);
977         } else {
978             // LOGGER.error("This should not happen");
979         }
980         for (String s : parts) {
981             statementHash.append(s);
982         }
983         String hash = makeHash(statementHash.toString().getBytes());
984         if (DEBUG)
985             System.out.println(statementHash + " -> " + hash);
986         return hash;
987     }
988
989     private String findHash(TransferableGraph1 graph, ResourceInfo info) {
990         if (info.name.startsWith("blank")) {
991             String hash = hashes.get(info.name);
992             if (hash == null) {
993                 String oldName = info.name;
994                 if (DEBUG)
995                     System.out.print("calculating hash for " + oldName + " ");
996                 hash = calculateHash(graph, info);
997                 if (hashes.put(oldName, hash) != null) {
998                     System.err.println("!!!!A clash occured for " + info + " with hash " + hash);
999                 }
1000             }
1001             return hash;
1002         } else {
1003             return info.name;
1004         }
1005     }
1006
1007     private THashMap<String, String> hashes = new THashMap<>();
1008
1009     public static String print(TransferableGraph1 tg, boolean ignoreIdentifiers) throws Exception {
1010         StringBuilder b = new StringBuilder();
1011         new PrettyPrintTG(b, ignoreIdentifiers).prettyPrint(tg);
1012         return b.toString();
1013     }
1014
1015     public static void main(String[] args) throws Exception {
1016         if (args.length < 1) {
1017             System.out.println("Required arguments: <input .sharedOntology file> [<output .tg file>]");
1018         } else if (args.length < 2) {
1019             Path input = Paths.get(args[0]);
1020             Path output = input.getParent().resolve(input.getName(input.getNameCount() - 1) + ".fixed");
1021             new PrettyPrintTG().prettyPrint(input, output);
1022         } else {
1023             new PrettyPrintTG().prettyPrint(Paths.get(args[0]), Paths.get(args[1]));
1024         }
1025     }
1026
1027     private static void log(String string, Object... args) {
1028         if (LOGGER.isDebugEnabled() && DEBUG)
1029             LOGGER.debug(string, args);
1030     }
1031
1032 }