1 /*
   2  * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole.inspector;
  27 
  28 import javax.swing.*;
  29 import javax.swing.event.*;
  30 import javax.swing.table.*;
  31 import javax.swing.tree.*;
  32 import java.awt.BorderLayout;
  33 import java.awt.Color;
  34 import java.awt.GridLayout;
  35 import java.awt.FlowLayout;
  36 import java.awt.Component;
  37 import java.awt.EventQueue;
  38 import java.awt.event.*;
  39 import java.awt.Insets;
  40 import java.awt.Dimension;
  41 import java.util.*;
  42 import java.io.*;
  43 
  44 import java.lang.reflect.Array;
  45 
  46 import javax.management.*;
  47 import javax.management.openmbean.CompositeData;
  48 import javax.management.openmbean.TabularData;
  49 
  50 import sun.tools.jconsole.MBeansTab;
  51 import sun.tools.jconsole.Plotter;
  52 import sun.tools.jconsole.JConsole;
  53 import sun.tools.jconsole.Messages;
  54 import sun.tools.jconsole.ProxyClient.SnapshotMBeanServerConnection;
  55 
  56 /*IMPORTANT :
  57   There is a deadlock issue there if we don't synchronize well loadAttributes,
  58   refresh attributes and empty table methods since a UI thread can call
  59   loadAttributes and at the same time a JMX notification can raise an
  60   emptyTable. Since there are synchronization in the JMX world it's
  61   COMPULSORY to not call the JMX world in synchronized blocks */
  62 @SuppressWarnings("serial")
  63 public class XMBeanAttributes extends XTable {
  64     private final static String[] columnNames =
  65     {Messages.NAME,
  66      Messages.VALUE};
  67 
  68     private boolean editable = true;
  69 
  70     private XMBean mbean;
  71     private MBeanInfo mbeanInfo;
  72     private MBeanAttributeInfo[] attributesInfo;
  73     private HashMap<String, Object> attributes;
  74     private HashMap<String, Object> unavailableAttributes;
  75     private HashMap<String, Object> viewableAttributes;
  76     private WeakHashMap<XMBean, HashMap<String, ZoomedCell>> viewersCache =
  77             new WeakHashMap<XMBean, HashMap<String, ZoomedCell>>();
  78     private TableModelListener attributesListener;
  79     private MBeansTab mbeansTab;
  80     private XTable table;
  81     private TableCellEditor valueCellEditor = new ValueCellEditor();
  82     private int rowMinHeight = -1;
  83     private AttributesMouseListener mouseListener = new AttributesMouseListener();
  84 
  85     private static TableCellEditor editor =
  86             new Utils.ReadOnlyTableCellEditor(new JTextField());
  87 
  88     public XMBeanAttributes(MBeansTab mbeansTab) {
  89         super();
  90         this.mbeansTab = mbeansTab;
  91         ((DefaultTableModel)getModel()).setColumnIdentifiers(columnNames);
  92         getModel().addTableModelListener(attributesListener =
  93                                          new AttributesListener(this));
  94         getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(40);
  95 
  96         addMouseListener(mouseListener);
  97         getTableHeader().setReorderingAllowed(false);
  98         setColumnEditors();
  99         addKeyListener(new Utils.CopyKeyAdapter());
 100     }
 101 
 102     public synchronized Component prepareRenderer(TableCellRenderer renderer,
 103                                                   int row, int column) {
 104         //In case we have a repaint thread that is in the process of
 105         //repainting an obsolete table, just ignore the call.
 106         //It can happen when MBean selection is switched at a very quick rate
 107         if(row >= getRowCount())
 108             return null;
 109         else
 110             return super.prepareRenderer(renderer, row, column);
 111     }
 112 
 113     void updateRowHeight(Object obj, int row) {
 114         ZoomedCell cell = null;
 115         if(obj instanceof ZoomedCell) {
 116             cell = (ZoomedCell) obj;
 117             if(cell.isInited())
 118                 setRowHeight(row, cell.getHeight());
 119             else
 120                 if(rowMinHeight != - 1)
 121                     setRowHeight(row, rowMinHeight);
 122         } else
 123             if(rowMinHeight != - 1)
 124                 setRowHeight(row, rowMinHeight);
 125     }
 126 
 127     public synchronized TableCellRenderer getCellRenderer(int row,
 128             int column) {
 129         //In case we have a repaint thread that is in the process of
 130         //repainting an obsolete table, just ignore the call.
 131         //It can happen when MBean selection is switched at a very quick rate
 132         if (row >= getRowCount()) {
 133             return null;
 134         } else {
 135             if (column == VALUE_COLUMN) {
 136                 Object obj = getModel().getValueAt(row, column);
 137                 if (obj instanceof ZoomedCell) {
 138                     ZoomedCell cell = (ZoomedCell) obj;
 139                     if (cell.isInited()) {
 140                         DefaultTableCellRenderer renderer =
 141                                 (DefaultTableCellRenderer) cell.getRenderer();
 142                         renderer.setToolTipText(getToolTip(row,column));
 143                         return renderer;
 144                     }
 145                 }
 146             }
 147             DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)
 148                 super.getCellRenderer(row, column);
 149             if (!isCellError(row, column)) {
 150                 if (!(isColumnEditable(column) && isWritable(row) &&
 151                       Utils.isEditableType(getClassName(row)))) {
 152                     renderer.setForeground(getDefaultColor());
 153                 }
 154             }
 155             return renderer;
 156         }
 157     }
 158 
 159     private void setColumnEditors() {
 160         TableColumnModel tcm = getColumnModel();
 161         for (int i = 0; i < columnNames.length; i++) {
 162             TableColumn tc = tcm.getColumn(i);
 163             if (isColumnEditable(i)) {
 164                 tc.setCellEditor(valueCellEditor);
 165             } else {
 166                 tc.setCellEditor(editor);
 167             }
 168         }
 169     }
 170 
 171     public void cancelCellEditing() {
 172         TableCellEditor editor = getCellEditor();
 173         if (editor != null) {
 174             editor.cancelCellEditing();
 175         }
 176     }
 177 
 178     public void stopCellEditing() {
 179         TableCellEditor editor = getCellEditor();
 180         if (editor != null) {
 181             editor.stopCellEditing();
 182         }
 183     }
 184 
 185     public final boolean editCellAt(int row, int column, EventObject e) {
 186         boolean retVal = super.editCellAt(row, column, e);
 187         if (retVal) {
 188             TableCellEditor editor =
 189                     getColumnModel().getColumn(column).getCellEditor();
 190             if (editor == valueCellEditor) {
 191                 ((JComponent) editor).requestFocus();
 192             }
 193         }
 194         return retVal;
 195     }
 196 
 197     @Override
 198     public boolean isCellEditable(int row, int col) {
 199         // All the cells in non-editable columns are editable
 200         if (!isColumnEditable(col)) {
 201             return true;
 202         }
 203         // Maximized zoomed cells are editable
 204         Object obj = getModel().getValueAt(row, col);
 205         if (obj instanceof ZoomedCell) {
 206             ZoomedCell cell = (ZoomedCell) obj;
 207             return cell.isMaximized();
 208         }
 209         return true;
 210     }
 211 
 212     @Override
 213     public void setValueAt(Object value, int row, int column) {
 214         if (!isCellError(row, column) && isColumnEditable(column) &&
 215             isWritable(row) && Utils.isEditableType(getClassName(row))) {
 216             super.setValueAt(value, row, column);
 217         }
 218     }
 219 
 220     //Table methods
 221 
 222     public boolean isTableEditable() {
 223         return true;
 224     }
 225 
 226     public void setTableValue(Object value, int row) {
 227     }
 228 
 229     public boolean isColumnEditable(int column) {
 230         if (column < getColumnCount()) {
 231             return getColumnName(column).equals(Messages.VALUE);
 232         }
 233         else {
 234             return false;
 235         }
 236     }
 237 
 238     public String getClassName(int row) {
 239         int index = convertRowToIndex(row);
 240         if (index != -1) {
 241             return attributesInfo[index].getType();
 242         }
 243         else {
 244             return null;
 245         }
 246     }
 247 
 248 
 249     public String getValueName(int row) {
 250         int index = convertRowToIndex(row);
 251         if (index != -1) {
 252             return attributesInfo[index].getName();
 253         }
 254         else {
 255             return null;
 256         }
 257     }
 258 
 259 
 260     public Object getValue(int row) {
 261         return ((DefaultTableModel) getModel()).getValueAt(row, VALUE_COLUMN);
 262     }
 263 
 264     //tool tip only for editable column
 265     public String getToolTip(int row, int column) {
 266         if (isCellError(row, column)) {
 267             return (String) unavailableAttributes.get(getValueName(row));
 268         }
 269         if (isColumnEditable(column)) {
 270             Object value = getValue(row);
 271             String tip = null;
 272             if (value != null) {
 273                 tip = value.toString();
 274                 if(isAttributeViewable(row, VALUE_COLUMN))
 275                     tip = Messages.DOUBLE_CLICK_TO_EXPAND_FORWARD_SLASH_COLLAPSE+
 276                         ". " + tip;
 277             }
 278 
 279             return tip;
 280         }
 281 
 282         if(column == NAME_COLUMN) {
 283             int index = convertRowToIndex(row);
 284             if (index != -1) {
 285                 return attributesInfo[index].getDescription();
 286             }
 287         }
 288         return null;
 289     }
 290 
 291     public synchronized boolean isWritable(int row) {
 292         int index = convertRowToIndex(row);
 293         if (index != -1) {
 294             return (attributesInfo[index].isWritable());
 295         }
 296         else {
 297             return false;
 298         }
 299     }
 300 
 301     /**
 302      * Override JTable method in order to make any call to this method
 303      * atomic with TableModel elements.
 304      */
 305     public synchronized int getRowCount() {
 306         return super.getRowCount();
 307     }
 308 
 309     public synchronized boolean isReadable(int row) {
 310         int index = convertRowToIndex(row);
 311         if (index != -1) {
 312             return (attributesInfo[index].isReadable());
 313         }
 314         else {
 315             return false;
 316         }
 317     }
 318 
 319     public synchronized boolean isCellError(int row, int col) {
 320         return (isColumnEditable(col) &&
 321                 (unavailableAttributes.containsKey(getValueName(row))));
 322     }
 323 
 324     public synchronized boolean isAttributeViewable(int row, int col) {
 325         boolean isViewable = false;
 326         if(col == VALUE_COLUMN) {
 327             Object obj = getModel().getValueAt(row, col);
 328             if(obj instanceof ZoomedCell)
 329                 isViewable = true;
 330         }
 331 
 332         return isViewable;
 333     }
 334 
 335     public void loadAttributes(final XMBean mbean, MBeanInfo mbeanInfo) {
 336         // To avoid deadlock with events coming from the JMX side,
 337         // we retrieve all JMX stuff in a non synchronized block.
 338 
 339         if(mbean == null) return;
 340 
 341         final MBeanAttributeInfo[] attributesInfo = mbeanInfo.getAttributes();
 342         final HashMap<String, Object> attributes =
 343             new HashMap<String, Object>(attributesInfo.length);
 344         final HashMap<String, Object> unavailableAttributes =
 345             new HashMap<String, Object>(attributesInfo.length);
 346         final HashMap<String, Object> viewableAttributes =
 347             new HashMap<String, Object>(attributesInfo.length);
 348         AttributeList list = null;
 349 
 350         try {
 351             list = mbean.getAttributes(attributesInfo);
 352         }catch(Exception e) {
 353             list = new AttributeList();
 354             //Can't load all attributes, do it one after each other.
 355             for(int i = 0; i < attributesInfo.length; i++) {
 356                 String name = null;
 357                 try {
 358                     name = attributesInfo[i].getName();
 359                     Object value =
 360                         mbean.getAttribute(name);
 361                     list.add(new Attribute(name, value));
 362                 }catch(Exception ex) {
 363                     if(attributesInfo[i].isReadable()) {
 364                         unavailableAttributes.put(name,
 365                                                   Utils.getActualException(ex).
 366                                                   toString());
 367                     }
 368                 }
 369             }
 370         }
 371         try {
 372             int att_length = list.size();
 373             for (int i=0;i<att_length;i++) {
 374                 Attribute attribute = (Attribute) list.get(i);
 375                 if(isViewable(attribute)) {
 376                     viewableAttributes.put(attribute.getName(),
 377                                            attribute.getValue());
 378                 }
 379                 else
 380                     attributes.put(attribute.getName(),attribute.getValue());
 381 
 382             }
 383             // if not all attributes are accessible,
 384             // check them one after the other.
 385             if (att_length < attributesInfo.length) {
 386                 for (int i=0;i<attributesInfo.length;i++) {
 387                     MBeanAttributeInfo attributeInfo = attributesInfo[i];
 388                     if (!attributes.containsKey(attributeInfo.getName()) &&
 389                         !viewableAttributes.containsKey(attributeInfo.
 390                                                         getName()) &&
 391                         !unavailableAttributes.containsKey(attributeInfo.
 392                                                            getName())) {
 393                         if (attributeInfo.isReadable()) {
 394                             // getAttributes didn't help resolving the
 395                             // exception.
 396                             // We must call it again to understand what
 397                             // went wrong.
 398                             try {
 399                                 Object v =
 400                                     mbean.getAttribute(attributeInfo.
 401                                                        getName());
 402                                 //What happens if now it is ok?
 403                                 // Be pragmatic, add it to readable...
 404                                 attributes.put(attributeInfo.getName(),
 405                                                v);
 406                             }catch(Exception e) {
 407                                 //Put the exception that will be displayed
 408                                 // in tooltip
 409                                 unavailableAttributes.put(attributeInfo.
 410                                                           getName(),
 411                                                           Utils.
 412                                                           getActualException(e)
 413                                                           .toString());
 414                             }
 415                         }
 416                     }
 417                 }
 418             }
 419         }
 420         catch(Exception e) {
 421             //sets all attributes unavailable except the writable ones
 422             for (int i=0;i<attributesInfo.length;i++) {
 423                 MBeanAttributeInfo attributeInfo = attributesInfo[i];
 424                 if (attributeInfo.isReadable()) {
 425                     unavailableAttributes.put(attributeInfo.getName(),
 426                                               Utils.getActualException(e).
 427                                               toString());
 428                 }
 429             }
 430         }
 431         //end of retrieval
 432 
 433         //one update at a time
 434         synchronized(this) {
 435 
 436             this.mbean = mbean;
 437             this.mbeanInfo = mbeanInfo;
 438             this.attributesInfo = attributesInfo;
 439             this.attributes = attributes;
 440             this.unavailableAttributes = unavailableAttributes;
 441             this.viewableAttributes = viewableAttributes;
 442 
 443             EventQueue.invokeLater(new Runnable() {
 444                 public void run() {
 445                     DefaultTableModel tableModel =
 446                             (DefaultTableModel) getModel();
 447 
 448                     // don't listen to these events
 449                     tableModel.removeTableModelListener(attributesListener);
 450 
 451                     // add attribute information
 452                     emptyTable();
 453 
 454                     addTableData(tableModel,
 455                                  mbean,
 456                                  attributesInfo,
 457                                  attributes,
 458                                  unavailableAttributes,
 459                                  viewableAttributes);
 460 
 461                     // update the model with the new data
 462                     tableModel.newDataAvailable(new TableModelEvent(tableModel));
 463                     // re-register for change events
 464                     tableModel.addTableModelListener(attributesListener);
 465                 }
 466             });
 467         }
 468     }
 469 
 470     void collapse(String attributeName, final Component c) {
 471         final int row = getSelectedRow();
 472         Object obj = getModel().getValueAt(row, VALUE_COLUMN);
 473         if(obj instanceof ZoomedCell) {
 474             cancelCellEditing();
 475             ZoomedCell cell = (ZoomedCell) obj;
 476             cell.reset();
 477             setRowHeight(row,
 478                          cell.getHeight());
 479             editCellAt(row,
 480                        VALUE_COLUMN);
 481             invalidate();
 482             repaint();
 483         }
 484     }
 485 
 486     ZoomedCell updateZoomedCell(int row,
 487                                 int col) {
 488         Object obj = getModel().getValueAt(row, VALUE_COLUMN);
 489         ZoomedCell cell = null;
 490         if(obj instanceof ZoomedCell) {
 491             cell = (ZoomedCell) obj;
 492             if(!cell.isInited()) {
 493                 Object elem = cell.getValue();
 494                 String attributeName =
 495                     (String) getModel().getValueAt(row,
 496                                                    NAME_COLUMN);
 497                 Component comp = mbeansTab.getDataViewer().
 498                         createAttributeViewer(elem, mbean, attributeName, this);
 499                 if(comp != null){
 500                     if(rowMinHeight == -1)
 501                         rowMinHeight = getRowHeight(row);
 502 
 503                     cell.init(super.getCellRenderer(row, col),
 504                               comp,
 505                               rowMinHeight);
 506 
 507                     XDataViewer.registerForMouseEvent(
 508                             comp, mouseListener);
 509                 } else
 510                     return cell;
 511             }
 512 
 513             cell.switchState();
 514             setRowHeight(row,
 515                          cell.getHeight());
 516 
 517             if(!cell.isMaximized()) {
 518                 cancelCellEditing();
 519                 //Back to simple editor.
 520                 editCellAt(row,
 521                            VALUE_COLUMN);
 522             }
 523 
 524             invalidate();
 525             repaint();
 526         }
 527         return cell;
 528     }
 529 
 530      public void refreshAttributes() {
 531          MBeanServerConnection mbsc = mbeansTab.getMBeanServerConnection();
 532          if (mbsc instanceof SnapshotMBeanServerConnection) {
 533              ((SnapshotMBeanServerConnection) mbsc).flush();
 534          }
 535          stopCellEditing();
 536          loadAttributes(mbean, mbeanInfo);
 537      }
 538 
 539 
 540      public void emptyTable() {
 541          synchronized(this) {
 542              ((DefaultTableModel) getModel()).
 543                  removeTableModelListener(attributesListener);
 544              super.emptyTable();
 545          }
 546      }
 547 
 548     private boolean isViewable(Attribute attribute) {
 549         Object data = attribute.getValue();
 550         return XDataViewer.isViewableValue(data);
 551 
 552     }
 553 
 554     synchronized void removeAttributes() {
 555         if (attributes != null) {
 556             attributes.clear();
 557         }
 558         if (unavailableAttributes != null) {
 559             unavailableAttributes.clear();
 560         }
 561         if (viewableAttributes != null) {
 562             viewableAttributes.clear();
 563         }
 564         mbean = null;
 565     }
 566 
 567     private ZoomedCell getZoomedCell(XMBean mbean, String attribute, Object value) {
 568         synchronized (viewersCache) {
 569             HashMap<String, ZoomedCell> viewers;
 570             if (viewersCache.containsKey(mbean)) {
 571                 viewers = viewersCache.get(mbean);
 572             } else {
 573                 viewers = new HashMap<String, ZoomedCell>();
 574             }
 575             ZoomedCell cell;
 576             if (viewers.containsKey(attribute)) {
 577                 cell = viewers.get(attribute);
 578                 cell.setValue(value);
 579                 if (cell.isMaximized() && cell.getType() != XDataViewer.NUMERIC) {
 580                     // Plotters are the only viewers with auto update capabilities.
 581                     // Other viewers need to be updated manually.
 582                     Component comp =
 583                         mbeansTab.getDataViewer().createAttributeViewer(
 584                             value, mbean, attribute, XMBeanAttributes.this);
 585                     cell.init(cell.getMinRenderer(), comp, cell.getMinHeight());
 586                     XDataViewer.registerForMouseEvent(comp, mouseListener);
 587                 }
 588             } else {
 589                 cell = new ZoomedCell(value);
 590                 viewers.put(attribute, cell);
 591             }
 592             viewersCache.put(mbean, viewers);
 593             return cell;
 594         }
 595     }
 596 
 597     //will be called in a synchronized block
 598     protected void addTableData(DefaultTableModel tableModel,
 599                                 XMBean mbean,
 600                                 MBeanAttributeInfo[] attributesInfo,
 601                                 HashMap<String, Object> attributes,
 602                                 HashMap<String, Object> unavailableAttributes,
 603                                 HashMap<String, Object> viewableAttributes) {
 604 
 605         Object rowData[] = new Object[2];
 606         int col1Width = 0;
 607         int col2Width = 0;
 608         for (int i = 0; i < attributesInfo.length; i++) {
 609             rowData[0] = (attributesInfo[i].getName());
 610             if (unavailableAttributes.containsKey(rowData[0])) {
 611                 rowData[1] = Messages.UNAVAILABLE;
 612             } else if (viewableAttributes.containsKey(rowData[0])) {
 613                 rowData[1] = viewableAttributes.get(rowData[0]);
 614                 if (!attributesInfo[i].isWritable() ||
 615                     !Utils.isEditableType(attributesInfo[i].getType())) {
 616                     rowData[1] = getZoomedCell(mbean, (String) rowData[0], rowData[1]);
 617                 }
 618             } else {
 619                 rowData[1] = attributes.get(rowData[0]);
 620             }
 621 
 622             tableModel.addRow(rowData);
 623 
 624             //Update column width
 625             //
 626             String str = null;
 627             if(rowData[0] != null) {
 628                 str = rowData[0].toString();
 629                 if(str.length() > col1Width)
 630                     col1Width = str.length();
 631             }
 632             if(rowData[1] != null) {
 633                 str = rowData[1].toString();
 634                 if(str.length() > col2Width)
 635                     col2Width = str.length();
 636             }
 637         }
 638         updateColumnWidth(col1Width, col2Width);
 639     }
 640 
 641     private void updateColumnWidth(int col1Width, int col2Width) {
 642         TableColumnModel colModel = getColumnModel();
 643 
 644         //Get the column at index pColumn, and set its preferred width.
 645         col1Width = col1Width * 7;
 646         col2Width = col2Width * 7;
 647         if(col1Width + col2Width <
 648            (int) getPreferredScrollableViewportSize().getWidth())
 649             col2Width = (int) getPreferredScrollableViewportSize().getWidth()
 650                 - col1Width;
 651 
 652         colModel.getColumn(NAME_COLUMN).setPreferredWidth(50);
 653     }
 654 
 655     class AttributesMouseListener extends MouseAdapter {
 656 
 657         public void mousePressed(MouseEvent e) {
 658             if(e.getButton() == MouseEvent.BUTTON1) {
 659                 if(e.getClickCount() >= 2) {
 660 
 661                     int row = XMBeanAttributes.this.getSelectedRow();
 662                     int col = XMBeanAttributes.this.getSelectedColumn();
 663                     if(col != VALUE_COLUMN) return;
 664                     if(col == -1 || row == -1) return;
 665 
 666                     XMBeanAttributes.this.updateZoomedCell(row, col);
 667                 }
 668             }
 669         }
 670     }
 671 
 672     class ValueCellEditor extends XTextFieldEditor {
 673         // implements javax.swing.table.TableCellEditor
 674         public Component getTableCellEditorComponent(JTable table,
 675                                                      Object value,
 676                                                      boolean isSelected,
 677                                                      int row,
 678                                                      int column) {
 679             Object val = value;
 680             if(column == VALUE_COLUMN) {
 681                 Object obj = getModel().getValueAt(row,
 682                                                    column);
 683                 if(obj instanceof ZoomedCell) {
 684                     ZoomedCell cell = (ZoomedCell) obj;
 685                     if(cell.getRenderer() instanceof MaximizedCellRenderer) {
 686                         MaximizedCellRenderer zr =
 687                             (MaximizedCellRenderer) cell.getRenderer();
 688                         return zr.getComponent();
 689                     }
 690                 } else {
 691                     Component comp = super.getTableCellEditorComponent(
 692                             table, val, isSelected, row, column);
 693                     if (isCellError(row, column) ||
 694                         !isWritable(row) ||
 695                         !Utils.isEditableType(getClassName(row))) {
 696                         textField.setEditable(false);
 697                     }
 698                     return comp;
 699                 }
 700             }
 701             return super.getTableCellEditorComponent(table,
 702                                                      val,
 703                                                      isSelected,
 704                                                      row,
 705                                                      column);
 706         }
 707         @Override
 708         public boolean stopCellEditing() {
 709             int editingRow = getEditingRow();
 710             int editingColumn = getEditingColumn();
 711             if (editingColumn == VALUE_COLUMN) {
 712                 Object obj = getModel().getValueAt(editingRow, editingColumn);
 713                 if (obj instanceof ZoomedCell) {
 714                     ZoomedCell cell = (ZoomedCell) obj;
 715                     if (cell.isMaximized()) {
 716                         this.cancelCellEditing();
 717                         return true;
 718                     }
 719                 }
 720             }
 721             return super.stopCellEditing();
 722         }
 723     }
 724 
 725     class MaximizedCellRenderer extends  DefaultTableCellRenderer {
 726         Component comp;
 727         MaximizedCellRenderer(Component comp) {
 728             this.comp = comp;
 729             Dimension d = comp.getPreferredSize();
 730             if (d.getHeight() > 200) {
 731                 comp.setPreferredSize(new Dimension((int) d.getWidth(), 200));
 732             }
 733         }
 734         public Component getTableCellRendererComponent(JTable table,
 735                                                        Object value,
 736                                                        boolean isSelected,
 737                                                        boolean hasFocus,
 738                                                        int row,
 739                                                        int column) {
 740             return comp;
 741         }
 742         public Component getComponent() {
 743             return comp;
 744         }
 745     }
 746 
 747     class ZoomedCell {
 748         TableCellRenderer minRenderer;
 749         MaximizedCellRenderer maxRenderer;
 750         int minHeight;
 751         boolean minimized = true;
 752         boolean init = false;
 753         int type;
 754         Object value;
 755         ZoomedCell(Object value) {
 756             type = XDataViewer.getViewerType(value);
 757             this.value = value;
 758         }
 759 
 760         boolean isInited() {
 761             return init;
 762         }
 763 
 764         Object getValue() {
 765             return value;
 766         }
 767 
 768         void setValue(Object value) {
 769             this.value = value;
 770         }
 771 
 772         void init(TableCellRenderer minRenderer,
 773                   Component maxComponent,
 774                   int minHeight) {
 775             this.minRenderer = minRenderer;
 776             this.maxRenderer = new MaximizedCellRenderer(maxComponent);
 777 
 778             this.minHeight = minHeight;
 779             init = true;
 780         }
 781 
 782         int getType() {
 783             return type;
 784         }
 785 
 786         void reset() {
 787             init = false;
 788             minimized = true;
 789         }
 790 
 791         void switchState() {
 792             minimized = !minimized;
 793         }
 794         boolean isMaximized() {
 795             return !minimized;
 796         }
 797         void minimize() {
 798             minimized = true;
 799         }
 800 
 801         void maximize() {
 802             minimized = false;
 803         }
 804 
 805         int getHeight() {
 806             if(minimized) return minHeight;
 807             else
 808                 return (int) maxRenderer.getComponent().
 809                     getPreferredSize().getHeight() ;
 810         }
 811 
 812         int getMinHeight() {
 813             return minHeight;
 814         }
 815 
 816         public String toString() {
 817 
 818             if(value == null) return null;
 819 
 820             if(value.getClass().isArray()) {
 821                 String name =
 822                     Utils.getArrayClassName(value.getClass().getName());
 823                 int length = Array.getLength(value);
 824                 return name + "[" + length +"]";
 825             }
 826 
 827             if(value instanceof CompositeData ||
 828                value instanceof TabularData)
 829                 return value.getClass().getName();
 830 
 831             return value.toString();
 832         }
 833 
 834         TableCellRenderer getRenderer() {
 835             if(minimized) return minRenderer;
 836             else return maxRenderer;
 837         }
 838 
 839         TableCellRenderer getMinRenderer() {
 840             return minRenderer;
 841         }
 842     }
 843 
 844     class AttributesListener implements  TableModelListener {
 845 
 846         private Component component;
 847 
 848         public AttributesListener(Component component) {
 849             this.component = component;
 850         }
 851 
 852         public void tableChanged(final TableModelEvent e) {
 853             final TableModel model = (TableModel)e.getSource();
 854             // only post changes to the draggable column
 855             if (isColumnEditable(e.getColumn())) {
 856                 mbeansTab.workerAdd(new Runnable() {
 857                         public void run() {
 858                             try {
 859                                 Object tableValue =
 860                                     model.getValueAt(e.getFirstRow(),
 861                                                      e.getColumn());
 862                                 // if it's a String, try construct new value
 863                                 // using the defined type.
 864                                 if (tableValue instanceof String) {
 865                                     tableValue =
 866                                         Utils.createObjectFromString(getClassName(e.getFirstRow()), // type
 867                                                                      (String)tableValue);// value
 868                                 }
 869                                 String attributeName =
 870                                     getValueName(e.getFirstRow());
 871                                 Attribute attribute =
 872                                     new Attribute(attributeName,tableValue);
 873                                 mbean.setAttribute(attribute);
 874                             }
 875                             catch (Throwable ex) {
 876                                 if (JConsole.isDebug()) {
 877                                     ex.printStackTrace();
 878                                 }
 879                                 ex = Utils.getActualException(ex);
 880 
 881                                 String message = (ex.getMessage() != null) ? ex.getMessage() : ex.toString();
 882                                 EventQueue.invokeLater(new ThreadDialog(component,
 883                                                                         message+"\n",
 884                                                                         Messages.PROBLEM_SETTING_ATTRIBUTE,
 885                                                                         JOptionPane.ERROR_MESSAGE));
 886                             }
 887                             refreshAttributes();
 888                         }
 889                     });
 890             }
 891         }
 892     }
 893 }