/*
* ControllerListModel.java
*
* Copyright (c) 2001-2002 Digi International
* This program and the information contained in it is confidential and
* proprietary to Digi International and may not be used, copied, or re-
* produced without the prior written permission of Digi International.
*
*/
package com.digi.config.ui;
import com.digi.config.core.*;
import com.digi.config.util.*;
import java.io.Serializable;
import java.util.*;
import javax.swing.ListModel;
import javax.swing.event.*;
/**
* This class serves as a ListModel adapter for a config Controller
* object. It listens for items to be added or removed from the controller
* and turns them into the appropriate ListModel events.
*
*/
public class ControllerListModel implements ListModel, Serializable {
/** The controller we are providing a ListModel adapter over */
protected Controller controller;
/** Flag which indicates list model being updated */
protected boolean listUpdates=false;
/** A chain of filters used to determine what controlled items should not be exposed by this list model */
protected ArrayList filterChain;
/**
* This is a list of items being exposed by the ListModel. It is based on the
* contents of the controller but only contains items that have passed through
* the filters in the filterChain.
*/
protected ArrayMap exposedItems;
/** List of listeners */
protected EventListenerList listenerList=new EventListenerList();
/**
* Adds a filter to the controller list model. A filter can block a controlled
* item from being exposed by the controller list model. This filter will be
* added to a chain of filters maintained by the controller list model. All
* filters in the chain have the opportunity to block out controlled items.
*
* @param filter the filter to add to the controller list model filter chain
*/
public void addFilter(ControlledItemFilter filter) {
filterChain.add(filter);
resetExposedItems();
}
/**
* Removes a filter to the controller list model. A filter can block a controlled
* item from being exposed by the controller list model. This filter will be
* removed from the chain of filters maintained by the controller list model. All
* filters in the chain have the opportunity to block out controlled items.
*
* @param filter the filter to be removed from the controller list model filter chain
*/
public void removeFilter(ControlledItemFilter filter) {
filterChain.remove(filter);
resetExposedItems();
}
/**
* Called when a current filter has been updated.
*/
public void updateFilter(ControlledItemFilter filter) {
resetExposedItems();
}
/**
* Resets the controlled items that are exposed by this ControllerListModel.
* This method applies all the filters added to the list model to determine which
* controlled items will be filtered out (ie not exposed). Only items that
* pass all the filters will be exposed by this list model. After the exposed
* items have been determined, this method fires a ListChangedEvent indicating
* the entire list has been updated.
*/
protected void resetExposedItems() {
exposedItems.clear();
for (Iterator i=controller.getIterator(); i.hasNext(); ) {
ControlledItem item=(ControlledItem)i.next();
if (!isFiltered(item)) {
exposedItems.put(item, item);
}
}
refreshList();
}
/**
* Determines if the specified controlled item is filtered by any of the
* filters held by the ControllerListModel. If it is filtered, it will not
* be exposed by the ControllerListModel.
*
* @param item the controlled item to test for filtering
* @return boolean flag indicating if the item is filtered or not
*/
protected boolean isFiltered(ControlledItem item) {
for (Iterator i=filterChain.iterator(); i.hasNext(); ) {
ControlledItemFilter filter=(ControlledItemFilter)i.next();
if (filter.isFiltered(item)) {
//System.out.println("Item is filtered: "+item);
return true;
}
}
//System.out.println("Item is not filtered: "+item);
return false;
}
/**
* Given the position of a controlledItem in the underlying controller, this
* method determines the position that item should be placed in the ExposedItemList
*
* @param controlledItemPosition the original position of the item in the underlying controller
* @return the position in the ExposedItemList that this item should be placed at
*/
protected int findExposedItemPosition(int controlledItemPosition) {
// Try walking backwards in the controller to find the previous exposed item
Object controlledItems[]=controller.getElements().toArray();
ControlledItem prevItem=null;
for (int i=controlledItemPosition-1; i>=0; i--) {
ControlledItem item=(ControlledItem)controlledItems[i];
if (exposedItems.get(item)!=null) {
// Once found, this item will be in the next exposedItem position
return exposedItems.valuesList().indexOf(item)+1;
}
}
// If no previous items were exposed, then we are the first
return 0;
}
/**
* Construct a new ControllerListModel over the specified controller
*/
ControllerListModel(Controller controller) {
// Save off the controller
this.controller=controller;
this.filterChain=new ArrayList();
this.exposedItems=new ArrayMap();
// Listen for items being added or removed from the controller
controller.addControllerMembershipListener(new ControllerMembershipListener() {
public void itemsAdded(ControllerMembershipEvent e) {
controllerItemsAdded(e);
}
public void itemsRemoved(ControllerMembershipEvent e) {
controllerItemsRemoved(e);
}
public void itemsResorted(Controller c) {
controllerItemsResorted();
}
});
// Notify the list that it should be refreshed
resetExposedItems();
}
//---------------------------------------------------------------------------------
// ListModel methods
//---------------------------------------------------------------------------------
/**
* Returns the length of the list.
*/
public int getSize() {
return this.exposedItems.size();
}
/**
* Returns the value at the specified index.
*/
public Object getElementAt(int index) {
return(ControlledItem)exposedItems.valuesList().get(index);
}
/**
* Add a listener to the list that's notified each time a change
* to the data model occurs.
* @param l the ListDataListener
*/
public void addListDataListener(ListDataListener l) {
listenerList.add(ListDataListener.class, l);
}
/**
* Remove a listener from the list that's notified each time a
* change to the data model occurs.
* @param l the ListDataListener
*/
public void removeListDataListener(ListDataListener l) {
listenerList.remove(ListDataListener.class, l);
}
/**
* AbstractListModel subclasses must call this method after
* one or more elements of the list change. The changed elements
* are specified by a closed interval index0, index1, i.e. the
* range that includes both index0 and index1. Note that
* index0 need not be less than or equal to index1.
*
* @param index0 One end of the new interval.
* @param index1 The other end of the new interval.
* @see EventListenerList
* @see javax.swing.DefaultListModel
*/
protected void fireListContentsChanged(int index0, int index1) {
Object[] listeners=listenerList.getListenerList();
ListDataEvent e=null;
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ListDataListener.class) {
if (e==null) {
e=new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED,
index0, index1);
}
((ListDataListener)listeners[i+1]).contentsChanged(e);
}
}
}
/**
* AbstractListModel subclasses must call this method after
* one or more elements are added to the model. The new elements
* are specified by a closed interval index0, index1, i.e. the
* range that includes both index0 and index1. Note that
* index0 need not be less than or equal to index1.
*
* @param index0 One end of the new interval.
* @param index1 The other end of the new interval.
* @see EventListenerList
* @see javax.swing.DefaultListModel
*/
protected void fireListIntervalAdded(int index0, int index1) {
Object[] listeners=listenerList.getListenerList();
ListDataEvent e=null;
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ListDataListener.class) {
if (e==null) {
e=new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED,
index0, index1);
}
((ListDataListener)listeners[i+1]).intervalAdded(e);
}
}
}
/**
* AbstractListModel subclasses must call this method after
* one or more elements are removed from the model. The new elements
* are specified by a closed interval index0, index1, i.e. the
* range that includes both index0 and index1. Note that
* index0 need not be less than or equal to index1.
*
* @param index0 One end of the new interval.
* @param index1 The other end of the new interval.
* @see EventListenerList
* @see javax.swing.DefaultListModel
*/
protected void fireListIntervalRemoved(int index0, int index1) {
Object[] listeners=listenerList.getListenerList();
ListDataEvent e=null;
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ListDataListener.class) {
if (e==null) {
e=new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED,
index0, index1);
}
((ListDataListener)listeners[i+1]).intervalRemoved(e);
}
}
}
//---------------------------------------------------------------------------------
// Internal implementation methods
//---------------------------------------------------------------------------------
/**
* Notify that rows were added to the exposed list
*/
protected void rowsAdded(int firstRow, int lastRow) {
listUpdates=true;
fireListIntervalAdded(firstRow, lastRow);
listUpdates=false;
}
/**
* Notify that rows were removed from the exposed list
*/
protected void rowsRemoved(int firstRow, int lastRow) {
listUpdates=true;
fireListIntervalRemoved(firstRow, lastRow);
listUpdates=false;
}
/**
* Notify that the exposed lists data has changed.
*/
protected void refreshList() {
listUpdates=true;
fireListContentsChanged(0, this.getSize());
listUpdates=false;
}
/**
* One or more items were added to the controller.
*
* @param e describes which items were added to the controller
*/
protected void controllerItemsAdded(ControllerMembershipEvent e) {
int itemCount=e.items().size();
if (itemCount>0) {
// If these items are exposed, where will they go in the exposedItems List?
int controllerPosition=e.getReferencePosition();
int firstExposedPosition=this.findExposedItemPosition(controllerPosition);
int lastExposedPosition=firstExposedPosition-1;
// look through each item to see which ones are actually exposed
for (Iterator i=e.iterator(); i.hasNext(); ) {
ControlledItem item=(ControlledItem)i.next();
if (!this.isFiltered(item)) {
this.exposedItems.putAt(item,
++lastExposedPosition, item);
}
}
// If any items were actually exposed, then trigger a rowAdded event
if (lastExposedPosition>=firstExposedPosition) {
//System.out.println("ItemsAdded. ControllerPosition=" +
// controllerPosition+". ExposedPosition range =" +
// firstExposedPosition+"-" +lastExposedPosition);
rowsAdded(firstExposedPosition, lastExposedPosition);
}
}
}
/**
* One or more items were removed from the controller.
*
* @param e describes which items were removed from the controller
*/
protected void controllerItemsRemoved(ControllerMembershipEvent e) {
int itemCount=e.items().size();
if (itemCount>0) {
// If these items were exposed, we need to find where they
// were in the exposedItems List?
int firstExposedPosition=-1;
int lastExposedPosition=firstExposedPosition-1;
// look through each item to see which ones are actually exposed
for (Iterator i=e.iterator(); i.hasNext(); ) {
ControlledItem item=(ControlledItem)i.next();
if (exposedItems.containsKey(item)) {
// if they were exposed update the exposedPosition indicators
if (firstExposedPosition==-1) {
firstExposedPosition=
exposedItems.valuesList().indexOf(item);
lastExposedPosition=firstExposedPosition;
} else {
lastExposedPosition++;
}
// and remove the item from the exposedItems list
exposedItems.remove(item);
}
}
// If any exposed items were actually removed, then trigger a rowRemoved event
if (lastExposedPosition>=firstExposedPosition) {
//System.out.println("ItemsRemoved. ExposedPosition range =" +
// firstExposedPosition+"-" +lastExposedPosition);
rowsRemoved(firstExposedPosition, lastExposedPosition);
}
}
}
/**
* The order of the elements in the controller have changed
**/
protected void controllerItemsResorted() {
// reset the exposed items and fire a refreshList event
this.resetExposedItems();
refreshList();
}
}