/* * 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(); } }