diff options
5 files changed, 176 insertions, 289 deletions
diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/Computation.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/Computation.java index 703bff220..f88761bd5 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/Computation.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/Computation.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 IBM Corporation and others. + * Copyright (c) 2009, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,91 +10,23 @@ *******************************************************************************/ package org.eclipse.e4.core.internal.contexts; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; -import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.internal.contexts.EclipseContext.Scheduled; abstract public class Computation { - /** - * Computations must define equals because they are stored in a set. - */ - public abstract boolean equals(Object arg0); + protected boolean validComputation = true; - /** - * Computations must define hashCode because they are stored in a set. - */ - public abstract int hashCode(); - - protected Map<EclipseContext, Set<String>> dependencies = new HashMap<EclipseContext, Set<String>>(); - - public void addDependency(EclipseContext context, String name) { - Set<String> properties = dependencies.get(context); - if (properties == null) { - properties = new HashSet<String>(4); - dependencies.put(context, properties); - } - properties.add(name); - } - - protected void doHandleInvalid(ContextChangeEvent event, List<Scheduled> scheduled) { - // nothing to do in default computation - } - - public void handleInvalid(ContextChangeEvent event, List<Scheduled> scheduled) { - String name = event.getName(); - EclipseContext context = (EclipseContext) event.getContext(); - - stopListening(context, name); - doHandleInvalid(event, scheduled); - } - - /** - * Remove this computation from all contexts that are tracking it - */ - protected void removeAll() { - for (EclipseContext c : dependencies.keySet()) { - c.removeListener(this); - } - dependencies.clear(); - } - - public void startListening() { - for (EclipseContext c : dependencies.keySet()) { - c.addListener(this, dependencies.get(c)); - } + public void handleInvalid(ContextChangeEvent event, Set<Scheduled> scheduled) { + invalidateComputation(); } - public void stopListening(EclipseContext context, String name) { - if (context == null) { - Set<EclipseContext> dependentContexts = dependencies.keySet(); - for (EclipseContext dependentContext : dependentContexts) { - dependentContext.removeListener(this); - } - return; - } - if (name == null) { - dependencies.remove(context); - context.removeListener(this); - return; - } - Set<String> properties = dependencies.get(context); - if (properties != null) { - properties.remove(name); - // if we no longer track any values in the context, remove dependency - if (properties.isEmpty()) { - dependencies.remove(context); - context.removeListener(this); - } - } + public boolean isValid() { + return validComputation; } - public Set<String> dependsOnNames(IEclipseContext context) { - return dependencies.get(context); + public void invalidateComputation() { + validComputation = false; } }
\ No newline at end of file diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java index 6a26d0062..2e21e0791 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java @@ -12,6 +12,7 @@ package org.eclipse.e4.core.internal.contexts; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Stack; import javax.inject.Named; import org.eclipse.e4.core.contexts.Active; import org.eclipse.e4.core.contexts.IEclipseContext; @@ -122,9 +123,6 @@ public class ContextObjectSupplier extends PrimaryObjectSupplier { final private IEclipseContext context; - private Computation outerComputation; - private int paused = 0; - public ContextObjectSupplier(IEclipseContext context, IInjector injector) { this.context = context; } @@ -205,16 +203,15 @@ public class ContextObjectSupplier extends PrimaryObjectSupplier { } synchronized public void pauseRecording() { - if (paused == 0) - outerComputation = EclipseContext.localComputation().get(); - EclipseContext.localComputation().set(null); - paused++; + Stack<Computation> current = EclipseContext.getCalculatedComputations(); + current.push(null); } synchronized public void resumeRecoding() { - paused--; - if (paused == 0) - EclipseContext.localComputation().set(outerComputation); + Stack<Computation> current = EclipseContext.getCalculatedComputations(); + Computation plug = current.pop(); + if (plug != null) + throw new IllegalArgumentException("Internal error in nested computation processing"); } static public ContextObjectSupplier getObjectSupplier(IEclipseContext context, IInjector injector) { diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java index 3ae8acf59..a68061dfd 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 IBM Corporation and others. + * Copyright (c) 2009, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,13 +12,17 @@ package org.eclipse.e4.core.internal.contexts; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; +import java.util.Vector; import org.eclipse.e4.core.contexts.IContextFunction; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.contexts.RunAndTrack; @@ -69,11 +73,10 @@ public class EclipseContext implements IEclipseContext { } } - static ThreadLocal<Computation> currentComputation = new ThreadLocal<Computation>(); + private Map<String, Vector<WeakReference<Computation>>> listeners = Collections.synchronizedMap(new HashMap<String, Vector<WeakReference<Computation>>>()); + private Map<String, ValueComputation> localValueComputations = Collections.synchronizedMap(new HashMap<String, ValueComputation>()); + private Set<Computation> activeRATs = new HashSet<Computation>(); - private Map<String, Set<Computation>> listeners = Collections.synchronizedMap(new HashMap<String, Set<Computation>>()); - - final Map<String, ValueComputation> localValueComputations = Collections.synchronizedMap(new HashMap<String, ValueComputation>()); final Map<String, Object> localValues = Collections.synchronizedMap(new HashMap<String, Object>()); private final ILookupStrategy strategy; @@ -86,6 +89,8 @@ public class EclipseContext implements IEclipseContext { private Set<IContextDisposalListener> notifyOnDisposal = new HashSet<IContextDisposalListener>(); + static private ThreadLocal<Stack<Computation>> currentComputation = new ThreadLocal<Stack<Computation>>(); + /** * A context key (value "activeChildContext") that identifies another {@link IEclipseContext} * that is a child of the context. The meaning of active is up to the application. @@ -167,11 +172,8 @@ public class EclipseContext implements IEclipseContext { } ContextChangeEvent event = new ContextChangeEvent(this, ContextChangeEvent.DISPOSE, null, null, null); - List<Scheduled> scheduled = new ArrayList<Scheduled>(); - Set<Computation> allComputations = new HashSet<Computation>(); - for (Set<Computation> computations : listeners.values()) { - allComputations.addAll(computations); - } + Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>(); + Set<Computation> allComputations = getListeners(); listeners.clear(); for (Computation computation : allComputations) { computation.handleInvalid(event, scheduled); @@ -216,9 +218,8 @@ public class EclipseContext implements IEclipseContext { trackAccess(name); if (this == originatingContext) { ValueComputation valueComputation = localValueComputations.get(name); - if (valueComputation != null) { + if (valueComputation != null && valueComputation.isValid()) return valueComputation.get(); - } } Object result = null; @@ -233,18 +234,21 @@ public class EclipseContext implements IEclipseContext { // if we found something, compute the concrete value and return if (result != null) { if (result instanceof IContextFunction) { - ValueComputation valueComputation = new ValueComputation(this, originatingContext, name, ((IContextFunction) result)); + ValueComputation valueComputation = new ValueComputation(name, originatingContext, ((IContextFunction) result)); + + // do calculations before adding listeners + result = valueComputation.get(); + originatingContext.localValueComputations.put(name, valueComputation); - // the cached value depends on all entries with this name and all parent relationships - // between the originating context and this context, inclusive + // need to manually add dependency as the computation haven't being created yet at the time + // we walked context hierarchy to find its definition for (EclipseContext step = originatingContext; step != null; step = step.getParent()) { - valueComputation.addDependency(step, name); + step.addDependency(name, valueComputation); if (step == this) break; - valueComputation.addDependency(step, PARENT); + step.addDependency(PARENT, valueComputation); } - result = valueComputation.get(); } return result; } @@ -262,26 +266,33 @@ public class EclipseContext implements IEclipseContext { * The given name has been modified or removed in this context. Invalidate all local value * computations and listeners that depend on this name. */ - public void invalidate(String name, int eventType, Object oldValue, List<Scheduled> scheduled) { - if (DebugHelper.DEBUG_NAMES) - System.out.println("[context] invalidating \"" + name + "\" on " + toString()); //$NON-NLS-1$ //$NON-NLS-2$ - removeLocalValueComputations(name); - handleInvalid(name, eventType, oldValue, scheduled); - } - - /** - * The value of the given name has changed in this context. This either means the value has been - * changed directly, or the value is a function that has been invalidated (one of the function's - * dependencies has changed). - */ - void handleInvalid(String name, int eventType, Object oldValue, List<Scheduled> scheduled) { - Set<Computation> computations = listeners.remove(name); - if (computations == null) - return; - ContextChangeEvent event = new ContextChangeEvent(this, eventType, null, name, oldValue); - for (Computation computation : computations) { + public void invalidate(String name, int eventType, Object oldValue, Set<Scheduled> scheduled) { + ContextChangeEvent event = new ContextChangeEvent(this, ContextChangeEvent.ADDED, null, name, oldValue); + ValueComputation computation = localValueComputations.get(name); + if (computation != null && computation.isValid()) { computation.handleInvalid(event, scheduled); } + Vector<WeakReference<Computation>> namedComputations = listeners.get(name); + if (namedComputations != null) { + int invalidListenersCount = 0; + for (WeakReference<Computation> listenerRef : namedComputations) { + Computation listener = listenerRef.get(); + if (listener != null && listener.isValid()) + listener.handleInvalid(event, scheduled); + else + invalidListenersCount++; + } + // more than half of listeners are invalid, clean the listener list + if ((invalidListenersCount << 2) > namedComputations.size()) { + Vector<WeakReference<Computation>> tmp = new Vector<WeakReference<Computation>>(namedComputations.size() - invalidListenersCount); + for (WeakReference<Computation> listenerRef : namedComputations) { + Computation listener = listenerRef.get(); + if (listener != null && listener.isValid()) + tmp.add(listenerRef); + } + listeners.put(name, tmp); + } + } } private boolean isSetLocally(String name) { @@ -292,37 +303,27 @@ public class EclipseContext implements IEclipseContext { public void remove(String name) { if (isSetLocally(name)) { Object oldValue = localValues.remove(name); - List<Scheduled> scheduled = new ArrayList<Scheduled>(); + Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>(); invalidate(name, ContextChangeEvent.REMOVED, oldValue, scheduled); processScheduled(scheduled); } } - /** - * Removes all local value computations associated with the given name. - * @param name The name to remove - */ - public void removeLocalValueComputations(String name) { - synchronized (localValueComputations) { - ValueComputation removed = localValueComputations.remove(name); - if (removed != null) - removed.stopListening(null, name); - } - } - public void runAndTrack(final RunAndTrack runnable) { - ContextChangeEvent event = new ContextChangeEvent(this, ContextChangeEvent.INITIAL, null, null, null); TrackableComputationExt computation = new TrackableComputationExt(runnable, this); - computation.update(event); + ContextChangeEvent event = new ContextChangeEvent(this, ContextChangeEvent.INITIAL, null, null, null); + boolean result = computation.update(event); + if (result) + activeRATs.add(computation); + } + + public void removeRAT(Computation computation) { + activeRATs.remove(computation); } - protected void processScheduled(List<Scheduled> scheduledList) { - HashSet<Scheduled> sent = new HashSet<Scheduled>(scheduledList.size()); + protected void processScheduled(Set<Scheduled> scheduledList) { for (Iterator<Scheduled> i = scheduledList.iterator(); i.hasNext();) { Scheduled scheduled = i.next(); - // don't send the same event twice - if (!sent.add(scheduled)) - continue; scheduled.runnable.update(scheduled.event); } } @@ -338,20 +339,20 @@ public class EclipseContext implements IEclipseContext { boolean containsKey = localValues.containsKey(name); Object oldValue = localValues.put(name, value); if (!containsKey || value != oldValue) { - List<Scheduled> scheduled = new ArrayList<Scheduled>(); + Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>(); invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled); processScheduled(scheduled); } } public void modify(String name, Object value) { - List<Scheduled> scheduled = new ArrayList<Scheduled>(); + Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>(); if (!internalModify(name, value, scheduled)) set(name, value); processScheduled(scheduled); } - public boolean internalModify(String name, Object value, List<Scheduled> scheduled) { + public boolean internalModify(String name, Object value, Set<Scheduled> scheduled) { boolean containsKey = localValues.containsKey(name); if (containsKey) { if (!checkModifiable(name)) { @@ -381,7 +382,7 @@ public class EclipseContext implements IEclipseContext { return; // no-op if (parentContext != null) parentContext.removeChild(this); - List<Scheduled> scheduled = new ArrayList<Scheduled>(); + Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>(); handleReparent((EclipseContext) parent, scheduled); localValues.put(PARENT, parent); if (parent != null) @@ -399,10 +400,22 @@ public class EclipseContext implements IEclipseContext { } private void trackAccess(String name) { - Computation computation = currentComputation.get(); - if (computation != null) { - computation.addDependency(this, name); + Stack<Computation> current = getCalculatedComputations(); + if (current.isEmpty()) + return; + Computation computation = current.peek(); // only track in the top-most one + if (computation == null) + return; + addDependency(name, computation); + } + + public void addDependency(String name, Computation computation) { + Vector<WeakReference<Computation>> nameListeners = listeners.get(name); + if (nameListeners == null) { + nameListeners = new Vector<WeakReference<Computation>>(); + listeners.put(name, nameListeners); } + nameListeners.add(new WeakReference<Computation>(computation)); } public void declareModifiable(String name) { @@ -431,16 +444,28 @@ public class EclipseContext implements IEclipseContext { if (object == null) return; ContextChangeEvent event = new ContextChangeEvent(this, ContextChangeEvent.UNINJECTED, new Object[] {object}, null, null); - // TBD computation here removes listeners. We should do that inside this method instead - Set<Computation> computations = getListeners(); - Computation[] ls = computations.toArray(new Computation[computations.size()]); - for (Computation computation : ls) { + Set<Computation> comps = getListeners(); + for (Computation computation : comps) { if (computation instanceof TrackableComputationExt) ((TrackableComputationExt) computation).update(event); } } - private void handleReparent(EclipseContext newParent, List<Scheduled> scheduled) { + public Set<Computation> getListeners() { + Collection<Vector<WeakReference<Computation>>> collection = listeners.values(); + Set<Computation> comps = new HashSet<Computation>(); + + for (Vector<WeakReference<Computation>> set : collection) { + for (WeakReference<Computation> ref : set) { + Computation comp = ref.get(); + if (comp != null && comp.isValid()) + comps.add(comp); + } + } + return comps; + } + + private void handleReparent(EclipseContext newParent, Set<Scheduled> scheduled) { // TBD should we lock waiting list while doing reparent? // Add "boolean inReparent" on the root context and process right away? processWaiting(); @@ -460,6 +485,7 @@ public class EclipseContext implements IEclipseContext { invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled); } localValueComputations.clear(); + // XXX localValueComputations -> all invalidate } public void processWaiting() { @@ -569,10 +595,6 @@ public class EclipseContext implements IEclipseContext { } } - static public ThreadLocal<Computation> localComputation() { - return currentComputation; - } - public IEclipseContext getActiveChild() { return (EclipseContext) internalGet(this, ACTIVE_CHILD, true); } @@ -638,7 +660,11 @@ public class EclipseContext implements IEclipseContext { public Map<String, Object> cachedCachedContextFunctions() { Map<String, Object> result = new HashMap<String, Object>(localValueComputations.size()); for (String string : localValueComputations.keySet()) { - result.put(string, localValueComputations.get(string).get()); + ValueComputation vc = localValueComputations.get(string); + if (vc == null) + continue; + if (vc.isValid()) + result.put(string, localValueComputations.get(string).get()); } return result; } @@ -653,38 +679,37 @@ public class EclipseContext implements IEclipseContext { // This method is for debug only, do not use externally public Set<Computation> getListeners(String name) { - Set<Computation> tmp = listeners.get(name); + Vector<WeakReference<Computation>> tmp = listeners.get(name); + if (tmp == null) + return null; Set<Computation> result = new HashSet<Computation>(tmp.size()); - result.addAll(tmp); + for (WeakReference<Computation> ref : tmp) { + Computation listener = ref.get(); + if (listener != null && listener.isValid()) + result.add(listener); + } return result; } - public void addListener(Computation computation, Set<String> names) { - for (String name : names) { - if (listeners.containsKey(name)) { - Set<Computation> existingDependencies = listeners.get(name); - existingDependencies.add(computation); - } else { - Set<Computation> computations = new HashSet<Computation>(); - computations.add(computation); - listeners.put(name, computations); - } + static public Stack<Computation> getCalculatedComputations() { + Stack<Computation> current = currentComputation.get(); + if (current == null) { + current = new Stack<Computation>(); + currentComputation.set(current); } + return current; } - public void removeListener(Computation computation) { - for (Map.Entry<String, Set<Computation>> entry : listeners.entrySet()) { - Set<Computation> computations = entry.getValue(); - computations.remove(computation); - } + public void pushComputation(Computation comp) { + Stack<Computation> current = getCalculatedComputations(); + current.push(comp); } - public Set<Computation> getListeners() { - Set<Computation> computations = new HashSet<Computation>(); - for (Map.Entry<String, Set<Computation>> entry : listeners.entrySet()) { - computations.addAll(entry.getValue()); - } - return computations; + public void popComputation(Computation comp) { + Stack<Computation> current = getCalculatedComputations(); + Computation ended = current.pop(); + if (ended != comp) + throw new IllegalArgumentException("Internal error: Invalid nested computation processing"); //$NON-NLS-1$ } } diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/TrackableComputationExt.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/TrackableComputationExt.java index a45fa482e..fcbd937e8 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/TrackableComputationExt.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/TrackableComputationExt.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.e4.core.internal.contexts; -import java.util.List; +import java.util.Set; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.contexts.RunAndTrack; import org.eclipse.e4.core.internal.contexts.EclipseContext.Scheduled; @@ -27,7 +27,11 @@ public class TrackableComputationExt extends Computation { } public int hashCode() { - return 31 + ((runnable == null) ? 0 : runnable.hashCode()); + final int prime = 31; + int result = 1; + result = prime * result + ((originatingContext == null) ? 0 : originatingContext.hashCode()); + result = prime * result + ((runnable == null) ? 0 : runnable.hashCode()); + return result; } public boolean equals(Object obj) { @@ -38,6 +42,11 @@ public class TrackableComputationExt extends Computation { if (getClass() != obj.getClass()) return false; TrackableComputationExt other = (TrackableComputationExt) obj; + if (originatingContext == null) { + if (other.originatingContext != null) + return false; + } else if (!originatingContext.equals(other.originatingContext)) + return false; if (runnable == null) { if (other.runnable != null) return false; @@ -46,7 +55,8 @@ public class TrackableComputationExt extends Computation { return true; } - protected void doHandleInvalid(ContextChangeEvent event, List<Scheduled> scheduledList) { + public void handleInvalid(ContextChangeEvent event, Set<Scheduled> scheduledList) { + // don't call super - we keep the link unless uninjected / disposed int eventType = event.getEventType(); if (eventType == ContextChangeEvent.INITIAL || eventType == ContextChangeEvent.DISPOSE) { // process right away @@ -70,8 +80,7 @@ public class TrackableComputationExt extends Computation { } } - Computation oldComputation = EclipseContext.localComputation().get(); - EclipseContext.localComputation().set(this); + ((EclipseContext) originatingContext).pushComputation(this); boolean result = true; try { if (cachedEvent != null) { @@ -81,7 +90,7 @@ public class TrackableComputationExt extends Computation { cachedEvent = null; } else { if (eventType != ContextChangeEvent.DISPOSE && eventType != ContextChangeEvent.UNINJECTED) { - result = runnable.changed(cachedEvent.getContext()); + result = runnable.changed(originatingContext); cachedEvent = null; } } @@ -91,24 +100,25 @@ public class TrackableComputationExt extends Computation { result = ((RunAndTrackExt) runnable).update(event.getContext(), event.getEventType(), event.getArguments()); else { if (eventType != ContextChangeEvent.DISPOSE && eventType != ContextChangeEvent.UNINJECTED) - result = runnable.changed(event.getContext()); + result = runnable.changed(originatingContext); } } } finally { - EclipseContext.localComputation().set(oldComputation); + ((EclipseContext) originatingContext).popComputation(this); } EclipseContext eventsContext = (EclipseContext) event.getContext(); if (eventType == ContextChangeEvent.DISPOSE) { if (originatingContext.equals(eventsContext)) { - removeAll(); + ((EclipseContext) originatingContext).removeRAT(this); + invalidateComputation(); return false; } } - if (result) - startListening(); - else - removeAll(); + if (!result) { + ((EclipseContext) originatingContext).removeRAT(this); + invalidateComputation(); + } return result; } @@ -116,11 +126,4 @@ public class TrackableComputationExt extends Computation { return runnable.toString(); } - public void startAccessRecording() { - EclipseContext.localComputation().set(this); - } - - public void stopAccessRecording() { - EclipseContext.localComputation().set(null); - } } diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ValueComputation.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ValueComputation.java index b55344a1e..af2fe1ffb 100644 --- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ValueComputation.java +++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ValueComputation.java @@ -10,124 +10,54 @@ *******************************************************************************/ package org.eclipse.e4.core.internal.contexts; -import java.util.List; +import java.util.Set; import org.eclipse.e4.core.contexts.IContextFunction; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.internal.contexts.EclipseContext.Scheduled; public class ValueComputation extends Computation { - static class CycleException extends RuntimeException { - private static final long serialVersionUID = 1L; - private final String cycleMessage; + final static private Object NotAValue = new Object(); - CycleException(String cycleMessage) { - super("Cycle while computing value"); //$NON-NLS-1$ - this.cycleMessage = cycleMessage; - } - - String getCycleMessage() { - return cycleMessage; - } - - public String toString() { - return "\n" + cycleMessage + '\n'; //$NON-NLS-1$ - } - } - - private Object cachedValue; - private IEclipseContext context; - private String name; - private boolean valid; + private Object cachedValue = NotAValue; private IContextFunction function; private EclipseContext originatingContext; private boolean computing; // cycle detection + private String name; - public ValueComputation(IEclipseContext context, IEclipseContext originatingContext, String name, IContextFunction computedValue) { - this.context = context; + public ValueComputation(String name, IEclipseContext originatingContext, IContextFunction computedValue) { this.originatingContext = (EclipseContext) originatingContext; - this.name = name; this.function = computedValue; + this.name = name; } - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((context == null) ? 0 : context.hashCode()); - result = prime * result + ((function == null) ? 0 : function.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((originatingContext == null) ? 0 : originatingContext.hashCode()); - return result; - } + public void handleInvalid(ContextChangeEvent event, Set<Scheduled> scheduled) { + cachedValue = NotAValue; - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ValueComputation other = (ValueComputation) obj; - if (context == null) { - if (other.context != null) - return false; - } else if (!context.equals(other.context)) - return false; - if (function == null) { - if (other.function != null) - return false; - } else if (!function.equals(other.function)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (originatingContext == null) { - if (other.originatingContext != null) - return false; - } else if (!originatingContext.equals(other.originatingContext)) - return false; - return true; - } + if (name.equals(event.getName())) + invalidateComputation(); - protected void doHandleInvalid(ContextChangeEvent event, List<Scheduled> scheduled) { - valid = false; - cachedValue = null; int eventType = event.getEventType(); - // if the originating context is being disposed, remove this value computation completely - if (eventType == ContextChangeEvent.DISPOSE) { - IEclipseContext eventsContext = event.getContext(); - if (originatingContext.equals(eventsContext)) { - removeAll(); - return; - } - return; - } - if (event.getName().equals(name)) - originatingContext.removeLocalValueComputations(name); originatingContext.invalidate(name, eventType == ContextChangeEvent.DISPOSE ? ContextChangeEvent.REMOVED : eventType, event.getOldValue(), scheduled); } public Object get() { - if (valid) + if (!isValid()) + throw new IllegalArgumentException("Reusing invalidated computation"); //$NON-NLS-1$ + if (cachedValue != NotAValue) return cachedValue; if (this.computing) - throw new CycleException(this.toString()); + throw new RuntimeException("Cycle while computing value" + this.toString()); //$NON-NLS-1$ - Computation oldComputation = EclipseContext.currentComputation.get(); - EclipseContext.currentComputation.set(this); + originatingContext.pushComputation(this); computing = true; try { cachedValue = function.compute(originatingContext); - valid = true; - } catch (CycleException ex) { - throw new CycleException(ex.getCycleMessage() + '\n' + this.toString()); + validComputation = true; } finally { computing = false; - EclipseContext.currentComputation.set(oldComputation); + originatingContext.popComputation(this); } - startListening(); return cachedValue; } |