af6af0872163a8e3ec6be21461becc5c90199c68
[simantics/platform.git] / bundles / org.simantics.scl.osgi / src / org / simantics / scl / osgi / internal / BundleModuleSource.java
1 package org.simantics.scl.osgi.internal;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URL;
7 import java.nio.charset.Charset;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.security.MessageDigest;
11 import java.security.NoSuchAlgorithmException;
12 import java.util.Arrays;
13
14 import org.eclipse.core.runtime.FileLocator;
15 import org.osgi.framework.Bundle;
16 import org.osgi.framework.wiring.BundleWiring;
17 import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidatorFactory;
18 import org.simantics.scl.compiler.module.ImportDeclaration;
19 import org.simantics.scl.compiler.module.repository.UpdateListener;
20 import org.simantics.scl.compiler.source.EncodedTextualModuleSource;
21 import org.simantics.scl.compiler.types.Type;
22
23 import gnu.trove.set.hash.THashSet;
24
25 public class BundleModuleSource extends EncodedTextualModuleSource implements UpdateListener.Observable {
26
27     public static final ImportDeclaration[] DEFAULT_IMPORTS = new ImportDeclaration[] {
28         new ImportDeclaration("Builtin", ""),
29         new ImportDeclaration("StandardLibrary", "")
30     };
31     
32     public static final ImportDeclaration[] DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY = new ImportDeclaration[] {
33         new ImportDeclaration("Builtin", ""),
34     };
35     
36     public final Bundle bundle;
37     public final URL url;
38     
39     private byte[] digest;
40     private THashSet<UpdateListener> listeners;
41     
42     public BundleModuleSource(String moduleName, Bundle bundle, URL url) {
43         super(moduleName);
44         this.bundle = bundle;
45         this.url = url;
46     }
47     
48     @Override
49     public void removeListener(UpdateListener listener) {
50         if(listeners != null)
51             synchronized(listeners) {
52                 listeners.remove(listener);
53             }
54     }
55
56     @Override
57     public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
58         if(bundle.getSymbolicName().equals("org.simantics.scl.runtime"))
59             return DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY;
60         else
61             return DEFAULT_IMPORTS;
62     }
63     
64     private byte[] computeDigest() {
65         try {
66             InputStream stream = url.openStream();
67             try {
68                 MessageDigest digest = MessageDigest.getInstance("SHA1");
69                 byte[] buffer = new byte[1024];
70                 while(true) {
71                     int count = stream.read(buffer);
72                     if(count <= 0)
73                         break;
74                     digest.update(buffer, 0, count);
75                 }
76                 return digest.digest();
77             } catch (NoSuchAlgorithmException e) {
78                 e.printStackTrace();
79                 return new byte[0];
80             } finally {
81                 stream.close();
82             }
83         } catch(IOException e) {
84             e.printStackTrace();
85             return new byte[0];
86         }
87     }
88     
89     @Override
90     protected InputStream getSourceStream(UpdateListener listener)
91             throws IOException {
92         if(digest == null)
93             digest = computeDigest();
94         if(listener != null) {
95             if(listeners == null)
96                 listeners = new THashSet<UpdateListener>(4);
97             listeners.add(listener);
98             listener.addObservable(this);
99         }
100         return url.openStream();
101     }
102     
103     @Override
104     public ClassLoader getClassLoader() {
105         if(bundle.getSymbolicName().equals("org.simantics.scl.runtime"))
106             return Type.class.getClassLoader();
107         else {
108             BundleWiring wiring = bundle.adapt(BundleWiring.class);
109             if(wiring != null)
110                 return wiring.getClassLoader();
111             else
112                 return getClass().getClassLoader();
113         }
114     }
115
116     public void checkUpdates() {
117         if(digest != null && listeners != null) {
118             byte[] newDigest = computeDigest();
119             if(!Arrays.equals(digest, newDigest)) {
120                 digest = newDigest;
121                 THashSet<UpdateListener> oldListeners = listeners;
122                 listeners = null;
123                 for(UpdateListener listener : oldListeners)
124                     listener.notifyAboutUpdate();
125             }
126         }
127     }
128
129     /*
130      * This code is a copy from org.simantics.utils.ui.BundleUtils
131      */
132     public static File resolveWritableBundleFile(URL url) throws IOException {
133         // This returns file, jar, http etc. - essentially resolves the bundle protocol
134         URL resolved = FileLocator.resolve(url);
135         if (resolved.getProtocol().equals("file")) {
136             return new File(resolved.getPath());
137         }
138         return null;
139     }
140
141     private Path getPath() throws IOException {
142         File file = resolveWritableBundleFile(url);
143         return file != null ? file.toPath() : null;
144     }
145
146     @Override
147     public boolean isUpdateable() {
148         try {
149             return Files.exists(getPath());
150         } catch (IOException e) {
151             return false;
152         }
153     }
154     
155     @Override
156     public void update(String newSourceText) {
157         try {
158             Path path = getPath();
159             Files.write(path, newSourceText.getBytes(Charset.forName("UTF-8")));
160         } catch(IOException e) {
161             e.printStackTrace();
162         }
163         checkUpdates();
164     }
165
166     public void clear() {
167         if (listeners != null) {
168             listeners.clear();
169             listeners = null;
170         }
171     }
172
173     public JavaReferenceValidatorFactory getJavaReferenceValidatorFactory() {
174         return new OsgiJavaReferenceValidatorFactory(bundle);
175     }
176 }