001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Brad Reynolds - bug 116920
011: *******************************************************************************/package org.eclipse.jface.databinding.viewers;
012:
013: import java.util.Collections;
014: import java.util.HashSet;
015: import java.util.Set;
016:
017: import org.eclipse.core.databinding.observable.Diffs;
018: import org.eclipse.core.databinding.observable.IStaleListener;
019: import org.eclipse.core.databinding.observable.StaleEvent;
020: import org.eclipse.core.databinding.observable.set.IObservableSet;
021: import org.eclipse.core.databinding.observable.set.ISetChangeListener;
022: import org.eclipse.core.databinding.observable.set.ObservableSet;
023: import org.eclipse.core.databinding.observable.set.SetChangeEvent;
024: import org.eclipse.jface.databinding.swt.SWTObservables;
025: import org.eclipse.jface.viewers.AbstractListViewer;
026: import org.eclipse.jface.viewers.IStructuredContentProvider;
027: import org.eclipse.jface.viewers.TableViewer;
028: import org.eclipse.jface.viewers.Viewer;
029: import org.eclipse.swt.widgets.Display;
030:
031: /**
032: * @since 1.1
033: *
034: */
035: public final class ObservableSetContentProvider implements
036: IStructuredContentProvider {
037:
038: private class KnownElementsSet extends ObservableSet {
039:
040: KnownElementsSet(Set wrappedSet) {
041: super (SWTObservables.getRealm(Display.getDefault()),
042: wrappedSet, Object.class);
043: }
044:
045: void doFireDiff(Set added, Set removed) {
046: fireSetChange(Diffs.createSetDiff(added, removed));
047: }
048:
049: void doFireStale(boolean isStale) {
050: if (isStale) {
051: fireStale();
052: } else {
053: fireChange();
054: }
055: }
056: }
057:
058: private IObservableSet readableSet;
059:
060: private Viewer viewer;
061:
062: /**
063: * This readableSet returns the same elements as the input readableSet.
064: * However, it only fires events AFTER the elements have been added or
065: * removed from the viewer.
066: */
067: private KnownElementsSet knownElements;
068:
069: private ISetChangeListener listener = new ISetChangeListener() {
070:
071: public void handleSetChange(SetChangeEvent event) {
072: boolean wasStale = knownElements.isStale();
073: if (isDisposed()) {
074: return;
075: }
076: doDiff(event.diff.getAdditions(), event.diff.getRemovals(),
077: true);
078: if (!wasStale && event.getObservableSet().isStale()) {
079: knownElements.doFireStale(true);
080: }
081: }
082: };
083:
084: private IStaleListener staleListener = new IStaleListener() {
085: public void handleStale(StaleEvent event) {
086: knownElements.doFireStale(event.getObservable().isStale());
087: }
088: };
089:
090: /**
091: *
092: */
093: public ObservableSetContentProvider() {
094: readableSet = new ObservableSet(SWTObservables.getRealm(Display
095: .getDefault()), Collections.EMPTY_SET, Object.class) {
096: };
097: knownElements = new KnownElementsSet(readableSet);
098: }
099:
100: public void dispose() {
101: setInput(null);
102: }
103:
104: private void doDiff(Set added, Set removed, boolean updateViewer) {
105: knownElements.doFireDiff(added, Collections.EMPTY_SET);
106:
107: if (updateViewer) {
108: Object[] toAdd = added.toArray();
109: if (viewer instanceof TableViewer) {
110: TableViewer tv = (TableViewer) viewer;
111: tv.add(toAdd);
112: } else if (viewer instanceof AbstractListViewer) {
113: AbstractListViewer lv = (AbstractListViewer) viewer;
114: lv.add(toAdd);
115: }
116: Object[] toRemove = removed.toArray();
117: if (viewer instanceof TableViewer) {
118: TableViewer tv = (TableViewer) viewer;
119: tv.remove(toRemove);
120: } else if (viewer instanceof AbstractListViewer) {
121: AbstractListViewer lv = (AbstractListViewer) viewer;
122: lv.remove(toRemove);
123: }
124: }
125: knownElements.doFireDiff(Collections.EMPTY_SET, removed);
126: }
127:
128: public Object[] getElements(Object inputElement) {
129: return readableSet.toArray();
130: }
131:
132: /**
133: * Returns the readableSet of elements known to this content provider. Items
134: * are added to this readableSet before being added to the viewer, and they
135: * are removed after being removed from the viewer. The readableSet is
136: * always updated after the viewer. This is intended for use by label
137: * providers, as it will always return the items that need labels.
138: *
139: * @return readableSet of items that will need labels
140: */
141: public IObservableSet getKnownElements() {
142: return knownElements;
143: }
144:
145: public void inputChanged(Viewer viewer, Object oldInput,
146: Object newInput) {
147: this .viewer = viewer;
148:
149: if (!(viewer instanceof TableViewer || viewer instanceof AbstractListViewer)) {
150: // use reflection to avoid 3.3 dependency:
151: Class abstractTableViewerClass = null;
152: try {
153: abstractTableViewerClass = Class
154: .forName("org.eclipse.jface.viewers.AbstractTableViewer"); //$NON-NLS-1$
155: } catch (Exception ex) {
156: // ignore, we might be running against 3.2
157: }
158: if (abstractTableViewerClass == null
159: || !abstractTableViewerClass.isInstance(viewer)) {
160: throw new IllegalArgumentException(
161: "This content provider only works with (Abstract)TableViewer or AbstractListViewer"); //$NON-NLS-1$
162: }
163: }
164:
165: if (newInput != null && !(newInput instanceof IObservableSet)) {
166: throw new IllegalArgumentException(
167: "This content provider only works with input of type IReadableSet"); //$NON-NLS-1$
168: }
169:
170: setInput((IObservableSet) newInput);
171: }
172:
173: private boolean isDisposed() {
174: return viewer.getControl() == null
175: || viewer.getControl().isDisposed();
176: }
177:
178: private void setInput(IObservableSet newSet) {
179: boolean updateViewer = true;
180: if (newSet == null) {
181: newSet = new ObservableSet(SWTObservables.getRealm(Display
182: .getDefault()), Collections.EMPTY_SET, Object.class) {
183: };
184: // don't update the viewer - its input is null
185: updateViewer = false;
186: }
187:
188: boolean wasStale = false;
189: if (readableSet != null) {
190: wasStale = readableSet.isStale();
191: readableSet.removeSetChangeListener(listener);
192: readableSet.removeStaleListener(staleListener);
193: }
194:
195: HashSet additions = new HashSet();
196: HashSet removals = new HashSet();
197:
198: additions.addAll(newSet);
199: additions.removeAll(readableSet);
200:
201: removals.addAll(readableSet);
202: removals.removeAll(newSet);
203:
204: readableSet = newSet;
205:
206: doDiff(additions, removals, updateViewer);
207:
208: if (readableSet != null) {
209: readableSet.addSetChangeListener(listener);
210: readableSet.addStaleListener(staleListener);
211: }
212:
213: boolean isStale = (readableSet != null && readableSet.isStale());
214: if (isStale != wasStale) {
215: knownElements.doFireStale(isStale);
216: }
217: }
218:
219: }
|