package org.simantics.db.layer0.util;
-import java.util.HashMap;
-import java.util.Map;
-
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
import org.simantics.db.common.request.ParametrizedPrimitiveRead;
protected void fillEnvironmentSpecification(EnvironmentSpecification environmentSpecification) {
}
- static class UpdateListenerImpl implements UpdateListener {
+ static class UpdateListenerImpl extends UpdateListener {
final EnvironmentSpecification environmentSpecification;
final Listener<RuntimeEnvironment> callback;
@Override
public void notifyAboutUpdate() {
if(callback.isDisposed()) {
+ stopListening();
return;
}
getRuntimeEnvironment(environmentSpecification, callback, this);
}
+ };
- final public static void getRuntimeEnvironment(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback, UpdateListenerImpl listener) {
-
- try {
-
- SCLContext context = SCLContext.getCurrent();
-
- RuntimeEnvironment env;
- Object graph = context.get("graph");
- if(graph == null)
- try {
- env = SimanticsInternal.getSession().syncRequest(new Read<RuntimeEnvironment>() {
- @Override
- public RuntimeEnvironment perform(ReadGraph graph) throws DatabaseException {
-
- SCLContext sclContext = SCLContext.getCurrent();
- Object oldGraph = sclContext.get("graph");
- try {
- sclContext.put("graph", graph);
- return SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
- environmentSpecification,
- callback.getClass().getClassLoader(), listener);
- } catch (ImportFailureException e) {
- throw new DatabaseException(e);
- } catch (Throwable t) {
- throw new DatabaseException(t);
- } finally {
- sclContext.put("graph", oldGraph);
- }
+ public static void getRuntimeEnvironment(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback, UpdateListenerImpl listener) {
+
+ try {
+
+ SCLContext context = SCLContext.getCurrent();
+
+ RuntimeEnvironment env;
+ Object graph = context.get("graph");
+ if(graph == null)
+ try {
+ env = SimanticsInternal.getSession().syncRequest(new Read<RuntimeEnvironment>() {
+ @Override
+ public RuntimeEnvironment perform(ReadGraph graph) throws DatabaseException {
+
+ SCLContext sclContext = SCLContext.getCurrent();
+ Object oldGraph = sclContext.get("graph");
+ try {
+ sclContext.put("graph", graph);
+ return SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
+ environmentSpecification,
+ callback.getClass().getClassLoader(), listener);
+ } catch (ImportFailureException e) {
+ throw new DatabaseException(e);
+ } catch (Throwable t) {
+ throw new DatabaseException(t);
+ } finally {
+ sclContext.put("graph", oldGraph);
}
- });
- } catch (DatabaseException e) {
- callback.exception(e);
- return;
- }
- else
- env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
- environmentSpecification,
- callback.getClass().getClassLoader(), listener);
- callback.execute(env);
- } catch (ImportFailureException e) {
- callback.exception(new DatabaseException(e));
- }
-
- }
-
- };
+ }
+ });
+ } catch (DatabaseException e) {
+ callback.exception(e);
+ return;
+ }
+ else
+ env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
+ environmentSpecification,
+ callback.getClass().getClassLoader(), listener);
+ callback.execute(env);
+ } catch (ImportFailureException e) {
+ callback.exception(new DatabaseException(e));
+ }
- // This is needed to prevent garbage collection from collecting UpdateListenerImpls
- // -ModuleRepository only makes a weak reference to the listener
- final static Map<String, UpdateListenerImpl> map = new HashMap<String, UpdateListenerImpl>();
+ }
@Override
public RuntimeEnvironment perform(ReadGraph graph)
throws DatabaseException {
final EnvironmentSpecification environmentSpecification = EnvironmentSpecification.of(
"Builtin", "",
- "Prelude", "",
+ "StandardLibrary", "",
"Simantics/All", "");
fillEnvironmentSpecification(environmentSpecification);
Resource mainModule = Layer0Utils.getPossibleChild(graph, parameter, "SCLMain");
mainModuleUri = graph.getURI(parameter) + "/#"; // Add something dummy to the model uri that cannot be in a real URI
return graph.syncRequest(new ParametrizedPrimitiveRead<String, RuntimeEnvironment>(mainModuleUri) {
-
+
+ UpdateListenerImpl sclListener;
+
@Override
public void register(ReadGraph graph, Listener<RuntimeEnvironment> procedure) {
try {
if(procedure.isDisposed()) {
- UpdateListenerImpl.getRuntimeEnvironment(environmentSpecification, procedure, null);
+ getRuntimeEnvironment(environmentSpecification, procedure, null);
} else {
- UpdateListenerImpl impl = new UpdateListenerImpl(environmentSpecification, procedure);
- impl.notifyAboutUpdate();
- map.put(parameter, impl);
- }
+ sclListener = new UpdateListenerImpl(environmentSpecification, procedure);
+ sclListener.notifyAboutUpdate();
+ }
} finally {
context.put("graph", oldGraph);
@Override
public void unregistered() {
- map.remove(parameter);
+ if(sclListener != null)
+ sclListener.stopListening();
}
});
return 31*getClass().hashCode() + super.hashCode();
}
- public static void flush() {
- map.clear();
- }
-
}
protected void fillEnvironmentSpecification(EnvironmentSpecification environmentSpecification) {
}
- static class UpdateListenerImpl implements UpdateListener {
-
- final EnvironmentSpecification environmentSpecification;
- final Listener<RuntimeEnvironment> callback;
-
- UpdateListenerImpl(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback) {
- this.environmentSpecification = environmentSpecification;
- this.callback = callback;
- }
+ static class UpdateListenerImpl extends UpdateListener {
+
+ final EnvironmentSpecification environmentSpecification;
+ final Listener<RuntimeEnvironment> callback;
+
+ UpdateListenerImpl(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback) {
+ this.environmentSpecification = environmentSpecification;
+ this.callback = callback;
+ }
@Override
public void notifyAboutUpdate() {
- if(callback.isDisposed()) {
- return;
- }
- getRuntimeEnvironment(environmentSpecification, callback, this);
+ if(callback.isDisposed()) {
+ stopListening();
+ return;
+ }
+ getRuntimeEnvironment(environmentSpecification, callback, this);
+ }
+ };
+
+ final public static void getRuntimeEnvironment(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback, UpdateListenerImpl listener) {
+
+ try {
+
+ SCLContext context = SCLContext.getCurrent();
+
+ RuntimeEnvironment env;
+ Object graph = context.get("graph");
+ if(graph == null)
+ try {
+ env = SimanticsInternal.getSession().syncRequest(new Read<RuntimeEnvironment>() {
+ @Override
+ public RuntimeEnvironment perform(ReadGraph graph) throws DatabaseException {
+
+ SCLContext sclContext = SCLContext.getCurrent();
+ Object oldGraph = sclContext.get("graph");
+ try {
+ sclContext.put("graph", graph);
+ return SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
+ environmentSpecification,
+ callback.getClass().getClassLoader(), listener);
+ } catch (ImportFailureException e) {
+ throw new DatabaseException(e);
+ } catch (Throwable t) {
+ throw new DatabaseException(t);
+ } finally {
+ sclContext.put("graph", oldGraph);
+ }
+ }
+ });
+ } catch (DatabaseException e) {
+ callback.exception(e);
+ return;
+ }
+ else
+ env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
+ environmentSpecification,
+ callback.getClass().getClassLoader(), listener);
+ callback.execute(env);
+ } catch (ImportFailureException e) {
+ callback.exception(new DatabaseException(e));
}
- final public static void getRuntimeEnvironment(EnvironmentSpecification environmentSpecification, Listener<RuntimeEnvironment> callback, UpdateListenerImpl listener) {
+ }
- try {
-
- SCLContext context = SCLContext.getCurrent();
-
- RuntimeEnvironment env;
- Object graph = context.get("graph");
- if(graph == null)
- try {
- env = SimanticsInternal.getSession().syncRequest(new Read<RuntimeEnvironment>() {
- @Override
- public RuntimeEnvironment perform(ReadGraph graph) throws DatabaseException {
-
- SCLContext sclContext = SCLContext.getCurrent();
- Object oldGraph = sclContext.get("graph");
- try {
- sclContext.put("graph", graph);
- return SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
- environmentSpecification,
- callback.getClass().getClassLoader(), listener);
- } catch (ImportFailureException e) {
- throw new DatabaseException(e);
- } catch (Throwable t) {
- throw new DatabaseException(t);
- } finally {
- sclContext.put("graph", oldGraph);
- }
- }
- });
- } catch (DatabaseException e) {
- callback.exception(e);
- return;
- }
- else
- env = SCLOsgi.MODULE_REPOSITORY.createRuntimeEnvironment(
- environmentSpecification,
- callback.getClass().getClassLoader(), listener);
- callback.execute(env);
- } catch (ImportFailureException e) {
- callback.exception(new DatabaseException(e));
- }
-
- }
-
- };
-
- // This is needed to prevent garbage collection from collecting UpdateListenerImpls
- // -ModuleRepository only makes a weak reference to the listener
- final static Map<EnvironmentSpecification, UpdateListenerImpl> map = new HashMap<>();
-
@Override
public RuntimeEnvironment perform(ReadGraph graph)
throws DatabaseException {
final EnvironmentSpecification environmentSpecification = EnvironmentSpecification.of(
"Builtin", "",
- "Prelude", "",
+ "StandardLibrary", "",
"Simantics/All", "");
fillEnvironmentSpecification(environmentSpecification);
-
+
Layer0 L0 = Layer0.getInstance(graph);
Collection<Resource> sclModules = graph.syncRequest(new ObjectsWithType(parameter, L0.ConsistsOf, L0.SCLModule));
for (Resource sclModule : sclModules)
environmentSpecification.importModule(graph.getURI(sclModule), "");
-
+
Resource mainModule = Layer0Utils.getPossibleChild(graph, parameter2, "SCLMain");
if(mainModule != null)
environmentSpecification.importModule(graph.getURI(mainModule), "");
-
+
return graph.syncRequest(new ParametrizedPrimitiveRead<EnvironmentSpecification, RuntimeEnvironment>(environmentSpecification) {
-
- @Override
- public void register(ReadGraph graph, Listener<RuntimeEnvironment> procedure) {
-
- SCLContext context = SCLContext.getCurrent();
- Object oldGraph = context.put("graph", graph);
- try {
-
- if(procedure.isDisposed()) {
- UpdateListenerImpl.getRuntimeEnvironment(parameter, procedure, null);
- } else {
- UpdateListenerImpl impl = new UpdateListenerImpl(parameter, procedure);
- impl.notifyAboutUpdate();
- map.put(parameter, impl);
- }
-
- } finally {
- context.put("graph", oldGraph);
- }
-
- }
-
+ UpdateListenerImpl sclListener;
+ @Override
+ public void register(ReadGraph graph, Listener<RuntimeEnvironment> procedure) {
+
+ SCLContext context = SCLContext.getCurrent();
+ Object oldGraph = context.put("graph", graph);
+ try {
+
+ if(procedure.isDisposed()) {
+ getRuntimeEnvironment(parameter, procedure, null);
+ } else {
+ sclListener = new UpdateListenerImpl(parameter, procedure);
+ sclListener.notifyAboutUpdate();
+ }
+
+ } finally {
+ context.put("graph", oldGraph);
+ }
+
+ }
+
@Override
public void unregistered() {
- map.remove(parameter);
+ if(sclListener != null)
+ sclListener.stopListening();
}
-
+
});
}
-
+
@Override
public int hashCode() {
return 31*getClass().hashCode() + super.hashCode();
}
- public static void flush() {
- map.clear();
- }
-
}
\ No newline at end of file
package org.simantics.modeling;
-import java.util.ArrayList;
-
import org.simantics.db.ReadGraph;
import org.simantics.db.common.request.ParametrizedPrimitiveRead;
import org.simantics.db.procedure.Listener;
import org.simantics.scl.runtime.SCLContext;
public class ComponentTypeScriptRuntimeEnvironmentRequest extends ParametrizedPrimitiveRead<EnvironmentSpecification, RuntimeEnvironment> {
-
- // This array list is only needed to keep strong references to update listeners preventing their garbage collection.
- ArrayList<UpdateListener> listeners;
+ UpdateListener listener;
public ComponentTypeScriptRuntimeEnvironmentRequest(EnvironmentSpecification parameter) {
super(parameter);
@Override
public void register(ReadGraph graph, Listener<RuntimeEnvironment> procedure) {
- UpdateListener listener = null;
- if(!procedure.isDisposed()) {
+ if(!procedure.isDisposed() && listener == null) {
listener = new UpdateListener() {
@Override
public void notifyAboutUpdate() {
createRuntimeEnvironment(graph, procedure, this);
}
};
- if(listeners == null)
- listeners = new ArrayList<UpdateListener>(2);
- listeners.add(listener);
}
createRuntimeEnvironment(graph, procedure, listener);
}
+
+ @Override
+ public void unregistered() {
+ if(listener != null)
+ listener.stopListening();
+ }
private void createRuntimeEnvironment(ReadGraph graph, Listener<RuntimeEnvironment> procedure, UpdateListener listener) {
SCLContext context = SCLContext.getCurrent();
import org.simantics.layer0.Layer0;
import org.simantics.modeling.ModelingUtils;
import org.simantics.scl.compiler.module.repository.UpdateListener;
+import org.simantics.scl.compiler.module.repository.UpdateListener.Observable;
import org.simantics.scl.compiler.source.ModuleSource;
import org.simantics.scl.compiler.source.StringModuleSource;
import org.simantics.scl.compiler.source.repository.ModuleSourceRepository;
import org.simantics.scl.runtime.SCLContext;
-import org.simantics.structural2.utils.StructuralUtils;
import org.simantics.scl.runtime.tuple.Tuple0;
+import org.simantics.structural2.utils.StructuralUtils;
import gnu.trove.procedure.TObjectProcedure;
import gnu.trove.set.hash.THashSet;
}
}
- static class ModuleListener implements SyncListener<ModuleSource> {
+ static class ModuleListener implements SyncListener<ModuleSource>, Observable {
UpdateListener listener;
boolean alreadyExecutedOnce;
final String moduleName;
public ModuleListener(UpdateListener listener, String moduleName) {
this.listener = listener;
+ this.listener.addObservable(this);
this.moduleName = moduleName;
}
@Override
+ public void removeListener(UpdateListener listener) {
+ listener = null;
+ }
+ @Override
public boolean isDisposed() {
return listener == null;
}
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
import org.simantics.scl.compiler.module.ImportDeclaration;
import org.simantics.scl.compiler.module.Module;
import org.simantics.scl.compiler.module.options.ModuleCompilationOptionsAdvisor;
+import org.simantics.scl.compiler.module.repository.UpdateListener.Observable;
import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
import org.simantics.scl.compiler.runtime.RuntimeEnvironmentImpl;
import org.simantics.scl.compiler.runtime.RuntimeModule;
PENDING_MODULES.get().remove(moduleName);
}
- private class ModuleEntry implements UpdateListener {
+ private class ModuleEntry extends UpdateListener implements Observable {
final String moduleName;
- WeakHashMap<UpdateListener,Object> listeners = new WeakHashMap<UpdateListener,Object>();
+ THashSet<UpdateListener> listeners = new THashSet<UpdateListener>();
ModuleSource source;
Failable<Module> compilationResult;
}
synchronized void addListener(UpdateListener listener) {
- if(listener != null)
- listeners.put(listener, null);
+ if(listener == null || listeners == null)
+ return;
+ listeners.add(listener);
+ listener.addObservable(this);
+ }
+
+ public synchronized void removeListener(UpdateListener listener) {
+ if (listeners == null)
+ return;
+ listeners.remove(listener);
}
@Override
public void notifyAboutUpdate() {
- if (listeners == null)
- return;
ArrayList<UpdateListener> externalListeners = new ArrayList<UpdateListener>();
notifyAboutUpdate(externalListeners);
for(UpdateListener listener : externalListeners)
}
synchronized void notifyAboutUpdate(ArrayList<UpdateListener> externalListeners) {
+ stopListening();
+ if (listeners == null)
+ return;
if(moduleCache.get(moduleName) == this) {
moduleCache.remove(moduleName);
if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) {
System.out.println("Invalidate " + moduleName);
- for(UpdateListener l : listeners.keySet())
+ for(UpdateListener l : listeners)
System.out.println(" " + l);
}
- for(UpdateListener l : listeners.keySet())
+ THashSet<UpdateListener> listenersCopy = listeners;
+ listeners = null;
+ for(UpdateListener l : listenersCopy)
+ l.stopListening();
+ for(UpdateListener l : listenersCopy)
if(l instanceof ModuleEntry)
((ModuleEntry)l).notifyAboutUpdate(externalListeners);
- else
+ else {
externalListeners.add(l);
+ }
}
}
return runtimeModule;
}
- public void dispose() {
+ public synchronized void dispose() {
if (listeners != null)
listeners.clear();
listeners = null;
+ stopListening();
source = null;
compilationResult = null;
if (runtimeModule != null) {
public Failable<RuntimeModule> getRuntimeModule(String moduleName) {
return getRuntimeModule(moduleName, null);
}
-
+
private ModuleEntry getModuleEntry(String moduleName, UpdateListener listener) {
/* It is deliberate that the following code does not try to prevent
* simultaneous compilation of the same module. This is because in
THashMap<String, ModuleEntry> result = new THashMap<String, ModuleEntry>();
Collection<ImportFailure> failures = null;
+ THashSet<String> originalImports = new THashSet<String>();
ArrayList<ImportDeclaration> stack = new ArrayList<ImportDeclaration>(imports.length);
- for(ImportDeclaration import_ : imports)
+ for(ImportDeclaration import_ : imports) {
stack.add(import_);
+ originalImports.add(import_.moduleName);
+ }
while(!stack.isEmpty()) {
ImportDeclaration import_ = stack.remove(stack.size()-1);
if(!result.containsKey(import_.moduleName)) {
- ModuleEntry entry = getModuleEntry(import_.moduleName, listener);
+ ModuleEntry entry = getModuleEntry(import_.moduleName, originalImports.contains(import_.moduleName) ? listener : null);
Failable<Module> compilationResult = entry.compilationResult;
if(compilationResult.didSucceed()) {
result.put(import_.moduleName, entry);
package org.simantics.scl.compiler.module.repository;
-public interface UpdateListener {
- void notifyAboutUpdate();
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * Listener that is notified about changes in modules and their dependencies. It is possible
+ * to listen multiple different modules with one listener. When a change happens, the listener
+ * automatically stops listening any other changes. The idea is that the client then asks all modules
+ * again using the listener as a parameter.
+ */
+public abstract class UpdateListener {
+ private final THashSet<Observable> observables = new THashSet<Observable>();
+
+ public interface Observable {
+ void removeListener(UpdateListener listener);
+ }
+
+ public abstract void notifyAboutUpdate();
+
+ /**
+ * Registers an observable to the listener. The client code should never
+ * call this method. It is needed so that it is possible to implement
+ * {@link #stopListening}.
+ */
+ public void addObservable(Observable observable) {
+ synchronized(observables) {
+ observables.add(observable);
+ }
+ }
+
+ /**
+ * Stops listening changes.
+ */
+ public void stopListening() {
+ synchronized(observables) {
+ for(Observable observable : observables)
+ observable.removeListener(this);
+ observables.clear();
+ }
+ }
}
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.simantics.scl.compiler.source.EncodedTextualModuleSource;
import org.simantics.scl.compiler.types.Type;
-public class BundleModuleSource extends EncodedTextualModuleSource {
+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", ""),
public final URL url;
private byte[] digest;
- private ArrayList<UpdateListener> listeners;
+ private THashSet<UpdateListener> 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
protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
digest = computeDigest();
if(listener != null) {
if(listeners == null)
- listeners = new ArrayList<UpdateListener>(2);
+ listeners = new THashSet<UpdateListener>(4);
listeners.add(listener);
+ listener.addObservable(this);
}
return url.openStream();
}
byte[] newDigest = computeDigest();
if(!Arrays.equals(digest, newDigest)) {
digest = newDigest;
- ArrayList<UpdateListener> oldListeners = listeners;
+ THashSet<UpdateListener> oldListeners = listeners;
listeners = null;
for(UpdateListener listener : oldListeners)
listener.notifyAboutUpdate();
@Override
public void disconnect(IDocument document) {
connected = false;
+ updateListener.stopListening();
super.disconnect(document);
}
}
import org.simantics.scl.compiler.module.repository.UpdateListener;
import gnu.trove.map.hash.THashMap;
+import gnu.trove.procedure.TObjectObjectProcedure;
import gnu.trove.procedure.TObjectProcedure;
public class SCLIssuesContentProvider implements IStructuredContentProvider {
}
private UpdateListener getUpdateListener(String moduleName) {
- UpdateListener listener = updateListeners.get(moduleName);
- if(listener == null) {
- listener = new UpdateListener() {
- @Override
- public void notifyAboutUpdate() {
- if(!disposed)
- listenModule(moduleName);
- }
- };
- updateListeners.put(moduleName, listener);
+ UpdateListener listener;
+ synchronized(updateListeners) {
+ listener = updateListeners.get(moduleName);
+ if(listener == null) {
+ listener = new UpdateListener() {
+ @Override
+ public void notifyAboutUpdate() {
+ if(!disposed)
+ listenModule(moduleName);
+ }
+ };
+ updateListeners.put(moduleName, listener);
+ }
}
return listener;
}
@Override
public void dispose() {
+ if(this.disposed)
+ return;
this.disposed = true;
+ if(repository != null)
+ synchronized(updateListeners) {
+ updateListeners.forEachEntry(new TObjectObjectProcedure<String, UpdateListener>() {
+ @Override
+ public boolean execute(String moduleName, UpdateListener listener) {
+ listener.stopListening();
+ return true;
+ }
+ });
+ updateListeners.clear();
+ }
}
@Override
ImageRegistry imageRegistry;
+ SCLIssuesContentProvider issuesContentProvider = new SCLIssuesContentProvider();
+
public SCLIssuesView() {
super();
imageRegistry = Activator.getInstance().getImageRegistry();
tableViewer = new TableViewer(parent,
SWT.FULL_SELECTION | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
ColumnViewerToolTipSupport.enableFor(tableViewer);
- tableViewer.setContentProvider(new SCLIssuesContentProvider());
+ tableViewer.setContentProvider(issuesContentProvider);
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
public void setFocus() {
tableViewer.getControl().setFocus();
}
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ issuesContentProvider.dispose();
+ }
}