--- /dev/null
+package org.simantics.scl.osgi.internal;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.util.tracker.BundleTracker;
+import org.simantics.scl.compiler.module.repository.UpdateListener;
+import org.simantics.scl.compiler.source.ModuleSource;
+import org.simantics.scl.compiler.source.repository.AbstractModuleSourceRepository;
+import org.simantics.scl.compiler.source.repository.ModuleSourceRepository;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.procedure.TObjectObjectProcedure;
+import gnu.trove.procedure.TObjectProcedure;
+import gnu.trove.set.hash.THashSet;
+
+@Component
+public class BundleModuleSourceRepository extends AbstractModuleSourceRepository implements ModuleSourceRepository {
+
+ Tracker tracker;
+ THashMap<String, BundleModuleSource> modules = new THashMap<String, BundleModuleSource>();
+ THashMap<Bundle, ArrayList<String>> modulesPerBundle = new THashMap<Bundle, ArrayList<String>>();
+ THashMap<String, BundleDocumentationSource> documentations = new THashMap<String, BundleDocumentationSource>();
+ THashMap<Bundle, ArrayList<String>> documentationsPerBundle = new THashMap<Bundle, ArrayList<String>>();
+
+ class Tracker extends BundleTracker<Bundle> {
+ public Tracker(BundleContext context) {
+ super(context, 0xffffffff, null);
+ }
+
+ @Override
+ public Bundle addingBundle(Bundle bundle, BundleEvent event) {
+ synchronized(BundleModuleSourceRepository.this) {
+ Enumeration<URL> moduleEntries = bundle.findEntries("scl", "*.scl", true);
+ if(moduleEntries != null) {
+ ArrayList<String> modulesInThisBundle = new ArrayList<String>();
+ while(moduleEntries.hasMoreElements()) {
+ URL url = moduleEntries.nextElement();
+ String path = url.getPath();
+ String moduleName = path.substring(5, path.length()-4);
+ modules.put(moduleName, new BundleModuleSource(moduleName, bundle, url));
+ modulesInThisBundle.add(moduleName);
+ }
+ modulesPerBundle.put(bundle, modulesInThisBundle);
+ }
+ else
+ modulesPerBundle.put(bundle, new ArrayList<String>(0));
+
+ Enumeration<URL> documentationEntries = bundle.findEntries("scl", "*.md", true);
+ if(documentationEntries != null) {
+ ArrayList<String> documentationsInThisBundle = new ArrayList<String>();
+ while(documentationEntries.hasMoreElements()) {
+ URL url = documentationEntries.nextElement();
+ String path = url.getPath();
+ String documentationName = path.substring(5, path.length()-3);
+ documentations.put(documentationName, new BundleDocumentationSource(documentationName, bundle, url));
+ documentationsInThisBundle.add(documentationName);
+ }
+ documentationsPerBundle.put(bundle, documentationsInThisBundle);
+ }
+ else
+ documentationsPerBundle.put(bundle, new ArrayList<String>(0));
+
+ return bundle;
+ }
+ }
+
+ @Override
+ public void removedBundle(Bundle bundle, BundleEvent event,
+ Bundle object) {
+ synchronized(BundleModuleSourceRepository.this) {
+ ArrayList<String> moduleList = modulesPerBundle.get(bundle);
+ if(moduleList != null)
+ for(String moduleName : moduleList)
+ modules.remove(moduleName);
+ ArrayList<String> documentationsList = documentationsPerBundle.get(bundle);
+ if(documentationsList != null)
+ for(String documentation : documentationsList)
+ documentations.remove(documentation);
+ }
+ }
+ };
+
+ @Activate
+ public void activate(ComponentContext context) {
+ tracker = new Tracker(context.getBundleContext());
+ tracker.open();
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ tracker.close();
+ }
+
+ @Override
+ synchronized public ModuleSource getModuleSource(String moduleName,
+ UpdateListener listener) {
+ return modules.get(moduleName);
+ }
+
+ @Override
+ synchronized public void forAllModules(TObjectProcedure<String> procedure) {
+ modules.forEachKey(procedure);
+ }
+
+ @Override
+ synchronized public String getDocumentation(String documentationName) {
+ BundleDocumentationSource source = documentations.get(documentationName);
+ if(source == null)
+ return null;
+ else
+ return source.getText();
+ }
+
+ @Override
+ synchronized public void forAllDocumentations(TObjectProcedure<String> procedure) {
+ documentations.forEachKey(procedure);
+ }
+
+ @Override
+ public void checkUpdates() {
+ synchronized(this) {
+ modulesPerBundle.forEachEntry(new TObjectObjectProcedure<Bundle, ArrayList<String>>() {
+ @Override
+ public boolean execute(Bundle bundle, ArrayList<String> oldModules) {
+ Enumeration<URL> moduleEntries = bundle.findEntries("scl", "*.scl", true);
+ if(moduleEntries != null) {
+ THashSet<String> oldModuleSet = new THashSet<String>(oldModules);
+ ArrayList<String> modulesInThisBundle = new ArrayList<String>();
+ while(moduleEntries.hasMoreElements()) {
+ URL url = moduleEntries.nextElement();
+ String path = url.getPath();
+ String moduleName = path.substring(5, path.length()-4);
+ if(!oldModuleSet.remove(moduleName))
+ modules.put(moduleName, new BundleModuleSource(moduleName, bundle, url));
+ modulesInThisBundle.add(moduleName);
+ }
+ for(String oldModule : oldModuleSet)
+ modules.remove(oldModule);
+ modulesPerBundle.put(bundle, modulesInThisBundle);
+ }
+ else {
+ for(String oldModule : oldModules)
+ modules.remove(oldModule);
+ modulesPerBundle.put(bundle, new ArrayList<String>(0));
+ }
+ return true;
+ }
+ });
+ documentationsPerBundle.forEachEntry(new TObjectObjectProcedure<Bundle, ArrayList<String>>() {
+ @Override
+ public boolean execute(Bundle bundle, ArrayList<String> oldDocumentations) {
+ Enumeration<URL> documentationEntries = bundle.findEntries("scl", "*.md", true);
+ if(documentationEntries != null) {
+ THashSet<String> oldDocSet = new THashSet<String>(oldDocumentations);
+ ArrayList<String> documentationsInThisBundle = new ArrayList<String>();
+ while(documentationEntries.hasMoreElements()) {
+ URL url = documentationEntries.nextElement();
+ String path = url.getPath();
+ String documentationName = path.substring(5, path.length()-3);
+ if(!oldDocSet.remove(documentationName))
+ documentations.put(documentationName, new BundleDocumentationSource(documentationName, bundle, url));
+ documentationsInThisBundle.add(documentationName);
+ }
+ for(String oldDocumentation : oldDocSet)
+ documentations.remove(oldDocumentation);
+ documentationsPerBundle.put(bundle, documentationsInThisBundle);
+ }
+ else {
+ for(String oldDocumentation : oldDocumentations)
+ documentations.remove(oldDocumentation);
+ documentationsPerBundle.put(bundle, new ArrayList<String>(0));
+ }
+ return true;
+ }
+ });
+ }
+ for(BundleModuleSource source : modules.values())
+ source.checkUpdates();
+ }
+
+ @Override
+ public void clear() {
+ for (BundleModuleSource source : modules.values()) {
+ source.clear();
+ }
+ }
+
+}