| /* |
| * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.tools.jconsole.inspector; |
| |
| |
| import java.awt.Component; |
| import java.awt.EventQueue; |
| import java.awt.Dimension; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.io.IOException; |
| |
| import java.lang.reflect.Array; |
| |
| import java.util.EventObject; |
| import java.util.HashMap; |
| import java.util.WeakHashMap; |
| |
| import java.util.concurrent.ExecutionException; |
| import java.lang.System.Logger; |
| import java.lang.System.Logger.Level; |
| import javax.management.JMException; |
| import javax.management.MBeanInfo; |
| import javax.management.MBeanAttributeInfo; |
| import javax.management.AttributeList; |
| import javax.management.Attribute; |
| import javax.management.openmbean.CompositeData; |
| import javax.management.openmbean.TabularData; |
| |
| import javax.swing.JComponent; |
| import javax.swing.JOptionPane; |
| import javax.swing.JTable; |
| import javax.swing.JTextField; |
| import javax.swing.SwingWorker; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.TableModelEvent; |
| import javax.swing.event.TableModelListener; |
| import javax.swing.table.DefaultTableCellRenderer; |
| import javax.swing.table.DefaultTableModel; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import javax.swing.table.TableColumn; |
| import javax.swing.table.TableColumnModel; |
| import javax.swing.table.TableModel; |
| |
| import sun.tools.jconsole.MBeansTab; |
| import sun.tools.jconsole.JConsole; |
| import sun.tools.jconsole.Messages; |
| import sun.tools.jconsole.ProxyClient.SnapshotMBeanServerConnection; |
| |
| /*IMPORTANT : |
| There is a deadlock issue there if we don't synchronize well loadAttributes, |
| refresh attributes and empty table methods since a UI thread can call |
| loadAttributes and at the same time a JMX notification can raise an |
| emptyTable. Since there are synchronization in the JMX world it's |
| COMPULSORY to not call the JMX world in synchronized blocks */ |
| @SuppressWarnings("serial") |
| public class XMBeanAttributes extends XTable { |
| |
| final Logger LOGGER = |
| System.getLogger(XMBeanAttributes.class.getPackage().getName()); |
| |
| private final static String[] columnNames = |
| {Messages.NAME, |
| Messages.VALUE}; |
| |
| private XMBean mbean; |
| private MBeanInfo mbeanInfo; |
| private MBeanAttributeInfo[] attributesInfo; |
| private HashMap<String, Object> attributes; |
| private HashMap<String, Object> unavailableAttributes; |
| private HashMap<String, Object> viewableAttributes; |
| private WeakHashMap<XMBean, HashMap<String, ZoomedCell>> viewersCache = |
| new WeakHashMap<XMBean, HashMap<String, ZoomedCell>>(); |
| private final TableModelListener attributesListener; |
| private MBeansTab mbeansTab; |
| private TableCellEditor valueCellEditor = new ValueCellEditor(); |
| private int rowMinHeight = -1; |
| private AttributesMouseListener mouseListener = new AttributesMouseListener(); |
| |
| private static TableCellEditor editor = |
| new Utils.ReadOnlyTableCellEditor(new JTextField()); |
| |
| public XMBeanAttributes(MBeansTab mbeansTab) { |
| super(); |
| this.mbeansTab = mbeansTab; |
| ((DefaultTableModel)getModel()).setColumnIdentifiers(columnNames); |
| attributesListener = new AttributesListener(this); |
| getModel().addTableModelListener(attributesListener); |
| getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(40); |
| |
| addMouseListener(mouseListener); |
| getTableHeader().setReorderingAllowed(false); |
| setColumnEditors(); |
| addKeyListener(new Utils.CopyKeyAdapter()); |
| } |
| |
| @Override |
| public synchronized Component prepareRenderer(TableCellRenderer renderer, |
| int row, int column) { |
| //In case we have a repaint thread that is in the process of |
| //repainting an obsolete table, just ignore the call. |
| //It can happen when MBean selection is switched at a very quick rate |
| if(row >= getRowCount()) |
| return null; |
| else |
| return super.prepareRenderer(renderer, row, column); |
| } |
| |
| void updateRowHeight(Object obj, int row) { |
| ZoomedCell cell = null; |
| if(obj instanceof ZoomedCell) { |
| cell = (ZoomedCell) obj; |
| if(cell.isInited()) |
| setRowHeight(row, cell.getHeight()); |
| else |
| if(rowMinHeight != - 1) |
| setRowHeight(row, rowMinHeight); |
| } else |
| if(rowMinHeight != - 1) |
| setRowHeight(row, rowMinHeight); |
| } |
| |
| @Override |
| public synchronized TableCellRenderer getCellRenderer(int row, |
| int column) { |
| //In case we have a repaint thread that is in the process of |
| //repainting an obsolete table, just ignore the call. |
| //It can happen when MBean selection is switched at a very quick rate |
| if (row >= getRowCount()) { |
| return null; |
| } else { |
| if (column == VALUE_COLUMN) { |
| Object obj = getModel().getValueAt(row, column); |
| if (obj instanceof ZoomedCell) { |
| ZoomedCell cell = (ZoomedCell) obj; |
| if (cell.isInited()) { |
| DefaultTableCellRenderer renderer = |
| (DefaultTableCellRenderer) cell.getRenderer(); |
| renderer.setToolTipText(getToolTip(row,column)); |
| return renderer; |
| } |
| } |
| } |
| DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) |
| super.getCellRenderer(row, column); |
| if (!isCellError(row, column)) { |
| if (!(isColumnEditable(column) && isWritable(row) && |
| Utils.isEditableType(getClassName(row)))) { |
| renderer.setForeground(getDefaultColor()); |
| } |
| } |
| return renderer; |
| } |
| } |
| |
| private void setColumnEditors() { |
| TableColumnModel tcm = getColumnModel(); |
| for (int i = 0; i < columnNames.length; i++) { |
| TableColumn tc = tcm.getColumn(i); |
| if (isColumnEditable(i)) { |
| tc.setCellEditor(valueCellEditor); |
| } else { |
| tc.setCellEditor(editor); |
| } |
| } |
| } |
| |
| public void cancelCellEditing() { |
| if (LOGGER.isLoggable(Level.TRACE)) { |
| LOGGER.log(Level.TRACE, "Cancel Editing Row: "+getEditingRow()); |
| } |
| final TableCellEditor tableCellEditor = getCellEditor(); |
| if (tableCellEditor != null) { |
| tableCellEditor.cancelCellEditing(); |
| } |
| } |
| |
| public void stopCellEditing() { |
| if (LOGGER.isLoggable(Level.TRACE)) { |
| LOGGER.log(Level.TRACE, "Stop Editing Row: "+getEditingRow()); |
| } |
| final TableCellEditor tableCellEditor = getCellEditor(); |
| if (tableCellEditor != null) { |
| tableCellEditor.stopCellEditing(); |
| } |
| } |
| |
| @Override |
| public final boolean editCellAt(final int row, final int column, EventObject e) { |
| if (LOGGER.isLoggable(Level.TRACE)) { |
| LOGGER.log(Level.TRACE, "editCellAt(row="+row+", col="+column+ |
| ", e="+e+")"); |
| } |
| if (JConsole.isDebug()) { |
| System.err.println("edit: "+getValueName(row)+"="+getValue(row)); |
| } |
| boolean retVal = super.editCellAt(row, column, e); |
| if (retVal) { |
| final TableCellEditor tableCellEditor = |
| getColumnModel().getColumn(column).getCellEditor(); |
| if (tableCellEditor == valueCellEditor) { |
| ((JComponent) tableCellEditor).requestFocus(); |
| } |
| } |
| return retVal; |
| } |
| |
| @Override |
| public boolean isCellEditable(int row, int col) { |
| // All the cells in non-editable columns are editable |
| if (!isColumnEditable(col)) { |
| return true; |
| } |
| // Maximized zoomed cells are editable |
| Object obj = getModel().getValueAt(row, col); |
| if (obj instanceof ZoomedCell) { |
| ZoomedCell cell = (ZoomedCell) obj; |
| return cell.isMaximized(); |
| } |
| return true; |
| } |
| |
| @Override |
| public void setValueAt(Object value, int row, int column) { |
| if (!isCellError(row, column) && isColumnEditable(column) && |
| isWritable(row) && Utils.isEditableType(getClassName(row))) { |
| if (JConsole.isDebug()) { |
| System.err.println("validating [row="+row+", column="+column+ |
| "]: "+getValueName(row)+"="+value); |
| } |
| super.setValueAt(value, row, column); |
| } |
| } |
| |
| //Table methods |
| |
| public boolean isTableEditable() { |
| return true; |
| } |
| |
| public void setTableValue(Object value, int row) { |
| } |
| |
| public boolean isColumnEditable(int column) { |
| if (column < getColumnCount()) { |
| return getColumnName(column).equals(Messages.VALUE); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| public String getClassName(int row) { |
| int index = convertRowToIndex(row); |
| if (index != -1) { |
| return attributesInfo[index].getType(); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| |
| public String getValueName(int row) { |
| int index = convertRowToIndex(row); |
| if (index != -1) { |
| return attributesInfo[index].getName(); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| public Object getValue(int row) { |
| final Object val = ((DefaultTableModel) getModel()) |
| .getValueAt(row, VALUE_COLUMN); |
| return val; |
| } |
| |
| //tool tip only for editable column |
| @Override |
| public String getToolTip(int row, int column) { |
| if (isCellError(row, column)) { |
| return (String) unavailableAttributes.get(getValueName(row)); |
| } |
| if (isColumnEditable(column)) { |
| Object value = getValue(row); |
| String tip = null; |
| if (value != null) { |
| tip = value.toString(); |
| if(isAttributeViewable(row, VALUE_COLUMN)) |
| tip = Messages.DOUBLE_CLICK_TO_EXPAND_FORWARD_SLASH_COLLAPSE+ |
| ". " + tip; |
| } |
| |
| return tip; |
| } |
| |
| if(column == NAME_COLUMN) { |
| int index = convertRowToIndex(row); |
| if (index != -1) { |
| return attributesInfo[index].getDescription(); |
| } |
| } |
| return null; |
| } |
| |
| public synchronized boolean isWritable(int row) { |
| int index = convertRowToIndex(row); |
| if (index != -1) { |
| return (attributesInfo[index].isWritable()); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| /** |
| * Override JTable method in order to make any call to this method |
| * atomic with TableModel elements. |
| */ |
| @Override |
| public synchronized int getRowCount() { |
| return super.getRowCount(); |
| } |
| |
| public synchronized boolean isReadable(int row) { |
| int index = convertRowToIndex(row); |
| if (index != -1) { |
| return (attributesInfo[index].isReadable()); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| public synchronized boolean isCellError(int row, int col) { |
| return (isColumnEditable(col) && |
| (unavailableAttributes.containsKey(getValueName(row)))); |
| } |
| |
| public synchronized boolean isAttributeViewable(int row, int col) { |
| boolean isViewable = false; |
| if(col == VALUE_COLUMN) { |
| Object obj = getModel().getValueAt(row, col); |
| if(obj instanceof ZoomedCell) |
| isViewable = true; |
| } |
| |
| return isViewable; |
| } |
| |
| // Call this in EDT |
| public void loadAttributes(final XMBean mbean, final MBeanInfo mbeanInfo) { |
| |
| final SwingWorker<Runnable,Void> load = |
| new SwingWorker<Runnable,Void>() { |
| @Override |
| protected Runnable doInBackground() throws Exception { |
| return doLoadAttributes(mbean,mbeanInfo); |
| } |
| |
| @Override |
| protected void done() { |
| try { |
| final Runnable updateUI = get(); |
| if (updateUI != null) updateUI.run(); |
| } catch (RuntimeException x) { |
| throw x; |
| } catch (ExecutionException x) { |
| if(JConsole.isDebug()) { |
| System.err.println( |
| "Exception raised while loading attributes: " |
| +x.getCause()); |
| x.printStackTrace(); |
| } |
| } catch (InterruptedException x) { |
| if(JConsole.isDebug()) { |
| System.err.println( |
| "Interrupted while loading attributes: "+x); |
| x.printStackTrace(); |
| } |
| } |
| } |
| |
| }; |
| mbeansTab.workerAdd(load); |
| } |
| |
| // Don't call this in EDT, but execute returned Runnable inside |
| // EDT - typically in the done() method of a SwingWorker |
| // This method can return null. |
| private Runnable doLoadAttributes(final XMBean mbean, MBeanInfo infoOrNull) |
| throws JMException, IOException { |
| // To avoid deadlock with events coming from the JMX side, |
| // we retrieve all JMX stuff in a non synchronized block. |
| |
| if(mbean == null) return null; |
| final MBeanInfo curMBeanInfo = |
| (infoOrNull==null)?mbean.getMBeanInfo():infoOrNull; |
| |
| final MBeanAttributeInfo[] attrsInfo = curMBeanInfo.getAttributes(); |
| final HashMap<String, Object> attrs = |
| new HashMap<String, Object>(attrsInfo.length); |
| final HashMap<String, Object> unavailableAttrs = |
| new HashMap<String, Object>(attrsInfo.length); |
| final HashMap<String, Object> viewableAttrs = |
| new HashMap<String, Object>(attrsInfo.length); |
| AttributeList list = null; |
| |
| try { |
| list = mbean.getAttributes(attrsInfo); |
| }catch(Exception e) { |
| if (JConsole.isDebug()) { |
| System.err.println("Error calling getAttributes() on MBean \"" + |
| mbean.getObjectName() + "\". JConsole will " + |
| "try to get them individually calling " + |
| "getAttribute() instead. Exception:"); |
| e.printStackTrace(System.err); |
| } |
| list = new AttributeList(); |
| //Can't load all attributes, do it one after each other. |
| for(int i = 0; i < attrsInfo.length; i++) { |
| String name = null; |
| try { |
| name = attrsInfo[i].getName(); |
| Object value = |
| mbean.getMBeanServerConnection(). |
| getAttribute(mbean.getObjectName(), name); |
| list.add(new Attribute(name, value)); |
| }catch(Exception ex) { |
| if(attrsInfo[i].isReadable()) { |
| unavailableAttrs.put(name, |
| Utils.getActualException(ex).toString()); |
| } |
| } |
| } |
| } |
| try { |
| int att_length = list.size(); |
| for (int i=0;i<att_length;i++) { |
| Attribute attribute = (Attribute) list.get(i); |
| if(isViewable(attribute)) { |
| viewableAttrs.put(attribute.getName(), |
| attribute.getValue()); |
| } |
| else |
| attrs.put(attribute.getName(),attribute.getValue()); |
| |
| } |
| // if not all attributes are accessible, |
| // check them one after the other. |
| if (att_length < attrsInfo.length) { |
| for (int i=0;i<attrsInfo.length;i++) { |
| MBeanAttributeInfo attributeInfo = attrsInfo[i]; |
| if (!attrs.containsKey(attributeInfo.getName()) && |
| !viewableAttrs.containsKey(attributeInfo. |
| getName()) && |
| !unavailableAttrs.containsKey(attributeInfo. |
| getName())) { |
| if (attributeInfo.isReadable()) { |
| // getAttributes didn't help resolving the |
| // exception. |
| // We must call it again to understand what |
| // went wrong. |
| try { |
| Object v = |
| mbean.getMBeanServerConnection().getAttribute( |
| mbean.getObjectName(), attributeInfo.getName()); |
| //What happens if now it is ok? |
| // Be pragmatic, add it to readable... |
| attrs.put(attributeInfo.getName(), |
| v); |
| }catch(Exception e) { |
| //Put the exception that will be displayed |
| // in tooltip |
| unavailableAttrs.put(attributeInfo.getName(), |
| Utils.getActualException(e).toString()); |
| } |
| } |
| } |
| } |
| } |
| } |
| catch(Exception e) { |
| //sets all attributes unavailable except the writable ones |
| for (int i=0;i<attrsInfo.length;i++) { |
| MBeanAttributeInfo attributeInfo = attrsInfo[i]; |
| if (attributeInfo.isReadable()) { |
| unavailableAttrs.put(attributeInfo.getName(), |
| Utils.getActualException(e). |
| toString()); |
| } |
| } |
| } |
| //end of retrieval |
| |
| //one update at a time |
| return new Runnable() { |
| public void run() { |
| synchronized (XMBeanAttributes.this) { |
| XMBeanAttributes.this.mbean = mbean; |
| XMBeanAttributes.this.mbeanInfo = curMBeanInfo; |
| XMBeanAttributes.this.attributesInfo = attrsInfo; |
| XMBeanAttributes.this.attributes = attrs; |
| XMBeanAttributes.this.unavailableAttributes = unavailableAttrs; |
| XMBeanAttributes.this.viewableAttributes = viewableAttrs; |
| |
| DefaultTableModel tableModel = |
| (DefaultTableModel) getModel(); |
| |
| // add attribute information |
| emptyTable(tableModel); |
| |
| addTableData(tableModel, |
| mbean, |
| attrsInfo, |
| attrs, |
| unavailableAttrs, |
| viewableAttrs); |
| |
| // update the model with the new data |
| tableModel.newDataAvailable(new TableModelEvent(tableModel)); |
| // re-register for change events |
| tableModel.addTableModelListener(attributesListener); |
| } |
| } |
| }; |
| } |
| |
| void collapse(String attributeName, final Component c) { |
| final int row = getSelectedRow(); |
| Object obj = getModel().getValueAt(row, VALUE_COLUMN); |
| if(obj instanceof ZoomedCell) { |
| cancelCellEditing(); |
| ZoomedCell cell = (ZoomedCell) obj; |
| cell.reset(); |
| setRowHeight(row, |
| cell.getHeight()); |
| editCellAt(row, |
| VALUE_COLUMN); |
| invalidate(); |
| repaint(); |
| } |
| } |
| |
| ZoomedCell updateZoomedCell(int row, |
| int col) { |
| Object obj = getModel().getValueAt(row, VALUE_COLUMN); |
| ZoomedCell cell = null; |
| if(obj instanceof ZoomedCell) { |
| cell = (ZoomedCell) obj; |
| if(!cell.isInited()) { |
| Object elem = cell.getValue(); |
| String attributeName = |
| (String) getModel().getValueAt(row, |
| NAME_COLUMN); |
| Component comp = mbeansTab.getDataViewer(). |
| createAttributeViewer(elem, mbean, attributeName, this); |
| if(comp != null){ |
| if(rowMinHeight == -1) |
| rowMinHeight = getRowHeight(row); |
| |
| cell.init(super.getCellRenderer(row, col), |
| comp, |
| rowMinHeight); |
| |
| XDataViewer.registerForMouseEvent( |
| comp, mouseListener); |
| } else |
| return cell; |
| } |
| |
| cell.switchState(); |
| setRowHeight(row, |
| cell.getHeight()); |
| |
| if(!cell.isMaximized()) { |
| cancelCellEditing(); |
| //Back to simple editor. |
| editCellAt(row, |
| VALUE_COLUMN); |
| } |
| |
| invalidate(); |
| repaint(); |
| } |
| return cell; |
| } |
| |
| // This is called by XSheet when the "refresh" button is pressed. |
| // In this case we will commit any pending attribute values by |
| // calling 'stopCellEditing'. |
| // |
| public void refreshAttributes() { |
| refreshAttributes(true); |
| } |
| |
| // refreshAttributes(false) is called by tableChanged(). |
| // in this case we must not call stopCellEditing, because it's already |
| // been called - e.g. |
| // lostFocus/mousePressed -> stopCellEditing -> setValueAt -> tableChanged |
| // -> refreshAttributes(false) |
| // |
| // Can be called in EDT - as long as the implementation of |
| // mbeansTab.getCachedMBeanServerConnection() and mbsc.flush() doesn't |
| // change |
| // |
| private void refreshAttributes(final boolean stopCellEditing) { |
| SwingWorker<Void,Void> sw = new SwingWorker<Void,Void>() { |
| |
| @Override |
| protected Void doInBackground() throws Exception { |
| SnapshotMBeanServerConnection mbsc = |
| mbeansTab.getSnapshotMBeanServerConnection(); |
| mbsc.flush(); |
| return null; |
| } |
| |
| @Override |
| protected void done() { |
| try { |
| get(); |
| if (stopCellEditing) stopCellEditing(); |
| loadAttributes(mbean, mbeanInfo); |
| } catch (Exception x) { |
| if (JConsole.isDebug()) { |
| x.printStackTrace(); |
| } |
| } |
| } |
| }; |
| mbeansTab.workerAdd(sw); |
| } |
| // We need to call stop editing here - otherwise edits are lost |
| // when resizing the table. |
| // |
| @Override |
| public void columnMarginChanged(ChangeEvent e) { |
| if (isEditing()) stopCellEditing(); |
| super.columnMarginChanged(e); |
| } |
| |
| // We need to call stop editing here - otherwise the edited value |
| // is transferred to the wrong row... |
| // |
| @Override |
| void sortRequested(int column) { |
| if (isEditing()) stopCellEditing(); |
| super.sortRequested(column); |
| } |
| |
| |
| @Override |
| public synchronized void emptyTable() { |
| emptyTable((DefaultTableModel)getModel()); |
| } |
| |
| // Call this in synchronized block. |
| private void emptyTable(DefaultTableModel model) { |
| model.removeTableModelListener(attributesListener); |
| super.emptyTable(); |
| } |
| |
| private boolean isViewable(Attribute attribute) { |
| Object data = attribute.getValue(); |
| return XDataViewer.isViewableValue(data); |
| |
| } |
| |
| synchronized void removeAttributes() { |
| if (attributes != null) { |
| attributes.clear(); |
| } |
| if (unavailableAttributes != null) { |
| unavailableAttributes.clear(); |
| } |
| if (viewableAttributes != null) { |
| viewableAttributes.clear(); |
| } |
| mbean = null; |
| } |
| |
| private ZoomedCell getZoomedCell(XMBean mbean, String attribute, Object value) { |
| synchronized (viewersCache) { |
| HashMap<String, ZoomedCell> viewers; |
| if (viewersCache.containsKey(mbean)) { |
| viewers = viewersCache.get(mbean); |
| } else { |
| viewers = new HashMap<String, ZoomedCell>(); |
| } |
| ZoomedCell cell; |
| if (viewers.containsKey(attribute)) { |
| cell = viewers.get(attribute); |
| cell.setValue(value); |
| if (cell.isMaximized() && cell.getType() != XDataViewer.NUMERIC) { |
| // Plotters are the only viewers with auto update capabilities. |
| // Other viewers need to be updated manually. |
| Component comp = |
| mbeansTab.getDataViewer().createAttributeViewer( |
| value, mbean, attribute, XMBeanAttributes.this); |
| cell.init(cell.getMinRenderer(), comp, cell.getMinHeight()); |
| XDataViewer.registerForMouseEvent(comp, mouseListener); |
| } |
| } else { |
| cell = new ZoomedCell(value); |
| viewers.put(attribute, cell); |
| } |
| viewersCache.put(mbean, viewers); |
| return cell; |
| } |
| } |
| |
| //will be called in a synchronized block |
| protected void addTableData(DefaultTableModel tableModel, |
| XMBean mbean, |
| MBeanAttributeInfo[] attributesInfo, |
| HashMap<String, Object> attributes, |
| HashMap<String, Object> unavailableAttributes, |
| HashMap<String, Object> viewableAttributes) { |
| |
| Object rowData[] = new Object[2]; |
| int col1Width = 0; |
| int col2Width = 0; |
| for (int i = 0; i < attributesInfo.length; i++) { |
| rowData[0] = (attributesInfo[i].getName()); |
| if (unavailableAttributes.containsKey(rowData[0])) { |
| rowData[1] = Messages.UNAVAILABLE; |
| } else if (viewableAttributes.containsKey(rowData[0])) { |
| rowData[1] = viewableAttributes.get(rowData[0]); |
| if (!attributesInfo[i].isWritable() || |
| !Utils.isEditableType(attributesInfo[i].getType())) { |
| rowData[1] = getZoomedCell(mbean, (String) rowData[0], rowData[1]); |
| } |
| } else { |
| rowData[1] = attributes.get(rowData[0]); |
| } |
| |
| tableModel.addRow(rowData); |
| |
| //Update column width |
| // |
| String str = null; |
| if(rowData[0] != null) { |
| str = rowData[0].toString(); |
| if(str.length() > col1Width) |
| col1Width = str.length(); |
| } |
| if(rowData[1] != null) { |
| str = rowData[1].toString(); |
| if(str.length() > col2Width) |
| col2Width = str.length(); |
| } |
| } |
| updateColumnWidth(col1Width, col2Width); |
| } |
| |
| private void updateColumnWidth(int col1Width, int col2Width) { |
| TableColumnModel colModel = getColumnModel(); |
| |
| //Get the column at index pColumn, and set its preferred width. |
| col1Width = col1Width * 7; |
| col2Width = col2Width * 7; |
| if(col1Width + col2Width < |
| (int) getPreferredScrollableViewportSize().getWidth()) |
| col2Width = (int) getPreferredScrollableViewportSize().getWidth() |
| - col1Width; |
| |
| colModel.getColumn(NAME_COLUMN).setPreferredWidth(50); |
| } |
| |
| class AttributesMouseListener extends MouseAdapter { |
| |
| @Override |
| public void mousePressed(MouseEvent e) { |
| if(e.getButton() == MouseEvent.BUTTON1) { |
| if(e.getClickCount() >= 2) { |
| |
| int row = XMBeanAttributes.this.getSelectedRow(); |
| int col = XMBeanAttributes.this.getSelectedColumn(); |
| if(col != VALUE_COLUMN) return; |
| if(col == -1 || row == -1) return; |
| |
| XMBeanAttributes.this.updateZoomedCell(row, col); |
| } |
| } |
| } |
| } |
| |
| class ValueCellEditor extends XTextFieldEditor { |
| // implements javax.swing.table.TableCellEditor |
| @Override |
| public Component getTableCellEditorComponent(JTable table, |
| Object value, |
| boolean isSelected, |
| int row, |
| int column) { |
| Object val = value; |
| if(column == VALUE_COLUMN) { |
| Object obj = getModel().getValueAt(row, |
| column); |
| if(obj instanceof ZoomedCell) { |
| ZoomedCell cell = (ZoomedCell) obj; |
| if(cell.getRenderer() instanceof MaximizedCellRenderer) { |
| MaximizedCellRenderer zr = |
| (MaximizedCellRenderer) cell.getRenderer(); |
| return zr.getComponent(); |
| } |
| } else { |
| Component comp = super.getTableCellEditorComponent( |
| table, val, isSelected, row, column); |
| if (isCellError(row, column) || |
| !isWritable(row) || |
| !Utils.isEditableType(getClassName(row))) { |
| textField.setEditable(false); |
| } |
| return comp; |
| } |
| } |
| return super.getTableCellEditorComponent(table, |
| val, |
| isSelected, |
| row, |
| column); |
| } |
| @Override |
| public boolean stopCellEditing() { |
| int editingRow = getEditingRow(); |
| int editingColumn = getEditingColumn(); |
| if (editingColumn == VALUE_COLUMN) { |
| Object obj = getModel().getValueAt(editingRow, editingColumn); |
| if (obj instanceof ZoomedCell) { |
| ZoomedCell cell = (ZoomedCell) obj; |
| if (cell.isMaximized()) { |
| this.cancelCellEditing(); |
| return true; |
| } |
| } |
| } |
| return super.stopCellEditing(); |
| } |
| } |
| |
| class MaximizedCellRenderer extends DefaultTableCellRenderer { |
| Component comp; |
| MaximizedCellRenderer(Component comp) { |
| this.comp = comp; |
| Dimension d = comp.getPreferredSize(); |
| if (d.getHeight() > 220) { |
| comp.setPreferredSize(new Dimension((int) d.getWidth(), 220)); |
| } |
| } |
| @Override |
| public Component getTableCellRendererComponent(JTable table, |
| Object value, |
| boolean isSelected, |
| boolean hasFocus, |
| int row, |
| int column) { |
| return comp; |
| } |
| public Component getComponent() { |
| return comp; |
| } |
| } |
| |
| class ZoomedCell { |
| TableCellRenderer minRenderer; |
| MaximizedCellRenderer maxRenderer; |
| int minHeight; |
| boolean minimized = true; |
| boolean init = false; |
| int type; |
| Object value; |
| ZoomedCell(Object value) { |
| type = XDataViewer.getViewerType(value); |
| this.value = value; |
| } |
| |
| boolean isInited() { |
| return init; |
| } |
| |
| Object getValue() { |
| return value; |
| } |
| |
| void setValue(Object value) { |
| this.value = value; |
| } |
| |
| void init(TableCellRenderer minRenderer, |
| Component maxComponent, |
| int minHeight) { |
| this.minRenderer = minRenderer; |
| this.maxRenderer = new MaximizedCellRenderer(maxComponent); |
| |
| this.minHeight = minHeight; |
| init = true; |
| } |
| |
| int getType() { |
| return type; |
| } |
| |
| void reset() { |
| init = false; |
| minimized = true; |
| } |
| |
| void switchState() { |
| minimized = !minimized; |
| } |
| boolean isMaximized() { |
| return !minimized; |
| } |
| void minimize() { |
| minimized = true; |
| } |
| |
| void maximize() { |
| minimized = false; |
| } |
| |
| int getHeight() { |
| if(minimized) return minHeight; |
| else |
| return (int) maxRenderer.getComponent(). |
| getPreferredSize().getHeight() ; |
| } |
| |
| int getMinHeight() { |
| return minHeight; |
| } |
| |
| @Override |
| public String toString() { |
| |
| if(value == null) return null; |
| |
| if(value.getClass().isArray()) { |
| String name = |
| Utils.getArrayClassName(value.getClass().getName()); |
| int length = Array.getLength(value); |
| return name + "[" + length +"]"; |
| } |
| |
| if(value instanceof CompositeData || |
| value instanceof TabularData) |
| return value.getClass().getName(); |
| |
| return value.toString(); |
| } |
| |
| TableCellRenderer getRenderer() { |
| if(minimized) return minRenderer; |
| else return maxRenderer; |
| } |
| |
| TableCellRenderer getMinRenderer() { |
| return minRenderer; |
| } |
| } |
| |
| class AttributesListener implements TableModelListener { |
| |
| private Component component; |
| |
| public AttributesListener(Component component) { |
| this.component = component; |
| } |
| |
| // Call this in EDT |
| public void tableChanged(final TableModelEvent e) { |
| // only post changes to the draggable column |
| if (isColumnEditable(e.getColumn())) { |
| final TableModel model = (TableModel)e.getSource(); |
| Object tableValue = model.getValueAt(e.getFirstRow(), |
| e.getColumn()); |
| |
| if (LOGGER.isLoggable(Level.TRACE)) { |
| LOGGER.log(Level.TRACE, |
| "tableChanged: firstRow="+e.getFirstRow()+ |
| ", lastRow="+e.getLastRow()+", column="+e.getColumn()+ |
| ", value="+tableValue); |
| } |
| // if it's a String, try construct new value |
| // using the defined type. |
| if (tableValue instanceof String) { |
| try { |
| tableValue = |
| Utils.createObjectFromString(getClassName(e.getFirstRow()), // type |
| (String)tableValue);// value |
| } catch (Throwable ex) { |
| popupAndLog(ex,"tableChanged", |
| Messages.PROBLEM_SETTING_ATTRIBUTE); |
| } |
| } |
| final String attributeName = getValueName(e.getFirstRow()); |
| final Attribute attribute = |
| new Attribute(attributeName,tableValue); |
| setAttribute(attribute, "tableChanged"); |
| } |
| } |
| |
| // Call this in EDT |
| private void setAttribute(final Attribute attribute, final String method) { |
| final SwingWorker<Void,Void> setAttribute = |
| new SwingWorker<Void,Void>() { |
| @Override |
| protected Void doInBackground() throws Exception { |
| try { |
| if (JConsole.isDebug()) { |
| System.err.println("setAttribute("+ |
| attribute.getName()+ |
| "="+attribute.getValue()+")"); |
| } |
| mbean.setAttribute(attribute); |
| } catch (Throwable ex) { |
| popupAndLog(ex,method,Messages.PROBLEM_SETTING_ATTRIBUTE); |
| } |
| return null; |
| } |
| @Override |
| protected void done() { |
| try { |
| get(); |
| } catch (Exception x) { |
| if (JConsole.isDebug()) |
| x.printStackTrace(); |
| } |
| refreshAttributes(false); |
| } |
| |
| }; |
| mbeansTab.workerAdd(setAttribute); |
| } |
| |
| // Call this outside EDT |
| private void popupAndLog(Throwable ex, String method, String title) { |
| ex = Utils.getActualException(ex); |
| if (JConsole.isDebug()) ex.printStackTrace(); |
| |
| String message = (ex.getMessage() != null) ? ex.getMessage() |
| : ex.toString(); |
| EventQueue.invokeLater( |
| new ThreadDialog(component, message+"\n", |
| title, |
| JOptionPane.ERROR_MESSAGE)); |
| } |
| } |
| } |