Package org.eclipse.core.internal.databinding.property.value

Source Code of org.eclipse.core.internal.databinding.property.value.MapSimpleValueObservableMap$EntrySet

/*******************************************************************************
* Copyright (c) 2008, 2010 Matthew Hall 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:
*     Matthew Hall - initial API and implementation (bug 194734)
*     Matthew Hall - bugs 262269, 265561, 262287, 268688, 278550, 303847
*     Ovidio Mallo - bugs 299619, 301370
******************************************************************************/

package org.eclipse.core.internal.databinding.property.value;

import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.IStaleListener;
import org.eclipse.core.databinding.observable.ObservableTracker;
import org.eclipse.core.databinding.observable.StaleEvent;
import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
import org.eclipse.core.databinding.observable.map.IMapChangeListener;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.map.MapChangeEvent;
import org.eclipse.core.databinding.observable.map.MapDiff;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
import org.eclipse.core.databinding.property.INativePropertyListener;
import org.eclipse.core.databinding.property.IProperty;
import org.eclipse.core.databinding.property.IPropertyObservable;
import org.eclipse.core.databinding.property.ISimplePropertyListener;
import org.eclipse.core.databinding.property.SimplePropertyEvent;
import org.eclipse.core.databinding.property.value.SimpleValueProperty;
import org.eclipse.core.internal.databinding.identity.IdentityMap;
import org.eclipse.core.internal.databinding.identity.IdentityObservableSet;
import org.eclipse.core.internal.databinding.identity.IdentitySet;
import org.eclipse.core.internal.databinding.property.Util;

/**
* @since 1.2
*
*/
public class MapSimpleValueObservableMap extends AbstractObservableMap
    implements IPropertyObservable {
  private IObservableMap masterMap;
  private SimpleValueProperty detailProperty;

  private IObservableSet knownMasterValues;
  private Map cachedValues;
  private Set staleMasterValues;

  private boolean updating = false;

  private IMapChangeListener masterListener = new IMapChangeListener() {
    public void handleMapChange(final MapChangeEvent event) {
      if (!isDisposed()) {
        updateKnownValues();
        if (!updating)
          fireMapChange(convertDiff(event.diff));
      }
    }

    private void updateKnownValues() {
      Set knownValues = new IdentitySet(masterMap.values());
      knownMasterValues.retainAll(knownValues);
      knownMasterValues.addAll(knownValues);
    }

    private MapDiff convertDiff(MapDiff diff) {
      Map oldValues = new IdentityMap();
      Map newValues = new IdentityMap();

      Set addedKeys = diff.getAddedKeys();
      for (Iterator it = addedKeys.iterator(); it.hasNext();) {
        Object key = it.next();
        Object newSource = diff.getNewValue(key);
        Object newValue = detailProperty.getValue(newSource);
        newValues.put(key, newValue);
      }

      Set removedKeys = diff.getRemovedKeys();
      for (Iterator it = removedKeys.iterator(); it.hasNext();) {
        Object key = it.next();
        Object oldSource = diff.getOldValue(key);
        Object oldValue = detailProperty.getValue(oldSource);
        oldValues.put(key, oldValue);
      }

      Set changedKeys = new IdentitySet(diff.getChangedKeys());
      for (Iterator it = changedKeys.iterator(); it.hasNext();) {
        Object key = it.next();

        Object oldSource = diff.getOldValue(key);
        Object newSource = diff.getNewValue(key);

        Object oldValue = detailProperty.getValue(oldSource);
        Object newValue = detailProperty.getValue(newSource);

        if (Util.equals(oldValue, newValue)) {
          it.remove();
        } else {
          oldValues.put(key, oldValue);
          newValues.put(key, newValue);
        }
      }

      return Diffs.createMapDiff(addedKeys, removedKeys, changedKeys,
          oldValues, newValues);
    }
  };

  private IStaleListener staleListener = new IStaleListener() {
    public void handleStale(StaleEvent staleEvent) {
      fireStale();
    }
  };

  private INativePropertyListener detailListener;

  /**
   * @param map
   * @param valueProperty
   */
  public MapSimpleValueObservableMap(IObservableMap map,
      SimpleValueProperty valueProperty) {
    super(map.getRealm());
    this.masterMap = map;
    this.detailProperty = valueProperty;

    ISimplePropertyListener listener = new ISimplePropertyListener() {
      public void handleEvent(final SimplePropertyEvent event) {
        if (!isDisposed() && !updating) {
          getRealm().exec(new Runnable() {
            public void run() {
              if (event.type == SimplePropertyEvent.CHANGE) {
                notifyIfChanged(event.getSource());
              } else if (event.type == SimplePropertyEvent.STALE) {
                boolean wasStale = !staleMasterValues.isEmpty();
                staleMasterValues.add(event.getSource());
                if (!wasStale)
                  fireStale();
              }
            }
          });
        }
      }
    };
    this.detailListener = detailProperty.adaptListener(listener);
  }

  public Object getKeyType() {
    return masterMap.getKeyType();
  }

  public Object getValueType() {
    return detailProperty.getValueType();
  }

  protected void firstListenerAdded() {
    ObservableTracker.setIgnore(true);
    try {
      knownMasterValues = new IdentityObservableSet(getRealm(), null);
    } finally {
      ObservableTracker.setIgnore(false);
    }

    cachedValues = new IdentityMap();
    staleMasterValues = new IdentitySet();
    knownMasterValues.addSetChangeListener(new ISetChangeListener() {
      public void handleSetChange(SetChangeEvent event) {
        for (Iterator it = event.diff.getRemovals().iterator(); it
            .hasNext();) {
          Object key = it.next();
          if (detailListener != null)
            detailListener.removeFrom(key);
          cachedValues.remove(key);
          staleMasterValues.remove(key);
        }
        for (Iterator it = event.diff.getAdditions().iterator(); it
            .hasNext();) {
          Object key = it.next();
          cachedValues.put(key, detailProperty.getValue(key));
          if (detailListener != null)
            detailListener.addTo(key);
        }
      }
    });

    getRealm().exec(new Runnable() {
      public void run() {
        knownMasterValues.addAll(masterMap.values());

        masterMap.addMapChangeListener(masterListener);
        masterMap.addStaleListener(staleListener);
      }
    });
  }

  protected void lastListenerRemoved() {
    masterMap.removeMapChangeListener(masterListener);
    masterMap.removeStaleListener(staleListener);
    if (knownMasterValues != null) {
      knownMasterValues.dispose();
      knownMasterValues = null;
    }
    cachedValues.clear();
    cachedValues = null;
    staleMasterValues.clear();
    staleMasterValues = null;
  }

  private Set entrySet;

  public Set entrySet() {
    getterCalled();
    if (entrySet == null)
      entrySet = new EntrySet();
    return entrySet;
  }

  class EntrySet extends AbstractSet {
    public Iterator iterator() {
      return new Iterator() {
        Iterator it = masterMap.entrySet().iterator();

        public boolean hasNext() {
          getterCalled();
          return it.hasNext();
        }

        public Object next() {
          getterCalled();
          Map.Entry next = (Map.Entry) it.next();
          return new MapEntry(next.getKey());
        }

        public void remove() {
          it.remove();
        }
      };
    }

    public int size() {
      return masterMap.size();
    }
  }

  class MapEntry implements Map.Entry {
    private Object key;

    MapEntry(Object key) {
      this.key = key;
    }

    public Object getKey() {
      getterCalled();
      return key;
    }

    public Object getValue() {
      getterCalled();
      if (!masterMap.containsKey(key))
        return null;
      return detailProperty.getValue(masterMap.get(key));
    }

    public Object setValue(Object value) {
      if (!masterMap.containsKey(key))
        return null;
      Object source = masterMap.get(key);

      Object oldValue = detailProperty.getValue(source);

      updating = true;
      try {
        detailProperty.setValue(source, value);
      } finally {
        updating = false;
      }

      notifyIfChanged(source);

      return oldValue;
    }

    public boolean equals(Object o) {
      getterCalled();
      if (o == this)
        return true;
      if (o == null)
        return false;
      if (!(o instanceof Map.Entry))
        return false;
      Map.Entry that = (Map.Entry) o;
      return Util.equals(this.getKey(), that.getKey())
          && Util.equals(this.getValue(), that.getValue());
    }

    public int hashCode() {
      getterCalled();
      Object value = getValue();
      return (key == null ? 0 : key.hashCode())
          ^ (value == null ? 0 : value.hashCode());
    }
  }

  public boolean containsKey(Object key) {
    getterCalled();

    return masterMap.containsKey(key);
  }

  public Object get(Object key) {
    getterCalled();

    return detailProperty.getValue(masterMap.get(key));
  }

  public Object put(Object key, Object value) {
    if (!masterMap.containsKey(key))
      return null;
    Object masterValue = masterMap.get(key);
    Object oldValue = detailProperty.getValue(masterValue);
    detailProperty.setValue(masterValue, value);
    notifyIfChanged(masterValue);
    return oldValue;
  }

  public Object remove(Object key) {
    checkRealm();

    Object masterValue = masterMap.get(key);
    Object oldValue = detailProperty.getValue(masterValue);

    masterMap.remove(key);

    return oldValue;
  }

  private void notifyIfChanged(Object masterValue) {
    if (cachedValues != null) {
      final Set keys = keysFor(masterValue);

      final Object oldValue = cachedValues.get(masterValue);
      final Object newValue = detailProperty.getValue(masterValue);

      if (!Util.equals(oldValue, newValue)
          || staleMasterValues.contains(masterValue)) {
        cachedValues.put(masterValue, newValue);
        staleMasterValues.remove(masterValue);
        fireMapChange(new MapDiff() {
          public Set getAddedKeys() {
            return Collections.EMPTY_SET;
          }

          public Set getChangedKeys() {
            return keys;
          }

          public Set getRemovedKeys() {
            return Collections.EMPTY_SET;
          }

          public Object getNewValue(Object key) {
            return newValue;
          }

          public Object getOldValue(Object key) {
            return oldValue;
          }
        });
      }
    }
  }

  private Set keysFor(Object value) {
    Set keys = new IdentitySet();

    for (Iterator it = masterMap.entrySet().iterator(); it.hasNext();) {
      Map.Entry entry = (Entry) it.next();
      if (entry.getValue() == value) {
        keys.add(entry.getKey());
      }
    }

    return keys;
  }

  public boolean isStale() {
    getterCalled();
    return masterMap.isStale() || staleMasterValues != null
        && !staleMasterValues.isEmpty();
  }

  private void getterCalled() {
    ObservableTracker.getterCalled(this);
  }

  public Object getObserved() {
    return masterMap;
  }

  public IProperty getProperty() {
    return detailProperty;
  }

  public synchronized void dispose() {
    if (masterMap != null) {
      masterMap.removeMapChangeListener(masterListener);
      masterMap = null;
    }
    if (knownMasterValues != null) {
      knownMasterValues.clear(); // detaches listeners
      knownMasterValues.dispose();
      knownMasterValues = null;
    }

    masterListener = null;
    detailListener = null;
    detailProperty = null;
    cachedValues = null;
    staleMasterValues = null;

    super.dispose();
  }
}
TOP

Related Classes of org.eclipse.core.internal.databinding.property.value.MapSimpleValueObservableMap$EntrySet

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.