blob: c79b5a61f673cbed78537a412f5b614fd411a371 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2012 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.ui.internal.jface;
import java.util.ConcurrentModificationException;
import org.eclipse.jpt.common.ui.jface.ItemTreeContentProvider;
import org.eclipse.jpt.common.utility.internal.ArrayTools;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent;
import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent;
import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent;
import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent;
import org.eclipse.jpt.common.utility.model.listener.CollectionChangeAdapter;
import org.eclipse.jpt.common.utility.model.listener.CollectionChangeListener;
import org.eclipse.jpt.common.utility.model.value.CollectionValueModel;
/**
* Abstract {@link ItemTreeContentProvider} that provides support for listening to an
* {@link #item} and notifying the
* {@link org.eclipse.jpt.common.ui.jface.ItemTreeContentProvider.Manager manager}
* whenever the item's children change in a significant way.
* <p>
* Subclasses must implement the following method:<ul>
* <li>{@link #buildChildrenModel()}<br>
* Return a {@link CollectionValueModel} that represents the item's children
* </ul>
*
* @see StaticItemTreeContentProvider
*/
public abstract class AbstractItemTreeContentProvider<I, C>
implements ItemTreeContentProvider
{
protected final Manager manager;
protected final I item;
protected volatile CollectionValueModel<C> childrenModel;
protected volatile CollectionChangeListener childrenListener;
protected AbstractItemTreeContentProvider(I item, Manager manager) {
this.item = item;
this.manager = manager;
}
// ********** children **********
/**
* Typical implementation for a tree.
*/
public Object[] getElements() {
return this.getChildren();
}
// TODO bjv
public Object[] getChildren() {
while (true) {
try {
return ArrayTools.array(this.getChildrenModel());
} catch (ConcurrentModificationException ex) {
// try again - hack: need to make value model stuff thread-safe...
}
}
}
/**
* Return the children model (lazy-initialized).
*/
protected synchronized CollectionValueModel<C> getChildrenModel() {
if (this.childrenModel == null) {
this.childrenModel = this.buildChildrenModel();
this.childrenListener = this.buildChildrenListener();
this.engageChildrenModel();
}
return this.childrenModel;
}
/**
* Construct a children model.
*/
protected abstract CollectionValueModel<C> buildChildrenModel();
/**
* Override with potentially more efficient logic.
*/
public boolean hasChildren() {
return this.getChildrenModel().iterator().hasNext();
}
protected void engageChildrenModel() {
this.childrenModel.addCollectionChangeListener(CollectionValueModel.VALUES, this.childrenListener);
}
protected void disengageChildrenModel() {
this.childrenModel.removeCollectionChangeListener(CollectionValueModel.VALUES, this.childrenListener);
}
// ********** listener **********
protected CollectionChangeListener buildChildrenListener() {
return new ChildrenListener();
}
protected class ChildrenListener
extends CollectionChangeAdapter
{
@Override
@SuppressWarnings("unchecked")
public void itemsAdded(CollectionAddEvent event) {
AbstractItemTreeContentProvider.this.childrenAdded((Iterable<C>) event.getItems());
}
@Override
@SuppressWarnings("unchecked")
public void itemsRemoved(CollectionRemoveEvent event) {
AbstractItemTreeContentProvider.this.childrenRemoved((Iterable<C>) event.getItems());
}
/**
* <strong>NB:</strong> Any removed children will malinger until the
* manager itself is disposed.
*/
@Override
public void collectionChanged(CollectionChangeEvent event) {
AbstractItemTreeContentProvider.this.childrenChanged();
}
/**
* <strong>NB:</strong> Any removed children will malinger until the
* manager itself is disposed.
*/
@Override
public void collectionCleared(CollectionClearEvent event) {
AbstractItemTreeContentProvider.this.childrenChanged();
}
}
protected void childrenAdded(@SuppressWarnings("unused") Iterable<C> children) {
this.manager.updateChildren(this.item);
}
protected void childrenRemoved(Iterable<C> children) {
this.manager.updateChildren(this.item);
for (Object child : children) {
this.manager.dispose(child);
}
}
protected void childrenChanged() {
this.manager.updateChildren(this.item);
}
// ********** dispose **********
public synchronized void dispose() {
if (this.childrenModel != null) {
this.dispose_();
}
}
/**
* Pre-condition: {@link #childrenModel} is not <code>null</code>.
*/
protected void dispose_() {
for (Object child : this.childrenModel) {
this.manager.dispose(child);
}
this.disengageChildrenModel();
this.childrenModel = null;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.item);
}
}