1 /* 2 * Copyright (c) 2004, 2008, 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 java.awt.event.*; 29 import java.lang.reflect.*; 30 import java.math.BigDecimal; 31 import java.math.BigInteger; 32 import java.util.*; 33 import java.util.concurrent.ExecutionException; 34 import javax.management.*; 35 import javax.management.openmbean.*; 36 import javax.swing.*; 37 import javax.swing.text.*; 38 39 public class Utils { 40 41 private Utils() { 42 } 43 private static Set<Integer> tableNavigationKeys = 44 new HashSet<Integer>(Arrays.asList(new Integer[]{ 45 KeyEvent.VK_TAB, KeyEvent.VK_ENTER, 46 KeyEvent.VK_HOME, KeyEvent.VK_END, 47 KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, 48 KeyEvent.VK_UP, KeyEvent.VK_DOWN, 49 KeyEvent.VK_PAGE_UP, KeyEvent.VK_PAGE_DOWN 50 })); 51 private static final Set<Class<?>> primitiveWrappers = 52 new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{ 53 Byte.class, Short.class, Integer.class, Long.class, 54 Float.class, Double.class, Character.class, Boolean.class 55 })); 56 private static final Set<Class<?>> primitives = new HashSet<Class<?>>(); 57 private static final Map<String, Class<?>> primitiveMap = 58 new HashMap<String, Class<?>>(); 59 private static final Map<String, Class<?>> primitiveToWrapper = 60 new HashMap<String, Class<?>>(); 61 private static final Set<String> editableTypes = new HashSet<String>(); 62 private static final Set<Class<?>> extraEditableClasses = 63 new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{ 64 BigDecimal.class, BigInteger.class, Number.class, 65 String.class, ObjectName.class 66 })); 67 private static final Set<String> numericalTypes = new HashSet<String>(); 68 private static final Set<String> extraNumericalTypes = 69 new HashSet<String>(Arrays.asList(new String[]{ 70 BigDecimal.class.getName(), BigInteger.class.getName(), 71 Number.class.getName() 72 })); 73 private static final Set<String> booleanTypes = 74 new HashSet<String>(Arrays.asList(new String[]{ 75 Boolean.TYPE.getName(), Boolean.class.getName() 76 })); 77 78 static { 79 // compute primitives/primitiveMap/primitiveToWrapper 80 for (Class<?> c : primitiveWrappers) { 81 try { 82 Field f = c.getField("TYPE"); 83 Class<?> p = (Class<?>) f.get(null); 84 primitives.add(p); 85 primitiveMap.put(p.getName(), p); 86 primitiveToWrapper.put(p.getName(), c); 87 } catch (Exception e) { 88 throw new AssertionError(e); 89 } 90 } 91 // compute editableTypes 92 for (Class<?> c : primitives) { 93 editableTypes.add(c.getName()); 94 } 95 for (Class<?> c : primitiveWrappers) { 96 editableTypes.add(c.getName()); 97 } 98 for (Class<?> c : extraEditableClasses) { 99 editableTypes.add(c.getName()); 100 } 101 // compute numericalTypes 102 for (Class<?> c : primitives) { 103 String name = c.getName(); 104 if (!name.equals(Boolean.TYPE.getName())) { 105 numericalTypes.add(name); 106 } 107 } 108 for (Class<?> c : primitiveWrappers) { 109 String name = c.getName(); 110 if (!name.equals(Boolean.class.getName())) { 111 numericalTypes.add(name); 112 } 113 } 114 } 115 116 /** 117 * This method returns the class matching the name className. 118 * It's used to cater for the primitive types. 119 */ 120 public static Class<?> getClass(String className) 121 throws ClassNotFoundException { 122 Class<?> c; 123 if ((c = primitiveMap.get(className)) != null) { 124 return c; 125 } 126 return Class.forName(className); 127 } 128 129 /** 130 * Check if the given collection is a uniform collection of the given type. 131 */ 132 public static boolean isUniformCollection(Collection<?> c, Class<?> e) { 133 if (e == null) { 134 throw new IllegalArgumentException("Null reference type"); 135 } 136 if (c == null) { 137 throw new IllegalArgumentException("Null collection"); 138 } 139 if (c.isEmpty()) { 140 return false; 141 } 142 for (Object o : c) { 143 if (o == null || !e.isAssignableFrom(o.getClass())) { 144 return false; 145 } 146 } 147 return true; 148 } 149 150 /** 151 * Check if the given element denotes a supported array-friendly data 152 * structure, i.e. a data structure jconsole can render as an array. 153 */ 154 public static boolean canBeRenderedAsArray(Object elem) { 155 if (isSupportedArray(elem)) { 156 return true; 157 } 158 if (elem instanceof Collection) { 159 Collection<?> c = (Collection<?>) elem; 160 if (c.isEmpty()) { 161 // Empty collections of any Java type are not handled as arrays 162 // 163 return false; 164 } else { 165 // - Collections of CompositeData/TabularData are not handled 166 // as arrays 167 // - Collections of other Java types are handled as arrays 168 // 169 return !isUniformCollection(c, CompositeData.class) && 170 !isUniformCollection(c, TabularData.class); 171 } 172 } 173 if (elem instanceof Map) { 174 return !(elem instanceof TabularData); 175 } 176 return false; 177 } 178 179 /** 180 * Check if the given element is an array. 181 * 182 * Multidimensional arrays are not supported. 183 * 184 * Non-empty 1-dimensional arrays of CompositeData 185 * and TabularData are not handled as arrays but as 186 * tabular data. 187 */ 188 public static boolean isSupportedArray(Object elem) { 189 if (elem == null || !elem.getClass().isArray()) { 190 return false; 191 } 192 Class<?> ct = elem.getClass().getComponentType(); 193 if (ct.isArray()) { 194 return false; 195 } 196 if (Array.getLength(elem) > 0 && 197 (CompositeData.class.isAssignableFrom(ct) || 198 TabularData.class.isAssignableFrom(ct))) { 199 return false; 200 } 201 return true; 202 } 203 204 /** 205 * This method provides a readable classname if it's an array, 206 * i.e. either the classname of the component type for arrays 207 * of java reference types or the name of the primitive type 208 * for arrays of java primitive types. Otherwise, it returns null. 209 */ 210 public static String getArrayClassName(String name) { 211 String className = null; 212 if (name.startsWith("[")) { 213 int index = name.lastIndexOf("["); 214 className = name.substring(index, name.length()); 215 if (className.startsWith("[L")) { 216 className = className.substring(2, className.length() - 1); 217 } else { 218 try { 219 Class<?> c = Class.forName(className); 220 className = c.getComponentType().getName(); 221 } catch (ClassNotFoundException e) { 222 // Should not happen 223 throw new IllegalArgumentException( 224 "Bad class name " + name, e); 225 } 226 } 227 } 228 return className; 229 } 230 231 /** 232 * This methods provides a readable classname. If the supplied name 233 * parameter denotes an array this method returns either the classname 234 * of the component type for arrays of java reference types or the name 235 * of the primitive type for arrays of java primitive types followed by 236 * n-times "[]" where 'n' denotes the arity of the array. Otherwise, if 237 * the supplied name doesn't denote an array it returns the same classname. 238 */ 239 public static String getReadableClassName(String name) { 240 String className = getArrayClassName(name); 241 if (className == null) { 242 return name; 243 } 244 int index = name.lastIndexOf("["); 245 StringBuilder brackets = new StringBuilder(className); 246 for (int i = 0; i <= index; i++) { 247 brackets.append("[]"); 248 } 249 return brackets.toString(); 250 } 251 252 /** 253 * This method tells whether the type is editable 254 * (means can be created with a String or not) 255 */ 256 public static boolean isEditableType(String type) { 257 return editableTypes.contains(type); 258 } 259 260 /** 261 * This method inserts a default value for the standard java types, 262 * else it inserts the text name of the expected class type. 263 * It acts to give a clue as to the input type. 264 */ 265 public static String getDefaultValue(String type) { 266 if (numericalTypes.contains(type) || 267 extraNumericalTypes.contains(type)) { 268 return "0"; 269 } 270 if (booleanTypes.contains(type)) { 271 return "true"; 272 } 273 type = getReadableClassName(type); 274 int i = type.lastIndexOf('.'); 275 if (i > 0) { 276 return type.substring(i + 1, type.length()); 277 } else { 278 return type; 279 } 280 } 281 282 /** 283 * Try to create a Java object using a one-string-param constructor. 284 */ 285 public static Object newStringConstructor(String type, String param) 286 throws Exception { 287 Constructor<?> c = Utils.getClass(type).getConstructor(String.class); 288 try { 289 return c.newInstance(param); 290 } catch (InvocationTargetException e) { 291 Throwable t = e.getTargetException(); 292 if (t instanceof Exception) { 293 throw (Exception) t; 294 } else { 295 throw e; 296 } 297 } 298 } 299 300 /** 301 * Try to convert a string value into a numerical value. 302 */ 303 private static Number createNumberFromStringValue(String value) 304 throws NumberFormatException { 305 final String suffix = value.substring(value.length() - 1); 306 if ("L".equalsIgnoreCase(suffix)) { 307 return Long.valueOf(value.substring(0, value.length() - 1)); 308 } 309 if ("F".equalsIgnoreCase(suffix)) { 310 return Float.valueOf(value.substring(0, value.length() - 1)); 311 } 312 if ("D".equalsIgnoreCase(suffix)) { 313 return Double.valueOf(value.substring(0, value.length() - 1)); 314 } 315 try { 316 return Integer.valueOf(value); 317 } catch (NumberFormatException e) { 318 // OK: Ignore exception... 319 } 320 try { 321 return Long.valueOf(value); 322 } catch (NumberFormatException e1) { 323 // OK: Ignore exception... 324 } 325 try { 326 return Double.valueOf(value); 327 } catch (NumberFormatException e2) { 328 // OK: Ignore exception... 329 } 330 throw new NumberFormatException("Cannot convert string value '" + 331 value + "' into a numerical value"); 332 } 333 334 /** 335 * This method attempts to create an object of the given "type" 336 * using the "value" parameter. 337 * e.g. calling createObjectFromString("java.lang.Integer", "10") 338 * will return an Integer object initialized to 10. 339 */ 340 public static Object createObjectFromString(String type, String value) 341 throws Exception { 342 Object result; 343 if (primitiveToWrapper.containsKey(type)) { 344 if (type.equals(Character.TYPE.getName())) { 345 result = new Character(value.charAt(0)); 346 } else { 347 result = newStringConstructor( 348 ((Class<?>) primitiveToWrapper.get(type)).getName(), 349 value); 350 } 351 } else if (type.equals(Character.class.getName())) { 352 result = new Character(value.charAt(0)); 353 } else if (Number.class.isAssignableFrom(Utils.getClass(type))) { 354 result = createNumberFromStringValue(value); 355 } else if (value == null || value.toString().equals("null")) { 356 // hack for null value 357 result = null; 358 } else { 359 // try to create a Java object using 360 // the one-string-param constructor 361 result = newStringConstructor(type, value); 362 } 363 return result; 364 } 365 366 /** 367 * This method is responsible for converting the inputs given by the user 368 * into a useful object array for passing into a parameter array. 369 */ 370 public static Object[] getParameters(XTextField[] inputs, String[] params) 371 throws Exception { 372 Object result[] = new Object[inputs.length]; 373 Object userInput; 374 for (int i = 0; i < inputs.length; i++) { 375 userInput = inputs[i].getValue(); 376 // if it's already a complex object, use the value 377 // else try to instantiate with string constructor 378 if (userInput instanceof XObject) { 379 result[i] = ((XObject) userInput).getObject(); 380 } else { 381 result[i] = createObjectFromString(params[i].toString(), 382 (String) userInput); 383 } 384 } 385 return result; 386 } 387 388 /** 389 * If the exception is wrapped, unwrap it. 390 */ 391 public static Throwable getActualException(Throwable e) { 392 if (e instanceof ExecutionException) { 393 e = e.getCause(); 394 } 395 if (e instanceof MBeanException || 396 e instanceof RuntimeMBeanException || 397 e instanceof RuntimeOperationsException || 398 e instanceof ReflectionException) { 399 Throwable t = e.getCause(); 400 if (t != null) { 401 return t; 402 } 403 } 404 return e; 405 } 406 407 @SuppressWarnings("serial") 408 public static class ReadOnlyTableCellEditor 409 extends DefaultCellEditor { 410 411 public ReadOnlyTableCellEditor(JTextField tf) { 412 super(tf); 413 tf.addFocusListener(new Utils.EditFocusAdapter(this)); 414 tf.addKeyListener(new Utils.CopyKeyAdapter()); 415 } 416 } 417 418 public static class EditFocusAdapter extends FocusAdapter { 419 420 private CellEditor editor; 421 422 public EditFocusAdapter(CellEditor editor) { 423 this.editor = editor; 424 } 425 426 @Override 427 public void focusLost(FocusEvent e) { 428 editor.stopCellEditing(); 429 } 430 } 431 432 public static class CopyKeyAdapter extends KeyAdapter { 433 private static final String defaultEditorKitCopyActionName = 434 DefaultEditorKit.copyAction; 435 private static final String transferHandlerCopyActionName = 436 (String) TransferHandler.getCopyAction().getValue(Action.NAME); 437 @Override 438 public void keyPressed(KeyEvent e) { 439 // Accept "copy" key strokes 440 KeyStroke ks = KeyStroke.getKeyStroke( 441 e.getKeyCode(), e.getModifiers()); 442 JComponent comp = (JComponent) e.getSource(); 443 for (int i = 0; i < 3; i++) { 444 InputMap im = comp.getInputMap(i); 445 Object key = im.get(ks); 446 if (defaultEditorKitCopyActionName.equals(key) || 447 transferHandlerCopyActionName.equals(key)) { 448 return; 449 } 450 } 451 // Accept JTable navigation key strokes 452 if (!tableNavigationKeys.contains(e.getKeyCode())) { 453 e.consume(); 454 } 455 } 456 457 @Override 458 public void keyTyped(KeyEvent e) { 459 e.consume(); 460 } 461 } 462 }