Package org.eclipse.core.databinding.observable.set

Source Code of org.eclipse.core.databinding.observable.set.UnionSet

/*******************************************************************************
* Copyright (c) 2006, 2007 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.core.databinding.observable.set;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.internal.databinding.observable.IStalenessConsumer;
import org.eclipse.core.internal.databinding.observable.StalenessTracker;

/**
* Represents a set consisting of the union of elements from one or more other
* sets. This object does not need to be explicitly disposed. If nobody is
* listening to the UnionSet, the set will remove its listeners.
*
* <p>
* This class is thread safe. All state accessing methods must be invoked from
* the {@link Realm#isCurrent() current realm}. Methods for adding and removing
* listeners may be invoked from any thread.
* </p>
*
* @since 1.0
*/
public final class UnionSet extends ObservableSet {

  /**
   * child sets
   */
  private IObservableSet[] childSets;

  private boolean stale = false;

  /**
   * Map of elements onto Integer reference counts. This map is constructed
   * when the first listener is added to the union set. Null if nobody is
   * listening to the UnionSet.
   */
  private HashMap refCounts = null;

  private StalenessTracker stalenessTracker;

  /**
   * @param childSets
   */
  public UnionSet(IObservableSet[] childSets) {
    super(childSets[0].getRealm(), null, childSets[0].getElementType());
    this.childSets = childSets;
    this.stalenessTracker = new StalenessTracker(childSets,
        stalenessConsumer);
  }

  private ISetChangeListener childSetChangeListener = new ISetChangeListener() {
    public void handleSetChange(SetChangeEvent event) {
      processAddsAndRemoves(event.diff.getAdditions(), event.diff.getRemovals());
    }
  };

  private IStalenessConsumer stalenessConsumer = new IStalenessConsumer() {
    public void setStale(boolean stale) {
      boolean oldStale = UnionSet.this.stale;
      UnionSet.this.stale = stale;
      if (stale && !oldStale) {
        fireStale();
      }
    }
  };

  public boolean isStale() {
    checkRealm();
    if (refCounts != null) {
      return stale;
    }

    for (int i = 0; i < childSets.length; i++) {
      IObservableSet childSet = childSets[i];

      if (childSet.isStale()) {
        return true;
      }
    }
    return false;
  }

  private void processAddsAndRemoves(Set adds, Set removes) {
    Set addsToFire = new HashSet();
    Set removesToFire = new HashSet();

    for (Iterator iter = adds.iterator(); iter.hasNext();) {
      Object added = iter.next();

      Integer refCount = (Integer) refCounts.get(added);
      if (refCount == null) {
        refCounts.put(added, new Integer(1));
        addsToFire.add(added);
      } else {
        int refs = refCount.intValue();
        refCount = new Integer(refs + 1);
        refCounts.put(added, refCount);
      }
    }

    for (Iterator iter = removes.iterator(); iter.hasNext();) {
      Object removed = iter.next();

      Integer refCount = (Integer) refCounts.get(removed);
      if (refCount != null) {
        int refs = refCount.intValue();
        if (refs <= 1) {
          removesToFire.add(removed);
          refCounts.remove(removed);
        } else {
          refCount = new Integer(refCount.intValue() - 1);
          refCounts.put(removed, refCount);
        }
      }
    }

    // just in case the removes overlapped with the adds
    addsToFire.removeAll(removesToFire);

    if (addsToFire.size() > 0 || removesToFire.size() > 0) {
      fireSetChange(Diffs.createSetDiff(addsToFire, removesToFire));
    }
  }

  protected void firstListenerAdded() {
    super.firstListenerAdded();

    refCounts = new HashMap();
    for (int i = 0; i < childSets.length; i++) {
      IObservableSet next = childSets[i];
      next.addSetChangeListener(childSetChangeListener);
      incrementRefCounts(next);
    }
    stalenessTracker = new StalenessTracker(childSets, stalenessConsumer);
    setWrappedSet(refCounts.keySet());
  }

  protected void lastListenerRemoved() {
    super.lastListenerRemoved();

    for (int i = 0; i < childSets.length; i++) {
      IObservableSet next = childSets[i];

      next.removeSetChangeListener(childSetChangeListener);
      stalenessTracker.removeObservable(next);
    }
    refCounts = null;
    stalenessTracker = null;
    setWrappedSet(null);
  }

  private ArrayList incrementRefCounts(Collection added) {
    ArrayList adds = new ArrayList();

    for (Iterator iter = added.iterator(); iter.hasNext();) {
      Object next = iter.next();

      Integer refCount = (Integer) refCounts.get(next);
      if (refCount == null) {
        adds.add(next);
        refCount = new Integer(1);
        refCounts.put(next, refCount);
      } else {
        refCount = new Integer(refCount.intValue() + 1);
        refCounts.put(next, refCount);
      }
    }
    return adds;
  }

  protected void getterCalled() {
    super.getterCalled();
    if (refCounts == null) {
      // no listeners, recompute
      setWrappedSet(computeElements());
    }
  }

  private Set computeElements() {
    // If there is no cached value, compute the union from scratch
    if (refCounts == null) {
      Set result = new HashSet();
      for (int i = 0; i < childSets.length; i++) {
        result.addAll(childSets[i]);
      }
      return result;
    }

    // Else there is a cached value. Return it.
    return refCounts.keySet();
  }

}
TOP

Related Classes of org.eclipse.core.databinding.observable.set.UnionSet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.