/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.uitools.app.swing;
import javax.swing.AbstractListModel;
import javax.swing.event.ListDataListener;
import org.eclipse.persistence.tools.workbench.uitools.app.CollectionListValueModelAdapter;
import org.eclipse.persistence.tools.workbench.uitools.app.CollectionValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ListValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.ValueModel;
import org.eclipse.persistence.tools.workbench.utility.events.ListChangeEvent;
import org.eclipse.persistence.tools.workbench.utility.events.ListChangeListener;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
/**
* This javax.swing.ListModel can be used to keep a ListDataListener
* (e.g. a JList) in synch with a ListValueModel (or a CollectionValueModel).
*
* An instance of this ListModel *must* be supplied with a value model,
* which is a ListValueModel on the bound list or a CollectionValueModel
* on the bound collection. This is required - the list (or collection)
* itself can be null, but the value model that holds it cannot.
*/
public class ListModelAdapter extends AbstractListModel {
/** A value model on the underlying model list. */
protected ListValueModel listHolder;
/**
* Cache the size of the list for "dramatic" changes.
* @see #listChanged(ListChangeEvent)
*/
protected int listSize;
/** A listener that allows us to forward changes made to the underlying model list. */
protected ListChangeListener listChangeListener;
// ********** constructors **********
/**
* Default constructor - initialize stuff.
*/
private ListModelAdapter() {
super();
this.initialize();
}
/**
* Constructor - the list holder is required.
*/
public ListModelAdapter(ListValueModel listHolder) {
this();
this.setModel(listHolder);
}
/**
* Constructor - the collection holder is required.
*/
public ListModelAdapter(CollectionValueModel collectionHolder) {
this();
this.setModel(collectionHolder);
}
// ********** initialization **********
protected void initialize() {
this.listSize = 0;
this.listChangeListener = this.buildListChangeListener();
}
protected ListChangeListener buildListChangeListener() {
return new ListChangeListener() {
public void itemsAdded(ListChangeEvent e) {
ListModelAdapter.this.itemsAdded(e);
}
public void itemsRemoved(ListChangeEvent e) {
ListModelAdapter.this.itemsRemoved(e);
}
public void itemsReplaced(ListChangeEvent e) {
ListModelAdapter.this.itemsReplaced(e);
}
public void listChanged(ListChangeEvent e) {
ListModelAdapter.this.listChanged();
}
public String toString() {
return "list listener";
}
};
}
// ********** ListModel implementation **********
/**
* @see javax.swing.ListModel#getSize()
*/
public int getSize() {
return this.listHolder.size();
}
/**
* @see javax.swing.ListModel#getElementAt(int)
*/
public Object getElementAt(int index) {
return this.listHolder.getItem(index);
}
/**
* Extend to start listening to the underlying model list if necessary.
* @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener)
*/
public void addListDataListener(ListDataListener l) {
if (this.hasNoListDataListeners()) {
this.engageModel();
this.listSize = this.listHolder.size();
}
super.addListDataListener(l);
}
/**
* Extend to stop listening to the underlying model list if appropriate.
* @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener)
*/
public void removeListDataListener(ListDataListener l) {
super.removeListDataListener(l);
if (this.hasNoListDataListeners()) {
this.disengageModel();
this.listSize = 0;
}
}
// ********** public API **********
/**
* Return the underlying list model.
*/
public ListValueModel getModel() {
return this.listHolder;
}
/**
* Set the underlying list model.
*/
public void setModel(ListValueModel listHolder) {
if (listHolder == null) {
throw new NullPointerException();
}
boolean hasListeners = this.hasListDataListeners();
if (hasListeners) {
this.disengageModel();
}
this.listHolder = listHolder;
if (hasListeners) {
this.engageModel();
this.listChanged();
}
}
/**
* Set the underlying collection model.
*/
public void setModel(CollectionValueModel collectionHolder) {
this.setModel(new CollectionListValueModelAdapter(collectionHolder));
}
// ********** queries **********
/**
* Return whether this model has no listeners.
*/
protected boolean hasNoListDataListeners() {
return this.getListDataListeners().length == 0;
}
/**
* Return whether this model has any listeners.
*/
protected boolean hasListDataListeners() {
return ! this.hasNoListDataListeners();
}
// ********** behavior **********
protected void engageModel() {
this.listHolder.addListChangeListener(ValueModel.VALUE, this.listChangeListener);
}
protected void disengageModel() {
this.listHolder.removeListChangeListener(ValueModel.VALUE, this.listChangeListener);
}
// ********** list change support **********
/**
* Items were added to the underlying model list.
* Notify listeners of the changes.
*/
protected void itemsAdded(ListChangeEvent e) {
int start = e.getIndex();
int end = start + e.size() - 1;
this.fireIntervalAdded(this, start, end);
this.listSize += e.size();
}
/**
* Items were removed from the underlying model list.
* Notify listeners of the changes.
*/
protected void itemsRemoved(ListChangeEvent e) {
int start = e.getIndex();
int end = start + e.size() - 1;
this.fireIntervalRemoved(this, start, end);
this.listSize -= e.size();
}
/**
* Items were replaced in the underlying model list.
* Notify listeners of the changes.
*/
protected void itemsReplaced(ListChangeEvent e) {
int start = e.getIndex();
int end = start + e.size() - 1;
this.fireContentsChanged(this, start, end);
}
/**
* The underlying model list has changed "dramatically".
* Notify listeners of the changes.
*/
protected void listChanged() {
if (this.listSize != 0) {
this.fireIntervalRemoved(this, 0, this.listSize - 1);
}
this.listSize = this.listHolder.size();
if (this.listSize != 0) {
this.fireIntervalAdded(this, 0, this.listSize - 1);
} else {
// this can happen when the "context" of the list changes;
// even though there are no items in the list,
// listeners might want to know that the list has "changed"
this.fireContentsChanged(this, 0, -1);
}
}
// ********** Object overrides **********
public String toString() {
return StringTools.buildToStringFor(this, this.listHolder);
}
}