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