001: import java.awt.*; 002: import java.awt.event.*; 003: import java.beans.*; 004: import java.lang.reflect.*; 005: import java.util.*; 006: import javax.swing.*; 007: import javax.swing.event.*; 008: 009: 010: /** 011: A component filled with editors for all editable properties 012: of an object. 013: */ 014: public class PropertySheet extends JPanel 015: { 016: /** 017: Constructs a property sheet that shows the editable 018: properties of a given object. 019: @param object the object whose properties are being edited 020: */ 021: public PropertySheet(Object bean) 022: { 023: try 024: { 025: BeanInfo info 026: = Introspector.getBeanInfo(bean.getClass()); 027: PropertyDescriptor[] descriptors 028: = (PropertyDescriptor[])info.getPropertyDescriptors().clone(); 029: setLayout(new FormLayout()); 030: for (int i = 0; i < descriptors.length; i++) 031: { 032: PropertyEditor editor 033: = getEditor(bean, descriptors[i]); 034: if (editor != null) 035: { 036: add(new JLabel(descriptors[i].getName())); 037: add(getEditorComponent(editor)); 038: } 039: } 040: } 041: catch (IntrospectionException exception) 042: { 043: exception.printStackTrace(); 044: } 045: } 046: 047: /** 048: Gets the property editor for a given property, 049: and wires it so that it updates the given object. 050: @param bean the object whose properties are being edited 051: @param descriptor the descriptor of the property to 052: be edited 053: @return a property editor that edits the property 054: with the given descriptor and updates the given object 055: */ 056: public PropertyEditor getEditor(final Object bean, 057: PropertyDescriptor descriptor) 058: { 059: try 060: { 061: Method getter = descriptor.getReadMethod(); 062: if (getter == null) return null; 063: final Method setter = descriptor.getWriteMethod(); 064: if (setter == null) return null; 065: Class type = descriptor.getPropertyType(); 066: PropertyEditor ed = null; 067: Class editorClass = descriptor.getPropertyEditorClass(); 068: if (editorClass != null) 069: ed = (PropertyEditor) editorClass.newInstance(); 070: else 071: ed = PropertyEditorManager.findEditor(type); 072: if (ed == null && Enum.class.isAssignableFrom(type)) 073: ed = new EnumEditor(type); 074: if (ed == null) return null; 075: 076: final PropertyEditor editor = ed; 077: 078: Object value = getter.invoke(bean, new Object[] {}); 079: editor.setValue(value); 080: editor.addPropertyChangeListener(new 081: PropertyChangeListener() 082: { 083: public void propertyChange(PropertyChangeEvent event) 084: { 085: try 086: { 087: setter.invoke(bean, 088: new Object[] { editor.getValue() }); 089: fireStateChanged(null); 090: } 091: catch (IllegalAccessException exception) 092: { 093: exception.printStackTrace(); 094: } 095: catch (InvocationTargetException exception) 096: { 097: exception.printStackTrace(); 098: } 099: } 100: }); 101: return editor; 102: } 103: catch (InstantiationException exception) 104: { 105: exception.printStackTrace(); 106: return null; 107: } 108: catch (IllegalAccessException exception) 109: { 110: exception.printStackTrace(); 111: return null; 112: } 113: catch (InvocationTargetException exception) 114: { 115: exception.printStackTrace(); 116: return null; 117: } 118: } 119: 120: /** 121: Wraps a property editor into a component. 122: @param editor the editor to wrap 123: @return a button (if there is a custom editor), 124: combo box (if the editor has tags), or text field (otherwise) 125: */ 126: public Component getEditorComponent(final PropertyEditor editor) 127: { 128: String[] tags = editor.getTags(); 129: String text = editor.getAsText(); 130: if (editor.supportsCustomEditor()) 131: return editor.getCustomEditor(); 132: else if (tags != null) 133: { 134: // make a combo box that shows all tags 135: final JComboBox comboBox = new JComboBox(tags); 136: comboBox.setSelectedItem(text); 137: comboBox.addItemListener(new 138: ItemListener() 139: { 140: public void itemStateChanged(ItemEvent event) 141: { 142: if (event.getStateChange() == ItemEvent.SELECTED) 143: editor.setAsText( 144: (String)comboBox.getSelectedItem()); 145: } 146: }); 147: return comboBox; 148: } 149: else 150: { 151: final JTextField textField = new JTextField(text, 10); 152: textField.getDocument().addDocumentListener(new 153: DocumentListener() 154: { 155: public void insertUpdate(DocumentEvent e) 156: { 157: try 158: { 159: editor.setAsText(textField.getText()); 160: } 161: catch (IllegalArgumentException exception) 162: { 163: } 164: } 165: public void removeUpdate(DocumentEvent e) 166: { 167: try 168: { 169: editor.setAsText(textField.getText()); 170: } 171: catch (IllegalArgumentException exception) 172: { 173: } 174: } 175: public void changedUpdate(DocumentEvent e) 176: { 177: } 178: }); 179: return textField; 180: } 181: } 182: 183: /** 184: Adds a change listener to the list of listeners. 185: @param listener the listener to add 186: */ 187: public void addChangeListener(ChangeListener listener) 188: { 189: changeListeners.add(listener); 190: } 191: 192: /** 193: Notifies all listeners of a state change. 194: @param event the event to propagate 195: */ 196: private void fireStateChanged(ChangeEvent event) 197: { 198: for (ChangeListener listener : changeListeners) 199: listener.stateChanged(event); 200: } 201: 202: private ArrayList<ChangeListener> changeListeners 203: = new ArrayList<ChangeListener>(); 204: } 205: