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