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 javax.swing.border.*;
  33 import java.awt.BorderLayout;
  34 import java.awt.GridLayout;
  35 import java.awt.FlowLayout;
  36 import java.awt.Component;
  37 import java.awt.EventQueue;
  38 import java.awt.Color;
  39 import java.awt.Font;
  40 import java.awt.Rectangle;
  41 import java.awt.event.*;
  42 import java.awt.Insets;
  43 import java.awt.Dimension;
  44 import java.awt.GridBagConstraints;
  45 import java.awt.GridBagLayout;
  46 import java.util.*;
  47 import java.io.*;
  48 import java.lang.reflect.Array;
  49 
  50 import javax.management.*;
  51 import javax.management.openmbean.*;
  52 
  53 import sun.tools.jconsole.BorderedComponent;
  54 import sun.tools.jconsole.JConsole;
  55 import sun.tools.jconsole.LabeledComponent;
  56 import sun.tools.jconsole.Resources;
  57 import sun.tools.jconsole.VariableGridLayout;
  58 
  59 @SuppressWarnings("serial")
  60 public class XOpenTypeViewer extends JPanel implements ActionListener {
  61     JButton prev, incr, decr, tabularPrev, tabularNext;
  62     JLabel compositeLabel, tabularLabel;
  63     JScrollPane container;
  64     XOpenTypeData current;
  65     XOpenTypeDataListener listener = new XOpenTypeDataListener();
  66 
  67     private static final String compositeNavigationSingle =
  68             Resources.getText("MBeansTab.compositeNavigationSingle");
  69     private static final String tabularNavigationSingle =
  70             Resources.getText("MBeansTab.tabularNavigationSingle");
  71 
  72     private static TableCellEditor editor =
  73             new Utils.ReadOnlyTableCellEditor(new JTextField());
  74 
  75     class XOpenTypeDataListener extends MouseAdapter {
  76         XOpenTypeDataListener() {
  77         }
  78 
  79         public void mousePressed(MouseEvent e) {
  80             if(e.getButton() == MouseEvent.BUTTON1) {
  81                 if(e.getClickCount() >= 2) {
  82                     XOpenTypeData elem = getSelectedViewedOpenType();
  83                     if(elem != null) {
  84                         try {
  85                             elem.viewed(XOpenTypeViewer.this);
  86                         }catch(Exception ex) {
  87                             //Nothing to change, the element
  88                             //can't be displayed
  89                         }
  90                     }
  91                 }
  92             }
  93         }
  94 
  95         private XOpenTypeData getSelectedViewedOpenType() {
  96             int row = XOpenTypeViewer.this.current.getSelectedRow();
  97             int col = XOpenTypeViewer.this.current.getSelectedColumn();
  98             Object elem =
  99                     XOpenTypeViewer.this.current.getModel().getValueAt(row, col);
 100             if(elem instanceof XOpenTypeData)
 101                 return (XOpenTypeData) elem;
 102             else
 103                 return null;
 104         }
 105     }
 106 
 107     static interface Navigatable {
 108         public void incrElement();
 109         public void decrElement();
 110         public boolean canDecrement();
 111         public boolean canIncrement();
 112         public int getElementCount();
 113         public int getSelectedElementIndex();
 114     }
 115 
 116     static interface XViewedTabularData extends Navigatable {
 117     }
 118 
 119     static interface XViewedArrayData extends Navigatable {
 120     }
 121 
 122     static abstract class XOpenTypeData extends JTable {
 123         XOpenTypeData parent;
 124         private Color defaultColor;
 125         protected int col1Width = -1;
 126         protected int col2Width = -1;
 127         private boolean init;
 128         private Font normalFont, boldFont;
 129         protected XOpenTypeData(XOpenTypeData parent) {
 130             this.parent = parent;
 131         }
 132 
 133         public XOpenTypeData getViewedParent() {
 134             return parent;
 135         }
 136 
 137         public String getToolTip(int row, int col) {
 138             if(col == 1) {
 139                 Object value = getModel().getValueAt(row, col);
 140                 if (value != null) {
 141                     if(isClickableElement(value))
 142                         return Resources.getText("Double click to visualize")
 143                         + ". " + value.toString();
 144                     else
 145                         return value.toString();
 146                 }
 147             }
 148             return null;
 149         }
 150 
 151         public TableCellRenderer getCellRenderer(int row, int column) {
 152             DefaultTableCellRenderer tcr =
 153                     (DefaultTableCellRenderer)super.getCellRenderer(row,column);
 154             tcr.setToolTipText(getToolTip(row,column));
 155             return tcr;
 156         }
 157 
 158         public void renderKey(String key,  Component comp) {
 159             comp.setFont(normalFont);
 160         }
 161 
 162         public Component prepareRenderer(TableCellRenderer renderer,
 163                 int row, int column) {
 164             Component comp = super.prepareRenderer(renderer, row, column);
 165 
 166             if (normalFont == null) {
 167                 normalFont = comp.getFont();
 168                 boldFont = normalFont.deriveFont(Font.BOLD);
 169             }
 170 
 171             Object o = ((DefaultTableModel) getModel()).getValueAt(row, column);
 172             if (column == 0) {
 173                 String key = o.toString();
 174                 renderKey(key, comp);
 175             } else {
 176                 if (isClickableElement(o)) {
 177                     comp.setFont(boldFont);
 178                 } else {
 179                     comp.setFont(normalFont);
 180                 }
 181             }
 182 
 183             return comp;
 184         }
 185 
 186         protected boolean isClickableElement(Object obj) {
 187             if (obj instanceof XOpenTypeData) {
 188                 if (obj instanceof Navigatable) {
 189                     return (((Navigatable) obj).getElementCount() != 0);
 190                 } else {
 191                     return (obj instanceof XCompositeData);
 192                 }
 193             }
 194             return false;
 195         }
 196 
 197         protected void updateColumnWidth() {
 198             if (!init) {
 199                 TableColumnModel colModel = getColumnModel();
 200                 if (col2Width == -1) {
 201                     col1Width = col1Width * 7;
 202                     if (col1Width <
 203                             getPreferredScrollableViewportSize().getWidth()) {
 204                         col1Width = (int)
 205                         getPreferredScrollableViewportSize().getWidth();
 206                     }
 207                     colModel.getColumn(0).setPreferredWidth(col1Width);
 208                     init = true;
 209                     return;
 210                 }
 211                 col1Width = (col1Width * 7) + 7;
 212                 col1Width = Math.max(col1Width, 70);
 213                 col2Width = (col2Width * 7) + 7;
 214                 if (col1Width + col2Width <
 215                         getPreferredScrollableViewportSize().getWidth()) {
 216                     col2Width = (int)
 217                     getPreferredScrollableViewportSize().getWidth() -
 218                             col1Width;
 219                 }
 220                 colModel.getColumn(0).setPreferredWidth(col1Width);
 221                 colModel.getColumn(1).setPreferredWidth(col2Width);
 222                 init = true;
 223             }
 224         }
 225 
 226         public abstract void viewed(XOpenTypeViewer viewer) throws Exception;
 227 
 228         protected void initTable(String[] columnNames) {
 229             setRowSelectionAllowed(false);
 230             setColumnSelectionAllowed(false);
 231             getTableHeader().setReorderingAllowed(false);
 232             ((DefaultTableModel) getModel()).setColumnIdentifiers(columnNames);
 233             for (Enumeration<TableColumn> e = getColumnModel().getColumns();
 234             e.hasMoreElements();) {
 235                 TableColumn tc = e.nextElement();
 236                 tc.setCellEditor(editor);
 237             }
 238             addKeyListener(new Utils.CopyKeyAdapter());
 239             setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
 240             setPreferredScrollableViewportSize(new Dimension(350, 200));
 241         }
 242 
 243         protected void emptyTable() {
 244             invalidate();
 245             while (getModel().getRowCount()>0)
 246                 ((DefaultTableModel) getModel()).removeRow(0);
 247             validate();
 248         }
 249 
 250         public void setValueAt(Object value, int row, int col) {
 251         }
 252     }
 253 
 254     static class TabularDataComparator implements Comparator<CompositeData> {
 255 
 256         private final List<String> indexNames;
 257 
 258         public TabularDataComparator(TabularType type) {
 259             indexNames = type.getIndexNames();
 260         }
 261 
 262         @SuppressWarnings("unchecked")
 263         public int compare(CompositeData o1, CompositeData o2) {
 264             for (String key : indexNames) {
 265                 Object c1 = o1.get(key);
 266                 Object c2 = o2.get(key);
 267                 if (c1 instanceof Comparable && c2 instanceof Comparable) {
 268                     int result = ((Comparable) c1).compareTo(c2);
 269                     if (result != 0)
 270                         return result;
 271                 }
 272             }
 273             return 0;
 274         }
 275     }
 276 
 277     static class XTabularData extends XCompositeData
 278             implements XViewedTabularData {
 279 
 280         final TabularData tabular;
 281         final TabularType type;
 282         int currentIndex = 0;
 283         final Object[] elements;
 284         final int size;
 285         private Font normalFont, italicFont;
 286 
 287         @SuppressWarnings("unchecked")
 288         public XTabularData(XOpenTypeData parent, TabularData tabular) {
 289             super(parent, accessFirstElement(tabular));
 290             this.tabular = tabular;
 291             type = tabular.getTabularType();
 292             size = tabular.values().size();
 293             if (size > 0) {
 294                 // Order tabular data elements using index names
 295                 List<CompositeData> data = new ArrayList<CompositeData>(
 296                         (Collection<CompositeData>) tabular.values());
 297                 Collections.sort(data, new TabularDataComparator(type));
 298                 elements = data.toArray();
 299                 loadCompositeData((CompositeData) elements[0]);
 300             } else {
 301                 elements = new Object[0];
 302             }
 303         }
 304 
 305         private static CompositeData accessFirstElement(TabularData tabular) {
 306             if(tabular.values().size() == 0) return null;
 307             return (CompositeData) tabular.values().toArray()[0];
 308         }
 309 
 310         public void renderKey(String key,  Component comp) {
 311             if (normalFont == null) {
 312                 normalFont = comp.getFont();
 313                 italicFont = normalFont.deriveFont(Font.ITALIC);
 314             }
 315             for(Object k : type.getIndexNames()) {
 316                 if(key.equals(k))
 317                     comp.setFont(italicFont);
 318             }
 319         }
 320 
 321         public int getElementCount() {
 322             return size;
 323         }
 324 
 325         public int getSelectedElementIndex() {
 326             return currentIndex;
 327         }
 328 
 329         public void incrElement() {
 330             currentIndex++;
 331             loadCompositeData((CompositeData)elements[currentIndex]);
 332         }
 333 
 334         public void decrElement() {
 335             currentIndex--;
 336             loadCompositeData((CompositeData)elements[currentIndex]);
 337         }
 338 
 339         public boolean canDecrement() {
 340             if(currentIndex == 0)
 341                 return false;
 342             else
 343                 return true;
 344         }
 345 
 346         public boolean canIncrement(){
 347             if(size == 0 ||
 348                     currentIndex == size -1)
 349                 return false;
 350             else
 351                 return true;
 352         }
 353 
 354         public String toString() {
 355             return type == null ? "" : type.getDescription();
 356         }
 357     }
 358 
 359     static class XCompositeData extends XOpenTypeData {
 360         protected final String[] columnNames = {
 361             Resources.getText("Name"), Resources.getText("Value")
 362         };
 363         CompositeData composite;
 364 
 365         public XCompositeData() {
 366             super(null);
 367             initTable(columnNames);
 368         }
 369 
 370         //In sync with array, no init table.
 371         public XCompositeData(XOpenTypeData parent) {
 372             super(parent);
 373         }
 374 
 375         public XCompositeData(XOpenTypeData parent,
 376                 CompositeData composite) {
 377             super(parent);
 378             initTable(columnNames);
 379             if(composite != null) {
 380                 this.composite = composite;
 381                 loadCompositeData(composite);
 382             }
 383         }
 384 
 385         public void viewed(XOpenTypeViewer viewer) throws Exception {
 386             viewer.setOpenType(this);
 387             updateColumnWidth();
 388         }
 389 
 390         public String toString() {
 391             return composite == null ? "" :
 392                 composite.getCompositeType().getTypeName();
 393         }
 394 
 395         protected Object formatKey(String key) {
 396             return key;
 397         }
 398 
 399         private void load(CompositeData data) {
 400             CompositeType type = data.getCompositeType();
 401             Set keys = type.keySet();
 402             Iterator it = keys.iterator();
 403             Object[] rowData = new Object[2];
 404             while (it.hasNext()) {
 405                 String key = (String) it.next();
 406                 Object val = data.get(key);
 407                 rowData[0] = formatKey(key);
 408                 if (val == null) {
 409                     rowData[1] = "";
 410                 } else {
 411                     OpenType openType = type.getType(key);
 412                     if (openType instanceof CompositeType) {
 413                         rowData[1] =
 414                                 new XCompositeData(this, (CompositeData) val);
 415                     } else if (openType instanceof ArrayType) {
 416                         rowData[1] =
 417                                 new XArrayData(this, (ArrayType) openType, val);
 418                     } else if (openType instanceof SimpleType) {
 419                         rowData[1] = val;
 420                     } else if (openType instanceof TabularType) {
 421                         rowData[1] = new XTabularData(this, (TabularData) val);
 422                     }
 423                 }
 424                 // Update column width
 425                 String str = null;
 426                 if (rowData[0] != null) {
 427                     str = rowData[0].toString();
 428                     if (str.length() > col1Width) {
 429                         col1Width = str.length();
 430                     }
 431                 }
 432                 if (rowData[1] != null) {
 433                     str = rowData[1].toString();
 434                     if (str.length() > col2Width) {
 435                         col2Width = str.length();
 436                     }
 437                 }
 438                 ((DefaultTableModel) getModel()).addRow(rowData);
 439             }
 440         }
 441 
 442         protected void loadCompositeData(CompositeData data) {
 443             composite = data;
 444             emptyTable();
 445             load(data);
 446             DefaultTableModel tableModel = (DefaultTableModel) getModel();
 447             tableModel.newDataAvailable(new TableModelEvent(tableModel));
 448         }
 449     }
 450 
 451     static class XArrayData extends XCompositeData
 452             implements XViewedArrayData {
 453 
 454         private int dimension;
 455         private int size;
 456         private OpenType elemType;
 457         private Object val;
 458         private boolean isCompositeType;
 459         private boolean isTabularType;
 460         private int currentIndex;
 461         private CompositeData[] elements;
 462         private final String[] arrayColumns = {Resources.getText("Value")};
 463         private Font normalFont, boldFont;
 464 
 465         XArrayData(XOpenTypeData parent, ArrayType type, Object val) {
 466             this(parent, type.getDimension(), type.getElementOpenType(), val);
 467         }
 468 
 469         XArrayData(XOpenTypeData parent, int dimension,
 470                 OpenType elemType, Object val) {
 471             super(parent);
 472             this.dimension = dimension;
 473             this.elemType = elemType;
 474             this.val = val;
 475             String[] columns = null;
 476 
 477             if (dimension > 1) return;
 478 
 479             isCompositeType = (elemType instanceof CompositeType);
 480             isTabularType = (elemType instanceof TabularType);
 481             columns = isCompositeType ? columnNames : arrayColumns;
 482 
 483             initTable(columns);
 484             loadArray();
 485         }
 486 
 487         public void viewed(XOpenTypeViewer viewer) throws Exception {
 488             if (size == 0)
 489                 throw new Exception(Resources.getText("Empty array"));
 490             if (dimension > 1)
 491                 throw new Exception(Resources.getText("Dimension is not " +
 492                         "supported:") +
 493                         dimension);
 494             super.viewed(viewer);
 495         }
 496 
 497         public int getElementCount() {
 498             return size;
 499         }
 500 
 501         public int getSelectedElementIndex() {
 502             return currentIndex;
 503         }
 504 
 505         public void renderKey(String key,  Component comp) {
 506             if (normalFont == null) {
 507                 normalFont = comp.getFont();
 508                 boldFont = normalFont.deriveFont(Font.BOLD);
 509             }
 510             if (isTabularType) {
 511                 comp.setFont(boldFont);
 512             }
 513         }
 514 
 515         public void incrElement() {
 516             currentIndex++;
 517             loadCompositeData(elements[currentIndex]);
 518         }
 519 
 520         public void decrElement() {
 521             currentIndex--;
 522             loadCompositeData(elements[currentIndex]);
 523         }
 524 
 525         public boolean canDecrement() {
 526             if (isCompositeType && currentIndex > 0) {
 527                 return true;
 528             }
 529             return false;
 530         }
 531 
 532         public boolean canIncrement() {
 533             if (isCompositeType && currentIndex < size - 1) {
 534                 return true;
 535             }
 536             return false;
 537         }
 538 
 539         private void loadArray() {
 540             if (isCompositeType) {
 541                 elements = (CompositeData[]) val;
 542                 size = elements.length;
 543                 if (size != 0) {
 544                     loadCompositeData(elements[0]);
 545                 }
 546             } else {
 547                 load();
 548             }
 549         }
 550 
 551         private void load() {
 552             Object[] rowData = new Object[1];
 553             size = Array.getLength(val);
 554             for (int i = 0; i < size; i++) {
 555                 rowData[0] = isTabularType ?
 556                     new XTabularData(this, (TabularData) Array.get(val, i)) :
 557                     Array.get(val, i);
 558                 String str = rowData[0].toString();
 559                 if (str.length() > col1Width) {
 560                     col1Width = str.length();
 561                 }
 562                 ((DefaultTableModel) getModel()).addRow(rowData);
 563             }
 564         }
 565 
 566         public String toString() {
 567             if (dimension > 1) {
 568                 return Resources.getText("Dimension is not supported:") +
 569                         dimension;
 570             } else {
 571                 return elemType.getTypeName() + "[" + size + "]";
 572             }
 573         }
 574     }
 575 
 576     /**
 577      * The supplied value is viewable iff:
 578      * - it's a CompositeData/TabularData, or
 579      * - it's a non-empty array of CompositeData/TabularData, or
 580      * - it's a non-empty Collection of CompositeData/TabularData.
 581      */
 582     public static boolean isViewableValue(Object value) {
 583         // Check for CompositeData/TabularData
 584         //
 585         if (value instanceof CompositeData || value instanceof TabularData) {
 586             return true;
 587         }
 588         // Check for non-empty array of CompositeData/TabularData
 589         //
 590         if (value instanceof CompositeData[] || value instanceof TabularData[]) {
 591             return Array.getLength(value) > 0;
 592         }
 593         // Check for non-empty Collection of CompositeData/TabularData
 594         //
 595         if (value instanceof Collection) {
 596             Collection<?> c = (Collection<?>) value;
 597             if (c.isEmpty()) {
 598                 // Empty Collections are not viewable
 599                 //
 600                 return false;
 601             } else {
 602                 // Only Collections of CompositeData/TabularData are viewable
 603                 //
 604                 return Utils.isUniformCollection(c, CompositeData.class) ||
 605                         Utils.isUniformCollection(c, TabularData.class);
 606             }
 607         }
 608         return false;
 609     }
 610 
 611     public static Component loadOpenType(Object value) {
 612         Component comp = null;
 613         if(isViewableValue(value)) {
 614             XOpenTypeViewer open =
 615                     new XOpenTypeViewer(value);
 616             comp = open;
 617         }
 618         return comp;
 619     }
 620 
 621     private XOpenTypeViewer(Object value) {
 622         XOpenTypeData comp = null;
 623         if (value instanceof CompositeData) {
 624             comp = new XCompositeData(null, (CompositeData) value);
 625         } else if (value instanceof TabularData) {
 626             comp = new XTabularData(null, (TabularData) value);
 627         } else if (value instanceof CompositeData[]) {
 628             CompositeData cda[] = (CompositeData[]) value;
 629             CompositeType ct = cda[0].getCompositeType();
 630             comp = new XArrayData(null, 1, ct, cda);
 631         } else if (value instanceof TabularData[]) {
 632             TabularData tda[] = (TabularData[]) value;
 633             TabularType tt = tda[0].getTabularType();
 634             comp = new XArrayData(null, 1, tt, tda);
 635         } else if (value instanceof Collection) {
 636             // At this point we know 'value' is a uniform collection, either
 637             // Collection<CompositeData> or Collection<TabularData>, because
 638             // isViewableValue() has been called before calling the private
 639             // XOpenTypeViewer() constructor.
 640             //
 641             Object e = ((Collection<?>) value).iterator().next();
 642             if (e instanceof CompositeData) {
 643                 Collection<?> cdc = (Collection<?>) value;
 644                 CompositeData cda[] = cdc.toArray(new CompositeData[0]);
 645                 CompositeType ct = cda[0].getCompositeType();
 646                 comp = new XArrayData(null, 1, ct, cda);
 647             } else if (e instanceof TabularData) {
 648                 Collection<?> tdc = (Collection<?>) value;
 649                 TabularData tda[] = tdc.toArray(new TabularData[0]);
 650                 TabularType tt = tda[0].getTabularType();
 651                 comp = new XArrayData(null, 1, tt, tda);
 652             }
 653         }
 654         setupDisplay(comp);
 655         try {
 656             comp.viewed(this);
 657         } catch (Exception e) {
 658             // Nothing to change, the element can't be displayed
 659             if (JConsole.isDebug()) {
 660                 System.out.println("Exception viewing openType : " + e);
 661                 e.printStackTrace();
 662             }
 663         }
 664     }
 665 
 666     void setOpenType(XOpenTypeData data) {
 667         if (current != null) {
 668             current.removeMouseListener(listener);
 669         }
 670 
 671         current = data;
 672 
 673         // Enable/Disable the previous (<<) button
 674         if (current.getViewedParent() == null) {
 675             prev.setEnabled(false);
 676         } else {
 677             prev.setEnabled(true);
 678         }
 679 
 680         // Set the listener to handle double-click mouse events
 681         current.addMouseListener(listener);
 682 
 683         // Enable/Disable the tabular buttons
 684         if (!(data instanceof XViewedTabularData)) {
 685             tabularPrev.setEnabled(false);
 686             tabularNext.setEnabled(false);
 687             tabularLabel.setText(tabularNavigationSingle);
 688             tabularLabel.setEnabled(false);
 689         } else {
 690             XViewedTabularData tabular = (XViewedTabularData) data;
 691             tabularNext.setEnabled(tabular.canIncrement());
 692             tabularPrev.setEnabled(tabular.canDecrement());
 693             boolean hasMoreThanOneElement =
 694                     tabular.canIncrement() || tabular.canDecrement();
 695             if (hasMoreThanOneElement) {
 696                 tabularLabel.setText(
 697                         Resources.getText("MBeansTab.tabularNavigationMultiple",
 698                         String.format("%d", tabular.getSelectedElementIndex() + 1),
 699                         String.format("%d", tabular.getElementCount())));
 700             } else {
 701                 tabularLabel.setText(tabularNavigationSingle);
 702             }
 703             tabularLabel.setEnabled(hasMoreThanOneElement);
 704         }
 705 
 706         // Enable/Disable the composite buttons
 707         if (!(data instanceof XViewedArrayData)) {
 708             incr.setEnabled(false);
 709             decr.setEnabled(false);
 710             compositeLabel.setText(compositeNavigationSingle);
 711             compositeLabel.setEnabled(false);
 712         } else {
 713             XViewedArrayData array = (XViewedArrayData) data;
 714             incr.setEnabled(array.canIncrement());
 715             decr.setEnabled(array.canDecrement());
 716             boolean hasMoreThanOneElement =
 717                     array.canIncrement() || array.canDecrement();
 718             if (hasMoreThanOneElement) {
 719                 compositeLabel.setText(
 720                         Resources.getText("MBeansTab.compositeNavigationMultiple",
 721                         String.format("%d", array.getSelectedElementIndex() + 1),
 722                         String.format("%d", array.getElementCount())));
 723             } else {
 724                 compositeLabel.setText(compositeNavigationSingle);
 725             }
 726             compositeLabel.setEnabled(hasMoreThanOneElement);
 727         }
 728 
 729         container.invalidate();
 730         container.setViewportView(current);
 731         container.validate();
 732     }
 733 
 734     public void actionPerformed(ActionEvent event) {
 735         if (event.getSource() instanceof JButton) {
 736             JButton b = (JButton) event.getSource();
 737             if (b == prev) {
 738                 XOpenTypeData parent = current.getViewedParent();
 739                 try {
 740                     parent.viewed(this);
 741                 } catch (Exception e) {
 742                     //Nothing to change, the element can't be displayed
 743                 }
 744             } else if (b == incr) {
 745                 ((XViewedArrayData) current).incrElement();
 746                 try {
 747                     current.viewed(this);
 748                 } catch (Exception e) {
 749                     //Nothing to change, the element can't be displayed
 750                 }
 751             } else if (b == decr) {
 752                 ((XViewedArrayData) current).decrElement();
 753                 try {
 754                     current.viewed(this);
 755                 } catch (Exception e) {
 756                     //Nothing to change, the element can't be displayed
 757                 }
 758             } else if (b == tabularNext) {
 759                 ((XViewedTabularData) current).incrElement();
 760                 try {
 761                     current.viewed(this);
 762                 } catch (Exception e) {
 763                     //Nothing to change, the element can't be displayed
 764                 }
 765             } else if (b == tabularPrev) {
 766                 ((XViewedTabularData) current).decrElement();
 767                 try {
 768                     current.viewed(this);
 769                 } catch (Exception e) {
 770                     //Nothing to change, the element can't be displayed
 771                 }
 772             }
 773         }
 774     }
 775 
 776     private void setupDisplay(XOpenTypeData data) {
 777         setBackground(Color.white);
 778         container =
 779                 new JScrollPane(data,
 780                 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
 781                 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 782 
 783         JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
 784         tabularPrev = new JButton(Resources.getText("<"));
 785         tabularNext = new JButton(Resources.getText(">"));
 786         JPanel tabularButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));
 787         tabularButtons.add(tabularPrev);
 788         tabularPrev.addActionListener(this);
 789         tabularLabel = new JLabel(tabularNavigationSingle);
 790         tabularLabel.setEnabled(false);
 791         tabularButtons.add(tabularLabel);
 792         tabularButtons.add(tabularNext);
 793         tabularNext.addActionListener(this);
 794         tabularButtons.setBackground(Color.white);
 795 
 796         prev = new JButton(Resources.getText("<<"));
 797         prev.addActionListener(this);
 798         buttons.add(prev);
 799 
 800         incr = new JButton(Resources.getText(">"));
 801         incr.addActionListener(this);
 802         decr = new JButton(Resources.getText("<"));
 803         decr.addActionListener(this);
 804 
 805         JPanel array = new JPanel();
 806         array.setBackground(Color.white);
 807         array.add(decr);
 808         compositeLabel = new JLabel(compositeNavigationSingle);
 809         compositeLabel.setEnabled(false);
 810         array.add(compositeLabel);
 811         array.add(incr);
 812 
 813         buttons.add(array);
 814         setLayout(new BorderLayout());
 815         buttons.setBackground(Color.white);
 816 
 817         JPanel navigationPanel = new JPanel(new BorderLayout());
 818         navigationPanel.setBackground(Color.white);
 819         navigationPanel.add(tabularButtons, BorderLayout.NORTH);
 820         navigationPanel.add(buttons, BorderLayout.WEST);
 821         add(navigationPanel, BorderLayout.NORTH);
 822 
 823         add(container, BorderLayout.CENTER);
 824         Dimension d = new Dimension((int)container.getPreferredSize().
 825                 getWidth() + 20,
 826                 (int)container.getPreferredSize().
 827                 getHeight() + 20);
 828         setPreferredSize(d);
 829     }
 830 }