001: package abbot.editor;
002:
003: import java.awt.*;
004: import java.lang.reflect.*;
005: import java.util.*;
006:
007: import javax.swing.table.DefaultTableModel;
008:
009: import abbot.Log;
010: import abbot.i18n.Strings;
011: import abbot.script.PropertyCall;
012: import abbot.tester.ComponentTester;
013:
014: class ComponentPropertyModel extends DefaultTableModel {
015:
016: public static final int PROPERTY_NAME = 0;
017: public static final int PROPERTY_VALUE = 1;
018: public static final int ACCESSIBLE = 2;
019: public static final int METHOD_OBJECT = 3;
020:
021: private static Set filteredMethods = new HashSet();
022:
023: static {
024: // Indicate things that aren't particularly interesting
025: filteredMethods.addAll(Arrays.asList(new String[] {
026: // Component
027: "getAccessibleContext",
028: "getAlignmentX",
029: "getAlignmentY",
030: "getColorModel",
031: "getComponentListeners",
032: "getComponentOrientation",
033: "getDropTarget",
034: "getFocusListeners",
035: "getGraphics",
036: "getGraphicsConfiguration",
037: "getHierarchyBoundsListeners",
038: "getHierarchyListeners",
039: "getInputContext",
040: "getInputMethodListeners",
041: "getInputMethodRequests",
042: "getKeyListeners",
043: "getMouseListeners",
044: "getMouseMotionListeners",
045: "getMouseWheelListeners",
046: "getParent",
047: "getPeer",
048: "getToolkit",
049: "getTreeLock",
050: // Container
051: "getComponents",
052: "getContainerListeners",
053: // JComponent
054: "getActionMap", "getAncestorListeners",
055: "getAutoscrolls", "getBufferStrategy",
056: "getDebugGraphicsOptions", "getInputMap",
057: "getInputVerifier", "getPropertyChangeListeners",
058: "getRegisteredKeyStrokes", "getRootPane",
059: "getTopLevelAncestor", "getUIClassID",
060: "getVerifyInputWhenFocusTarget",
061: "getVetoableChangeListeners", "getVisibleRect",
062: "isFocusCycleRoot", "isOpaque",
063: "isOptimizedDrawingEnabled", "isPaintingTile",
064: "isPreferredSizeSet", "isRequestFocusEnabled",
065: "isValidateRoot",
066: // Window
067: "getOwnedWindows", "getWindowFocusListeners",
068: "getWindowListeners", "getWindowStateListeners",
069: // Frame
070: "getFrames", }));
071: }
072:
073: private Map noAccess = new WeakHashMap();
074:
075: /** Install the given filtered property method properties. Add-on
076: ComponentTester classes should invoke this for the list of property
077: methods they want to appear in the filtered property list. */
078: public static void setFilteredPropertyMethods(String[] methods) {
079: filteredMethods.addAll(Arrays.asList(methods));
080: }
081:
082: /** Create a model with two columns, the property name and the property
083: * value.
084: */
085: public ComponentPropertyModel() {
086: super (
087: new Object[] { Strings.get("Name"),
088: Strings.get("Value") }, 0);
089: }
090:
091: public void clear() {
092: // The setNumRows() method is required to be compatible with JDK
093: // 1.2.2 and should be replaced with setRowCount() for 1.3 and above.
094: //propTableModel.setNumRows(0);
095: setRowCount(0);
096: }
097:
098: public void setComponent(Component comp) {
099: setComponent(comp, true);
100: }
101:
102: /** The current list of property methods and values corresponds to this
103: * component.
104: */
105: private Component currentComponent = null;
106: /** Whether the current list is filtered. */
107: private boolean filtered = false;
108:
109: /** Update the list of property methods based on the newly selected
110: component.
111: */
112: public void setComponent(Component comp, boolean filter) {
113: Class cls = comp != null ? comp.getClass() : null;
114: if (currentComponent == comp && filter == filtered)
115: return;
116:
117: clear();
118: currentComponent = comp;
119: filtered = filter;
120: Method[] all = getPropertyMethods(cls, filter);
121: Arrays.sort(all, new Comparator() {
122: public int compare(Object o1, Object o2) {
123: String n1 = getPropertyName(((Method) o1).getName());
124: String n2 = getPropertyName(((Method) o2).getName());
125: return n1.compareTo(n2);
126: }
127: });
128: Object[] noArgs = new Object[0];
129: Object[] oneArg = new Object[] { comp };
130: for (int i = 0; i < all.length; i++) {
131: Method method = all[i];
132: Object value = "";
133: try {
134: Object target = comp;
135: Object[] args = noArgs;
136: if (ComponentTester.class.isAssignableFrom(method
137: .getDeclaringClass())) {
138: target = ComponentTester.getTester(comp);
139: args = oneArg;
140: }
141: if ((method.getModifiers() & Modifier.PUBLIC) == 0
142: || (method.getDeclaringClass().getModifiers() & Modifier.PUBLIC) == 0) {
143: noAccess.put(method, Boolean.TRUE);
144: method.setAccessible(true);
145: }
146: value = method.invoke(target, args);
147: } catch (IllegalArgumentException e) {
148: value = "<illegal argument>";
149: Log.debug(e);
150: } catch (InvocationTargetException e) {
151: value = "<target exception>";
152: Log.debug(e);
153: } catch (IllegalAccessException e) {
154: // method was somehow protected?
155: value = "<not accessible>";
156: Log.debug(e);
157: }
158: addRow(new Object[] { method, value });
159: }
160: fireTableDataChanged();
161: }
162:
163: Method[] getPropertyMethods(Class cls, boolean filter) {
164: if (cls == null)
165: return new Method[0];
166:
167: // Make sure we only get one of each named method
168: HashMap processed = new HashMap();
169: Method[] methods = cls.getMethods();
170: for (int i = 0; i < methods.length; i++) {
171: if (isGetterMethod(methods[i], false)
172: && !processed.containsKey(methods[i].getName())) {
173: if (filter
174: && filteredMethods.contains(methods[i]
175: .getName())) {
176: continue;
177: }
178: processed.put(methods[i].getName(), methods[i]);
179: }
180: }
181: // Now look up propert accessors provided by the corresponding
182: // ComponentTester class.
183: ComponentTester tester = ComponentTester.getTester(cls);
184: methods = tester.getPropertyMethods();
185: for (int i = 0; i < methods.length; i++) {
186: if (!processed.containsKey(methods[i].getName())) {
187: // Properties provided by the ComponentTester are never
188: // filtered.
189: processed.put(methods[i].getName(), methods[i]);
190: }
191: }
192:
193: return (Method[]) processed.values().toArray(
194: new Method[processed.size()]);
195: }
196:
197: /**
198: * Method to check if the method specified on the class
199: * is a "getter" for an attribute of that class
200: *
201: * @param method The method to be tested
202: * @return true if the method is a "getter"
203: */
204: private boolean isGetterMethod(Method method, boolean isTester) {
205: Class[] types = method.getParameterTypes();
206: int argc = types.length;
207: return ((isTester && argc == 1 && Component.class
208: .isAssignableFrom(types[0])) || (!isTester && argc == 0))
209: && PropertyCall.isPropertyMethod(method);
210: }
211:
212: /**
213: * Method to "extract" the property name from the method name
214: * following the convention specified for a bean
215: *
216: * @param methodName The name of the method
217: * @return The name of the attribute
218: */
219: private String getPropertyName(String methodName) {
220: String propName = methodName;
221: if (methodName.startsWith("get")
222: || methodName.startsWith("has")) {
223: propName = methodName.substring(3);
224: } else if (methodName.startsWith("is")) {
225: propName = methodName.substring(2);
226: }
227: return propName.substring(0, 1).toLowerCase()
228: + propName.substring(1);
229: }
230:
231: public boolean isCellEditable(int row, int col) {
232: return false;
233: }
234:
235: /** Display the property name column apropriately. */
236: public Object getValueAt(int row, int col) {
237: // The Method object is in column zero, we want only the property part
238: // to appear.
239: if (col == PROPERTY_NAME) {
240: Method m = (Method) super .getValueAt(row, col);
241: return getPropertyName(m.getName());
242: }
243: if (col == METHOD_OBJECT) {
244: return (Method) super .getValueAt(row, 0);
245: }
246: if (col == ACCESSIBLE) {
247: Method m = ((Method) getValueAt(row, METHOD_OBJECT));
248: return noAccess.get(m) != null ? Boolean.TRUE
249: : Boolean.FALSE;
250: }
251: return super.getValueAt(row, col);
252: }
253: }
|