package org.simantics.scl.osgi.internal; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import org.eclipse.core.runtime.FileLocator; import org.osgi.framework.Bundle; import org.osgi.framework.wiring.BundleWiring; import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidatorFactory; import org.simantics.scl.compiler.module.ImportDeclaration; import org.simantics.scl.compiler.module.repository.UpdateListener; import org.simantics.scl.compiler.source.EncodedTextualModuleSource; import org.simantics.scl.compiler.types.Type; import gnu.trove.set.hash.THashSet; public class BundleModuleSource extends EncodedTextualModuleSource implements UpdateListener.Observable { public static final ImportDeclaration[] DEFAULT_IMPORTS = new ImportDeclaration[] { new ImportDeclaration("Builtin", ""), new ImportDeclaration("StandardLibrary", "") }; public static final ImportDeclaration[] DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY = new ImportDeclaration[] { new ImportDeclaration("Builtin", ""), }; public final Bundle bundle; public final URL url; private byte[] digest; private THashSet listeners; public BundleModuleSource(String moduleName, Bundle bundle, URL url) { super(moduleName); this.bundle = bundle; this.url = url; } @Override public void removeListener(UpdateListener listener) { if(listeners != null) synchronized(listeners) { listeners.remove(listener); } } @Override public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { if(bundle.getSymbolicName().equals("org.simantics.scl.runtime")) return DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY; else return DEFAULT_IMPORTS; } private byte[] computeDigest() { try { InputStream stream = url.openStream(); try { MessageDigest digest = MessageDigest.getInstance("SHA1"); byte[] buffer = new byte[1024]; while(true) { int count = stream.read(buffer); if(count <= 0) break; digest.update(buffer, 0, count); } return digest.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return new byte[0]; } finally { stream.close(); } } catch(IOException e) { e.printStackTrace(); return new byte[0]; } } @Override protected InputStream getSourceStream(UpdateListener listener) throws IOException { if(digest == null) digest = computeDigest(); if(listener != null) { if(listeners == null) listeners = new THashSet(4); listeners.add(listener); listener.addObservable(this); } return url.openStream(); } @Override public ClassLoader getClassLoader() { if(bundle.getSymbolicName().equals("org.simantics.scl.runtime")) return Type.class.getClassLoader(); else { BundleWiring wiring = bundle.adapt(BundleWiring.class); if(wiring != null) return wiring.getClassLoader(); else return getClass().getClassLoader(); } } public void checkUpdates() { if(digest != null && listeners != null) { byte[] newDigest = computeDigest(); if(!Arrays.equals(digest, newDigest)) { digest = newDigest; THashSet oldListeners = listeners; listeners = null; for(UpdateListener listener : oldListeners) listener.notifyAboutUpdate(); } } } /* * This code is a copy from org.simantics.utils.ui.BundleUtils */ public static File resolveWritableBundleFile(URL url) throws IOException { // This returns file, jar, http etc. - essentially resolves the bundle protocol URL resolved = FileLocator.resolve(url); if (resolved.getProtocol().equals("file")) { return new File(resolved.getPath()); } return null; } private Path getPath() throws IOException { File file = resolveWritableBundleFile(url); return file != null ? file.toPath() : null; } @Override public boolean isUpdateable() { try { Path path = getPath(); if(path == null) return false; return Files.exists(path); } catch (IOException e) { return false; } } @Override public void update(String newSourceText) { try { Path path = getPath(); if(path == null) return; Files.write(path, newSourceText.getBytes(Charset.forName("UTF-8"))); } catch(IOException e) { e.printStackTrace(); } checkUpdates(); } public void clear() { if (listeners != null) { listeners.clear(); listeners = null; } } public JavaReferenceValidatorFactory getJavaReferenceValidatorFactory() { return new OsgiJavaReferenceValidatorFactory(bundle); } }