package org.simantics.scl.osgi.internal; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; 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.module.ImportDeclaration; import org.simantics.scl.compiler.module.repository.UpdateListener; import org.simantics.scl.compiler.source.EncodedTextualModuleSource; import org.simantics.scl.compiler.types.Type; public class BundleModuleSource extends EncodedTextualModuleSource { 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 ArrayList listeners; public BundleModuleSource(String moduleName, Bundle bundle, URL url) { super(moduleName); this.bundle = bundle; this.url = url; } @Override protected 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 ArrayList(2); listeners.add(listener); } 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; ArrayList oldListeners = listeners; listeners = null; for(UpdateListener listener : oldListeners) listener.notifyAboutUpdate(); } } } private Path getPath() throws IOException { try { return Paths.get(FileLocator.toFileURL(url).toURI()); } catch (URISyntaxException e) { throw new IOException(e); } } @Override public boolean isUpdateable() { try { return Files.exists(getPath()); } catch (IOException e) { return false; } } @Override public void update(String newSourceText) { try { Path path = getPath(); 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; } } }