00d17100ec461bb5800a63e93e1b40175d90a204
[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.HashMap;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17 import java.util.TreeSet;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20
21 import org.simantics.databoard.Bindings;
22 import org.simantics.databoard.Datatypes;
23 import org.simantics.databoard.binding.Binding;
24 import org.simantics.databoard.binding.mutable.Variant;
25 import org.simantics.databoard.container.DataContainers;
26 import org.simantics.databoard.type.Datatype;
27
28 import gnu.trove.list.array.TIntArrayList;
29 import gnu.trove.map.hash.TIntObjectHashMap;
30 import gnu.trove.set.hash.TLongHashSet;
31
32 /**
33  * @author Antti Villberg
34  * @since 1.24.0
35  */
36 public class PrettyPrintTG extends TransferableGraphUtils {
37
38         int blankCounter = 0;
39         MessageDigest m;
40
41         private final Pattern versionExtractPattern = Pattern.compile("^.*-(\\d+\\.\\d+)");
42
43         final StringBuilder output;
44         final Map<String,String> ontologies = new HashMap<>(knownOntologies);
45         final Set<String> referencedOntologies = new TreeSet<>();
46
47         static class ResourceInfo {
48                 final boolean hasURI;
49                 final String name;
50                 final int resource;
51                 boolean newResource = false;
52                 
53                 // -1 = no owner, -2 = multiple owners
54                 int owner = -1;
55                 int ownerPredicate = 0;
56                 String aliasURI = null;
57                 TIntArrayList owned = new TIntArrayList();
58                 //TIntObjectHashMap<TIntHashSet> statements = new TIntObjectHashMap<TIntHashSet>();
59                 public ResourceInfo(boolean hasURI, String name, int resource) {
60                         this.hasURI = hasURI;
61                         this.name = name;
62                         this.resource = resource;
63                 }
64         }
65
66         public PrettyPrintTG(StringBuilder b) throws NoSuchAlgorithmException {
67                 output = b;
68                 m = MessageDigest.getInstance("SHA-256");
69         }
70
71         public PrettyPrintTG() throws NoSuchAlgorithmException {
72                 this(new StringBuilder());
73         }
74
75         TIntObjectHashMap<ResourceInfo> infos = new TIntObjectHashMap<>();
76
77         String tgNodeName(String name) {
78                 if(name.contains(" ")) return "\"" + name + "\"";
79                 else return name;
80         }
81
82         ResourceInfo recurseURI(TransferableGraph1 graph, Identity parent, String parentName) {
83                 String name = parentName + "." + tgNodeName(getName(parent));
84                 ResourceInfo info = new ResourceInfo(true, name, parent.resource); 
85                 infos.put(parent.resource, info);
86                 for(Identity child : getChildren(graph, parent)) {
87                         recurseURI(graph, child, name);
88                 }
89                 return info;
90         }
91
92         void discoverBlank(TransferableGraph1 graph, int resource, TIntArrayList todo) throws Exception {
93                 TIntArrayList statements = getStatements(graph, resource);
94                 for(int i=0;i<statements.size();i+=2) {
95                         int object = statements.get(i+1);
96                         Identity objectId = getIdentity(graph, object);
97                         if(objectId != null) {
98                                 if(objectId.definition instanceof External) continue;
99                         }
100                         Value value = TransferableGraphUtils.findValue(graph, object);
101                         if(value != null) {
102                                 infos.put(object, new ResourceInfo(false, printValue(value), object));
103                                 continue;
104                         }
105                         ResourceInfo existing = infos.get(object);
106                         if(existing == null) {
107                                 existing = new ResourceInfo(false, "blank" + blankCounter++, object);
108                                 infos.put(object, existing);
109                                 todo.add(object);
110                         }
111                 }
112         }
113
114         void discoverOwners(TransferableGraph1 graph, ResourceInfo info) {
115                 int resource = info.resource;
116                 TIntArrayList statements = getStatements(graph, resource);
117                 for(int i=0;i<statements.size();i+=2) {
118                         int predicate = statements.get(i);
119                         int object = statements.get(i+1);
120                         ResourceInfo existing = infos.get(object);
121                         if(existing == null) continue;
122                         if(existing.owner == -1) {
123                                 existing.owner = resource;
124                                 existing.ownerPredicate = predicate;
125                                 //System.err.println("First owner " + info.name + " => " + predicateURI + " " + existing.name);
126                         } else {
127                                 existing.owner = -2;
128                                 //System.err.println("Multiple owners " + info.name + " => " + predicateURI + " " + existing.name);
129                         }
130                 }
131                 
132         }
133
134         String printValue(Value value) throws Exception {
135                 Variant variant = value.value;
136                 Datatype dt = variant.getBinding().type();
137                 if(Datatypes.STRING.equals(dt)) {
138                         String s = (String)variant.getValue(Bindings.STRING);
139                         if(s.contains("\n")) {
140                                 return "\"\"\"" + s + "\"\"\"";
141                         } else {
142                                 return "\"" + s + "\"";         
143                         }
144                 } else if(Datatypes.BOOLEAN.equals(dt)) {
145                         Boolean i = (Boolean)variant.getValue(Bindings.BOOLEAN);
146                         return i ? "true" : "false";
147                 } else if(Datatypes.INTEGER.equals(dt)) {
148                         Integer i = (Integer)variant.getValue(Bindings.INTEGER);
149                         return i.toString();
150                 } else if(Datatypes.LONG.equals(dt)) {
151                         Long i = (Long)variant.getValue(Bindings.LONG);
152                         return i.toString();
153                 } else {
154                         byte[] data = variant.getBinding().serializer().serialize(variant.getValue());
155                         m.reset();
156                         m.update(data, 0, data.length);
157                         return "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
158                 }
159
160         }
161
162         //      void fixInstanceOf(TransferableGraph1 graph, ResourceInfo info) {
163         //              Identity id = getIdentity(graph, info.resource);
164         //              if(id == null) return;
165         //              if(id.definition instanceof Internal) {
166         //                      Identity instanceOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/InstanceOf");
167         //                      Identity library = findExternal(graph, "http://www.simantics.org/Layer0-1.1/Library");
168         //                      info.statements.add(instanceOf.resource);
169         //                      info.statements.add(library.resource);
170         //              }
171         //      }
172
173         public static String getExternalURI(TransferableGraph1 tg, External ext) {
174                 String name = ext.name;
175                 if(name.contains(" ")) name = name.replace(" ", "_").replaceAll("@", "_");//name = "\"" + name + "\"";
176                 int parentId = ext.parent;
177                 //if(parentId == 0) return ext.name;
178                 //              else {
179                 Identity id = getIdentity(tg, parentId);
180                 if(id.definition instanceof External) {
181                         return getExternalURI(tg, (External)id.definition) + "/" + name;
182                 } else if(id.definition instanceof Root) {
183                         Root root = (Root)id.definition;
184                         return "http:/" + root.name + "/" + name; 
185                 } else {
186                         return null;
187                 }
188                 //              }
189         }
190
191         public static String getExternalURI(TransferableGraph1 tg, int resource) {
192                 Identity id = getIdentity(tg, resource);
193                 if(id == null) return null;
194                 if(id.definition instanceof External) {
195                         External ext = (External)id.definition;
196                         return getExternalURI(tg, ext);
197                 }
198                 return null;
199         }
200
201         String rewritePredicateURI(TransferableGraph1 graph, int predicate) {
202                 
203                 String uri = getExternalURI(graph, predicate);
204                 if(uri == null) {
205                         ResourceInfo info = infos.get(predicate);
206                         if(info != null) return info.name;
207                         return "_";
208                 }
209                 
210                 for(String ontology : ontologies.keySet()) {
211                         if(uri.contains(ontology)) {
212                                 String key = ontologies.get(ontology);
213                                 uri = uri.replace(ontology, key);
214                                 referencedOntologies.add(ontology);
215                         }
216                 }
217                 
218                 uri = uri.replace("/", ".");
219                 
220                 return uri;
221                 
222         }
223
224         void indent(int indent) {
225                 for(int i=0;i<indent;i++) output.append("  ");
226         }
227         
228         void printBlank(TransferableGraph1 graph, String predicateURI2, ResourceInfo info, int indent) {
229
230                 if(info.hasURI) return;
231                 indent(indent);
232                 output.append(predicateURI2 + " " + info.name + "\n");
233
234                 if(info.owner < 0) {
235                         printURI(graph, info, false, indent);
236                 }
237                 
238         }
239
240         long longStm(int predicate, int object) {
241                 return (predicate<<32) | (object & 0xffffffffL); 
242         }
243
244         void addStatement(Map<String,Set<String>> statements, String predicate, String object) {
245                 Set<String> objects = statements.get(predicate);
246                 if(objects == null) {
247                         objects = new TreeSet<>();
248                         statements.put(predicate, objects);
249                 }
250                 objects.add(object);
251         }
252
253         void printURI(TransferableGraph1 graph, ResourceInfo info, boolean requireURI, int indent) {
254
255                 if(requireURI && !info.hasURI) return;
256
257                 Map<String,Set<String>> statements = new TreeMap<>();
258                 Identity consistsOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/ConsistsOf");
259                 TLongHashSet processed = new TLongHashSet();
260                 for(int i=0;i<info.owned.size();i+=2) {
261                         long stmId = longStm(info.owned.get(i), info.owned.get(i+1));
262                         processed.add(stmId);
263                 }
264                 
265                 TIntArrayList rawStatements = getStatements(graph, info.resource);
266                 for(int i=0;i<rawStatements.size();i+=2) {
267                         long stmId = longStm(rawStatements.get(i), rawStatements.get(i+1));
268                         if(!processed.add(stmId)) continue;
269                         if(consistsOf.resource == rawStatements.get(i)) continue;
270                         String predicateURI = rewritePredicateURI(graph, rawStatements.get(i));
271                         ResourceInfo objectInfo = infos.get(rawStatements.get(i+1));
272                         if(objectInfo == null) {
273                                 String objectURI = rewritePredicateURI(graph, rawStatements.get(i+1));
274                                 addStatement(statements, predicateURI, objectURI);
275                         } else {
276                                 addStatement(statements, predicateURI, objectInfo.name);
277                         }
278                 }
279
280                 if(indent == 0) {
281                         if("ROOT".equals(info.name)) {
282                                 output.append("ROOT=<http:/>");
283                         } else if (info.aliasURI != null) {
284                                 output.append(info.name + " = <" + info.aliasURI + ">");
285                         } else {
286                                 output.append(info.name);
287                         }
288                         Set<String> instanceOfs = statements.get("L0.InstanceOf");
289                         if(instanceOfs != null) {
290                                 for(String instanceOf : instanceOfs) {
291                                         output.append(" : " + instanceOf);              
292                                 }
293                         }
294                         Set<String> subrelationOfs = statements.get("L0.SubrelationOf");
295                         if(subrelationOfs != null) {
296                                 for(String subrelationOf : subrelationOfs) {
297                                         output.append(" <R " + subrelationOf);          
298                                 }
299                         }
300                         Set<String> inherits = statements.get("L0.Inherits");
301                         if(inherits != null) {
302                                 for(String inherit : inherits) {
303                                         output.append(" <T " + inherit);                
304                                 }
305                         }
306                         output.append("\n");
307                 }
308
309                 if(info.newResource)
310                         output.append("  @L0.new\n");
311
312                 for(Map.Entry<String, Set<String>> entry : statements.entrySet()) {
313                         String predicate = entry.getKey();
314                         if("L0.InstanceOf".equals(predicate)) continue;
315                         if("L0.SubrelationOf".equals(predicate)) continue;
316                         if("L0.Inherits".equals(predicate)) continue;
317                         if("L0.PartOf".equals(predicate)) continue;
318                         Set<String> objects = entry.getValue();
319                         if(objects.size() == 1) {
320                                 indent(indent+1);
321                                 output.append(predicate + " " + objects.iterator().next() + "\n");      
322                         } else{
323                                 indent(indent+1);
324                                 output.append(predicate + "\n");        
325                                 for(String object : objects) {
326                                         indent(indent+1);
327                                         output.append("  " + object + "\n");    
328                                 }
329                         }
330                 }
331
332                 for(int i=0;i<info.owned.size();i+=2) {
333                         String predicateURI = rewritePredicateURI(graph, info.owned.get(i));
334                         ResourceInfo ownedInfo = infos.get(info.owned.get(i+1));
335                         printBlank(graph, predicateURI, ownedInfo, indent+1);
336                 }
337                 
338         }
339
340         void prettyPrint(Path input, Path output) throws Exception {
341
342                 System.out.format("Converting exported shared ontology%n\t" + input.toString() + "%nto bundle-compatible ontology%n\t" + output.toString());
343                 try (InputStream is = new BufferedInputStream(Files.newInputStream(input), 128*1024)) {
344                         DataInput dis = new DataInputStream(is);
345                         org.simantics.databoard.container.DataContainer container = 
346                                         DataContainers.readFile(dis); 
347                         Binding binding = TransferableGraph1.BINDING;
348                         TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
349                         prettyPrint(graph);
350                         Files.write(output, this.output.toString().getBytes());
351
352                 }
353
354         }
355         
356         static Map<String,String> knownOntologies = new HashMap<>();
357         
358         static {
359                 knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0");
360                 knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X");
361                 knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD");
362                 knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA");
363                 knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR");
364                 knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC");
365                 knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU");
366                 knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D");
367                 knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL");
368                 knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP");
369                 knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2");
370                 knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE");
371                 knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT");
372                 knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA");
373                 knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH");
374         }
375
376         
377         void prettyPrint(TransferableGraph1 graph) throws Exception {
378
379                 for(Identity id : graph.identities) {
380                         if(id.definition instanceof Internal) {
381                                 Internal internal = (Internal)id.definition;
382                                 Identity parent = TransferableGraphUtils.getIdentity(graph, internal.parent);
383                                 if(parent.definition instanceof External) {
384                                         String name = "BASE";
385                                         ResourceInfo info = new ResourceInfo(true, name, id.resource);
386                                         info.aliasURI = TransferableGraphUtils.getURI(graph, id.resource);
387                                         info.newResource = true;
388                                         infos.put(id.resource, info);
389                                         for(Identity child : getChildren(graph, id)) {
390                                                 recurseURI(graph, child, name);
391                                         }
392                                 }
393                         } else if (id.definition instanceof External) {
394                                 External ext = (External)id.definition;
395                                 // Try to detect shared libraries
396                                 if(ext.name.contains("@")) {
397                                         
398                                         int index = ext.name.indexOf('@');
399                                         String prefix = ext.name.substring(0, index);
400                                         int index2 = ext.name.indexOf('/', index);
401                                         String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2);  
402                                         String uri = TransferableGraphUtils.getURI(graph, id.resource);
403                                         ontologies.put(uri, prefix);
404                                         
405                                 } else if (ext.name.contains("-")) {
406
407                                         String uri = TransferableGraphUtils.getURI(graph, id.resource);
408                                 Matcher m = versionExtractPattern.matcher(uri);
409                                 if (m.matches()) {
410                                         if(!ontologies.containsKey(uri)) {
411                                                         int index = ext.name.indexOf('-');
412                                                         String prefix = ext.name.substring(0, index);
413                                                         ontologies.put(uri, prefix);
414                                         }
415                                 }
416
417                                 }
418                                 
419
420                                 
421                         }
422                 }
423                 // Discover other resources
424                 TIntArrayList todo = new TIntArrayList();
425                 for(ResourceInfo info : infos.valueCollection())
426                         todo.add(info.resource);
427                 while(!todo.isEmpty()) {
428                         int resource = todo.removeAt(todo.size()-1);
429                         discoverBlank(graph, resource, todo);
430                 }
431                 for(ResourceInfo info : infos.valueCollection())
432                         discoverOwners(graph, info);
433                 //                      for(ResourceInfo info : infos.valueCollection())
434                 //                              fixInstanceOf(graph, info);
435                 for(ResourceInfo info : infos.valueCollection())
436                         if(info.owner >= 0) {
437                                 ResourceInfo ownerInfo = infos.get(info.owner);
438                                 ownerInfo.owned.add(info.ownerPredicate);
439                                 ownerInfo.owned.add(info.resource);
440                         }
441
442                 TreeMap<String,ResourceInfo> order = new TreeMap<>();
443                 for(ResourceInfo info : infos.valueCollection())
444                         order.put(info.name, info);
445
446                 for(ResourceInfo info : order.values())
447                         printURI(graph, info, true, 0);
448
449                 for(ResourceInfo info : order.values())
450                         if(!info.hasURI && info.owner < 0)
451                                 printURI(graph, info, false, 0);
452
453                 StringBuilder refs = new StringBuilder();
454                 for(String ontology : referencedOntologies) {
455                         String key = ontologies.get(ontology);
456                         refs.append(key + " = <" + ontology + ">\n");   
457                 }
458                 output.insert(0, refs.toString());
459                 
460         }
461
462         public static String print(TransferableGraph1 tg) throws Exception {
463                 StringBuilder b = new StringBuilder();
464                 new PrettyPrintTG(b).prettyPrint(tg);
465                 return b.toString();
466         }
467
468         public static void main(String[] args) throws Exception {
469                 if (args.length < 1) {
470                         System.out.println("Required arguments: <input .sharedOntology file> [<output .tg file>]");
471                 } else if (args.length < 2) {
472                         Path input = Paths.get(args[0]);
473                         Path output = input.getParent().resolve(input.getName(input.getNameCount()-1) + ".fixed");
474                         new PrettyPrintTG().prettyPrint(input, output);
475                 } else {
476                         new PrettyPrintTG().prettyPrint(Paths.get(args[0]), Paths.get(args[1]));
477                 }
478         }
479
480 }