1/*
2 * Copyright (c) 2004, 2014, 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
26package sun.tools.jconsole.inspector;
27
28import javax.swing.*;
29import javax.swing.event.*;
30import javax.swing.table.*;
31import java.awt.BorderLayout;
32import java.awt.FlowLayout;
33import java.awt.Component;
34import java.awt.Color;
35import java.awt.Font;
36import java.awt.event.*;
37import java.awt.Dimension;
38import java.util.*;
39import java.lang.reflect.Array;
40
41import javax.management.openmbean.*;
42
43import sun.tools.jconsole.JConsole;
44import sun.tools.jconsole.Messages;
45import sun.tools.jconsole.Resources;
46
47@SuppressWarnings("serial")
48public 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 = 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}
817