<arguments>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
-source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
webdefault.xml,\
scl/
src.includes = scl/
-
+source.. = src/
<-- L0.Relation
--> L0.String
-L0.HasDefaultLiteralType <R L0.DependsOn : L0.FunctionalRelation
+L0.HasDefaultLiteralType <R L0.DependsOn : L0.FunctionalRelation
<-- L0.Relation
--> L0.Type
L0.RVIContext <T L0.Entity
-L0.SCLValue <T L0.Value
+L0.SCLValue <T L0.Value : L0.SCLValueType
@L0.tag L0.Abstract
>-- L0.SCLValue.expression --> L0.String <R L0.HasProperty : L0.TotalFunction
>-- L0.SCLValue.environment --> L0.SCLValue.Environment <R L0.IsRelatedTo : L0.TotalFunction
@L0.assert L0.ConvertsToValueWith L0.Functions.computeExpression
+L0.SCLValueType <T L0.Entity
+ >-- L0.SCLValueType.validator ==> "Variable -> <ReadGraph> String" <R L0.HasProperty
+
L0.scl : L0.Template
@template %subject %property %expression %valueType
%subject
@L0.tag L0.Abstract
@L0.tag L0.Deprecated
@L0.assert L0.ConvertsToValueWith L0.Functions.composedPropertyValue
-
-
\ No newline at end of file
Bundle-Vendor: VTT Technical Research Centre of Finland
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.simantics.modeling.internal.Activator
+Service-Component: OSGI-INF/sclExpressionIssueProvider.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.simantics.modeling.sclExpressionIssueProvider">
+ <implementation class="org.simantics.modeling.scl.issue.SCLExpressionIssueProvider$SCLExpressionIssueProviderFactory"/>
+ <service>
+ <provide interface="org.simantics.scl.osgi.issues.SCLIssueProviderFactory"/>
+ </service>
+</scr:component>
.,\
adapters.xml,\
plugin.xml,\
- scl/
+ scl/,\
+ OSGI-INF/
src.includes = scl/
--- /dev/null
+package org.simantics.modeling.scl.issue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.SyncListenerAdapter;
+import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingUtils;
+import org.simantics.scl.compiler.errors.CompilationError;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory.SCLIssueProvider;
+import org.simantics.scl.osgi.issues.SCLIssuesTableEntry;
+import org.simantics.scl.runtime.SCLContext;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.ui.workbench.action.DefaultActions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SCLExpressionIssueProvider implements SCLIssueProvider {
+
+ public static class SCLExpressionIssueProviderFactory implements SCLIssueProviderFactory {
+
+ @Override
+ public SCLIssueProvider getSCLIssueProvider() {
+ return new SCLExpressionIssueProvider();
+ }
+
+ }
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SCLExpressionIssueProvider.class);
+ private List<SCLIssuesTableEntry> currentIssues = new ArrayList<>();
+ private boolean disposed = false;
+
+ SCLExpressionIssueProvider() {
+ }
+
+ @Override
+ public void listenIssues(Runnable callback) {
+ Simantics.getSession().asyncRequest(new UniqueRead<List<SCLIssuesTableEntry>>() {
+
+ @Override
+ public List<SCLIssuesTableEntry> perform(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Set<Resource> indexRoots = new TreeSet<Resource>();
+ for(Resource ontology : Layer0Utils.listOntologies(graph)) {
+ if (graph.isInstanceOf(ontology, L0.SharedOntology)) {
+ indexRoots.add(ontology);
+ }
+ }
+
+ for(Resource child : graph.getObjects(Simantics.getProjectResource(), L0.ConsistsOf)) {
+ if (graph.isInstanceOf(child, L0.IndexRoot)) {
+ indexRoots.add(child);
+ }
+ }
+
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ List<SCLIssuesTableEntry> results = new ArrayList<>();
+
+ for (Resource ontology : indexRoots) {
+ List<Resource> components = ModelingUtils.searchByTypeShallow(graph, ontology, STR.Component);
+ for (Resource component : components) {
+
+ for (Resource predicate : graph.getPredicates(component)) {
+ if (graph.isSubrelationOf(predicate, L0.HasProperty)) {
+ for (Resource object : graph.getObjects(component, predicate)) {
+ if (graph.isInstanceOf(object, L0.SCLValue)) {
+ Resource type = graph.getPossibleType(object, L0.SCLValue);
+ Variable typeVariable = Variables.getVariable(graph, type);
+
+ Function1<Variable, String> func = typeVariable.getPossiblePropertyValue(graph, "validator");
+ if (func == null) {
+ // No validator available
+ if (LOGGER.isTraceEnabled())
+ LOGGER.trace("No validator available for " + typeVariable.getURI(graph));
+ continue;
+ }
+
+ Variable componentVariable = Variables.getVariable(graph, component);
+ Variable propertyVariable = componentVariable.getProperty(graph, predicate);
+
+ SCLContext sclContext = SCLContext.getCurrent();
+ Object oldGraph = sclContext.get("graph");
+ try {
+ sclContext.put("graph", graph);
+ String validatorValue = func.apply(propertyVariable);
+ if (validatorValue != null && !validatorValue.isEmpty()) {
+ results.add(new SCLIssuesTableEntry(propertyVariable.getURI(graph), new CompilationError(Locations.NO_LOCATION, validatorValue.replace("\n", " "))) {
+ @Override
+ public void openLocation() {
+ openResource(Display.getCurrent().getActiveShell(), component);
+ }
+ });
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to invoke type validator function " + func, t);
+ } finally {
+ sclContext.put("graph", oldGraph);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return results;
+ }
+ }, new SyncListenerAdapter<List<SCLIssuesTableEntry>>() {
+
+ @Override
+ public void execute(ReadGraph graph, List<SCLIssuesTableEntry> result) {
+ synchronized (currentIssues) {
+ currentIssues.clear();
+ currentIssues.addAll(result);
+ }
+ if (callback != null)
+ callback.run();
+ }
+
+ @Override
+ public void exception(ReadGraph graph, Throwable t) {
+ LOGGER.error("Could not get SCL issues", t);
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return disposed;
+ }
+ });
+ }
+
+ @Override
+ public List<SCLIssuesTableEntry> getIssues() {
+ synchronized (currentIssues) {
+ List<SCLIssuesTableEntry> results = new ArrayList<>(currentIssues);
+ return results;
+ }
+ }
+
+ @Override
+ public void dispose() {
+ disposed = true;
+ }
+
+ private static void openResource(Shell shell, Resource resource) {
+ DefaultActions.performDefaultAction(shell, new StructuredSelection(resource));
+ }
+
+}
Bundle-Activator: org.simantics.scl.osgi.internal.Activator
Require-Bundle: org.eclipse.core.runtime,
gnu.trove3;bundle-version="3.0.0",
- org.simantics.scl.compiler;bundle-version="0.6.0";visibility:=reexport
+ org.simantics.scl.compiler;bundle-version="0.6.0";visibility:=reexport,
+ org.slf4j.api
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: org.simantics.scl.osgi,
- org.simantics.scl.osgi.internal
+ org.simantics.scl.osgi.internal,
+ org.simantics.scl.osgi.issues
Service-Component: OSGI-INF/org.simantics.scl.osgi.internal.BundleModuleSourceRepository.xml,
OSGI-INF/org.simantics.scl.osgi.internal.FileSystemModuleSourceRepository.xml,
OSGI-INF/org.simantics.scl.osgi.internal.BundleTestScriptRepository.xml
public class Activator implements BundleActivator {
+ public static final String PLUGIN_ID = "org.simantics.scl.osgi";
+
private static BundleContext context;
public static BundleContext getContext() {
--- /dev/null
+package org.simantics.scl.osgi.issues;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.simantics.scl.osgi.internal.Activator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class SCLIssueFactoryProvider {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SCLIssueFactoryProvider.class);
+
+ public static List<SCLIssueProviderFactory> getSCLIssueProviderFactories() {
+ ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];
+ try {
+ serviceReferences = Activator.getContext().getAllServiceReferences(SCLIssueProviderFactory.class.getName(), null);
+ } catch (InvalidSyntaxException e) {
+ LOGGER.error("Could not get service references for " + SCLIssueProviderFactory.class.getName(), e);
+ }
+ if (serviceReferences == null || serviceReferences.length == 0)
+ return Collections.emptyList();
+
+ List<SCLIssueProviderFactory> services = new ArrayList<>(serviceReferences.length);
+ for (ServiceReference<?> reference : serviceReferences) {
+ SCLIssueProviderFactory service = (SCLIssueProviderFactory) Activator.getContext().getService(reference);
+ services.add(service);
+ }
+ return services;
+ }
+
+}
--- /dev/null
+package org.simantics.scl.osgi.issues;
+
+import java.util.List;
+
+public interface SCLIssueProviderFactory {
+
+ SCLIssueProvider getSCLIssueProvider();
+
+ public static interface SCLIssueProvider {
+
+ void listenIssues(Runnable callback);
+
+ List<SCLIssuesTableEntry> getIssues();
+
+ void dispose();
+ }
+
+}
-package org.simantics.scl.ui.issues;
+package org.simantics.scl.osgi.issues;
import org.simantics.scl.compiler.errors.CompilationError;
import org.simantics.scl.compiler.errors.Locations;
-public class SCLIssuesTableEntry implements Comparable<SCLIssuesTableEntry> {
+public abstract class SCLIssuesTableEntry implements Comparable<SCLIssuesTableEntry> {
public final String moduleName;
public final CompilationError error;
this.error = error;
}
+ public abstract void openLocation();
+
@Override
public int compareTo(SCLIssuesTableEntry o) {
if(this == o)
org.simantics.scl.osgi;bundle-version="1.0.0",
org.simantics.scl.compiler;bundle-version="0.6.0",
org.junit;bundle-version="4.12.0";resolution:=optional,
- com.ibm.icu
+ com.ibm.icu,
+ org.slf4j.api
Export-Package: org.simantics.scl.ui.console,
org.simantics.scl.ui.editor,
org.simantics.scl.ui.editor2,
org.simantics.scl.ui.imports
Bundle-Activator: org.simantics.scl.ui.Activator
Bundle-ActivationPolicy: lazy
+Service-Component: OSGI-INF/sclModuleIssueProvider.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.simantics.scl.ui.sclModuleIssueProvider">
+ <implementation class="org.simantics.scl.ui.issues.SCLModuleIssueProvider$SCLModuleIssueProviderFactory"/>
+ <service>
+ <provide interface="org.simantics.scl.osgi.issues.SCLIssueProviderFactory"/>
+ </service>
+</scr:component>
-source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
- icons/
+ icons/,\
+ OSGI-INF/sclModuleIssueProvider.xml
+source.. = src/
public class Activator extends AbstractUIPlugin {
+ public static final String PLUGIN_ID = "org.simantics.scl.ui";
+
private static Activator INSTANCE;
@Override
package org.simantics.scl.ui.issues;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
-import org.simantics.scl.compiler.errors.CompilationError;
-import org.simantics.scl.compiler.errors.DoesNotExist;
-import org.simantics.scl.compiler.errors.Failable;
-import org.simantics.scl.compiler.errors.Failure;
-import org.simantics.scl.compiler.module.Module;
-import org.simantics.scl.compiler.module.repository.ModuleRepository;
-import org.simantics.scl.compiler.module.repository.UpdateListener;
-
-import gnu.trove.map.hash.THashMap;
-import gnu.trove.procedure.TObjectObjectProcedure;
-import gnu.trove.procedure.TObjectProcedure;
+import org.simantics.scl.osgi.issues.SCLIssueFactoryProvider;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory.SCLIssueProvider;
+import org.simantics.scl.osgi.issues.SCLIssuesTableEntry;
public class SCLIssuesContentProvider implements IStructuredContentProvider {
public static final int MAX_ISSUE_COUNT = 1000;
-
- Viewer viewer;
- ModuleRepository repository;
+
+ private Viewer viewer;
boolean disposed = false;
- AtomicBoolean refreshInProgress = new AtomicBoolean(false);
-
- THashMap<String, CompilationError[]> currentFailures = new THashMap<String, CompilationError[]>();
- THashMap<String, UpdateListener> updateListeners = new THashMap<String, UpdateListener>();
-
+ private AtomicBoolean refreshInProgress = new AtomicBoolean(false);
+ private List<SCLIssueProvider> issueProviders = new ArrayList<>();
+
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = viewer;
- this.repository = (ModuleRepository)newInput;
- if(newInput != null)
- listenIssues();
- }
-
- private UpdateListener getUpdateListener(String moduleName) {
- 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);
- }
+ if (!issueProviders.isEmpty()) {
+ issueProviders.forEach(p -> p.dispose());
+ issueProviders.clear();
}
- return listener;
- }
-
- private void listenModule(String moduleName) {
- if(repository == null)
- return;
- Failable<Module> result = repository.getModule(moduleName, getUpdateListener(moduleName));
- synchronized(currentFailures) {
- if(result instanceof Failure) {
- Failure failure = (Failure)result;
- currentFailures.put(moduleName, failure.errors);
- }
- else if(result == DoesNotExist.INSTANCE) {
- if(currentFailures.remove(moduleName) == null)
- return;
- }
- else {
- CompilationError[] warnings = result.getResult().getWarnings();
- if(warnings.length == 0) {
- if(currentFailures.remove(moduleName) == null)
- return;
- }
- else {
- currentFailures.put(moduleName, warnings);
- }
- }
+ if(newInput != null) {
+ issueProviders = SCLIssueFactoryProvider.getSCLIssueProviderFactories().stream().map(f -> f.getSCLIssueProvider()).collect(Collectors.toList());
+ issueProviders.forEach(p -> p.listenIssues(() -> refresh()));
}
- refresh();
}
-
+
private void refresh() {
if(!refreshInProgress.compareAndSet(false, true))
return;
});
}
- private void listenIssues() {
- new Thread() {
- public void run() {
- if(repository == null)
- return;
- repository.getSourceRepository().forAllModules(new TObjectProcedure<String>() {
- @Override
- public boolean execute(String moduleName) {
- listenModule(moduleName);
- return true;
- }
- });
- }
- }.start();
- }
-
- @Override
public void dispose() {
if(this.disposed)
return;
+ issueProviders.forEach(p -> p.dispose());
+ issueProviders.clear();
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
public Object[] getElements(Object inputElement) {
- ArrayList<SCLIssuesTableEntry> result = new ArrayList<SCLIssuesTableEntry>();
- synchronized(currentFailures) {
- String[] moduleNames = currentFailures.keySet().toArray(new String[currentFailures.size()]);
- Arrays.sort(moduleNames);
- for(String moduleName : moduleNames) {
- CompilationError[] errors = currentFailures.get(moduleName);
- for(CompilationError error : errors)
- result.add(new SCLIssuesTableEntry(moduleName, error));
- if(result.size() >= MAX_ISSUE_COUNT)
- break;
- }
- }
+ ArrayList<SCLIssuesTableEntry> result = new ArrayList<>();
+ issueProviders.forEach(p -> result.addAll(p.getIssues()));
Collections.sort(result);
return result.toArray();
}
import org.eclipse.ui.part.ViewPart;
import org.simantics.scl.compiler.errors.ErrorSeverity;
import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.scl.osgi.issues.SCLIssuesTableEntry;
import org.simantics.scl.ui.Activator;
-import org.simantics.scl.ui.editor2.OpenSCLDefinition;
public class SCLIssuesView extends ViewPart {
IAction action = new Action("Refresh") {
@Override
public void run() {
- SCLOsgi.MODULE_REPOSITORY.getSourceRepository().checkUpdates();
+ tableViewer.setInput(SCLOsgi.MODULE_REPOSITORY);
+// issuesContentProvider.refresh()
}
};
action.setImageDescriptor(imageRegistry.getDescriptor("arrow_refresh"));
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection)event.getSelection();
SCLIssuesTableEntry entry = (SCLIssuesTableEntry)selection.getFirstElement();
- OpenSCLDefinition.openDefinition(entry.moduleName, entry.error.location);
+ entry.openLocation();
}
});
}
--- /dev/null
+package org.simantics.scl.ui.issues;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.simantics.scl.compiler.errors.CompilationError;
+import org.simantics.scl.compiler.errors.DoesNotExist;
+import org.simantics.scl.compiler.errors.Failable;
+import org.simantics.scl.compiler.errors.Failure;
+import org.simantics.scl.compiler.module.Module;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.compiler.module.repository.UpdateListener;
+import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory;
+import org.simantics.scl.osgi.issues.SCLIssueProviderFactory.SCLIssueProvider;
+import org.simantics.scl.osgi.issues.SCLIssuesTableEntry;
+import org.simantics.scl.ui.editor2.OpenSCLDefinition;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.procedure.TObjectObjectProcedure;
+import gnu.trove.procedure.TObjectProcedure;
+
+public class SCLModuleIssueProvider implements SCLIssueProvider {
+
+ public static class SCLModuleIssueProviderFactory implements SCLIssueProviderFactory {
+
+ @Override
+ public SCLIssueProvider getSCLIssueProvider() {
+ return new SCLModuleIssueProvider();
+ }
+
+ }
+
+ ModuleRepository repository = SCLOsgi.MODULE_REPOSITORY;
+
+ THashMap<String, CompilationError[]> currentFailures = new THashMap<>();
+ THashMap<String, UpdateListener> updateListeners = new THashMap<>();
+
+ private boolean disposed;
+
+ SCLModuleIssueProvider() {
+ }
+
+ private UpdateListener getUpdateListener(String moduleName, Runnable callback) {
+ UpdateListener listener;
+ synchronized(updateListeners) {
+ listener = updateListeners.get(moduleName);
+ if(listener == null) {
+ listener = new UpdateListener() {
+ @Override
+ public void notifyAboutUpdate() {
+ if(!disposed)
+ listenModule(moduleName, callback);
+ }
+ };
+ updateListeners.put(moduleName, listener);
+ }
+ }
+ return listener;
+ }
+
+ private void listenModule(String moduleName, Runnable callback) {
+ if(repository == null)
+ return;
+ Failable<Module> result = repository.getModule(moduleName, getUpdateListener(moduleName, callback));
+ synchronized(currentFailures) {
+ if(result instanceof Failure) {
+ Failure failure = (Failure)result;
+ currentFailures.put(moduleName, failure.errors);
+ }
+ else if(result == DoesNotExist.INSTANCE) {
+ if(currentFailures.remove(moduleName) == null)
+ return;
+ }
+ else {
+ CompilationError[] warnings = result.getResult().getWarnings();
+ if(warnings.length == 0) {
+ if(currentFailures.remove(moduleName) == null)
+ return;
+ }
+ else {
+ currentFailures.put(moduleName, warnings);
+ }
+ }
+ }
+ if (callback != null)
+ callback.run();
+ }
+
+ public void listenIssues(Runnable callback) {
+ new Thread() {
+ public void run() {
+ if(repository == null)
+ return;
+ repository.getSourceRepository().forAllModules(new TObjectProcedure<String>() {
+ @Override
+ public boolean execute(String moduleName) {
+ listenModule(moduleName, callback);
+ return true;
+ }
+ });
+ }
+ }.start();
+ }
+
+ @Override
+ public List<SCLIssuesTableEntry> getIssues() {
+ ArrayList<SCLIssuesTableEntry> result = new ArrayList<>();
+ synchronized(currentFailures) {
+ String[] moduleNames = currentFailures.keySet().toArray(new String[currentFailures.size()]);
+ Arrays.sort(moduleNames);
+ for(String moduleName : moduleNames) {
+ CompilationError[] errors = currentFailures.get(moduleName);
+ for(CompilationError error : errors) {
+ result.add(new SCLIssuesTableEntry(moduleName, error) {
+ @Override
+ public void openLocation() {
+ OpenSCLDefinition.openDefinition(moduleName, error.location);
+ }
+ });
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void dispose() {
+ if (disposed)
+ return;
+ 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();
+ }
+ }
+ }
+
+}