KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jmeter > testbeans > gui > GenericTestBeanCustomizer


1 // $Header: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/GenericTestBeanCustomizer.java,v 1.5 2004/02/11 17:29:14 jsalvata Exp $
2
/*
3  * Copyright 2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.jmeter.testbeans.gui;
18
19 import java.awt.Component JavaDoc;
20 import java.awt.GridBagConstraints JavaDoc;
21 import java.awt.GridBagLayout JavaDoc;
22 import java.awt.Insets JavaDoc;
23
24 import java.beans.BeanInfo JavaDoc;
25 import java.beans.PropertyChangeEvent JavaDoc;
26 import java.beans.PropertyChangeListener JavaDoc;
27 import java.beans.PropertyDescriptor JavaDoc;
28 import java.beans.PropertyEditor JavaDoc;
29 import java.beans.PropertyEditorManager JavaDoc;
30
31 import java.text.MessageFormat JavaDoc;
32 import java.util.Arrays JavaDoc;
33 import java.util.Comparator JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.MissingResourceException JavaDoc;
36 import java.util.ResourceBundle JavaDoc;
37
38 import javax.swing.BorderFactory JavaDoc;
39 import javax.swing.Box JavaDoc;
40 import javax.swing.JLabel JavaDoc;
41 import javax.swing.JPanel JavaDoc;
42
43 import org.apache.jmeter.util.JMeterUtils;
44 import org.apache.jorphan.logging.LoggingManager;
45 import org.apache.log.Logger;
46
47 /**
48  * The GenericTestBeanCustomizer is designed to provide developers with a
49  * mechanism to quickly implement GUIs for new components.
50  * <p>
51  * It allows editing each of the public exposed properties of the
52  * edited type 'a la JavaBeans': as far as the types of those properties
53  * have an associated editor, there's no GUI development required.
54  * <p>
55  * This class understands the following PropertyDescriptor attributes:
56  * <dl>
57  * <dt>group: String</dt>
58  * <dd>Group under which the property should be shown in the GUI. The string is
59  * also used as a group title (but see comment on resourceBundle below). The
60  * default group is "".</dd>
61  * <dt>order: Integer</dt>
62  * <dd>Order in which the property will be shown in its group. A smaller
63  * integer means higher up in the GUI. The default order is 0. Properties
64  * of equal order are sorted alphabetically.</dd>
65  * <dt>tags: String[]</dt>
66  * <dd>List of values to be offered for the property in addition to those
67  * offered by its property editor.</dd>
68  * <dt>notUndefined: Boolean</dt>
69  * <dd>If true, the property should not be left undefined. A <b>default</b>
70  * attribute must be provided if this is set.</dd>
71  * <dd>notExpression: Boolean</dd>
72  * <dd>If true, the property content should always be constant: JMeter
73  * 'expressions' (strings using ${var}, etc...) can't be used.</dt>
74  * <dd>notOther: Boolean</dd>
75  * <dd>If true, the property content must always be one of the tags values or
76  * null.</dt>
77  * <dt>default: Object</dt>
78  * <dd>Initial value for the property's GUI. Must be provided and be non-null
79  * if <b>notUndefined</b> is set. Must be one of the provided tags (or null) if
80  * <b>notOther</b> is set.
81  * </dl>
82  * <p>
83  * The following BeanDescriptor attributes are also understood:
84  * <dl>
85  * <dt>group.<i>group</i>.order: Integer</dt>
86  * <dd>where <b><i>group</i></b> is a group name used in a <b>group</b>
87  * attribute in one or more PropertyDescriptors. Defines the order in which
88  * the group will be shown in the GUI. A smaller integer means higher up
89  * in the GUI. The default order is 0. Groups of equal order are sorted
90  * alphabetically.</dd>
91  * <dt>resourceBundle: ResourceBundle</dt>
92  * <dd>A resource bundle to be used for GUI localization: group display names
93  * will be obtained from property "<b><i>group</i>.displayName</b>" if
94  * available (where <b><i>group</i></b> is the group name).
95  * </dl>
96  *
97  * @author <a HREF="mailto:jsalvata@apache.org">Jordi Salvat i Alabart</a>
98  * @version $Revision: 1.5 $ updated on $Date: 2004/02/11 17:29:14 $
99  */

100 public class GenericTestBeanCustomizer extends JPanel JavaDoc
101         implements SharedCustomizer, PropertyChangeListener JavaDoc
102 {
103     private static Logger log = LoggingManager.getLoggerForClass();
104
105     public static final String JavaDoc GROUP= "group";
106     public static final String JavaDoc ORDER= "order";
107     public static final String JavaDoc TAGS= "tags";
108     public static final String JavaDoc NOT_UNDEFINED= "notUndefined";
109     public static final String JavaDoc NOT_EXPRESSION= "notExpression";
110     public static final String JavaDoc NOT_OTHER= "notOther";
111     public static final String JavaDoc DEFAULT= "default";
112     public static final String JavaDoc RESOURCE_BUNDLE= "resourceBundle";
113     public static final String JavaDoc ORDER(String JavaDoc group) {
114         return "group."+group+".order";
115     }
116
117     public static final String JavaDoc DEFAULT_GROUP= "";
118
119     /**
120      * BeanInfo object for the class of the objects being edited.
121      */

122     private BeanInfo JavaDoc beanInfo;
123
124     /**
125      * Property descriptors from the beanInfo.
126      */

127     private PropertyDescriptor JavaDoc[] descriptors;
128
129     /**
130      * Property editors -- or null if the property can't be edited.
131      * Unused if customizerClass==null.
132      */

133     private PropertyEditor JavaDoc[] editors;
134
135     /**
136      * Message format for property field labels:
137      */

138     private MessageFormat JavaDoc propertyFieldLabelMessage;
139     
140     /**
141      * Message format for property tooltips:
142      */

143     private MessageFormat JavaDoc propertyToolTipMessage;
144
145     /**
146      * The Map we're currently customizing. Set by setObject().
147      */

148     private Map JavaDoc propertyMap;
149
150     /**
151      * Create a customizer for a given test bean type.
152      *
153      * @param testBeanClass a subclass of TestBean
154      * @see org.apache.jmeter.testbeans.TestBean
155      */

156     GenericTestBeanCustomizer(BeanInfo JavaDoc beanInfo)
157     {
158         super();
159
160         this.beanInfo= beanInfo;
161         
162         // Get and sort the property descriptors:
163
descriptors= beanInfo.getPropertyDescriptors();
164         Arrays.sort(descriptors, new PropertyComparator());
165
166         // Obtain the propertyEditors:
167
editors= new PropertyEditor JavaDoc[descriptors.length];
168         for (int i=0; i<descriptors.length; i++)
169         {
170             String JavaDoc name= descriptors[i].getName();
171
172             // Don't get editors for hidden or non-read-write properties:
173
if (descriptors[i].isHidden()
174                 || (descriptors[i].isExpert() && ! JMeterUtils.isExpertMode())
175                 || descriptors[i].getReadMethod() == null
176                 || descriptors[i].getWriteMethod() == null)
177             {
178                 log.debug("No editor for property "+name);
179                 editors[i]= null;
180                 continue;
181             }
182
183             PropertyEditor JavaDoc propertyEditor;
184             Class JavaDoc editorClass= descriptors[i].getPropertyEditorClass();
185             
186             if (log.isDebugEnabled())
187             {
188                 log.debug("Property "+name
189                         +" has editor class "+editorClass);
190             }
191             
192             if (editorClass != null)
193             {
194                 try
195                 {
196                     propertyEditor= (PropertyEditor JavaDoc)editorClass.newInstance();
197                 }
198                 catch (InstantiationException JavaDoc e)
199                 {
200                     log.error("Can't create property editor.", e);
201                     throw new Error JavaDoc(e.toString());
202                 }
203                 catch (IllegalAccessException JavaDoc e)
204                 {
205                     log.error("Can't create property editor.", e);
206                     throw new Error JavaDoc(e.toString());
207                 }
208             }
209             else
210             {
211                 Class JavaDoc c= descriptors[i].getPropertyType();
212                 propertyEditor= PropertyEditorManager.findEditor(c);
213             }
214
215             if (log.isDebugEnabled())
216             {
217                 log.debug("Property "+name
218                         +" has property editor "+propertyEditor);
219             }
220             
221             if (propertyEditor == null)
222             {
223                 log.debug("No editor for property "+name);
224                 editors[i]= null;
225                 continue;
226             }
227             
228             if (! propertyEditor.supportsCustomEditor())
229             {
230                 propertyEditor= createWrapperEditor(
231                     propertyEditor, descriptors[i]);
232
233                 if (log.isDebugEnabled())
234                 {
235                     log.debug("Editor for property "+name
236                             +" is wrapped in "+propertyEditor);
237                 }
238             }
239             
240             editors[i]= propertyEditor;
241
242             // Initialize the editor with the provided default value or null:
243
setEditorValue(i, descriptors[i].getValue(DEFAULT));
244
245             // Now subscribe as a listener (we didn't want to receive the event
246
// for the setEditorValue above!)
247
propertyEditor.addPropertyChangeListener(this);
248         }
249
250         // Obtain message formats:
251
propertyFieldLabelMessage= new MessageFormat JavaDoc(
252             JMeterUtils.getResString("property_as_field_label"));
253         propertyToolTipMessage= new MessageFormat JavaDoc(
254             JMeterUtils.getResString("property_tool_tip"));
255
256         // Initialize the GUI:
257
init();
258     }
259
260     /**
261      * Find the default typeEditor and a suitable guiEditor for the given
262      * property descriptor, and combine them in a WrapperEditor.
263      *
264      * @param typeEditor
265      * @param descriptor
266      * @return
267      */

268     private WrapperEditor createWrapperEditor(
269             PropertyEditor JavaDoc typeEditor, PropertyDescriptor JavaDoc descriptor)
270     {
271         String JavaDoc[] editorTags= typeEditor.getTags();
272         String JavaDoc[] additionalTags= (String JavaDoc[])descriptor.getValue(TAGS);
273         String JavaDoc[] tags= null;
274         if (editorTags == null) tags= additionalTags;
275         else if (additionalTags == null) tags= editorTags;
276         else {
277             tags= new String JavaDoc[editorTags.length+additionalTags.length];
278             int j= 0;
279             for (int i=0; i<editorTags.length; i++) tags[j++]= editorTags[i];
280             for (int i=0; i<additionalTags.length; i++) tags[j++]= additionalTags[i];
281         }
282
283         boolean notNull=
284             Boolean.TRUE.equals(descriptor.getValue(NOT_UNDEFINED));
285         boolean notExpression=
286             Boolean.TRUE.equals(descriptor.getValue(NOT_EXPRESSION));
287         boolean notOther=
288             Boolean.TRUE.equals(descriptor.getValue(NOT_OTHER));
289
290         PropertyEditor JavaDoc guiEditor;
291         if (notNull && tags==null)
292         {
293             guiEditor= new FieldStringEditor();
294         }
295         else
296         {
297             ComboStringEditor e= new ComboStringEditor();
298             e.setNoUndefined(notNull);
299             e.setNoEdit(notExpression && notOther);
300             e.setTags(tags);
301             
302             guiEditor= e;
303         }
304
305         WrapperEditor wrapper= new WrapperEditor(
306             typeEditor, guiEditor,
307             !notNull, // acceptsNull
308
!notExpression, // acceptsExpressions
309
!notOther, // acceptsOther
310
descriptor.getValue(DEFAULT)
311             );
312
313         return wrapper;
314     }
315
316     /**
317      * Set the value of the i-th property, properly reporting a possible failure.
318      *
319      * @param i the index of the property in the descriptors and editors arrays
320      * @param value the value to be stored in the editor
321      *
322      * @throws IllegalArgumentException if the editor refuses the value
323      */

324     private void setEditorValue(int i, Object JavaDoc value)
325         throws IllegalArgumentException JavaDoc
326     {
327         try
328         {
329             editors[i].setValue(value);
330         }
331         catch (IllegalArgumentException JavaDoc e)
332         {
333             log.error("Could not set value "
334                 + ( value == null ? "NULL" : value.getClass().getName() )
335                 + ":" + value
336                 +" for property "+descriptors[i].getName());
337             throw e;
338         }
339     }
340
341     /* (non-Javadoc)
342      * @see org.apache.jmeter.gui.JMeterGUIComponent#configure(org.apache.jmeter.testelement.TestElement)
343      */

344     public void setObject(Object JavaDoc map)
345     {
346         propertyMap= (Map JavaDoc)map;
347
348         if (propertyMap.size() == 0)
349         {
350             // Uninitialized -- set it to the defaults:
351
for (int i=0; i<editors.length; i++)
352             {
353                 Object JavaDoc value= descriptors[i].getValue(DEFAULT);
354                 String JavaDoc name= descriptors[i].getName();
355                 if (value != null)
356                 {
357                     propertyMap.put(name, value);
358                     log.debug("Set "+name+"= "+value);
359                 }
360                 firePropertyChange(name, null, value);
361             }
362         }
363         
364         // Now set the editors to the element's values:
365
for (int i=0; i<editors.length; i++)
366         {
367             if (editors[i] == null) continue;
368             try
369             {
370                 setEditorValue(i, propertyMap.get(descriptors[i].getName()));
371             }
372             catch (IllegalArgumentException JavaDoc e)
373             {
374                 // I guess this can happen as a result of a bad
375
// file read? In this case, it would be better to replace the
376
// incorrect value with anything valid, e.g. the default value
377
// for the property.
378
// But for the time being, I just prefer to be aware of any
379
// problems occuring here, most likely programming errors,
380
// so I'll bail out.
381
throw new Error JavaDoc("Bad property value."+e);
382                 // TODO: review this and possibly change to:
383
// setEditorValue(i, descriptors[i].getValue(DEFAULT);
384
}
385         }
386     }
387
388     /**
389      * Find the index of the property of the given name.
390      *
391      * @param name the name of the property
392      * @return the index of that property in the descriptors array, or -1 if
393      * there's no property of this name.
394      */

395     private int descriptorIndex(String JavaDoc name) //NOTUSED
396
{
397         for (int i=0; i<descriptors.length; i++)
398         {
399             if (descriptors[i].getName().equals(name))
400             {
401                 return i;
402             }
403         }
404         return -1;
405     }
406
407     /**
408      * Initialize the GUI.
409      */

410     private void init()
411     {
412         setLayout(new GridBagLayout JavaDoc());
413         
414         GridBagConstraints JavaDoc cl= new GridBagConstraints JavaDoc(); // for labels
415
cl.gridx= 0;
416         cl.anchor= GridBagConstraints.EAST;//JDK1.4: was LINE_END
417
cl.insets= new Insets JavaDoc(0, 1, 0, 1);
418
419         GridBagConstraints JavaDoc ce= new GridBagConstraints JavaDoc(); // for editors
420
ce.fill= GridBagConstraints.BOTH;
421         ce.gridx= 1;
422         ce.weightx= 1.0;
423         ce.insets= new Insets JavaDoc(0, 1, 0, 1);
424         
425         GridBagConstraints JavaDoc cp= new GridBagConstraints JavaDoc(); // for panels
426
cp.fill= GridBagConstraints.BOTH;
427         cp.gridx= 1;
428         cp.gridy= GridBagConstraints.RELATIVE;
429         cp.gridwidth= 2;
430         cp.weightx= 1.0;
431
432         JPanel JavaDoc currentPanel= this;
433         String JavaDoc currentGroup= DEFAULT_GROUP;
434         int y=0;
435         
436         for (int i=0; i<editors.length; i++)
437         {
438             if (editors[i] == null) continue;
439
440             if (log.isDebugEnabled())
441             {
442                 log.debug("Laying property "+descriptors[i].getName());
443             }
444             
445             String JavaDoc g= group(descriptors[i]);
446             if (! currentGroup.equals(g))
447             {
448                 if (currentPanel != this)
449                 {
450                     add(currentPanel, cp);
451                 }
452                 currentGroup= g;
453                 currentPanel= new JPanel JavaDoc(new GridBagLayout JavaDoc());
454                 currentPanel.setBorder(
455                     BorderFactory.createTitledBorder(
456                         BorderFactory.createEtchedBorder(),
457                         groupDisplayName(g)));
458                 cp.weighty= 0.0;
459                 y= 0;
460             }
461
462             Component JavaDoc customEditor= editors[i].getCustomEditor();
463
464             boolean multiLineEditor= false;
465             if (customEditor.getPreferredSize().height > 50)
466             {
467                 // TODO: the above works in the current situation, but it's
468
// just a hack. How to get each editor to report whether it
469
// wants to grow bigger? Whether the property label should
470
// be at the left or at the top of the editor? ...?
471
multiLineEditor= true;
472             }
473             
474             JLabel JavaDoc label= createLabel(descriptors[i]);
475             label.setLabelFor(customEditor);
476
477             cl.gridy= y;
478             cl.gridwidth= multiLineEditor ? 2 : 1;
479             cl.anchor= multiLineEditor
480                 ? GridBagConstraints.CENTER
481                 : GridBagConstraints.EAST;//JDK1.4: was LINE_END
482
currentPanel.add(label, cl);
483
484             ce.gridx= multiLineEditor ? 0 : 1;
485             ce.gridy= multiLineEditor ? ++y : y;
486             ce.gridwidth= multiLineEditor ? 2 : 1;
487             ce.weighty= multiLineEditor ? 1.0 : 0.0;
488
489             cp.weighty+= ce.weighty;
490
491             currentPanel.add(customEditor, ce);
492
493             y++;
494         }
495         if (currentPanel != this)
496         {
497             add(currentPanel, cp);
498         }
499
500         // Add a 0-sized invisible component that will take all the vertical
501
// space that nobody wants:
502
cp.weighty= 0.0001;
503         add(Box.createHorizontalStrut(0), cp);
504     }
505
506     private JLabel JavaDoc createLabel(PropertyDescriptor JavaDoc desc)
507     {
508         String JavaDoc text= desc.getDisplayName();
509         if (! "".equals(text))
510         {
511             text= propertyFieldLabelMessage.format(
512                 new Object JavaDoc[] { desc.getDisplayName() } );
513         }
514         // if the displayName is the empty string, leave it like that.
515
JLabel JavaDoc label = new JLabel JavaDoc(text);
516         label.setHorizontalAlignment(JLabel.TRAILING);
517         text= propertyToolTipMessage.format(
518             new Object JavaDoc[] { desc.getName(), desc.getShortDescription() } );
519         label.setToolTipText(text);
520
521         return label;
522     }
523
524
525     /**
526      * Obtain a property descriptor's group.
527      *
528      * @param descriptor
529      * @return the group String.
530      */

531     private String JavaDoc group(PropertyDescriptor JavaDoc d)
532     {
533         String JavaDoc group= (String JavaDoc)d.getValue(GROUP);
534         if (group == null) group= DEFAULT_GROUP;
535         return group;
536     }
537
538     /**
539      * Obtain a group's display name
540      */

541     private String JavaDoc groupDisplayName(String JavaDoc group)
542     {
543         try {
544             ResourceBundle JavaDoc b= (ResourceBundle JavaDoc)
545                 beanInfo.getBeanDescriptor().getValue(RESOURCE_BUNDLE);
546             if (b == null) return group;
547             else return b.getString(group+".displayName");
548         }
549         catch (MissingResourceException JavaDoc e)
550         {
551             return group;
552         }
553     }
554
555     /**
556      * Comparator used to sort properties for presentation in the GUI.
557      */

558     private class PropertyComparator implements Comparator JavaDoc
559     {
560         public int compare(Object JavaDoc o1, Object JavaDoc o2)
561         {
562             return compare((PropertyDescriptor JavaDoc)o1, (PropertyDescriptor JavaDoc)o2);
563         }
564         
565         private int compare(PropertyDescriptor JavaDoc d1, PropertyDescriptor JavaDoc d2)
566         {
567             int result;
568         
569             String JavaDoc g1= group(d1), g2= group(d2);
570             Integer JavaDoc go1= groupOrder(g1), go2= groupOrder(g2);
571         
572             result= go1.compareTo(go2);
573             if (result != 0) return result;
574         
575             result= g1.compareTo(g2);
576             if (result != 0) return result;
577         
578             Integer JavaDoc po1= propertyOrder(d1), po2= propertyOrder(d2);
579             result= po1.compareTo(po2);
580             if (result != 0) return result;
581         
582             return d1.getName().compareTo(d2.getName());
583         }
584     
585         /**
586          * Obtain a group's order.
587          *
588          * @param group group name
589          * @return the group's order (zero by default)
590          */

591         private Integer JavaDoc groupOrder(String JavaDoc group)
592         {
593             Integer JavaDoc order= (Integer JavaDoc)beanInfo.getBeanDescriptor()
594                     .getValue(ORDER(group));
595             if (order == null) order= new Integer JavaDoc(0);
596             return order;
597         }
598
599         /**
600          * Obtain a property's order.
601          *
602          * @param d
603          * @return the property's order attribute (zero by default)
604          */

605         private Integer JavaDoc propertyOrder(PropertyDescriptor JavaDoc d)
606         {
607             Integer JavaDoc order= (Integer JavaDoc)d.getValue(ORDER);
608             if (order == null) order= new Integer JavaDoc(0);
609             return order;
610         }
611     }
612
613     /* (non-Javadoc)
614      * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
615      */

616     public void propertyChange(PropertyChangeEvent JavaDoc evt)
617     {
618         for (int i=0; i<editors.length; i++)
619         {
620             if (editors[i] == evt.getSource())
621             {
622                 Object JavaDoc value= editors[i].getValue();
623                 String JavaDoc name= descriptors[i].getName();
624                 if (value == null)
625                 {
626                     propertyMap.remove(name);
627                     log.debug("Unset "+name);
628                 }
629                 else {
630                     propertyMap.put(name, value);
631                     log.debug("Set "+name+"= "+value);
632                 }
633                 firePropertyChange(name, evt.getOldValue(), value);
634                 return;
635             }
636         }
637         throw new Error JavaDoc("Unexpected propertyChange event received: "+evt);
638     }
639 }
640
Popular Tags