/**
   * 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, Set<Scheduled> scheduled) {
    ContextChangeEvent event = new ContextChangeEvent(this, eventType, null, name, oldValue);
    ValueComputation computation = localValueComputations.get(name);
    if (computation != null) {
      if (computation.shouldRemove(event)) {
        localValueComputations.remove(name);
        Collection<HashSet<Computation>> allListeners = listeners.values();
        for (HashSet<Computation> group : allListeners) {
          group.remove(computation);
        }
      }
      computation.handleInvalid(event, scheduled);
    }
    HashSet<Computation> namedComputations = listeners.get(name);
    if (namedComputations != null) {
      for (Computation listener : namedComputations) {
        listener.handleInvalid(event, scheduled);
      }
    }

    // invalidate this name in child contexts
    for (EclipseContext childContext : getChildren()) {
      childContext.invalidate(name, eventType, oldValue, scheduled);
    }
  }
 public boolean containsKey(String name, boolean localOnly) {
   if (isSetLocally(name)) return true;
   if (localOnly) return false;
   EclipseContext parent = getParent();
   if (parent != null && parent.containsKey(name, localOnly)) return true;
   return false;
 }
  private void collectDependentNames(Set<String> usedNames) {
    Set<String> tmp = listeners.keySet(); // clone internal name list
    usedNames.addAll(tmp);

    for (EclipseContext childContext : getChildren()) {
      childContext.collectDependentNames(usedNames);
    }
  }
 protected EclipseContext getRoot() {
   EclipseContext current = this;
   EclipseContext root;
   do {
     root = current;
     current = current.getParent();
   } while (current != null);
   return root;
 }
 public void addWaiting(Computation cp) {
   // traverse to the root node
   EclipseContext parent = getParent();
   if (parent != null) {
     parent.addWaiting(cp);
     return;
   }
   if (waiting == null) // could happen on re-parent
   waiting = Collections.synchronizedList(new ArrayList<Computation>());
   waiting.add(cp);
 }
 public void setParent(IEclipseContext parent) {
   EclipseContext parentContext = (EclipseContext) localValues.get(PARENT);
   if (parent == parentContext) return; // no-op
   if (parentContext != null) parentContext.removeChild(this);
   Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>();
   handleReparent((EclipseContext) parent, scheduled);
   localValues.put(PARENT, parent);
   if (parent != null) ((EclipseContext) parent).addChild(this);
   processScheduled(scheduled);
   return;
 }
  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();
    // 1) everybody who depends on me: I need to collect combined list of names injected
    Set<String> usedNames = new HashSet<String>();
    collectDependentNames(usedNames);

    // 2) for each used name:
    for (Iterator<String> i = usedNames.iterator(); i.hasNext(); ) {
      String name = i.next();
      if (localValues.containsKey(name)) continue; // it is a local value
      Object oldValue = get(name);
      Object newValue = (newParent != null) ? newParent.get(name) : null;
      if (oldValue != newValue) invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled);
    }

    ContextChangeEvent event =
        new ContextChangeEvent(this, ContextChangeEvent.ADDED, null, null, null);
    for (Computation computation : localValueComputations.values()) {
      Collection<HashSet<Computation>> allListeners = listeners.values();
      for (HashSet<Computation> group : allListeners) {
        group.remove(computation);
      }
      computation.handleInvalid(event, scheduled);
    }
    localValueComputations.clear();
  }
 public void processWaiting() {
   // traverse to the root node
   EclipseContext parent = getParent();
   if (parent != null) {
     parent.processWaiting();
     return;
   }
   if (waiting == null) return;
   // create update notifications
   Computation[] ls = waiting.toArray(new Computation[waiting.size()]);
   waiting.clear();
   ContextChangeEvent event =
       new ContextChangeEvent(this, ContextChangeEvent.UPDATE, null, null, null);
   for (int i = 0; i < ls.length; i++) {
     if (ls[i] instanceof TrackableComputationExt) ((TrackableComputationExt) ls[i]).update(event);
   }
 }
 @Override
 public synchronized void resumeRecording() {
   Stack<Computation> current = EclipseContext.getCalculatedComputations();
   Computation plug = current.pop();
   if (plug != null)
     throw new IllegalArgumentException(
         "Internal error in nested computation processing"); //$NON-NLS-1$
 }
  public boolean internalModify(String name, Object value, Set<Scheduled> scheduled) {
    boolean containsKey = localValues.containsKey(name);
    if (containsKey) {
      if (!checkModifiable(name)) {
        String tmp =
            "Variable "
                + name
                + " is not modifiable in the context "
                + toString(); //$NON-NLS-1$ //$NON-NLS-2$
        throw new IllegalArgumentException(tmp);
      }
      Object oldValue = localValues.put(name, value);
      if (value != oldValue) invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled);
      return true;
    }

    EclipseContext parent = getParent();
    if (parent != null) return parent.internalModify(name, value, scheduled);
    return false;
  }
  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.e4.core.services.context.IEclipseContext#dispose()
   */
  public void dispose() {
    // dispose of child contexts first
    for (EclipseContext childContext : getChildren()) {
      childContext.dispose();
    }

    ContextChangeEvent event =
        new ContextChangeEvent(this, ContextChangeEvent.DISPOSE, null, null, null);
    Set<Scheduled> scheduled = new LinkedHashSet<Scheduled>();
    Set<Computation> allComputations = getListeners();
    listeners.clear();
    allComputations.addAll(activeRATs);
    activeRATs.clear();
    for (Computation computation : allComputations) {
      computation.handleInvalid(event, scheduled);
    }
    processScheduled(scheduled);

    synchronized (notifyOnDisposal) {
      for (IContextDisposalListener listener : notifyOnDisposal) {
        listener.disposed(this);
      }
    }

    localValueComputations.clear();

    // if this was the parent's active child, deactivate it
    EclipseContext parent = getParent();
    if (parent != null) {
      if (this == parent.getActiveChild()) parent.set(ACTIVE_CHILD, null);
    }

    localValues.clear();

    if (parent != null) parent.removeChild(this);

    if (debugAddOn != null)
      debugAddOn.notify(this, IEclipseContextDebugger.EventType.DISPOSED, null);
  }
 public void deactivate() {
   EclipseContext parent = getParent();
   if (parent == null) return;
   if (this != parent.getActiveChild()) return; // this is not an active context; return
   parent.set(ACTIVE_CHILD, null);
 }
 public void activate() {
   EclipseContext parent = getParent();
   if (parent == null) return;
   if (this == parent.getActiveChild()) return;
   parent.set(ACTIVE_CHILD, this);
 }
 @Override
 public synchronized void pauseRecording() {
   Stack<Computation> current = EclipseContext.getCalculatedComputations();
   current.push(null);
 }