Package org.eclipse.core.internal.databinding.observable.masterdetail

Source Code of org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList$DetailEntry

/*******************************************************************************
* Copyright (c) 2010 Ovidio Mallo 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:
*     Ovidio Mallo - initial API and implementation (bug 305367)
******************************************************************************/

package org.eclipse.core.internal.databinding.observable.masterdetail;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.RandomAccess;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.DisposeEvent;
import org.eclipse.core.databinding.observable.IDisposeListener;
import org.eclipse.core.databinding.observable.IObserving;
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.list.AbstractObservableList;
import org.eclipse.core.databinding.observable.list.IListChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.internal.databinding.identity.IdentityMap;
import org.eclipse.core.internal.databinding.identity.IdentitySet;

/**
* @since 1.4
*/
public class ListDetailValueObservableList extends AbstractObservableList
    implements IObserving, RandomAccess {

  private IObservableList masterList;

  private IObservableFactory detailFactory;

  private Object detailType;

  // The list of detail observables.
  private ArrayList detailList;

  // Maps every master to a DetailEntry containing the detail observable. This
  // map is used to avoid that multiple detail observables are created for the
  // same master.
  private IdentityMap masterDetailMap = new IdentityMap();

  private IdentitySet staleDetailObservables = new IdentitySet();

  private IListChangeListener masterListListener = new IListChangeListener() {
    public void handleListChange(ListChangeEvent event) {
      handleMasterListChange(event.diff);
    }
  };

  private IValueChangeListener detailValueListener = new IValueChangeListener() {
    public void handleValueChange(ValueChangeEvent event) {
      if (!event.getObservable().isStale()) {
        staleDetailObservables.remove(event.getObservable());
      }
      handleDetailValueChange(event);
    }
  };

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

  private IStaleListener detailStaleListener = new IStaleListener() {
    public void handleStale(StaleEvent staleEvent) {
      boolean wasStale = isStale();
      staleDetailObservables.add((staleEvent.getObservable()));
      if (!wasStale) {
        fireStale();
      }
    }
  };

  /**
   *
   * @param masterList
   * @param detailFactory
   * @param detailType
   */
  public ListDetailValueObservableList(IObservableList masterList,
      IObservableFactory detailFactory, Object detailType) {
    super(masterList.getRealm());
    this.masterList = masterList;
    this.detailFactory = detailFactory;
    this.detailType = detailType;
    this.detailList = new ArrayList();

    // Add change/stale/dispose listeners on the master list.
    masterList.addListChangeListener(masterListListener);
    masterList.addStaleListener(masterStaleListener);
    masterList.addDisposeListener(new IDisposeListener() {
      public void handleDispose(DisposeEvent event) {
        ListDetailValueObservableList.this.dispose();
      }
    });

    ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST,
        masterList);
    handleMasterListChange(initMasterDiff);
  }

  protected synchronized void firstListenerAdded() {
    for (int i = 0; i < detailList.size(); i++) {
      IObservableValue detail = (IObservableValue) detailList.get(i);
      detail.addValueChangeListener(detailValueListener);
      detail.addStaleListener(detailStaleListener);
      if (detail.isStale()) {
        staleDetailObservables.add(detail);
      }
    }
  }

  protected synchronized void lastListenerRemoved() {
    if (isDisposed()) {
      return;
    }

    for (int i = 0; i < detailList.size(); i++) {
      IObservableValue detail = (IObservableValue) detailList.get(i);
      detail.removeValueChangeListener(detailValueListener);
      detail.removeStaleListener(detailStaleListener);
    }
    staleDetailObservables.clear();
  }

  private void handleMasterListChange(ListDiff masterListDiff) {
    boolean wasStale = isStale();

    boolean hasListeners = hasListeners();
    ListDiffEntry[] masterEntries = masterListDiff.getDifferences();
    ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
    for (int i = 0; i < masterEntries.length; i++) {
      ListDiffEntry masterEntry = masterEntries[i];
      int index = masterEntry.getPosition();

      Object masterElement = masterEntry.getElement();
      Object detailValue;
      if (masterEntry.isAddition()) {
        detailValue = addDetailObservable(masterElement, index);
      } else {
        detailValue = removeDetailObservable(masterElement, index);
      }

      if (hasListeners) {
        // Create the corresponding diff for the detail list.
        detailEntries[i] = Diffs.createListDiffEntry(index,
            masterEntry.isAddition(), detailValue);
      }
    }

    if (hasListeners) {
      if (!wasStale && isStale()) {
        fireStale();
      }

      // Fire a list change event with the adapted diff.
      fireListChange(Diffs.createListDiff(detailEntries));
    }
  }

  private Object addDetailObservable(Object masterElement, int index) {
    DetailEntry detailEntry = (DetailEntry) masterDetailMap
        .get(masterElement);
    if (detailEntry != null) {
      // If we already have a detail observable for the given
      // masterElement, we increment the reference count.
      detailEntry.masterReferenceCount++;
      detailList.add(index, detailEntry.detailObservable);
      return detailEntry.detailObservable.getValue();
    }

    IObservableValue detail = createDetailObservable(masterElement);
    masterDetailMap.put(masterElement, new DetailEntry(detail));

    detailList.add(index, detail);

    if (hasListeners()) {
      detail.addValueChangeListener(detailValueListener);
      detail.addStaleListener(detailStaleListener);
      if (detail.isStale()) {
        staleDetailObservables.add(detail);
      }
    }

    return detail.getValue();
  }

  private Object removeDetailObservable(Object masterElement, int index) {
    IObservableValue detail = (IObservableValue) detailList.remove(index);
    Object detailValue = detail.getValue();

    DetailEntry detailEntry = (DetailEntry) masterDetailMap
        .get(masterElement);

    // We may only dispose the detail observable ASA there are no more
    // masters referencing it.
    detailEntry.masterReferenceCount--;
    if (detailEntry.masterReferenceCount == 0) {
      masterDetailMap.remove(masterElement);
      staleDetailObservables.remove(detail);
      detail.dispose();
    }

    return detailValue;
  }

  private void handleDetailValueChange(ValueChangeEvent event) {
    IObservableValue detail = event.getObservableValue();

    // When we get a change event on a detail observable, we must find its
    // position while there may also be duplicate entries.
    BitSet detailIndexes = new BitSet();
    for (int i = 0; i < detailList.size(); i++) {
      if (detailList.get(i) == detail) {
        detailIndexes.set(i);
      }
    }

    // Create the diff for every found position.
    Object oldValue = event.diff.getOldValue();
    Object newValue = event.diff.getNewValue();
    ListDiffEntry[] diffEntries = new ListDiffEntry[2 * detailIndexes
        .cardinality()];
    int diffIndex = 0;
    for (int b = detailIndexes.nextSetBit(0); b != -1; b = detailIndexes
        .nextSetBit(b + 1)) {
      diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, false,
          oldValue);
      diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, true,
          newValue);
    }
    fireListChange(Diffs.createListDiff(diffEntries));
  }

  private IObservableValue createDetailObservable(Object masterElement) {
    ObservableTracker.setIgnore(true);
    try {
      return (IObservableValue) detailFactory
          .createObservable(masterElement);
    } finally {
      ObservableTracker.setIgnore(false);
    }
  }

  protected int doGetSize() {
    return detailList.size();
  }

  public Object get(int index) {
    ObservableTracker.getterCalled(this);
    return ((IObservableValue) detailList.get(index)).getValue();
  }

  public Object set(int index, Object element) {
    IObservableValue detail = (IObservableValue) detailList.get(index);
    Object oldElement = detail.getValue();
    detail.setValue(element);
    return oldElement;
  }

  public Object move(int oldIndex, int newIndex) {
    throw new UnsupportedOperationException();
  }

  public boolean remove(Object o) {
    throw new UnsupportedOperationException();
  }

  public boolean removeAll(Collection c) {
    throw new UnsupportedOperationException();
  }

  public boolean retainAll(Collection c) {
    throw new UnsupportedOperationException();
  }

  public void clear() {
    throw new UnsupportedOperationException();
  }

  public Object getElementType() {
    return detailType;
  }

  public boolean isStale() {
    return super.isStale()
        || (masterList != null && masterList.isStale())
        || (staleDetailObservables != null && !staleDetailObservables
            .isEmpty());
  }

  public Object getObserved() {
    return masterList;
  }

  public synchronized void dispose() {
    if (masterList != null) {
      masterList.removeListChangeListener(masterListListener);
      masterList.removeStaleListener(masterStaleListener);
    }

    if (detailList != null) {
      for (Iterator iter = detailList.iterator(); iter.hasNext();) {
        IObservableValue detailValue = (IObservableValue) iter.next();
        detailValue.dispose();
      }
      detailList.clear();
    }

    masterList = null;
    detailFactory = null;
    detailType = null;
    masterListListener = null;
    detailValueListener = null;
    masterDetailMap = null;
    staleDetailObservables = null;

    super.dispose();
  }

  private static final class DetailEntry {

    private final IObservableValue detailObservable;

    private int masterReferenceCount = 1;

    public DetailEntry(IObservableValue detailObservable) {
      this.detailObservable = detailObservable;
    }
  }
}
TOP

Related Classes of org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList$DetailEntry

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.