package org.simantics.scl.osgi.internal; import java.net.URL; import java.util.ArrayList; import java.util.Collection; 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.ModuleSourceRepository; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectObjectProcedure; import gnu.trove.set.hash.THashSet; @Component public class BundleModuleSourceRepository implements ModuleSourceRepository { Tracker tracker; THashMap modules = new THashMap(); THashMap> modulesPerBundle = new THashMap>(); THashMap documentations = new THashMap(); THashMap> documentationsPerBundle = new THashMap>(); class Tracker extends BundleTracker { public Tracker(BundleContext context) { super(context, 0xffffffff, null); } @Override public Bundle addingBundle(Bundle bundle, BundleEvent event) { synchronized(BundleModuleSourceRepository.this) { Enumeration moduleEntries = bundle.findEntries("scl", "*.scl", true); if(moduleEntries != null) { ArrayList modulesInThisBundle = new ArrayList(); 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(0)); Enumeration documentationEntries = bundle.findEntries("scl", "*.md", true); if(documentationEntries != null) { ArrayList documentationsInThisBundle = new ArrayList(); 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(0)); return bundle; } } @Override public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) { synchronized(BundleModuleSourceRepository.this) { ArrayList moduleList = modulesPerBundle.get(bundle); if(moduleList != null) for(String moduleName : moduleList) modules.remove(moduleName); ArrayList 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 public synchronized Collection getModuleNames() { return new ArrayList(modules.keySet()); } @Override public synchronized Collection getDocumentationNames() { return new ArrayList(documentations.keySet()); } @Override synchronized public String getDocumentation(String documentationName) { BundleDocumentationSource source = documentations.get(documentationName); if(source == null) return null; else return source.getText(); } @Override public void checkUpdates() { synchronized(this) { modulesPerBundle.forEachEntry(new TObjectObjectProcedure>() { @Override public boolean execute(Bundle bundle, ArrayList oldModules) { Enumeration moduleEntries = bundle.findEntries("scl", "*.scl", true); if(moduleEntries != null) { THashSet oldModuleSet = new THashSet(oldModules); ArrayList modulesInThisBundle = new ArrayList(); 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(0)); } return true; } }); documentationsPerBundle.forEachEntry(new TObjectObjectProcedure>() { @Override public boolean execute(Bundle bundle, ArrayList oldDocumentations) { Enumeration documentationEntries = bundle.findEntries("scl", "*.md", true); if(documentationEntries != null) { THashSet oldDocSet = new THashSet(oldDocumentations); ArrayList documentationsInThisBundle = new ArrayList(); 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(0)); } return true; } }); } for(BundleModuleSource source : modules.values()) source.checkUpdates(); } @Override public void clear() { for (BundleModuleSource source : modules.values()) { source.clear(); } } }