]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/CompilePGraphs.java
7781fbeffdbe9d90cafd4b8e01346f27984c1f31
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / CompilePGraphs.java
1 package org.simantics.modeling;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.File;
5 import java.io.FileNotFoundException;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.UnsupportedEncodingException;
9 import java.net.URL;
10 import java.net.URLDecoder;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.Set;
17
18 import org.eclipse.core.runtime.FileLocator;
19 import org.eclipse.core.runtime.IProgressMonitor;
20 import org.eclipse.core.runtime.NullProgressMonitor;
21 import org.osgi.framework.Bundle;
22 import org.simantics.Simantics;
23 import org.simantics.databoard.Bindings;
24 import org.simantics.db.ReadGraph;
25 import org.simantics.db.Resource;
26 import org.simantics.db.WriteGraph;
27 import org.simantics.db.common.request.IndexRoot;
28 import org.simantics.db.common.request.ObjectsWithType;
29 import org.simantics.db.common.request.ReadRequest;
30 import org.simantics.db.common.request.UniqueRead;
31 import org.simantics.db.common.request.WriteRequest;
32 import org.simantics.db.common.utils.Logger;
33 import org.simantics.db.exception.DatabaseException;
34 import org.simantics.db.layer0.adapter.CopyHandler;
35 import org.simantics.db.layer0.adapter.SubgraphExtent.ExtentStatus;
36 import org.simantics.db.layer0.adapter.impl.DefaultCopyHandler;
37 import org.simantics.db.layer0.adapter.impl.DefaultPasteHandler;
38 import org.simantics.db.layer0.adapter.impl.DefaultPasteImportAdvisor;
39 import org.simantics.db.layer0.util.ClipboardUtils;
40 import org.simantics.db.layer0.util.DomainProcessorState;
41 import org.simantics.db.layer0.util.Layer0Utils;
42 import org.simantics.db.layer0.util.ModelTransferableGraphSource;
43 import org.simantics.db.layer0.util.ModelTransferableGraphSourceRequest;
44 import org.simantics.db.layer0.util.SimanticsClipboard.Representation;
45 import org.simantics.db.layer0.util.SimanticsClipboardImpl;
46 import org.simantics.db.layer0.util.SimanticsKeys;
47 import org.simantics.db.layer0.util.TransferableGraphConfiguration2;
48 import org.simantics.db.service.SerialisationSupport;
49 import org.simantics.graph.compiler.CompilationResult;
50 import org.simantics.graph.compiler.ExternalFileLoader;
51 import org.simantics.graph.compiler.GraphCompiler;
52 import org.simantics.graph.compiler.GraphCompilerPreferences;
53 import org.simantics.graph.compiler.ValidationMode;
54 import org.simantics.graph.db.TransferableGraphException;
55 import org.simantics.graph.db.TransferableGraphSource;
56 import org.simantics.graph.db.TransferableGraphs;
57 import org.simantics.graph.diff.Diff;
58 import org.simantics.graph.diff.TransferableGraphDelta1;
59 import org.simantics.graph.representation.Identity;
60 import org.simantics.graph.representation.Root;
61 import org.simantics.graph.representation.TransferableGraph1;
62 import org.simantics.graphfile.ontology.GraphFileResource;
63 import org.simantics.layer0.Layer0;
64 import org.simantics.ltk.ISource;
65 import org.simantics.ltk.Problem;
66 import org.simantics.modeling.internal.Activator;
67 import org.simantics.utils.FileUtils;
68 import org.simantics.utils.datastructures.Pair;
69 import org.slf4j.LoggerFactory;
70
71 /**
72  * @author Antti Villberg
73  */
74 public class CompilePGraphs {
75
76     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(CompilePGraphs.class);
77
78     public static interface UserAgent {
79         void reportProblems(CompilationResult result);
80     }
81
82     /**
83      * <code>Simantics/PGraph#compilePGraphs</code> SCL API.
84      * 
85      * @param r
86      * @throws IOException
87      * @throws DatabaseException
88      */
89     public static void compilePGraphs(Resource r) throws IOException, DatabaseException {
90         compilePGraphs(r, null);
91     }
92
93     public static void compilePGraphs(Resource r, UserAgent userAgent) throws IOException, DatabaseException {
94         compilePGraphs(r, userAgent, new NullProgressMonitor());
95     }
96     
97     public static void compilePGraphs(Resource r, UserAgent userAgent, IProgressMonitor monitor) throws IOException, DatabaseException {
98         final Collection<ISource> sources = new ArrayList<>();
99         Collection<TransferableGraph1> dependencies = new ArrayList<>();
100
101         final Pair<String, TransferableGraph1> thisOntology = Simantics.sync(new UniqueRead<Pair<String, TransferableGraph1>>() {
102
103             @Override
104             public Pair<String, TransferableGraph1> perform(ReadGraph graph) throws DatabaseException {
105                 Layer0 L0 = Layer0.getInstance(graph);
106                 Resource parent = graph.getSingleObject(r, L0.PartOf);
107
108                 CopyHandler ch = new DefaultCopyHandler(r) {
109                     protected TransferableGraphConfiguration2 createConfiguration(ReadGraph graph, boolean cut) throws DatabaseException {
110                         Map<Resource, ExtentStatus> preStatus = new HashMap<>();
111                         preStatus.put(r, ExtentStatus.EXTERNAL);
112                         if (!parent.equals(graph.getRootLibrary()))
113                             preStatus.put(parent, ExtentStatus.EXTERNAL);
114                         return new TransferableGraphConfiguration2(null, Collections.emptyList(), preStatus, true, true);
115                     }
116
117                     protected TransferableGraphSource computeSource(ReadGraph graph, TransferableGraphConfiguration2 conf) throws DatabaseException {
118                         return graph.syncRequest(new ModelTransferableGraphSourceRequest(conf) {
119                             protected ModelTransferableGraphSource getSource(ReadGraph graph, TransferableGraphConfiguration2 configuration, DomainProcessorState state, File otherStatementsFile, File valueFile) throws DatabaseException {
120                                 return new ModelTransferableGraphSource(graph, configuration, state, otherStatementsFile, valueFile) {
121                                     @Override
122                                     protected Identity getRootIdentity(DomainProcessorState state, SerialisationSupport support, Resource rootLibrary) throws DatabaseException {
123                                         return new Identity(state.ids.get(support.getTransientId(rootLibrary)), new Root("", ""));
124                                     }
125                                 };
126                             }
127                         });
128                     }
129                 };
130
131                 String uri = graph.getURI(r);
132                 SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl();
133                 ch.copyToClipboard(graph, clipboard);
134                 for (Set<Representation> object : clipboard.getContents()) {
135                     TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);
136                     if (tg != null)
137                         return Pair.make(uri, tg);
138                 }
139
140                 return null;
141             }
142         });
143
144         if (thisOntology == null)       
145             throw new DatabaseException("Failed to dump the containing ontology of " + r + " into TransferableGraph1");
146
147         dependencies.add(thisOntology.second);
148         
149         for (Bundle b : Activator.getContext().getBundles()) {
150                         String id = b.getSymbolicName();
151                         String name = (String) b.getHeaders().get("Bundle-Name");
152                         if (name == null) name = id;
153                         if (name.equals(thisOntology.first))
154                                 continue;
155                         if (name.contains("ModelBroker"))
156                             continue;
157             URL tg = b.getEntry("/graph.tg");
158             if (tg == null) continue;
159             File f = url2file(FileLocator.resolve(tg), b.getSymbolicName());
160             try {
161                 dependencies.add(GraphCompiler.read(f));
162             } catch (Exception e) {
163                 throw new IOException("Failed to read compiled transferable graph as dependency: " + f, e);
164             }
165         }
166
167
168         Simantics.sync(new ReadRequest() {
169             @Override
170             public void run(ReadGraph graph) throws DatabaseException {
171                 Layer0 L0 = Layer0.getInstance(graph);
172                 for (Resource file : graph.syncRequest(new ObjectsWithType(r, L0.ConsistsOf, L0.PGraph))) {
173                     String src = graph.getRelatedValue(file, L0.PGraph_definition, Bindings.STRING);
174                     sources.add(new StringSource(src));
175                 }
176             }
177         });
178
179         GraphCompilerPreferences prefs = new GraphCompilerPreferences();
180         prefs.validate = true;
181         prefs.validateRelationRestrictions = ValidationMode.ERROR;
182         prefs.validateResourceHasType = ValidationMode.IGNORE;
183         final CompilationResult result = Simantics.sync(new UniqueRead<CompilationResult>() {
184             @Override
185             public CompilationResult perform(ReadGraph graph) throws DatabaseException {
186                 final Resource root = graph.syncRequest(new IndexRoot(r));
187                 final String baseURI = graph.getURI(root);
188
189                 GraphFileResource GF = GraphFileResource.getInstance(graph);
190                 ExternalFileLoader fileLoader = fileName -> {
191                     try {
192                         Resource file = graph.getResource(baseURI + "/" + fileName);
193                         return graph.getRelatedValue(file, GF.HasFiledata, Bindings.BYTE_ARRAY);
194                     } catch (DatabaseException e) {
195                         throw new IOException(e);
196                     }
197                 };
198
199                 return GraphCompiler.compile("1.1", sources, dependencies, fileLoader, prefs);
200             }
201         });
202
203         if (!result.getErrors().isEmpty() || !result.getWarnings().isEmpty()) {
204             if (userAgent != null) {
205                 userAgent.reportProblems(result);
206                 return;
207             } else {
208                 StringBuilder error = new StringBuilder();
209                 for (Problem problem : result.getErrors())
210                     error.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
211                 for (Problem problem : result.getWarnings())
212                     error.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
213                 throw new DatabaseException(error.toString());
214             }
215         }
216
217         final Pair<TransferableGraph1, long[]> existing = Simantics.sync(new UniqueRead<Pair<TransferableGraph1, long[]>>() {
218             @Override
219             public Pair<TransferableGraph1, long[]> perform(ReadGraph graph) throws DatabaseException {
220                 Layer0 L0 = Layer0.getInstance(graph);
221                 TransferableGraph1 tg = graph.getPossibleRelatedValue(r, L0.SharedOntology_tg, Bindings.getBindingUnchecked( TransferableGraph1.class ));
222                 if(tg == null) return null;
223                 long[] tgResources = graph.getPossibleRelatedValue(r, L0.SharedOntology_tgResources, Bindings.LONG_ARRAY);
224                 if(tgResources == null) return null;
225                 return Pair.make(tg,  tgResources);
226             }
227         });
228
229         if (existing != null) {
230             Simantics.sync(new WriteRequest() {
231                 @Override
232                 public void perform(WriteGraph graph) throws DatabaseException {
233                     TransferableGraphDelta1 delta = new Diff(existing.first, result.getGraph()).diff();
234                     long[] resourceArray = TransferableGraphs.applyDelta(graph, existing.second, delta);
235
236                     Layer0 L0 = Layer0.getInstance(graph);
237                     graph.claimLiteral(r, L0.SharedOntology_tg, result.getGraph(), Bindings.getBindingUnchecked( TransferableGraph1.class ));
238                     graph.claimLiteral(r, L0.SharedOntology_tgResources, L0.ResourceIdArray, resourceArray, Bindings.LONG_ARRAY);
239
240                     Layer0Utils.addCommentMetadata(graph, "Compiled ontology " + graph.getURI(r));
241                 }
242             });
243         } else {
244             final DefaultPasteImportAdvisor advisor = new DefaultPasteImportAdvisor(r);
245             try {
246                 DefaultPasteHandler.defaultExecute(result.getGraph(), r, advisor);
247             } catch (TransferableGraphException e) {
248                 // TODO: defaultExecute never actually throws this exception!
249                 throw new DatabaseException(e);
250             }
251
252             Simantics.sync(new WriteRequest() {
253                 @Override
254                 public void perform(WriteGraph graph) throws DatabaseException {
255                     Layer0 L0 = Layer0.getInstance(graph);
256                     graph.claimLiteral(r, L0.SharedOntology_tg, result.getGraph(), Bindings.getBindingUnchecked( TransferableGraph1.class ));
257                     graph.claimLiteral(r, L0.SharedOntology_tgResources, L0.ResourceIdArray, advisor.getResourceIds(), Bindings.LONG_ARRAY);
258
259                     Layer0Utils.addCommentMetadata(graph, "Compiled ontology " + graph.getURI(r));
260                 }
261             });
262         }
263         
264         // Delete index to get rid of floating old instances of the same ontology
265 //        DatabaseIndexing.deleteAllIndexes();
266     }
267
268     private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
269         String tmpDirStr = System.getProperty("java.io.tmpdir");
270         if (tmpDirStr == null)
271             throw new NullPointerException("java.io.tmpdir property is null");
272         File tmpDir = new File(tmpDirStr);
273         File libFile = new File(tmpDir, libName);
274         return FileUtils.copyResource(libURL, libFile, false);
275     }
276
277     private static File url2file(URL url, String fileName) {
278         if ("file".equals(url.getProtocol())) {
279             try {
280                 File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
281                 return path;
282             } catch (UnsupportedEncodingException e) {
283                 Logger.defaultLogError(e);
284             }
285         } else if ("jar".equals(url.getProtocol())) {
286             try {
287                 File libFile = extractLib(url, fileName);
288                 return libFile;
289             } catch (FileNotFoundException e) {
290                 Logger.defaultLogError(e);
291             } catch (IOException e) {
292                 Logger.defaultLogError(e);
293             }
294         } else {
295             LOGGER.warn("Unsupported URL protocol");
296         }   
297         return null;
298     }
299
300     private static class StringSource implements ISource {
301         private String src;
302         private ByteArrayInputStream baos;
303
304         public StringSource(String s) {
305             this.src = s;
306             this.baos = new ByteArrayInputStream(src.getBytes());
307         }
308
309         @Override
310         public int startPos() {
311             return 0;
312         }
313
314         @Override
315         public int startLine() {
316             return 0;
317         }
318
319         @Override
320         public InputStream open() throws IOException {
321             return baos;
322         }
323
324         @Override
325         public int length() throws IOException {
326             return src.length();
327         }
328
329         @Override
330         public String getName() {
331             return "Source";
332         }
333     }
334
335 }