package abbot.editor; import java.awt.*; import java.lang.reflect.*; import java.util.*; import javax.swing.JComponent; import javax.swing.table.DefaultTableModel; import abbot.Log; import abbot.i18n.Strings; import abbot.script.PropertyCall; import abbot.tester.ComponentTester; class ComponentPropertyModel extends DefaultTableModel { public static final int PROPERTY_NAME = 0; public static final int PROPERTY_VALUE = 1; public static final int ACCESSIBLE = 2; public static final int METHOD_OBJECT = 3; private static Set filteredMethods = new HashSet(); static { // Indicate things that aren't particularly interesting filteredMethods.addAll(Arrays.asList(new String[] { // Component "getAccessibleContext", "getAlignmentX", "getAlignmentY", "getColorModel", "getComponentListeners", "getComponentOrientation", "getDropTarget", "getFocusListeners", "getGraphics", "getGraphicsConfiguration", "getHierarchyBoundsListeners", "getHierarchyListeners", "getInputContext", "getInputMethodListeners", "getInputMethodRequests", "getKeyListeners", "getMouseListeners", "getMouseMotionListeners", "getMouseWheelListeners", "getParent", "getPeer", "getToolkit", "getTreeLock", // Container "getComponents", "getContainerListeners", // JComponent "getActionMap", "getAncestorListeners", "getAutoscrolls", "getBufferStrategy", "getDebugGraphicsOptions", "getInputMap", "getInputVerifier", "getPropertyChangeListeners", "getRegisteredKeyStrokes", "getRootPane", "getTopLevelAncestor", "getUIClassID", "getVerifyInputWhenFocusTarget", "getVetoableChangeListeners", "getVisibleRect", "isFocusCycleRoot", "isOpaque", "isOptimizedDrawingEnabled", "isPaintingTile", "isPreferredSizeSet", "isRequestFocusEnabled", "isValidateRoot", // Window "getOwnedWindows", "getWindowFocusListeners", "getWindowListeners", "getWindowStateListeners", // Frame "getFrames", })); } private Map noAccess = new WeakHashMap(); /** Install the given filtered property method properties. Add-on ComponentTester classes should invoke this for the list of property methods they want to appear in the filtered property list. */ public static void setFilteredPropertyMethods(String[] methods) { filteredMethods.addAll(Arrays.asList(methods)); } /** Create a model with two columns, the property name and the property * value. */ public ComponentPropertyModel() { super(new Object[]{ Strings.get("Name"), Strings.get("Value") }, 0); } public void clear() { // The setNumRows() method is required to be compatible with JDK // 1.2.2 and should be replaced with setRowCount() for 1.3 and above. //propTableModel.setNumRows(0); setRowCount(0); } public void setComponent(Component comp) { setComponent(comp, true); } /** The current list of property methods and values corresponds to this * component. */ private Component currentComponent = null; /** Whether the current list is filtered. */ private boolean filtered = false; /** Update the list of property methods based on the newly selected component. */ public void setComponent(Component comp, boolean filter) { Class cls = comp != null ? comp.getClass() : null; if (currentComponent == comp && filter == filtered) return; clear(); currentComponent = comp; filtered = filter; Method[] all = getPropertyMethods(cls, filter); Arrays.sort(all, new Comparator() { public int compare(Object o1, Object o2) { String n1 = getPropertyName(((Method)o1).getName()); String n2 = getPropertyName(((Method)o2).getName()); return n1.compareTo(n2); } }); Object[] noArgs = new Object[0]; Object[] oneArg = new Object[] { comp }; for (int i=0;i < all.length;i++) { Method method = all[i]; Object value = ""; try { Object target = comp; Object[] args = noArgs; if (ComponentTester.class. isAssignableFrom(method.getDeclaringClass())) { target = ComponentTester.getTester(comp); args = oneArg; } if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getDeclaringClass().getModifiers() & Modifier.PUBLIC) == 0) { noAccess.put(method, Boolean.TRUE); method.setAccessible(true); } value = method.invoke(target, args); } catch(IllegalArgumentException e) { value = "<illegal argument>"; Log.debug(e); } catch(InvocationTargetException e) { value = "<target exception>"; Log.debug(e); } catch(IllegalAccessException e) { // method was somehow protected? value = "<not accessible>"; Log.debug(e); } addRow(new Object[] { method, value }); } fireTableDataChanged(); } Method[] getPropertyMethods(Class cls, boolean filter) { if (cls == null) return new Method[0]; // Make sure we only get one of each named method HashMap processed = new HashMap(); Method[] methods = cls.getMethods(); for(int i = 0; i < methods.length; i++) { if (isGetterMethod(methods[i], false) && !processed.containsKey(methods[i].getName())) { if (filter && filteredMethods.contains(methods[i].getName())) { continue; } processed.put(methods[i].getName(), methods[i]); } } // Now look up propert accessors provided by the corresponding // ComponentTester class. ComponentTester tester = ComponentTester.getTester(cls); methods = tester.getPropertyMethods(); for (int i=0;i < methods.length;i++) { if (!processed.containsKey(methods[i].getName())) { // Properties provided by the ComponentTester are never // filtered. processed.put(methods[i].getName(), methods[i]); } } return (Method[])processed.values().toArray(new Method[processed.size()]); } /** * Method to check if the method specified on the class * is a "getter" for an attribute of that class * * @param method The method to be tested * @return true if the method is a "getter" */ private boolean isGetterMethod(Method method, boolean isTester) { Class[] types = method.getParameterTypes(); int argc = types.length; return ((isTester && argc == 1 && Component.class.isAssignableFrom(types[0])) || (!isTester && argc == 0)) && PropertyCall.isPropertyMethod(method); } /** * Method to "extract" the property name from the method name * following the convention specified for a bean * * @param methodName The name of the method * @return The name of the attribute */ private String getPropertyName(String methodName) { String propName = methodName; if (methodName.startsWith("get") || methodName.startsWith("has")) { propName = methodName.substring(3); } else if(methodName.startsWith("is")) { propName = methodName.substring(2); } return propName.substring(0, 1).toLowerCase() + propName.substring(1); } public boolean isCellEditable(int row, int col) { return false; } /** Display the property name column apropriately. */ public Object getValueAt(int row, int col) { // The Method object is in column zero, we want only the property part // to appear. if (col == PROPERTY_NAME) { Method m = (Method)super.getValueAt(row, col); return getPropertyName(m.getName()); } if (col == METHOD_OBJECT) { return (Method)super.getValueAt(row, 0); } if (col == ACCESSIBLE) { Method m = ((Method)getValueAt(row, METHOD_OBJECT)); return noAccess.get(m) != null ? Boolean.TRUE : Boolean.FALSE; } return super.getValueAt(row, col); } }