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:                   }
085:                   catch (IllegalAccessException exception)
086:                   {
087:                   }
088:                   catch (InvocationTargetException exception)
089:                   {
090:                   }
091:                }
092:             });
093:          return editor;
094:       }
095:       catch (InstantiationException exception)
096:       {
097:          return null;
098:       }
099:       catch (IllegalAccessException exception)
100:       {
101:          return null;
102:       }
103:       catch (InvocationTargetException exception)
104:       {
105:          return null;
106:       }
107:    }
108: 
109:    /**
110:       Wraps a property editor into a component.
111:       @param editor the editor to wrap
112:       @return a button (if there is a custom editor), 
113:       combo box (if the editor has tags), or text field (otherwise)
114:    */      
115:    public Component getEditorComponent(final PropertyEditor editor)   
116:    {      
117:       String[] tags = editor.getTags();
118:       String text = editor.getAsText();
119:       if (editor.supportsCustomEditor())
120:       {
121:          // Make a button that pops up the custom editor
122:          final JButton button = new JButton();
123:          // if the editor is paintable, have it paint an icon
124:          if (editor.isPaintable())
125:          {
126:             button.setIcon(new 
127:                Icon()
128:                {
129:                   public int getIconWidth() { return WIDTH - 8; }
130:                   public int getIconHeight() { return HEIGHT - 8; }
131: 
132:                   public void paintIcon(Component c, Graphics g, 
133:                      int x, int y)
134:                   {
135:                      g.translate(x, y);
136:                      Rectangle r = new Rectangle(0, 0, 
137:                         getIconWidth(), getIconHeight());
138:                      Color oldColor = g.getColor();
139:                      g.setColor(Color.BLACK);
140:                      editor.paintValue(g, r);
141:                      g.setColor(oldColor);
142:                      g.translate(-x, -y);
143:                   }
144:                });
145:          } 
146:          else 
147:             button.setText(buttonText(text));
148:          // pop up custom editor when button is clicked
149:          button.addActionListener(new
150:             ActionListener()
151:             {
152:                public void actionPerformed(ActionEvent event)
153:                {
154:                   JOptionPane.showMessageDialog(null,
155:                      editor.getCustomEditor());
156:                   if (editor.isPaintable())
157:                      button.repaint();
158:                   else 
159:                      button.setText(buttonText(editor.getAsText()));
160:                }
161:             });
162:          return button; 
163:       }
164:       else if (tags != null)
165:       {
166:          // make a combo box that shows all tags
167:          final JComboBox comboBox = new JComboBox(tags);
168:          comboBox.setSelectedItem(text);
169:          comboBox.addItemListener(new
170:             ItemListener()
171:             {
172:                public void itemStateChanged(ItemEvent event)
173:                {
174:                   if (event.getStateChange() == ItemEvent.SELECTED)
175:                      editor.setAsText(
176:                         (String) comboBox.getSelectedItem());
177:                }
178:             });
179:          return comboBox;
180:       }
181:       else 
182:       {
183:          final JTextField textField = new JTextField(text, 10);
184:          textField.getDocument().addDocumentListener(new
185:             DocumentListener()
186:             {
187:                public void insertUpdate(DocumentEvent e) 
188:                {
189:                   try
190:                   {
191:                      editor.setAsText(textField.getText());
192:                   }
193:                   catch (IllegalArgumentException exception)
194:                   {
195:                   }
196:                }
197:                public void removeUpdate(DocumentEvent e) 
198:                {
199:                   try
200:                   {
201:                      editor.setAsText(textField.getText());
202:                   }
203:                   catch (IllegalArgumentException exception)
204:                   {
205:                   }
206:                }
207:                public void changedUpdate(DocumentEvent e) 
208:                {
209:                }
210:             });
211:          return textField;
212:       }
213:    }
214: 
215:    /**
216:       Formats text for the button that pops up a
217:       custom editor.
218:       @param text the property value as text
219:       @return the text to put on the button
220:    */
221:    private static String buttonText(String text)
222:    {
223:       if (text == null || text.equals("")) 
224:          return " ";
225:       if (text.length() > MAX_TEXT_LENGTH)
226:          return text.substring(0, MAX_TEXT_LENGTH) + "...";
227:       return text;
228:    }
229: 
230:    private ArrayList changeListeners = new ArrayList();
231:    private static final int WIDTH = 100;
232:    private static final int HEIGHT = 25;
233:    private static final int MAX_TEXT_LENGTH = 15;
234: }
235: