/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.modules.security; import com.sun.tools.visualvm.uisupport.UISupport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.net.SocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.BoundedRangeModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.awt.Mnemonics; import org.openide.util.NbBundle; /** * * @author Jiri Sedlacek */ abstract class ValuesCustomizer extends JPanel { // --- Private UI constants ------------------------------------------------ private static final Color DEFAULT_GRID_COLOR = new Color(240, 240, 240); // --- Public customizer types --------------------------------------------- static final ValuesCustomizer PROTOCOLS = new Protocols(); static final ValuesCustomizer CIPHER_SUITES = new CipherSuites(); // --- Public entrypoint --------------------------------------------------- static String customize(final ValuesCustomizer customizer, String selectedValues) { customizer.init(selectedValues); final DialogDescriptor dd = new DialogDescriptor(customizer, customizer.dialogTitle(), true, null); final Dialog d = DialogDisplayer.getDefault().createDialog(dd); d.pack(); SwingUtilities.invokeLater(new Runnable() { public void run() { customizer.onShown(); } }); d.setVisible(true); String result = dd.getValue() != DialogDescriptor.OK_OPTION ? null : customizer.getSelectedCipherSuites(); customizer.cleanup(); return result; } // --- Predefined customizers ---------------------------------------------- private static class Protocols extends ValuesCustomizer { private String[] allValues; String dialogTitle() { return NbBundle.getMessage(ValuesCustomizer.class, "CAP_SelectProtocols"); // NOI18N } String hintText() { return NbBundle.getMessage(ValuesCustomizer.class, "HINT_SelectProtocols"); // NOI18N } String valueName() { return NbBundle.getMessage(ValuesCustomizer.class, "COL_Protocols"); // NOI18N } synchronized String[] allValues() { if (allValues == null) { SocketFactory f = SSLSocketFactory.getDefault(); if (!(f instanceof SSLSocketFactory)) allValues = new String[0]; try { Socket s = ((SSLSocketFactory)f).createSocket(); if (!(s instanceof SSLSocket)) allValues = new String[0]; allValues = ((SSLSocket)s).getSupportedProtocols(); } catch (Exception e) { allValues = new String[0]; } } return allValues; } } private static class CipherSuites extends ValuesCustomizer { private String[] allValues; String dialogTitle() { return NbBundle.getMessage(ValuesCustomizer.class, "CAP_SelectCipherSuites"); // NOI18N } String hintText() { return NbBundle.getMessage(ValuesCustomizer.class, "HINT_SelectCipherSuites"); // NOI18N } String valueName() { return NbBundle.getMessage(ValuesCustomizer.class, "COL_CipherSuites"); // NOI18N } synchronized String[] allValues() { if (allValues == null) { SocketFactory f = SSLSocketFactory.getDefault(); if (!(f instanceof SSLSocketFactory)) allValues = new String[0]; allValues = ((SSLSocketFactory)f).getSupportedCipherSuites(); } return allValues; } } // --- Abstract interface -------------------------------------------------- abstract String dialogTitle(); abstract String hintText(); abstract String valueName(); abstract String[] allValues(); // --- Private implementation ---------------------------------------------- private void init(String selectedValues) { initModels(selectedValues); initComponents(); } private void onShown() { table.requestFocusInWindow(); } private String getSelectedCipherSuites() { StringBuilder b = new StringBuilder(); for (int i = 0; i < model.getRowCount(); i++) if (Boolean.TRUE.equals(model.getValueAt(i, 1))) b.append(model.getValueAt(i, 0).toString() + ","); // NOI18N int length = b.length(); if (length > 0) b.deleteCharAt(length - 1); return b.toString(); } private void cleanup() { removeAll(); table = null; model = null; } private void initModels(String selectedValues) { String[] allValuesArr = allValues(); String[] selectedValuesArr = selectedValues(selectedValues); final String[] cipherSuites = mergedValues(allValuesArr, selectedValuesArr); final boolean[] selectedMask = selectedValuesMask(cipherSuites, selectedValuesArr); final int rowsCount = cipherSuites.length; model = new DefaultTableModel() { public int getRowCount() { return rowsCount; } public int getColumnCount() { return 2; } public String getColumnName(int columnIndex) { if (columnIndex == 0) return valueName(); else return NbBundle.getMessage(ValuesCustomizer.class, "COL_Enabled"); // NOI18N } public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) return String.class; else return Boolean.class; } public boolean isCellEditable(int rowIndex, int columnIndex) { if (columnIndex == 0) return false; else return true; } public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) return cipherSuites[rowIndex]; else return selectedMask[rowIndex]; } public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (columnIndex == 1) selectedMask[rowIndex] = (Boolean)aValue; } }; } private static String[] selectedValues(String selectedValues) { StringTokenizer st = new StringTokenizer(selectedValues, ","); // NOI18N String[] cipherSuites = new String[st.countTokens()]; for (int i = 0; i < cipherSuites.length; i++) cipherSuites[i] = st.nextToken(); return cipherSuites; } private static String[] mergedValues(String[] supported, String[] selected) { List<String> supportedList = Arrays.asList(supported); List<String> selectedList = Arrays.asList(selected); Set<String> mergedSet = new HashSet(supportedList); mergedSet.addAll(selectedList); List<String> merged = new ArrayList(mergedSet); Collections.sort(merged); return merged.toArray(new String[merged.size()]); } private static boolean[] selectedValuesMask(String[] allValues, String[] selectedValues) { boolean[] mask = new boolean[allValues.length]; List<String> selectedValuesList = Arrays.asList(selectedValues); for (int i = 0; i < mask.length; i++) if (selectedValuesList.contains(allValues[i])) mask[i] = true; return mask; } private void setAllSelected(boolean selected) { for (int i = 0; i < model.getRowCount(); i++) model.setValueAt(selected, i, 1); model.fireTableDataChanged(); } private void initComponents() { // hintLabel JLabel hintLabel = new JLabel(); Mnemonics.setLocalizedText(hintLabel, hintText()); // table table = new JTable(model) { protected void processMouseEvent(MouseEvent e) { MouseEvent eventToDispatch = e; Point p = e.getPoint(); int column = columnAtPoint(p); if (column != 1) { int row = rowAtPoint(p); Rectangle cellRect = getCellRect(row, 1, false); p.x = cellRect.x + 1; eventToDispatch = new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), p.x, p.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()); } super.processMouseEvent(eventToDispatch); } protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { getColumnModel().getSelectionModel().setSelectionInterval(1, 1); return super.processKeyBinding(ks, e, condition, pressed); } protected void initializeLocalVars() { super.initializeLocalVars(); setPreferredScrollableViewportSize(new Dimension(450, 300)); } }; hintLabel.setLabelFor(table); table.setOpaque(true); table.setBackground(UISupport.getDefaultBackground()); table.setRowHeight(defaultRowHeight() + 4); table.setRowMargin(0); table.setAutoCreateRowSorter(true); table.setShowHorizontalLines(false); table.setShowVerticalLines(true); table.setGridColor(DEFAULT_GRID_COLOR); table.setDefaultRenderer(String.class, new Renderer( table.getDefaultRenderer(String.class))); table.setDefaultRenderer(Boolean.class, new BooleanRenderer( table.getDefaultRenderer(Boolean.class))); table.getColumnModel().setColumnMargin(1); TableColumn c = table.getColumnModel().getColumn(1); c.setMaxWidth(c.getPreferredWidth()); c.setResizable(false); // viewport JViewport viewport = new Viewport(table); // tableScroll JScrollPane tableScroll = new JScrollPane( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); tableScroll.setViewport(viewport); final JScrollBar vScrollBar = tableScroll.getVerticalScrollBar(); final BoundedRangeModel vScrollBarModel = vScrollBar.getModel(); vScrollBarModel.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { vScrollBar.setEnabled(vScrollBarModel.getExtent() != vScrollBarModel.getMaximum()); } }); // cornerButton final JButton cornerButton = new JButton(); cornerButton.setDefaultCapable(false); cornerButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JMenuItem selectAll = new JMenuItem (new AbstractAction() { public void actionPerformed(ActionEvent e) { setAllSelected(true); } }); Mnemonics.setLocalizedText(selectAll, NbBundle.getMessage( ValuesCustomizer.class, "ACT_SelectAll")); // NOI18N JMenuItem deselectAll = new JMenuItem(new AbstractAction() { public void actionPerformed(ActionEvent e) { setAllSelected(false); } }); Mnemonics.setLocalizedText(deselectAll, NbBundle.getMessage( ValuesCustomizer.class, "ACT_DeselectAll")); // NOI18N JPopupMenu popup = new JPopupMenu(); popup.add(selectAll); popup.add(deselectAll); Dimension s = popup.getPreferredSize(); popup.show(cornerButton, cornerButton.getWidth() / 2 - s.width, cornerButton.getHeight() / 2); } }); tableScroll.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, cornerButton); // this setOpaque(false); setBorder(BorderFactory.createEmptyBorder(15, 10, 5, 10)); setLayout(new BorderLayout(5, 5)); add(hintLabel, BorderLayout.NORTH); add(tableScroll, BorderLayout.CENTER); } private static int defaultRowHeight() { return new JLabel("X").getPreferredSize().height + 4; // NOI18N } private ValuesCustomizer() {} private DefaultTableModel model; private JTable table; private static class Renderer implements TableCellRenderer { private static final Color BACKGROUND; private static final Color DARKER_BACKGROUND; static { BACKGROUND = UISupport.getDefaultBackground(); int darkerR = BACKGROUND.getRed() - 11; if (darkerR < 0) darkerR += 26; int darkerG = BACKGROUND.getGreen() - 11; if (darkerG < 0) darkerG += 26; int darkerB = BACKGROUND.getBlue() - 11; if (darkerB < 0) darkerB += 26; DARKER_BACKGROUND = new Color(darkerR, darkerG, darkerB); } private TableCellRenderer impl; Renderer(TableCellRenderer impl) { this.impl = impl; } protected Object formatValue(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { return value; } protected void updateRenderer(Component c, JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (!isSelected) { c.setBackground(row % 2 == 0 ? DARKER_BACKGROUND : BACKGROUND); // Make sure the renderer paints its background (Nimbus) if (c instanceof JComponent) ((JComponent)c).setOpaque(true); } } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (impl == null) impl = table.getDefaultRenderer(table.getColumnClass(column)); value = formatValue(table, value, isSelected, false, row, column); Component c = impl.getTableCellRendererComponent(table, value, isSelected, false, row, column); updateRenderer(c, table, value, isSelected, false, row, column); return c; } } private static class BooleanRenderer extends Renderer { BooleanRenderer(TableCellRenderer renderer) { super(renderer); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // Workaround strange selection behavior for newly selected checkbox isSelected = isSelected || hasFocus; return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } } private static class Viewport extends JViewport { private final JTable view; private final Color background; Viewport(JTable view) { super(); setView(view); this.view = view; setOpaque(true); background = view.getBackground(); setBackground(background); view.getColumnModel().addColumnModelListener(new TableColumnModelListener() { public void columnAdded(TableColumnModelEvent e) { repaint(); } public void columnMoved(TableColumnModelEvent e) { repaint(); } public void columnRemoved(TableColumnModelEvent e) { repaint(); } public void columnMarginChanged(ChangeEvent e) { repaint(); } public void columnSelectionChanged(ListSelectionEvent e) {} }); } protected void paintComponent(Graphics g) { super.paintComponent(g); paintVerticalLines(g); } private void paintVerticalLines(Graphics g) { int height = getHeight(); int viewHeight = view.getHeight(); if (viewHeight >= height) return; g.setColor(background); g.fillRect(0, viewHeight, getWidth(), getHeight() - viewHeight); int cellX = 0; int cellWidth; TableColumnModel model = view.getColumnModel(); int columnCount = model.getColumnCount(); g.setColor(DEFAULT_GRID_COLOR); for (int i = 0; i < columnCount; i++) { cellWidth = model.getColumn(i).getWidth(); cellX += cellWidth; g.drawLine(cellX - 1, viewHeight, cellX - 1, height); } } } }