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: