0640f42f495ccc646f4770435ccf300bb834afef
[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.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.TreeMap;
18 import java.util.TreeSet;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import org.simantics.databoard.Bindings;
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.parser.DataValuePrinter;
27 import org.simantics.databoard.parser.PrintFormat;
28 import org.simantics.databoard.parser.repository.DataValueRepository;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import gnu.trove.list.array.TIntArrayList;
33 import gnu.trove.map.hash.TIntIntHashMap;
34 import gnu.trove.map.hash.TIntObjectHashMap;
35 import gnu.trove.procedure.TIntIntProcedure;
36 import gnu.trove.set.hash.TLongHashSet;
37
38 /**
39  * @author Antti Villberg
40  * @since 1.24.0
41  */
42 public class PrettyPrintTG {
43
44     private static final Logger LOGGER = LoggerFactory.getLogger(PrettyPrintTG.class);
45     
46     private static final boolean DEBUG = false;
47     
48         int blankCounter = 0;
49         MessageDigest m;
50
51         private final Pattern versionExtractPattern = Pattern.compile("^.*-(\\d+\\.\\d+)");
52
53         final StringBuilder output;
54         final Map<String,String> ontologies = new HashMap<>(knownOntologies);
55         final Set<String> referencedOntologies = new TreeSet<>();
56
57         static class ResourceInfo {
58                 final boolean hasURI;
59                 final String name;
60                 final int resource;
61                 boolean newResource = false;
62                 
63                 int parent;
64                 
65                 boolean inlined = false;
66                 
67                 // -1 = no owner, -2 = multiple owners
68                 
69                 // Set<ResourceInfo> ownedBy
70                 Set<ResourceInfo> ownedBy = new HashSet<>();
71                 
72                 // A Map<Integer, Integer> containing information about resource that this resource owns and what are the predicates for forming this ownership
73                 TIntIntHashMap ownedResourcesWithPredicates = new TIntIntHashMap();
74                 
75 //              int owner = -1;
76 //              int ownerPredicate = 0;
77                 String aliasURI = null;
78                 TIntArrayList owned = new TIntArrayList();
79                 //TIntObjectHashMap<TIntHashSet> statements = new TIntObjectHashMap<TIntHashSet>();
80                 public ResourceInfo(boolean hasURI, String name, int resource, int parent) {
81                         this.hasURI = hasURI;
82                         this.name = name;
83                         this.resource = resource;
84                         this.parent = parent;
85                 }
86                 
87                 @Override
88                 public String toString() {
89                     return name + (aliasURI != null ? " = <" + aliasURI + ">" : "");
90                 }
91         }
92
93         public PrettyPrintTG(StringBuilder b) throws NoSuchAlgorithmException {
94                 output = b;
95                 m = MessageDigest.getInstance("SHA-256");
96         }
97
98         public PrettyPrintTG() throws NoSuchAlgorithmException {
99                 this(new StringBuilder());
100         }
101
102         TIntObjectHashMap<ResourceInfo> infos = new TIntObjectHashMap<>();
103
104         String tgNodeName(String name) {
105                 if(name.contains(" ")) return "\"" + name + "\"";
106                 else return name;
107         }
108
109         ResourceInfo recurseURI(TransferableGraph1 graph, Identity parent, String parentName, int parentId) {
110                 String name = parentName + "." + tgNodeName(TransferableGraphUtils.getName(parent));
111                 ResourceInfo info = new ResourceInfo(true, name, parent.resource, parentId); 
112                 infos.put(parent.resource, info);
113                 for(Identity child : TransferableGraphUtils.getChildren(graph, parent)) {
114                         recurseURI(graph, child, name, info.resource);
115                 }
116                 return info;
117         }
118
119         void discoverBlank(TransferableGraph1 graph, int resource, TIntArrayList todo) throws Exception {
120                 TIntArrayList statements = TransferableGraphUtils.getStatements(graph, resource);
121                 for(int i=0;i<statements.size();i+=2) {
122                         int object = statements.get(i+1);
123                         Identity objectId = TransferableGraphUtils.getIdentity(graph, object);
124                         if(objectId != null) {
125                                 if(objectId.definition instanceof External) continue;
126                         }
127                         Value value = TransferableGraphUtils.findValue(graph, object);
128                         if(value != null) {
129                                 infos.put(object, new ResourceInfo(false, printValue(value), object, resource));
130                                 continue;
131                         }
132                         ResourceInfo existing = infos.get(object);
133                         if(existing == null) {
134                                 existing = new ResourceInfo(false, "blank" + blankCounter++, object, resource);
135                                 infos.put(object, existing);
136                                 todo.add(object);
137                         }
138                 }
139         }
140
141         void discoverOwners(TransferableGraph1 graph, ResourceInfo info) {
142             log("Discovering owners for " + info);
143                 int resource = info.resource;
144                 TIntArrayList statements = TransferableGraphUtils.getStatements(graph, resource);
145                 for(int i=0;i<statements.size();i+=2) {
146                         int predicate = statements.get(i);
147                         int object = statements.get(i+1);
148                         ResourceInfo existing = infos.get(object);
149                         if(existing != null) {
150                 // Add all owners here for now and resolve the best owner later
151                 existing.ownedResourcesWithPredicates.put(resource, predicate);
152                             // Check if predicate is inverse, this just resolves all predicates to be inverse with ending "Inverse"..
153                             String predicateUri = rewritePredicateURI(graph, predicate);
154                             if (!predicateUri.endsWith("Inverse")) {
155                                 existing.ownedBy.add(info);
156                              if (LOGGER.isDebugEnabled()) {
157                                     LOGGER.debug("    " + existing + " owns " + info + " with " + predicateUri);
158                              }
159                             }
160                         }
161                 }
162         }
163
164     DataValueRepository repo = new DataValueRepository();
165     DataValuePrinter printer = new DataValuePrinter(null, repo);
166         
167     String printValue(Value value) throws Exception {
168         StringBuilder sb = new StringBuilder();
169         printer.setFormat(PrintFormat.SINGLE_LINE);
170         printer.setOutput(sb);
171         
172         Variant variant = value.value;
173         printer.print(variant.getBinding(), variant.getValue());
174         String formattedOutput = sb.toString();
175         if (formattedOutput.length() > 100) {
176             // Ok, value too long, lets calculate a hash for it and store first 100 chars as comment
177               byte[] data = Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue());
178               m.reset();
179               m.update(data, 0, data.length);
180               String hash = "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
181               return hash + " // " + formattedOutput.substring(0, 100) + "..";
182         } else {
183             return formattedOutput;
184         }
185 //        
186 //        Variant variant = value.value;
187 //        Datatype dt = variant.getBinding().type();
188 //        if (Datatypes.STRING.equals(dt)) {
189 //            String s = (String) variant.getValue(Bindings.STRING);
190 //            if (s.contains("\n")) {
191 //                return "\"\"\"" + s + "\"\"\"";
192 //            } else {
193 //                return "\"" + s + "\"";
194 //            }
195 //        } else if (Datatypes.BOOLEAN.equals(dt)) {
196 //            Boolean i = (Boolean) variant.getValue(Bindings.BOOLEAN);
197 //            return i ? "true" : "false";
198 //        } else if (Datatypes.INTEGER.equals(dt)) {
199 //            Integer i = (Integer) variant.getValue(Bindings.INTEGER);
200 //            return i.toString();
201 //        } else if (Datatypes.LONG.equals(dt)) {
202 //            Long i = (Long) variant.getValue(Bindings.LONG);
203 //            return i.toString();
204 //        } else if (Datatypes.DOUBLE.equals(dt)) {
205 //            Double d = (Double) variant.getValue();
206 //            return d.toString();
207 //        } else if (Datatypes.FLOAT.equals(dt)) {
208 //            Float f = (Float) variant.getValue();
209 //            return f.toString();
210 //        } else if (Datatypes.STRING_ARRAY.equals(dt)) {
211 //            return Arrays.toString((String []) variant.getValue());
212 //        } else if (Datatypes.BOOLEAN_ARRAY.equals(dt)) {
213 //            return Arrays.toString((boolean []) variant.getValue());
214 //        } else if (Datatypes.INTEGER_ARRAY.equals(dt)) {
215 //            return Arrays.toString((int []) variant.getValue());
216 //        } else if (Datatypes.LONG_ARRAY.equals(dt)) {
217 //            return Arrays.toString((long []) variant.getValue());
218 //        } else if (Datatypes.DOUBLE_ARRAY.equals(dt)) {
219 //            return Arrays.toString((double []) variant.getValue());
220 //        } else if (Datatypes.FLOAT_ARRAY.equals(dt)) {
221 //            return Arrays.toString((float []) variant.getValue());
222 //        } else if (Datatypes.BYTE_ARRAY.equals(dt)) {
223 //            return Arrays.toString((byte []) variant.getValue());
224 ////        } else if (dt instanceof ArrayType) {
225 ////            return Arrays.toString((Object []) variant.getValue());
226 //        } else {
227 //            byte[] data = Bindings.getSerializerUnchecked(variant.getBinding()).serialize(variant.getValue());
228 //            m.reset();
229 //            m.update(data, 0, data.length);
230 //            return "\"" + new BigInteger(1, m.digest()).toString(16) + "\"";
231 //        }
232
233     }
234
235         //      void fixInstanceOf(TransferableGraph1 graph, ResourceInfo info) {
236         //              Identity id = getIdentity(graph, info.resource);
237         //              if(id == null) return;
238         //              if(id.definition instanceof Internal) {
239         //                      Identity instanceOf = findExternal(graph, "http://www.simantics.org/Layer0-1.1/InstanceOf");
240         //                      Identity library = findExternal(graph, "http://www.simantics.org/Layer0-1.1/Library");
241         //                      info.statements.add(instanceOf.resource);
242         //                      info.statements.add(library.resource);
243         //              }
244         //      }
245
246         public static String getExternalURI(TransferableGraph1 tg, External ext) {
247                 String name = ext.name;
248                 if(name.contains(" ")) name = name.replace(" ", "_").replaceAll("@", "_");//name = "\"" + 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) return null;
267                 if(id.definition instanceof External) {
268                         External ext = (External)id.definition;
269                         return getExternalURI(tg, ext);
270                 }
271                 return null;
272         }
273
274         String rewritePredicateURI(TransferableGraph1 graph, int predicate) {
275                 
276                 String uri = getExternalURI(graph, predicate);
277                 if(uri == null) {
278                         ResourceInfo info = infos.get(predicate);
279                         if(info != null) return info.name;
280                         return "_";
281                 }
282                 
283                 for(String ontology : ontologies.keySet()) {
284                         if(uri.contains(ontology)) {
285                                 String key = ontologies.get(ontology);
286                                 uri = uri.replace(ontology, key);
287                                 referencedOntologies.add(ontology);
288                         }
289                 }
290                 
291                 uri = uri.replace("/", ".");
292                 
293                 return uri;
294                 
295         }
296
297         static void indent(StringBuilder output, int indent) {
298                 for(int i=0;i<indent;i++)
299                     output.append("  ");
300         }
301         
302         String printBlank(TransferableGraph1 graph, String predicateURI2, ResourceInfo info, int indent) {
303
304                 if(info.hasURI)
305                     return null;
306                 
307                 StringBuilder output = new StringBuilder();
308                 indent(output, indent);
309                 output.append(predicateURI2 + " " + info.name + "\n");
310
311                 if (info.ownedResourcesWithPredicates.isEmpty()) {
312                     String uri = printURI(graph, info, false, indent, false);
313                     if (uri != null)
314                         output.append(uri);
315                 }
316 //              if(info.owner < 0) {
317 //                      printURI(graph, info, false, indent);
318 //              }
319                 return output.toString();
320         }
321
322         static long longStm(int predicate, int object) {
323                 return (predicate<<32) | (object & 0xffffffffL); 
324         }
325
326
327     private void addInlineStatement(TransferableGraph1 graph, Map<String, Set<String>> statements, String predicate, ResourceInfo objectInfo, int indent) {
328         Set<String> objects = statements.get(predicate);
329         if(objects == null) {
330             objects = new TreeSet<>();
331             statements.put(predicate, objects);
332         }
333         String uri = printURI(graph, objectInfo, false, indent + 1, true);
334         if (uri != null) {
335             // TODO: this is not the right place to remove trailing newline
336             uri = uri.endsWith("\n") ? uri.substring(0, uri.length() - 2) : uri;
337             objects.add(uri);
338         }
339         objectInfo.inlined = true;
340     }
341
342         
343         void addStatement(Map<String,Set<String>> statements, String predicate, String object) {
344             // TODO: fix this
345             if (predicate.endsWith("Inverse"))
346                 return;
347                 Set<String> objects = statements.get(predicate);
348                 if(objects == null) {
349                         objects = new TreeSet<>();
350                         statements.put(predicate, objects);
351                 }
352                 objects.add(object);
353         }
354
355         String printURI(TransferableGraph1 graph, ResourceInfo info, boolean requireURI, int indent, boolean inline) {
356
357                 if(requireURI && !info.hasURI)
358                     return null;
359                 
360                 // Check if this ResourceInfo is already inlined with some other ResourceInfo 
361                 if (info.inlined) 
362                     return null;
363
364                 Map<String,Set<String>> statements = new TreeMap<>();
365                 Identity consistsOf = TransferableGraphUtils.findExternal(graph, "http://www.simantics.org/Layer0-1.1/ConsistsOf");
366                 TLongHashSet processed = new TLongHashSet();
367                 for(int i=0;i<info.owned.size();i+=2) {
368                         long stmId = longStm(info.owned.get(i), info.owned.get(i+1));
369                         processed.add(stmId);
370                 }
371                 
372                 TIntArrayList rawStatements = TransferableGraphUtils.getStatements(graph, info.resource);
373                 for(int i=0;i<rawStatements.size();i+=2) {
374                         long stmId = longStm(rawStatements.get(i), rawStatements.get(i+1));
375                         if(!processed.add(stmId)) continue;
376                         if(consistsOf.resource == rawStatements.get(i)) continue;
377                         String predicateURI = rewritePredicateURI(graph, rawStatements.get(i));
378                         ResourceInfo objectInfo = infos.get(rawStatements.get(i+1));
379                         if(objectInfo == null) {
380                                 String objectURI = rewritePredicateURI(graph, rawStatements.get(i+1));
381                                 addStatement(statements, predicateURI, objectURI);
382                         } else if (objectInfo.ownedBy.size() == 1 && objectInfo.ownedBy.contains(info)) {
383                             // inline printing with _
384                             addInlineStatement(graph, statements, predicateURI, objectInfo, indent);
385                         } else {
386                                 addStatement(statements, predicateURI, objectInfo.name);
387                         }
388                 }
389
390                 HashSet<ResourceInfo> debug = new HashSet<>();
391                 info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() {
392             
393             @Override
394             public boolean execute(int owner, int predicate) {
395                 ResourceInfo ownerInfo = infos.get(owner);
396                 debug.add(ownerInfo);
397 //                ResourceInfo predicateInfo = infos.get(predicate);
398 //                debug.add(predicateInfo);
399                 return true;
400             }
401         });
402
403                 StringBuilder output = new StringBuilder();
404                 
405                 if(indent == 0 || inline) {
406                         if("ROOT".equals(info.name)) {
407                                 output.append("ROOT=<http:/>");
408                         } else if (info.aliasURI != null) {
409                                 output.append(info.name + " = <" + info.aliasURI + ">");
410                         } else {
411                             if (inline)
412                                 System.out.println("asdasd");
413                                 output.append(inline ? "_" : info.name);
414                         }
415                         Set<String> instanceOfs = statements.get("L0.InstanceOf");
416                         if(instanceOfs != null) {
417                                 for(String instanceOf : instanceOfs) {
418                                         output.append(" : " + instanceOf);              
419                                 }
420                         }
421                         Set<String> subrelationOfs = statements.get("L0.SubrelationOf");
422                         if(subrelationOfs != null) {
423                                 for(String subrelationOf : subrelationOfs) {
424                                         output.append(" <R " + subrelationOf);          
425                                 }
426                         }
427                         Set<String> inherits = statements.get("L0.Inherits");
428                         if(inherits != null) {
429                                 for(String inherit : inherits) {
430                                         output.append(" <T " + inherit);                
431                                 }
432                         }
433                         output.append("\n");
434                 }
435
436                 if(info.newResource)
437                         output.append("  @L0.new\n");
438
439                 for(Map.Entry<String, Set<String>> entry : statements.entrySet()) {
440                         String predicate = entry.getKey();
441                         if("L0.InstanceOf".equals(predicate)) continue;
442                         if("L0.SubrelationOf".equals(predicate)) continue;
443                         if("L0.Inherits".equals(predicate)) continue;
444                         if("L0.PartOf".equals(predicate)) continue;
445                         Set<String> objects = entry.getValue();
446                         indent(output, indent+1);
447                         if(objects.size() == 1) {
448                                 output.append(predicate + " " + objects.iterator().next() + "\n");      
449                         } else{
450                                 output.append(predicate + "\n");        
451                                 for(String object : objects) {
452                                         indent(output, indent+1);
453                                         output.append("  " + object + "\n");    
454                                 }
455                         }
456                 }
457
458                 for(int i=0;i<info.owned.size();i+=2) {
459                         String predicateURI = rewritePredicateURI(graph, info.owned.get(i));
460                         ResourceInfo ownedInfo = infos.get(info.owned.get(i+1));
461                         String blank = printBlank(graph, predicateURI, ownedInfo, indent+1);
462                         if (blank != null)
463                             output.append(blank);
464                 }
465                 
466                 return output.toString();
467         }
468
469     void prettyPrint(Path input, Path output) throws Exception {
470
471                 System.out.format("Converting exported shared ontology%n\t" + input.toString() + "%nto bundle-compatible ontology%n\t" + output.toString());
472                 try (InputStream is = new BufferedInputStream(Files.newInputStream(input), 128*1024)) {
473                         DataInput dis = new DataInputStream(is);
474                         org.simantics.databoard.container.DataContainer container = 
475                                         DataContainers.readFile(dis); 
476                         Binding binding = TransferableGraph1.BINDING;
477                         TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
478                         prettyPrint(graph);
479                         Files.write(output, this.output.toString().getBytes());
480
481                 }
482
483         }
484         
485         static Map<String,String> knownOntologies = new HashMap<>();
486         
487         static {
488                 knownOntologies.put("http://www.simantics.org/Layer0-1.1", "L0");
489                 knownOntologies.put("http://www.simantics.org/Layer0X-1.1", "L0X");
490                 knownOntologies.put("http://www.simantics.org/Modeling-1.2", "MOD");
491                 knownOntologies.put("http://www.simantics.org/Diagram-2.2", "DIA");
492                 knownOntologies.put("http://www.simantics.org/Structural-1.2", "STR");
493                 knownOntologies.put("http://www.simantics.org/Document-1.2", "DOC");
494                 knownOntologies.put("http://www.simantics.org/Documentation-1.2", "DOCU");
495                 knownOntologies.put("http://www.simantics.org/G2D-1.1", "G2D");
496                 knownOntologies.put("http://www.simantics.org/SelectionView-1.2", "SEL");
497                 knownOntologies.put("http://www.simantics.org/Viewpoint-1.2", "VP");
498                 knownOntologies.put("http://www.simantics.org/Image2-1.2", "IMAGE2");
499                 knownOntologies.put("http://www.simantics.org/GraphFile-0.1", "GRAPHFILE");
500                 knownOntologies.put("http://www.simantics.org/Project-1.2", "PROJECT");
501                 knownOntologies.put("http://www.semantum.fi/Simupedia-1.0", "SIMUPEDIA");
502                 knownOntologies.put("http://www.semantum.fi/SimupediaWorkbench-1.0", "SIMUPEDIA_WORKBENCH");
503         }
504
505         
506         void prettyPrint(TransferableGraph1 graph) throws Exception {
507
508             if (LOGGER.isDebugEnabled())
509                 LOGGER.debug("Starting prettyPrint for TransferableGraph with " + graph.resourceCount + " resources, " + graph.identities + " identities, " + graph.statements.length + " statements and " + graph.values.length + " values");
510             
511                 for(Identity id : graph.identities) {
512                         if(id.definition instanceof Internal) {
513                                 Internal internal = (Internal)id.definition;
514                                 Identity parent = TransferableGraphUtils.getIdentity(graph, internal.parent);
515                                 if(parent.definition instanceof External) {
516                         if (LOGGER.isDebugEnabled())
517                             LOGGER.debug("Resolving internal identity " + id);
518                                         String name = "BASE";
519                                         ResourceInfo info = new ResourceInfo(true, name, id.resource, -1);
520                                         info.aliasURI = TransferableGraphUtils.getURI(graph, id.resource);
521                                         info.newResource = true;
522                                         infos.put(id.resource, info);
523                     if (LOGGER.isDebugEnabled())
524                         LOGGER.debug("    which parent is external " + parent + " and has an aliasURI " + info.aliasURI) ;
525                                         for(Identity child : TransferableGraphUtils.getChildren(graph, id)) {
526                                                 recurseURI(graph, child, name, info.resource);
527                                         }
528                                         if (LOGGER.isDebugEnabled())
529                                             LOGGER.debug("    and has " + (infos.size() - 1) + " children");
530                                 }
531                         } else if (id.definition instanceof External) {
532                                 External ext = (External)id.definition;
533                                 // Try to detect shared libraries
534                                 if(ext.name.contains("@")) {
535                                     
536                                     if (LOGGER.isDebugEnabled())
537                                         LOGGER.debug("Detected an external shared library " + ext);
538                                     
539                                         int index = ext.name.indexOf('@');
540                                         String prefix = ext.name.substring(0, index);
541                                         int index2 = ext.name.indexOf('/', index);
542                                         String ontology = index2 == -1 ? ext.name : ext.name.substring(0, index2);  
543                                         String uri = TransferableGraphUtils.getURI(graph, id.resource);
544                                         
545                                         if (LOGGER.isDebugEnabled())
546                                             LOGGER.debug("    which was resolved as URI=" + uri + " and prefix " + prefix);
547                                         
548                                         ontologies.put(uri, prefix);
549                                         
550                                 } else if (ext.name.contains("-")) {
551                                     if (LOGGER.isDebugEnabled())
552                                         LOGGER.debug("Resolving possible ontology " + ext);
553                                         String uri = TransferableGraphUtils.getURI(graph, id.resource);
554                                 Matcher m = versionExtractPattern.matcher(uri);
555                                 if (m.matches()) {
556                                         if(!ontologies.containsKey(uri)) {
557                                                         int index = ext.name.indexOf('-');
558                             String prefix = ext.name.substring(0, index);
559                             if (LOGGER.isDebugEnabled())
560                                 LOGGER.debug("    and it was resolved as URI=" + uri + " and prefix " + prefix);
561                             ontologies.put(uri, prefix);
562                                         }
563                                 }
564                                 }
565                         }
566                 }
567                 // Discover other resources
568                 if (LOGGER.isDebugEnabled())
569                     LOGGER.debug("Discovering other resources..");
570                 
571                 TIntArrayList todo = new TIntArrayList();
572                 for(ResourceInfo info : infos.valueCollection())
573                         todo.add(info.resource);
574                 
575                 while(!todo.isEmpty()) {
576                         int resource = todo.removeAt(todo.size()-1);
577                         discoverBlank(graph, resource, todo);
578                 }
579                 for(ResourceInfo info : infos.valueCollection())
580                         discoverOwners(graph, info);
581                 //                      for(ResourceInfo info : infos.valueCollection())
582                 //                              fixInstanceOf(graph, info);
583                 
584         for (ResourceInfo info : infos.valueCollection()) {
585 //          Old implementation
586 //            if (info.owner >= 0) {
587 //                ResourceInfo ownerInfo = infos.get(info.owner);
588 //                System.out.println("originalOwner : " + info.owner + " originalPredicate: " + info.ownerPredicate);
589 //                ownerInfo.owned.add(info.ownerPredicate);
590 //                ownerInfo.owned.add(info.resource);
591 //            }
592             
593             if (!info.ownedResourcesWithPredicates.isEmpty() && info.ownedResourcesWithPredicates.size() == 1) {
594                 info.ownedResourcesWithPredicates.forEachEntry(new TIntIntProcedure() {
595                     
596                     @Override
597                     public boolean execute(int owner, int predicate) {
598                         ResourceInfo ownerInfo = infos.get(owner);
599                         ownerInfo.owned.add(predicate);
600                         ownerInfo.owned.add(info.resource);
601                         return false;
602                     }
603                 });
604             }
605         }
606
607         // Resolve inverses from ownedBy list
608         for (ResourceInfo info : infos.valueCollection()) {
609             for (int i = 0; i < info.owned.size(); i+=2) {
610                 int object = info.owned.get(i+1);
611                 ResourceInfo inf = infos.get(object);
612                 if (inf != null) {
613                     info.ownedBy.remove(inf);
614                 }
615             }
616         }
617         
618                 TreeMap<String,ResourceInfo> order = new TreeMap<>();
619                 for(ResourceInfo info : infos.valueCollection())
620                         order.put(info.name, info);
621
622                 for(ResourceInfo info : order.values()) {
623                         String uri = printURI(graph, info, true, 0, false);
624                         if (uri != null)
625                             output.append(uri);
626                 }
627
628                 for (ResourceInfo info : order.values()) {
629                     if (!info.hasURI && info.ownedResourcesWithPredicates.size() != 1) {
630                         String uri = printURI(graph, info, false, 0, false);
631                         if (uri != null)
632                             output.append(uri);
633                     }
634                 }
635 //              for(ResourceInfo info : order.values())
636 //                      if(!info.hasURI && info.owner < 0)
637 //                              printURI(graph, info, false, 0);
638
639                 StringBuilder refs = new StringBuilder();
640                 for(String ontology : referencedOntologies) {
641                         String key = ontologies.get(ontology);
642                         refs.append(key + " = <" + ontology + ">\n");   
643                 }
644                 if (!referencedOntologies.isEmpty())
645                     refs.append("\n");
646                 output.insert(0, refs.toString());
647                 
648         }
649
650     public static String print(TransferableGraph1 tg) throws Exception {
651                 StringBuilder b = new StringBuilder();
652                 new PrettyPrintTG(b).prettyPrint(tg);
653                 return b.toString();
654         }
655
656         public static void main(String[] args) throws Exception {
657                 if (args.length < 1) {
658                         System.out.println("Required arguments: <input .sharedOntology file> [<output .tg file>]");
659                 } else if (args.length < 2) {
660                         Path input = Paths.get(args[0]);
661                         Path output = input.getParent().resolve(input.getName(input.getNameCount()-1) + ".fixed");
662                         new PrettyPrintTG().prettyPrint(input, output);
663                 } else {
664                         new PrettyPrintTG().prettyPrint(Paths.get(args[0]), Paths.get(args[1]));
665                 }
666         }
667
668     private static void log(String string) {
669         if (LOGGER.isDebugEnabled() && DEBUG)
670             LOGGER.debug(string);
671     }
672
673 }