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: