blob: 33fc5720cc7454cb5b39759ba2448f28de2ae5de [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2013 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 org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jpt.common.ui.jface.ItemExtendedLabelProvider;
import org.eclipse.jpt.common.ui.jface.ItemLabelProvider;
import org.eclipse.jpt.common.utility.internal.ObjectTools;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.swt.graphics.Image;
/**
* Abstract {@link ItemLabelProvider} that provides support for listening to an
* {@link #item} and notifying the
* {@link org.eclipse.jpt.common.ui.jface.ItemExtendedLabelProvider.Manager manager}
* whenever the item changes in a significant way.
* <p>
* Subclasses can implement the following methods if the corresponding aspects
* of the {@link #item} change:<ul>
* <li>{@link #buildImageDescriptorModel()}<br>
* Return a {@link PropertyValueModel} that represents the item's image descriptor
* <li>{@link #buildTextModel()}<br>
* Return a {@link PropertyValueModel} that represents the item's text
* <li>{@link #buildDescriptionModel()}<br>
* Return a {@link PropertyValueModel} that represents the item's description
* </ul>
* Alternatively, subclasses can implement the following methods if the
* corresponding aspects of the {@link #item} do <em>not</em> change:<ul>
* <li>{@link #getImageDescriptor()}<br>
* Return the item's image descriptor
* <li>{@link #getText()}<br>
* Return the item's text
* <li>{@link #getDescription()}<br>
* Return the item's description
* </ul>
* For each aspect (image descriptor, text, and description) one and only one
* of the two methods must be overridden.
*
* @see StaticItemExtendedLabelProvider
*/
public abstract class AbstractItemExtendedLabelProvider<I>
implements ItemExtendedLabelProvider
{
protected final I item;
protected final ItemExtendedLabelProvider.Manager manager;
protected volatile PropertyValueModel<ImageDescriptor> imageDescriptorModel;
protected volatile PropertyChangeListener imageDescriptorListener;
protected volatile PropertyValueModel<String> textModel;
protected volatile PropertyChangeListener textListener;
protected volatile PropertyValueModel<String> descriptionModel;
protected volatile PropertyChangeListener descriptionListener;
protected AbstractItemExtendedLabelProvider(I item, ItemExtendedLabelProvider.Manager manager) {
super();
if (item == null) {
throw new NullPointerException();
}
if (manager == null) {
throw new NullPointerException();
}
this.item = item;
this.manager = manager;
}
// ********** image **********
public final Image getImage() {
ImageDescriptor descriptor = this.getImageDescriptor();
return (descriptor == null) ? null : this.manager.getResourceManager().createImage(descriptor);
}
protected ImageDescriptor getImageDescriptor() {
return this.getImageDescriptorModel().getValue();
}
/**
* Return the image descriptor model (lazy-initialized).
*/
protected synchronized PropertyValueModel<ImageDescriptor> getImageDescriptorModel() {
if (this.imageDescriptorModel == null) {
this.imageDescriptorModel = this.buildImageDescriptorModel();
this.engageImageDescriptorModel();
}
return this.imageDescriptorModel;
}
/**
* Construct an image descriptor model.
*/
protected PropertyValueModel<ImageDescriptor> buildImageDescriptorModel() {
throw new UnsupportedOperationException();
}
protected void engageImageDescriptorModel() {
this.imageDescriptorModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.getImageDescriptorListener());
}
protected void disengageImageDescriptorModel() {
this.imageDescriptorModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.imageDescriptorListener);
}
protected void disposeImageDescriptorModel() {
if (this.imageDescriptorModel != null) {
// for now, leave the image in the resource manager cache
// to be disposed when the view is disposed
this.disengageImageDescriptorModel();
this.imageDescriptorModel = null;
}
}
// ********** image descriptor listener **********
protected synchronized PropertyChangeListener getImageDescriptorListener() {
if (this.imageDescriptorListener == null) {
this.imageDescriptorListener = this.buildImageDescriptorListener();
}
return this.imageDescriptorListener;
}
/**
* Build a listener that will listen to the {@link #imageDescriptorModel}.
*/
protected PropertyChangeListener buildImageDescriptorListener() {
return new ImageDescriptorListener();
}
/* CU private */ class ImageDescriptorListener
extends PropertyChangeAdapter
{
@Override
public void propertyChanged(PropertyChangeEvent event) {
AbstractItemExtendedLabelProvider.this.imageDescriptorChanged();
}
}
/* CU private */ void imageDescriptorChanged() {
this.manager.updateLabel(this.item);
}
// ********** text **********
public String getText() {
return this.getTextModel().getValue();
}
/**
* Return the text model (lazy-initialized).
*/
protected synchronized PropertyValueModel<String> getTextModel() {
if (this.textModel == null) {
this.textModel = this.buildTextModel();
this.engageTextModel();
}
return this.textModel;
}
/**
* Construct a text model.
*/
protected PropertyValueModel<String> buildTextModel() {
throw new UnsupportedOperationException();
}
protected void engageTextModel() {
this.textModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.getTextListener());
}
protected void disengageTextModel() {
this.textModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.textListener);
}
protected void disposeTextModel() {
if (this.textModel != null) {
this.disengageTextModel();
this.textModel = null;
}
}
// ********** text listener **********
protected synchronized PropertyChangeListener getTextListener() {
if (this.textListener == null) {
this.textListener = this.buildTextListener();
}
return this.textListener;
}
/**
* Build a listener that will listen to the {@link #textModel}.
*/
protected PropertyChangeListener buildTextListener() {
return new TextListener();
}
/* CU private */ class TextListener
extends PropertyChangeAdapter
{
@Override
public void propertyChanged(PropertyChangeEvent event) {
AbstractItemExtendedLabelProvider.this.textChanged();
}
}
/* CU private */ void textChanged() {
this.manager.updateLabel(this.item);
}
// ********** description **********
public String getDescription() {
return this.getDescriptionModel().getValue();
}
/**
* Return the description model (lazy-initialized).
*/
protected synchronized PropertyValueModel<String> getDescriptionModel() {
if (this.descriptionModel == null) {
this.descriptionModel = this.buildDescriptionModel();
this.engageDescriptionModel();
}
return this.descriptionModel;
}
/**
* Construct a description model.
*/
protected PropertyValueModel<String> buildDescriptionModel() {
throw new UnsupportedOperationException();
}
protected void engageDescriptionModel() {
this.descriptionModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.getDescriptionListener());
}
protected void disengageDescriptionModel() {
this.descriptionModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.descriptionListener);
}
protected void disposeDescriptionModel() {
if (this.descriptionModel != null) {
this.disengageDescriptionModel();
this.descriptionModel = null;
}
}
// ********** description listener **********
protected synchronized PropertyChangeListener getDescriptionListener() {
if (this.descriptionListener == null) {
this.descriptionListener = this.buildDescriptionListener();
}
return this.descriptionListener;
}
/**
* Build a listener that will listen to the {@link #descriptionModel}.
*/
protected PropertyChangeListener buildDescriptionListener() {
return new DescriptionListener();
}
/* CU private */ class DescriptionListener
extends PropertyChangeAdapter
{
@Override
public void propertyChanged(PropertyChangeEvent event) {
AbstractItemExtendedLabelProvider.this.descriptionChanged();
}
}
/* CU private */ void descriptionChanged() {
this.manager.updateDescription(this.item);
}
// ********** dispose **********
public synchronized void dispose() {
this.disposeImageDescriptorModel();
this.disposeTextModel();
this.disposeDescriptionModel();
}
@Override
public String toString() {
return ObjectTools.toString(this, this.item);
}
}