components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/swing/view/ManagedObjectTableModel.java
changeset 827 0944d8c0158b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/visual-panels/core/src/java/vpanels/panel/com/oracle/solaris/vp/panel/swing/view/ManagedObjectTableModel.java	Thu May 24 04:16:47 2012 -0400
@@ -0,0 +1,307 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package com.oracle.solaris.vp.panel.swing.view;
+
+import java.beans.*;
+import java.util.*;
+import javax.swing.ListModel;
+import javax.swing.event.ListDataListener;
+import javax.swing.table.*;
+import com.oracle.solaris.vp.panel.common.model.ManagedObject;
+import com.oracle.solaris.vp.util.common.propinfo.PropInfo;
+import com.oracle.solaris.vp.util.misc.event.*;
+import com.oracle.solaris.vp.util.swing.*;
+import com.oracle.solaris.vp.util.swing.propinfo.SwingPropInfo;
+
+@SuppressWarnings({"serial"})
+public class ManagedObjectTableModel<C extends ManagedObject>
+    extends AbstractTableModel implements ListModel {
+
+    //
+    // Instance data
+    //
+
+    private ManagedObject<C> pObject;
+    private PropInfo<C, ?>[] props;
+    private List<C> children = new ArrayList<C>();
+
+    private Map<ListDataListener, ListDataListenerAdapter> listenerMap =
+	new HashMap<ListDataListener, ListDataListenerAdapter>();
+
+    private PropertyChangeListener pListener = new PropertyChangeListener() {
+	@Override
+	public void propertyChange(final PropertyChangeEvent e) {
+	    // We are likely not on the event queue thread
+	    GUIUtil.invokeAndWait(
+		new Runnable() {
+		    @Override
+		    public void run() {
+			int index = children.indexOf(e.getSource());
+			if (index != -1) {
+			    fireTableRowsUpdated(index, index);
+			}
+		    }
+		});
+	}
+    };
+
+    private IntervalListener iListener = new IntervalListener() {
+	@Override
+	public void intervalAdded(final IntervalEvent e) {
+	    // We are likely not on the event queue thread
+	    GUIUtil.invokeAndWait(
+		new Runnable() {
+		    @Override
+		    public void run() {
+			int f = e.getFirstIndex();
+			int l = e.getLastIndex();
+
+			/* synchronized pObject.getChildrenLock() implied */
+			List<C> adds = pObject.getChildren().subList(f, l + 1);
+
+			children.addAll(f, adds);
+
+			for (C mo : adds) {
+			    mo.addPropertyChangeListener(pListener);
+			}
+
+			fireTableRowsInserted(f, l);
+		    }
+		});
+	}
+
+	@Override
+	public void intervalRemoved(final IntervalEvent e) {
+	    // We are likely not on the event queue thread
+	    GUIUtil.invokeAndWait(
+		new Runnable() {
+		    @Override
+		    public void run() {
+			/*
+			 * It is likely the object being removed from the model
+			 * will be dereferenced and freed, but it isn't
+			 * guaranteed.  We need to remove ourselves so objects
+			 * don't accumulate stale references on us.
+			 */
+			int f = e.getFirstIndex();
+			int l = e.getLastIndex();
+			List<C> rems = children.subList(f, l + 1);
+
+			for (C mo : rems) {
+			    mo.removePropertyChangeListener(pListener);
+			}
+
+			rems.clear();
+			fireTableRowsDeleted(f, l);
+		    }
+		});
+	}
+    };
+
+    //
+    // Constructors
+    //
+
+    public ManagedObjectTableModel(ManagedObject<C> pObject,
+	PropInfo<C, ?>[] props) {
+
+	this.props = props;
+	setParentObject(pObject);
+    }
+
+    public ManagedObjectTableModel(ManagedObject<C> pObject) {
+	this(pObject, null);
+    }
+
+    //
+    // TableModel methods
+    //
+
+    @Override
+    public Class<?> getColumnClass(int column) {
+	try {
+	    return getValueAt(0, column).getClass();
+	}
+
+	// Most likely NullPointerException or IndexOutOfBoundsException
+	catch (Throwable e) {
+	    return Object.class;
+	}
+    }
+
+    @Override
+    public int getColumnCount() {
+	return props == null ? 0 : props.length;
+    }
+
+    @Override
+    public String getColumnName(int column) {
+	return props[column].getLabel();
+    }
+
+    @Override
+    public int getRowCount() {
+	return children.size();
+    }
+
+    @Override
+    public Object getValueAt(int row, int column) {
+	// Throws IndexOutOfBoundsException
+	C o = children.get(row);
+
+	return props[column].getValue(o);
+    }
+
+    @Override
+    public boolean isCellEditable(int row, int column) {
+	// Throws IndexOutOfBoundsException
+	C o = children.get(row);
+
+	return props[column].isEditable(o);
+    }
+
+    @Override
+    public void setValueAt(Object value, int row, int column) {
+	// Throws IndexOutOfBoundsException
+	C o = children.get(row);
+
+	@SuppressWarnings({"unchecked"})
+	PropInfo<C, Object> prop =
+	    (PropInfo<C, Object>)props[column];
+
+	prop.setValue(o, value);
+    }
+
+    //
+    // ListModel methods
+    //
+
+    @Override
+    public void addListDataListener(ListDataListener l) {
+	synchronized (listenerMap) {
+	    if (!listenerMap.containsKey(l)) {
+		ListDataListenerAdapter adapter =
+		    new ListDataListenerAdapter(l);
+
+		addTableModelListener(adapter);
+		listenerMap.put(l, adapter);
+	    }
+	}
+    }
+
+    @Override
+    public C getElementAt(int index) {
+	return children.get(index);
+    }
+
+    @Override
+    public int getSize() {
+	return getRowCount();
+    }
+
+    @Override
+    public void removeListDataListener(ListDataListener l) {
+	synchronized (listenerMap) {
+	    ListDataListenerAdapter adapter = listenerMap.get(l);
+	    if (adapter != null) {
+		removeTableModelListener(adapter);
+		listenerMap.remove(l);
+	    }
+	}
+    }
+
+    //
+    // ManagedObjectTableModel methods
+    //
+
+    public ManagedObject<C> getParentObject() {
+	return pObject;
+    }
+
+    public boolean isSortable(int column) {
+	return props[column].isSortable();
+    }
+
+    public TableCellEditor getTableCellEditor(int column) {
+	TableCellEditor editor = null;
+
+	PropInfo d = props[column];
+	if (d instanceof SwingPropInfo) {
+	    editor = ((SwingPropInfo)d).getTableCellEditor();
+	}
+
+	return editor;
+    }
+
+    public TableCellRenderer getTableCellRenderer(int column) {
+	TableCellRenderer renderer = null;
+
+	PropInfo d = props[column];
+	if (d instanceof SwingPropInfo) {
+	    renderer = ((SwingPropInfo)d).getTableCellRenderer();
+	}
+
+	return renderer;
+    }
+
+    public Comparator<?> getComparator(int column) {
+	return props[column].getComparator();
+    }
+
+    public boolean isCellEnabled(int row, int column) {
+	// Throws IndexOutOfBoundsException
+	C o = children.get(row);
+
+	return props[column].isEnabled(o);
+    }
+
+    public void setParentObject(ManagedObject<C> pObject) {
+	if (this.pObject != null) {
+	    this.pObject.removeIntervalListener(iListener);
+	    for (int i = children.size() - 1; i >= 0; i--) {
+		C child = children.get(i);
+		child.removePropertyChangeListener(pListener);
+		children.remove(i);
+	    }
+
+	    fireTableRowsDeleted(0, children.size() - 1);
+	}
+
+	assert (children.isEmpty());
+
+	this.pObject = pObject;
+
+	if (pObject != null) {
+	    for (C child : pObject.getChildren()) {
+		child.addPropertyChangeListener(pListener);
+		children.add(child);
+	    }
+
+	    fireTableRowsInserted(0, children.size() - 1);
+	    pObject.addIntervalListener(iListener);
+	}
+    }
+}