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: A component filled with editors for all editable properties 011: of an object. 012: */ 013: public class PropertySheet extends JPanel 014: { 015: /** 016: Constructs a property sheet that shows the editable 017: properties of a given object. 018: @param object the object whose properties are being edited 019: */ 020: public PropertySheet(Object bean) 021: { 022: try 023: { 024: BeanInfo info 025: = Introspector.getBeanInfo(bean.getClass()); 026: PropertyDescriptor[] descriptors 027: = info.getPropertyDescriptors(); 028: setLayout(new FormLayout()); 029: for (int i = 0; i < descriptors.length; i++) 030: { 031: PropertyEditor editor 032: = getEditor(bean, descriptors[i]); 033: if (editor != null) 034: { 035: add(new JLabel(descriptors[i].getName())); 036: add(getEditorComponent(editor)); 037: } 038: } 039: } 040: catch (IntrospectionException exception) 041: { 042: exception.printStackTrace(); 043: } 044: } 045: 046: /** 047: Gets the property editor for a given property, 048: and wires it so that it updates the given object. 049: @param bean the object whose properties are being edited 050: @param descriptor the descriptor of the property to 051: be edited 052: @return a property editor that edits the property 053: with the given descriptor and updates the given object 054: */ 055: public PropertyEditor getEditor(final Object bean, 056: PropertyDescriptor descriptor) 057: { 058: try 059: { 060: Method getter = descriptor.getReadMethod(); 061: if (getter == null) return null; 062: final Method setter = descriptor.getWriteMethod(); 063: if (setter == null) return null; 064: final PropertyEditor editor; 065: Class editorClass = descriptor.getPropertyEditorClass(); 066: if (editorClass != null) 067: editor = (PropertyEditor) editorClass.newInstance(); 068: else 069: editor = PropertyEditorManager.findEditor( 070: descriptor.getPropertyType()); 071: if (editor == null) return null; 072: 073: Object value = getter.invoke(bean, new Object[] {}); 074: editor.setValue(value); 075: editor.addPropertyChangeListener(new 076: PropertyChangeListener() 077: { 078: public void propertyChange(PropertyChangeEvent event) 079: { 080: try 081: { 082: setter.invoke(bean, 083: new Object[] { editor.getValue() }); 084: fireStateChanged(null); 085: } 086: catch (IllegalAccessException exception) 087: { 088: } 089: catch (InvocationTargetException exception) 090: { 091: } 092: } 093: }); 094: return editor; 095: } 096: catch (InstantiationException exception) 097: { 098: return null; 099: } 100: catch (IllegalAccessException exception) 101: { 102: return null; 103: } 104: catch (InvocationTargetException exception) 105: { 106: return null; 107: } 108: } 109: 110: /** 111: Wraps a property editor into a component. 112: @param editor the editor to wrap 113: @return a button (if there is a custom editor), 114: combo box (if the editor has tags), or text field (otherwise) 115: */ 116: public Component getEditorComponent(final PropertyEditor editor) 117: { 118: String[] tags = editor.getTags(); 119: String text = editor.getAsText(); 120: if (editor.supportsCustomEditor()) 121: { 122: // Make a button that pops up the custom editor 123: final JButton button = new JButton(); 124: // if the editor is paintable, have it paint an icon 125: if (editor.isPaintable()) 126: { 127: button.setIcon(new 128: Icon() 129: { 130: public int getIconWidth() { return WIDTH - 8; } 131: public int getIconHeight() { return HEIGHT - 8; } 132: 133: public void paintIcon(Component c, Graphics g, 134: int x, int y) 135: { 136: g.translate(x, y); 137: Rectangle r = new Rectangle(0, 0, 138: getIconWidth(), getIconHeight()); 139: Color oldColor = g.getColor(); 140: g.setColor(Color.BLACK); 141: editor.paintValue(g, r); 142: g.setColor(oldColor); 143: g.translate(-x, -y); 144: } 145: }); 146: } 147: else 148: button.setText(buttonText(text)); 149: // pop up custom editor when button is clicked 150: button.addActionListener(new 151: ActionListener() 152: { 153: public void actionPerformed(ActionEvent event) 154: { 155: JOptionPane.showMessageDialog(null, 156: editor.getCustomEditor()); 157: if (editor.isPaintable()) 158: button.repaint(); 159: else 160: button.setText(buttonText(editor.getAsText())); 161: } 162: }); 163: return button; 164: } 165: else if (tags != null) 166: { 167: // make a combo box that shows all tags 168: final JComboBox comboBox = new JComboBox(tags); 169: comboBox.setSelectedItem(text); 170: comboBox.addItemListener(new 171: ItemListener() 172: { 173: public void itemStateChanged(ItemEvent event) 174: { 175: if (event.getStateChange() == ItemEvent.SELECTED) 176: editor.setAsText( 177: (String) comboBox.getSelectedItem()); 178: } 179: }); 180: return comboBox; 181: } 182: else 183: { 184: final JTextField textField = new JTextField(text, 10); 185: textField.getDocument().addDocumentListener(new 186: DocumentListener() 187: { 188: public void insertUpdate(DocumentEvent e) 189: { 190: try 191: { 192: editor.setAsText(textField.getText()); 193: } 194: catch (IllegalArgumentException exception) 195: { 196: } 197: } 198: public void removeUpdate(DocumentEvent e) 199: { 200: try 201: { 202: editor.setAsText(textField.getText()); 203: } 204: catch (IllegalArgumentException exception) 205: { 206: } 207: } 208: public void changedUpdate(DocumentEvent e) 209: { 210: } 211: }); 212: return textField; 213: } 214: } 215: 216: /** 217: Formats text for the button that pops up a 218: custom editor. 219: @param text the property value as text 220: @return the text to put on the button 221: */ 222: private static String buttonText(String text) 223: { 224: if (text == null || text.equals("")) 225: return " "; 226: if (text.length() > MAX_TEXT_LENGTH) 227: return text.substring(0, MAX_TEXT_LENGTH) + "..."; 228: return text; 229: } 230: 231: /** 232: Adds a change listener to the list of listeners. 233: @param listener the listener to add 234: */ 235: public void addChangeListener(ChangeListener listener) 236: { 237: changeListeners.add(listener); 238: } 239: 240: /** 241: Notifies all listeners of a state change. 242: @param event the event to propagate 243: */ 244: private void fireStateChanged(ChangeEvent event) 245: { 246: for (int i = 0; i < changeListeners.size(); i++) 247: { 248: ChangeListener listener = (ChangeListener) changeListeners.get(i); 249: listener.stateChanged(event); 250: } 251: } 252: 253: private ArrayList changeListeners = new ArrayList(); 254: private static final int WIDTH = 100; 255: private static final int HEIGHT = 25; 256: private static final int MAX_TEXT_LENGTH = 15; 257: } 258: