/*
 * Copyright (c) 2004, 2013, 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 sun.tools.jconsole.inspector;

import java.awt.event.*;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ExecutionException;
import javax.management.*;
import javax.management.openmbean.*;
import javax.swing.*;
import javax.swing.text.*;

public class Utils {

    private Utils() {
    }
    private static Set<Integer> tableNavigationKeys =
            new HashSet<Integer>(Arrays.asList(new Integer[]{
        KeyEvent.VK_TAB, KeyEvent.VK_ENTER,
        KeyEvent.VK_HOME, KeyEvent.VK_END,
        KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT,
        KeyEvent.VK_UP, KeyEvent.VK_DOWN,
        KeyEvent.VK_PAGE_UP, KeyEvent.VK_PAGE_DOWN
    }));
    private static final Set<Class<?>> primitiveWrappers =
            new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{
        Byte.class, Short.class, Integer.class, Long.class,
        Float.class, Double.class, Character.class, Boolean.class
    }));
    private static final Set<Class<?>> primitives = new HashSet<Class<?>>();
    private static final Map<String, Class<?>> primitiveMap =
            new HashMap<String, Class<?>>();
    private static final Map<String, Class<?>> primitiveToWrapper =
            new HashMap<String, Class<?>>();
    private static final Set<String> editableTypes = new HashSet<String>();
    private static final Set<Class<?>> extraEditableClasses =
            new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{
        BigDecimal.class, BigInteger.class, Number.class,
        String.class, ObjectName.class
    }));
    private static final Set<String> numericalTypes = new HashSet<String>();
    private static final Set<String> extraNumericalTypes =
            new HashSet<String>(Arrays.asList(new String[]{
        BigDecimal.class.getName(), BigInteger.class.getName(),
        Number.class.getName()
    }));
    private static final Set<String> booleanTypes =
            new HashSet<String>(Arrays.asList(new String[]{
        Boolean.TYPE.getName(), Boolean.class.getName()
    }));

    static {
        // compute primitives/primitiveMap/primitiveToWrapper
        for (Class<?> c : primitiveWrappers) {
            try {
                Field f = c.getField("TYPE");
                Class<?> p = (Class<?>) f.get(null);
                primitives.add(p);
                primitiveMap.put(p.getName(), p);
                primitiveToWrapper.put(p.getName(), c);
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        }
        // compute editableTypes
        for (Class<?> c : primitives) {
            editableTypes.add(c.getName());
        }
        for (Class<?> c : primitiveWrappers) {
            editableTypes.add(c.getName());
        }
        for (Class<?> c : extraEditableClasses) {
            editableTypes.add(c.getName());
        }
        // compute numericalTypes
        for (Class<?> c : primitives) {
            String name = c.getName();
            if (!name.equals(Boolean.TYPE.getName())) {
                numericalTypes.add(name);
            }
        }
        for (Class<?> c : primitiveWrappers) {
            String name = c.getName();
            if (!name.equals(Boolean.class.getName())) {
                numericalTypes.add(name);
            }
        }
    }

    
This method returns the class matching the name className. It's used to cater for the primitive types.
/** * This method returns the class matching the name className. * It's used to cater for the primitive types. */
public static Class<?> getClass(String className) throws ClassNotFoundException { Class<?> c; if ((c = primitiveMap.get(className)) != null) { return c; } return Class.forName(className); }
Check if the given collection is a uniform collection of the given type.
/** * Check if the given collection is a uniform collection of the given type. */
public static boolean isUniformCollection(Collection<?> c, Class<?> e) { if (e == null) { throw new IllegalArgumentException("Null reference type"); } if (c == null) { throw new IllegalArgumentException("Null collection"); } if (c.isEmpty()) { return false; } for (Object o : c) { if (o == null || !e.isAssignableFrom(o.getClass())) { return false; } } return true; }
Check if the given element denotes a supported array-friendly data structure, i.e. a data structure jconsole can render as an array.
/** * Check if the given element denotes a supported array-friendly data * structure, i.e. a data structure jconsole can render as an array. */
public static boolean canBeRenderedAsArray(Object elem) { if (isSupportedArray(elem)) { return true; } if (elem instanceof Collection) { Collection<?> c = (Collection<?>) elem; if (c.isEmpty()) { // Empty collections of any Java type are not handled as arrays // return false; } else { // - Collections of CompositeData/TabularData are not handled // as arrays // - Collections of other Java types are handled as arrays // return !isUniformCollection(c, CompositeData.class) && !isUniformCollection(c, TabularData.class); } } if (elem instanceof Map) { return !(elem instanceof TabularData); } return false; }
Check if the given element is an array. Multidimensional arrays are not supported. Non-empty 1-dimensional arrays of CompositeData and TabularData are not handled as arrays but as tabular data.
/** * Check if the given element is an array. * * Multidimensional arrays are not supported. * * Non-empty 1-dimensional arrays of CompositeData * and TabularData are not handled as arrays but as * tabular data. */
public static boolean isSupportedArray(Object elem) { if (elem == null || !elem.getClass().isArray()) { return false; } Class<?> ct = elem.getClass().getComponentType(); if (ct.isArray()) { return false; } if (Array.getLength(elem) > 0 && (CompositeData.class.isAssignableFrom(ct) || TabularData.class.isAssignableFrom(ct))) { return false; } return true; }
This method provides a readable classname if it's an array, i.e. either the classname of the component type for arrays of java reference types or the name of the primitive type for arrays of java primitive types. Otherwise, it returns null.
/** * This method provides a readable classname if it's an array, * i.e. either the classname of the component type for arrays * of java reference types or the name of the primitive type * for arrays of java primitive types. Otherwise, it returns null. */
public static String getArrayClassName(String name) { String className = null; if (name.startsWith("[")) { int index = name.lastIndexOf('['); className = name.substring(index, name.length()); if (className.startsWith("[L")) { className = className.substring(2, className.length() - 1); } else { try { Class<?> c = Class.forName(className); className = c.getComponentType().getName(); } catch (ClassNotFoundException e) { // Should not happen throw new IllegalArgumentException( "Bad class name " + name, e); } } } return className; }
This methods provides a readable classname. If the supplied name parameter denotes an array this method returns either the classname of the component type for arrays of java reference types or the name of the primitive type for arrays of java primitive types followed by n-times "[]" where 'n' denotes the arity of the array. Otherwise, if the supplied name doesn't denote an array it returns the same classname.
/** * This methods provides a readable classname. If the supplied name * parameter denotes an array this method returns either the classname * of the component type for arrays of java reference types or the name * of the primitive type for arrays of java primitive types followed by * n-times "[]" where 'n' denotes the arity of the array. Otherwise, if * the supplied name doesn't denote an array it returns the same classname. */
public static String getReadableClassName(String name) { String className = getArrayClassName(name); if (className == null) { return name; } int index = name.lastIndexOf('['); StringBuilder brackets = new StringBuilder(className); for (int i = 0; i <= index; i++) { brackets.append("[]"); } return brackets.toString(); }
This method tells whether the type is editable (means can be created with a String or not)
/** * This method tells whether the type is editable * (means can be created with a String or not) */
public static boolean isEditableType(String type) { return editableTypes.contains(type); }
This method inserts a default value for the standard java types, else it inserts the text name of the expected class type. It acts to give a clue as to the input type.
/** * This method inserts a default value for the standard java types, * else it inserts the text name of the expected class type. * It acts to give a clue as to the input type. */
public static String getDefaultValue(String type) { if (numericalTypes.contains(type) || extraNumericalTypes.contains(type)) { return "0"; } if (booleanTypes.contains(type)) { return "true"; } type = getReadableClassName(type); int i = type.lastIndexOf('.'); if (i > 0) { return type.substring(i + 1, type.length()); } else { return type; } }
Try to create a Java object using a one-string-param constructor.
/** * Try to create a Java object using a one-string-param constructor. */
public static Object newStringConstructor(String type, String param) throws Exception { Constructor<?> c = Utils.getClass(type).getConstructor(String.class); try { return c.newInstance(param); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof Exception) { throw (Exception) t; } else { throw e; } } }
Try to convert a string value into a numerical value.
/** * Try to convert a string value into a numerical value. */
private static Number createNumberFromStringValue(String value) throws NumberFormatException { final String suffix = value.substring(value.length() - 1); if ("L".equalsIgnoreCase(suffix)) { return Long.valueOf(value.substring(0, value.length() - 1)); } if ("F".equalsIgnoreCase(suffix)) { return Float.valueOf(value.substring(0, value.length() - 1)); } if ("D".equalsIgnoreCase(suffix)) { return Double.valueOf(value.substring(0, value.length() - 1)); } try { return Integer.valueOf(value); } catch (NumberFormatException e) { // OK: Ignore exception... } try { return Long.valueOf(value); } catch (NumberFormatException e1) { // OK: Ignore exception... } try { return Double.valueOf(value); } catch (NumberFormatException e2) { // OK: Ignore exception... } throw new NumberFormatException("Cannot convert string value '" + value + "' into a numerical value"); }
This method attempts to create an object of the given "type" using the "value" parameter. e.g. calling createObjectFromString("java.lang.Integer", "10") will return an Integer object initialized to 10.
/** * This method attempts to create an object of the given "type" * using the "value" parameter. * e.g. calling createObjectFromString("java.lang.Integer", "10") * will return an Integer object initialized to 10. */
public static Object createObjectFromString(String type, String value) throws Exception { Object result; if (primitiveToWrapper.containsKey(type)) { if (type.equals(Character.TYPE.getName())) { result = value.charAt(0); } else { result = newStringConstructor( ((Class<?>) primitiveToWrapper.get(type)).getName(), value); } } else if (type.equals(Character.class.getName())) { result = value.charAt(0); } else if (Number.class.isAssignableFrom(Utils.getClass(type))) { result = createNumberFromStringValue(value); } else if (value == null || value.equals("null")) { // hack for null value result = null; } else { // try to create a Java object using // the one-string-param constructor result = newStringConstructor(type, value); } return result; }
This method is responsible for converting the inputs given by the user into a useful object array for passing into a parameter array.
/** * This method is responsible for converting the inputs given by the user * into a useful object array for passing into a parameter array. */
public static Object[] getParameters(XTextField[] inputs, String[] params) throws Exception { Object result[] = new Object[inputs.length]; Object userInput; for (int i = 0; i < inputs.length; i++) { userInput = inputs[i].getValue(); // if it's already a complex object, use the value // else try to instantiate with string constructor if (userInput instanceof XObject) { result[i] = ((XObject) userInput).getObject(); } else { result[i] = createObjectFromString(params[i].toString(), (String) userInput); } } return result; }
If the exception is wrapped, unwrap it.
/** * If the exception is wrapped, unwrap it. */
public static Throwable getActualException(Throwable e) { if (e instanceof ExecutionException) { e = e.getCause(); } if (e instanceof MBeanException || e instanceof RuntimeMBeanException || e instanceof RuntimeOperationsException || e instanceof ReflectionException) { Throwable t = e.getCause(); if (t != null) { return t; } } return e; } @SuppressWarnings("serial") public static class ReadOnlyTableCellEditor extends DefaultCellEditor { public ReadOnlyTableCellEditor(JTextField tf) { super(tf); tf.addFocusListener(new Utils.EditFocusAdapter(this)); tf.addKeyListener(new Utils.CopyKeyAdapter()); } } public static class EditFocusAdapter extends FocusAdapter { private CellEditor editor; public EditFocusAdapter(CellEditor editor) { this.editor = editor; } @Override public void focusLost(FocusEvent e) { editor.stopCellEditing(); } } public static class CopyKeyAdapter extends KeyAdapter { private static final String defaultEditorKitCopyActionName = DefaultEditorKit.copyAction; private static final String transferHandlerCopyActionName = (String) TransferHandler.getCopyAction().getValue(Action.NAME); @Override public void keyPressed(KeyEvent e) { // Accept "copy" key strokes KeyStroke ks = KeyStroke.getKeyStroke( e.getKeyCode(), e.getModifiersEx()); JComponent comp = (JComponent) e.getSource(); for (int i = 0; i < 3; i++) { InputMap im = comp.getInputMap(i); Object key = im.get(ks); if (defaultEditorKitCopyActionName.equals(key) || transferHandlerCopyActionName.equals(key)) { return; } } // Accept JTable navigation key strokes if (!tableNavigationKeys.contains(e.getKeyCode())) { e.consume(); } } @Override public void keyTyped(KeyEvent e) { e.consume(); } } }