You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jmeter-dev@jakarta.apache.org by js...@apache.org on 2004/01/28 11:35:26 UTC
cvs commit: jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui GenericTestBeanCustomizer.java SharedCustomizer.java PackageTest.java TestBeanGUI.java TestElementEditor.java BeanInfoSupport.java WrapperEditor.java
jsalvata 2004/01/28 02:35:26
Modified: src/core/org/apache/jmeter/testbeans/gui PackageTest.java
TestBeanGUI.java TestElementEditor.java
BeanInfoSupport.java WrapperEditor.java
Added: src/core/org/apache/jmeter/testbeans/gui
GenericTestBeanCustomizer.java
SharedCustomizer.java
Log:
* Add bean customizer support.
* Factor out the bean GUI to a GenericTestBeanCustomizer.
Revision Changes Path
1.3 +30 -24 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/PackageTest.java
Index: PackageTest.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/PackageTest.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- PackageTest.java 21 Jan 2004 13:50:31 -0000 1.2
+++ PackageTest.java 28 Jan 2004 10:35:12 -0000 1.3
@@ -116,7 +116,7 @@
beanInfo= Introspector.getBeanInfo(testBeanClass, TestBean.class);
bundle= (ResourceBundle) beanInfo
.getBeanDescriptor()
- .getValue(TestBeanGUI.RESOURCE_BUNDLE);
+ .getValue(GenericTestBeanCustomizer.RESOURCE_BUNDLE);
}
catch (IntrospectionException e)
{
@@ -127,6 +127,11 @@
if (bundle == null) throw new Error("This can't happen!");
}
+ public void tearDown()
+ {
+ JMeterUtils.setLocale(Locale.getDefault());
+ }
+
public void runTest()
{
if (bundle == defaultBundle) checkAllNecessaryKeysPresent();
@@ -175,10 +180,11 @@
bundle.getString(name+".displayName");
//bundle.getString(name+".shortDescription"); NOT MANDATORY
- String group= (String)descriptors[i].getValue(TestBeanGUI.GROUP);
- if (group != null) bundle.getString(group+".displayName");
- }
- }
+ String group= (String)descriptors[i]
+ .getValue(GenericTestBeanCustomizer.GROUP);
+ if (group != null) bundle.getString(group+".displayName");
+ }
+ }
public static Test suite() throws Exception
{
@@ -196,24 +202,24 @@
new Class[] { TestBean.class })
.iterator();
- while (iter.hasNext())
- {
- Class testBeanClass= Class.forName((String)iter.next());
- JMeterUtils.setLocale(new Locale(defaultLanguage,""));
- ResourceBundle defaultBundle;
- try
- {
- defaultBundle= (ResourceBundle)
- Introspector.getBeanInfo(testBeanClass, TestBean.class)
- .getBeanDescriptor()
- .getValue(TestBeanGUI.RESOURCE_BUNDLE);
- }
- catch (IntrospectionException e)
- {
- log.error("Can't get beanInfo for "+testBeanClass.getName(),
- e);
- throw new Error(e.toString()); // Programming error. Don't continue.
- }
+ while (iter.hasNext())
+ {
+ Class testBeanClass= Class.forName((String)iter.next());
+ JMeterUtils.setLocale(new Locale(defaultLanguage,""));
+ ResourceBundle defaultBundle;
+ try
+ {
+ defaultBundle= (ResourceBundle)
+ Introspector.getBeanInfo(testBeanClass, TestBean.class)
+ .getBeanDescriptor()
+ .getValue(GenericTestBeanCustomizer.RESOURCE_BUNDLE);
+ }
+ catch (IntrospectionException e)
+ {
+ log.error("Can't get beanInfo for "+testBeanClass.getName(),
+ e);
+ throw new Error(e.toString()); // Programming error. Don't continue.
+ }
if (defaultBundle == null)
{
1.8 +205 -657 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/TestBeanGUI.java
Index: TestBeanGUI.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/TestBeanGUI.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- TestBeanGUI.java 16 Jan 2004 02:52:48 -0000 1.7
+++ TestBeanGUI.java 28 Jan 2004 10:35:12 -0000 1.8
@@ -2,7 +2,7 @@
* ====================================================================
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -59,36 +59,26 @@
import java.awt.BorderLayout;
import java.awt.Component;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-
import java.beans.BeanInfo;
+import java.beans.Customizer;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
-import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
-
-import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
+import java.util.Map;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.config.ConfigElement;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.gui.AbstractJMeterGuiComponent;
+import org.apache.jmeter.gui.JMeterGUIComponent;
import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.processor.PreProcessor;
@@ -102,6 +92,7 @@
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.LongProperty;
import org.apache.jmeter.testelement.property.NullProperty;
+import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.testelement.property.TestElementProperty;
import org.apache.jmeter.timers.Timer;
@@ -111,126 +102,82 @@
import org.apache.log.Logger;
/**
- * The GenericGUI is designed to provide developers with a mechanism to
- * quickly implement GUIs for new components.
+ * JMeter GUI element editing for TestBean elements.
* <p>
- * It allows editing each of the public exposed properties of the
- * edited type 'a la JavaBeans': as far as the types of those properties
- * have an associated editor, there's no GUI development required.
+ * The actual GUI is always a bean customizer: if the bean descriptor provides
+ * one, it will be used; otherwise, a GenericTestBeanCustomizer will be
+ * created for this purpose.
* <p>
- * TestBeanGUI understands the following PropertyDescriptor attributes:
- * <dl>
- * <dt>group: String</dt>
- * <dd>Group under which the property should be shown in the GUI. The string is
- * also used as a group title (but see comment on resourceBundle below). The
- * default group is "".</dd>
- * <dt>order: Integer</dt>
- * <dd>Order in which the property will be shown in its group. A smaller
- * integer means higher up in the GUI. The default order is 0. Properties
- * of equal order are sorted alphabetically.</dd>
- * <dt>tags: String[]</dt>
- * <dd>List of values to be offered for the property in addition to those
- * offered by its property editor.</dd>
- * <dt>notUndefined: Boolean</dt>
- * <dd>If true, the property should not be left undefined. A <b>default</b>
- * attribute must be provided if this is set.</dd>
- * <dd>notExpression: Boolean</dd>
- * <dd>If true, the property content should always be constant: JMeter
- * 'expressions' (strings using ${var}, etc...) can't be used.</dt>
- * <dd>notOther: Boolean</dd>
- * <dd>If true, the property content must always be one of the tags values or
- * null.</dt>
- * <dt>default: Object</dt>
- * <dd>Initial value for the property's GUI. Must be provided and be non-null
- * if <b>notUndefined</b> is set. Must be one of the provided tags (or null) if
- * <b>notOther</b> is set.
- * </dl>
+ * Those customizers deviate from the standards only in that, instead of a
+ * bean, they will receive a Map in the setObject call. This will be a property
+ * name to value Map. The customizer is also in charge of initializing empty
+ * Maps with sensible initial values.
* <p>
- * The following BeanDescriptor attributes are also understood:
- * <dl>
- * <dt>group.<i>group</i>.order: Integer</dt>
- * <dd>where <b><i>group</i></b> is a group name used in a <b>group</b>
- * attribute in one or more PropertyDescriptors. Defines the order in which
- * the group will be shown in the GUI. A smaller integer means higher up
- * in the GUI. The default order is 0. Groups of equal order are sorted
- * alphabetically.</dd>
- * <dt>resourceBundle: ResourceBundle</dt>
- * <dd>A resource bundle to be used for GUI localization: group display names
- * will be obtained from property "<b><i>group</i>.displayName</b>" if
- * available (where <b><i>group</i></b> is the group name).
- * </dl>
+ * If the provided Customizer class implements the SharedCustomizer
+ * interface, the same instance of the customizer will be reused for all
+ * beans of the type: setObject(map) can then be called multiple times.
+ * Otherwise, one separate instance will be used for each element.
+ * For efficiency reasons, most customizers should implement
+ * SharedCustomizer.
*/
-public class TestBeanGUI extends AbstractJMeterGuiComponent
+public class TestBeanGUI
+ extends AbstractJMeterGuiComponent
+ implements JMeterGUIComponent
{
private static Logger log = LoggingManager.getLoggerForClass();
- public static final String GROUP= "group";
- public static final String ORDER= "order";
- public static final String TAGS= "tags";
- public static final String NOT_UNDEFINED= "notUndefined";
- public static final String NOT_EXPRESSION= "notExpression";
- public static final String NOT_OTHER= "notOther";
- public static final String DEFAULT= "default";
- public static final String RESOURCE_BUNDLE= "resourceBundle";
- public static final String ORDER(String group) {
- return "group."+group+".order";
- }
+ private Class testBeanClass;
+
+ private BeanInfo beanInfo;
+
+ private PropertyDescriptor[] properties;
- public static final String DEFAULT_GROUP= "";
+ private Class customizerClass;
/**
- * Class of the objects being edited.
+ * The single customizer if the customizer class implements
+ * SharedCustomizer, null otherwise.
*/
- private Class testBeanClass;
-
+ private Customizer customizer= null;
+
/**
- * BeanInfo object for the class of the objects being edited.
+ * TestElement to Customizer map if customizer is null.
*/
- private BeanInfo beanInfo;
+ private Map customizers= new HashMap();
/**
- * Property descriptors from the beanInfo.
+ * Index of the customizer in the JPanel's child component list:
*/
- private PropertyDescriptor[] descriptors;
-
+ private int customizerIndexInPanel;
+
/**
- * Property editors -- or null if the property can't be edited.
+ * The property name to value map that the active customizer
+ * edits:
*/
- private PropertyEditor[] editors;
-
- /**
- * Message format for property field labels:
- */
- private MessageFormat propertyFieldLabelMessage;
-
- /**
- * Message format for property tooltips:
- */
- private MessageFormat propertyToolTipMessage;
-
- static
- {
- List paths= new LinkedList();
- paths.add("org.apache.jmeter.testbeans.gui");
- paths.addAll(Arrays.asList(PropertyEditorManager.getEditorSearchPath()));
- String s= JMeterUtils.getPropDefault("propertyEditorSearchPath", null) ;
- if (s != null)
- {
- paths.addAll(Arrays.asList(JMeterUtils.split(s, ",", "")));
- }
- PropertyEditorManager.setEditorSearchPath((String[])paths.toArray(new String[0]));
- }
+ private Map propertyMap= new HashMap();
/**
- * Create a GUI for a given test bean type.
- *
- * @param testBeanClass a subclass of TestBean
- * @see org.apache.jmeter.testbeans.TestBean
+ * Whether the GUI components have been created.
*/
+ private boolean initialized= false;
+
+ static
+ {
+ List paths= new LinkedList();
+ paths.add("org.apache.jmeter.testbeans.gui");
+ paths.addAll(Arrays.asList(PropertyEditorManager.getEditorSearchPath()));
+ String s= JMeterUtils.getPropDefault("propertyEditorSearchPath", null) ;
+ if (s != null)
+ {
+ paths.addAll(Arrays.asList(JMeterUtils.split(s, ",", "")));
+ }
+ PropertyEditorManager.setEditorSearchPath((String[])paths.toArray(new String[0]));
+ }
+
public TestBeanGUI(Class testBeanClass)
{
super();
-
+
// A quick verification, just in case:
if (! TestBean.class.isAssignableFrom(testBeanClass))
{
@@ -245,7 +192,6 @@
try
{
beanInfo= Introspector.getBeanInfo(testBeanClass, TestBean.class);
- descriptors= beanInfo.getPropertyDescriptors();
}
catch (IntrospectionException e)
{
@@ -254,284 +200,54 @@
throw new Error(e.toString()); // Programming error. Don't continue.
}
- // Sort the property descriptors:
- Arrays.sort(descriptors, new PropertyComparator());
-
- // Obtain the propertyEditors:
- editors= new PropertyEditor[descriptors.length];
- for (int i=0; i<descriptors.length; i++)
- {
- String name= descriptors[i].getName();
-
- // Don't get editors for hidden or non-read-write properties:
- if (descriptors[i].isHidden()
- || descriptors[i].getReadMethod() == null
- || descriptors[i].getWriteMethod() == null)
- {
- log.debug("No editor for property "+name);
- editors[i]= null;
- continue;
- }
-
- PropertyEditor propertyEditor;
- Class editorClass= descriptors[i].getPropertyEditorClass();
-
- if (log.isDebugEnabled())
- {
- log.debug("Property "+name
- +" has editor class "+editorClass);
- }
-
- if (editorClass != null)
- {
- try
- {
- propertyEditor= (PropertyEditor)editorClass.newInstance();
- }
- catch (InstantiationException e)
- {
- log.error("Can't create property editor.", e);
- throw new Error(e.toString());
- }
- catch (IllegalAccessException e)
- {
- log.error("Can't create property editor.", e);
- throw new Error(e.toString());
- }
- }
- else
- {
- Class c= descriptors[i].getPropertyType();
- propertyEditor= PropertyEditorManager.findEditor(c);
- }
-
- if (log.isDebugEnabled())
- {
- log.debug("Property "+name
- +" has property editor "+propertyEditor);
- }
-
- if (propertyEditor == null)
- {
- log.debug("No editor for property "+name);
- editors[i]= null;
- continue;
- }
-
- if (! propertyEditor.supportsCustomEditor())
- {
- propertyEditor= createWrapperEditor(
- propertyEditor, descriptors[i]);
+ properties= beanInfo.getPropertyDescriptors();
+ customizerClass= beanInfo.getBeanDescriptor().getCustomizerClass();
- if (log.isDebugEnabled())
- {
- log.debug("Editor for property "+name
- +" is wrapped in "+propertyEditor);
- }
- }
-
- editors[i]= propertyEditor;
+ // Creation of the customizer and GUI initialization is delayed until the first
+ // configure call. We don't need all that just to find out the static label, menu
+ // categories, etc!
+ initialized= false;
+ }
- // Initialize the editor with the provided default value or null:
- setEditorValue(i, descriptors[i].getValue(DEFAULT));
+ private Customizer createCustomizer()
+ {
+ try
+ {
+ return (Customizer)customizerClass.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ log.error("Could not instantiate customizer of class "+customizerClass, e);
+ throw new Error(e.toString());
+ }
+ catch (IllegalAccessException e)
+ {
+ log.error("Could not instantiate customizer of class "+customizerClass, e);
+ throw new Error(e.toString());
}
-
- // Obtain message formats:
- propertyFieldLabelMessage= new MessageFormat(
- JMeterUtils.getResString("property_as_field_label"));
- propertyToolTipMessage= new MessageFormat(
- JMeterUtils.getResString("property_tool_tip"));
-
- // Initialize the GUI:
- init();
}
- /**
- * Find the default typeEditor and a suitable guiEditor for the given
- * property descriptor, and combine them in a WrapperEditor.
- *
- * @param typeEditor
- * @param descriptor
- * @return
- */
- private WrapperEditor createWrapperEditor(
- PropertyEditor typeEditor, PropertyDescriptor descriptor)
- {
- String[] editorTags= typeEditor.getTags();
- String[] additionalTags= (String[])descriptor.getValue(TAGS);
- String[] tags= null;
- if (editorTags == null) tags= additionalTags;
- else if (additionalTags == null) tags= editorTags;
- else {
- tags= new String[editorTags.length+additionalTags.length];
- int j= 0;
- for (int i=0; i<editorTags.length; i++) tags[j++]= editorTags[i];
- for (int i=0; i<additionalTags.length; i++) tags[j++]= additionalTags[i];
- }
-
- boolean notNull=
- Boolean.TRUE.equals(descriptor.getValue(NOT_UNDEFINED));
- boolean notExpression=
- Boolean.TRUE.equals(descriptor.getValue(NOT_EXPRESSION));
- boolean notOther=
- Boolean.TRUE.equals(descriptor.getValue(NOT_OTHER));
-
- PropertyEditor guiEditor;
- if (notNull && tags==null)
- {
- guiEditor= new FieldStringEditor();
- }
- else
- {
- ComboStringEditor e= new ComboStringEditor();
- e.setNoUndefined(notNull);
- e.setNoEdit(notExpression && notOther);
- e.setTags(tags);
-
- guiEditor= e;
- }
-
- WrapperEditor wrapper= new WrapperEditor(
- typeEditor, guiEditor,
- !notNull, // acceptsNull
- !notExpression, // acceptsExpressions
- !notOther // acceptsOther
- );
-
- Object defaultValue= descriptor.getValue(DEFAULT);
-
- try
- {
- wrapper.setValue(defaultValue);
- }
- catch (IllegalArgumentException e)
- {
- log.error("The default value for property " + descriptor.getName()
- +" is not valid. Or a default value was not provided and "
- +" property attribute notUndefined is set to true.");
- throw new Error(e.toString()); // programming error, so bail out.
- }
-
- if (guiEditor instanceof ComboStringEditor)
- {
- // Provide an initial edit value if necessary:
-
- /*
- What follows encodes this correspondence:
-
- ot ex nl -- default or 1st tag or ${}
- ot ex !nl -- "": use "" or last valid value
- ot !ex nl -- default or 1st tag or "" or last valid value :-(
- ot !ex !nl -- "": use "" or last valid value
- !ot ex nl -- ${}
- !ot ex !nl -- ${}
- !ot !ex nl -- not necessary (not editable)
- !ot !ex !nl -- not necessary (not editable)
-
- [ot=other, ex=expressions, nl=null]
-
- */
-
- String v;
- if (notOther) v="${}";
- else if (notNull) v= "";
- else if (defaultValue != null) v= wrapper.getAsText();
- else if (tags != null && tags.length>0) v= tags[0];
- else if (notExpression) v= "";
- else v="${}";
-
- ((ComboStringEditor)guiEditor).setInitialEditValue(v);
- }
-
- return wrapper;
- }
-
- /**
- * Set the value of the i-th property, properly reporting a possible failure.
- *
- * @param i the index of the property in the descriptors and editors arrays
- * @param value the value to be stored in the editor
- *
- * @throws IllegalArgumentException if the editor refuses the value
- */
- private void setEditorValue(int i, Object value)
- throws IllegalArgumentException
- {
- try
- {
- editors[i].setValue(value);
- }
- catch (IllegalArgumentException e)
- {
- log.error("Could not set value "
- + ( value == null ? "NULL" : value.getClass().getName() )
- + ":" + value
- +" for property "+descriptors[i].getName());
- throw e;
- }
- }
-
- public String getStaticLabel() {
+ /* (non-Javadoc)
+ * @see org.apache.jmeter.gui.JMeterGUIComponent#getStaticLabel()
+ */
+ public String getStaticLabel()
+ {
if (beanInfo == null) return "null";
return beanInfo.getBeanDescriptor().getDisplayName();
}
/* (non-Javadoc)
- * @see org.apache.jmeter.gui.JMeterGUIComponent#configure(org.apache.jmeter.testelement.TestElement)
+ * @see org.apache.jmeter.gui.JMeterGUIComponent#createTestElement()
*/
- public void configure(TestElement element)
- {
- super.configure(element);
-
- for (int i=0; i<editors.length; i++)
- {
- if (editors[i] == null) continue;
- JMeterProperty jprop= element.getProperty(descriptors[i].getName());
- try
- {
- setEditorValue(i, jprop.getObjectValue());
- }
- catch (IllegalArgumentException e)
- {
- // I guess this can happen as a result of a bad
- // file read? In this case, it would be better to replace the
- // incorrect value with anything valid, e.g. the default value
- // for the property.
- // But for the time being, I just prefer to be aware of any
- // problems occuring here, most likely programming errors,
- // so I'll bail out.
- throw new Error("Bad property value."+e);
- // TODO: review this and possibly change to:
- // setEditorValue(i, descriptors[i].getValue(DEFAULT);
- }
- }
- }
-
- /**
- * Find the index of the property of the given name.
- *
- * @param name the name of the property
- * @return the index of that property in the descriptors array, or -1 if
- * there's no property of this name.
- */
- private int descriptorIndex(String name) //NOTUSED
- {
- for (int i=0; i<descriptors.length; i++)
- {
- if (descriptors[i].getName().equals(name))
- {
- return i;
- }
- }
- return -1;
- }
-
public TestElement createTestElement()
{
try
{
TestElement element= (TestElement)testBeanClass.newInstance();
- modifyTestElement(element);
+ configure(element);
+ super.clear(); // set name, enabled.
+ configureTestElement(element);
+ modifyTestElement(element); // put the default values back into the new element
return element;
}
catch (InstantiationException e)
@@ -551,93 +267,125 @@
*/
public void modifyTestElement(TestElement element)
{
- configureTestElement(element);
- for (int i=0; i<editors.length; i++)
- {
- if (editors[i] == null) continue;
- Object value= editors[i].getValue();
-
- if (value == null)
- {
- element.removeProperty(descriptors[i].getName());
- }
- else {
- JMeterProperty jprop= wrapInProperty(value);
- jprop.setName(descriptors[i].getName());
- element.setProperty(jprop);
- }
+ super.configureTestElement(element);
+
+ // Copy all property values from the map into the element:
+ PropertyDescriptor[] props= beanInfo.getPropertyDescriptors();
+ for (int i=0; i<props.length; i++)
+ {
+ String name= props[i].getName();
+ JMeterProperty jprop= wrapInProperty(propertyMap.get(name));
+ jprop.setName(name);
+ element.setProperty(jprop);
}
}
- /**
- * Utility method to wrap an object in a property of an appropriate type.
- * <p>
- * I plan to get rid of this sooner than later, so please don't use it much.
- *
- * @param value Object to be wrapped.
- * @return an unnamed property holding the provided value.
- * @deprecated
- */
- private static JMeterProperty wrapInProperty(Object value)
- {
- // TODO: Awful, again...
+ /**
+ * Utility method to wrap an object in a property of an appropriate type.
+ * <p>
+ * I plan to get rid of this sooner than later, so please don't use it much.
+ *
+ * @param value Object to be wrapped.
+ * @return an unnamed property holding the provided value.
+ * @deprecated
+ */
+ private static JMeterProperty wrapInProperty(Object value)
+ {
+ // TODO: Awful, again...
- if (value instanceof JMeterProperty)
- {
- return (JMeterProperty)value;
- }
+ if (value instanceof JMeterProperty)
+ {
+ return (JMeterProperty)value;
+ }
- JMeterProperty property;
- if (value == null)
- {
- property= new NullProperty();
- }
- else if (value instanceof Boolean)
- {
- property= new BooleanProperty();
- }
- else if (value instanceof Double)
- {
- property= new DoubleProperty();
- }
- else if (value instanceof Float)
- {
- property= new FloatProperty();
- }
- else if (value instanceof Integer)
- {
- property= new IntegerProperty();
- }
- else if (value instanceof Long)
- {
- property= new LongProperty();
- }
- else if (value instanceof String)
- {
- property= new StringProperty();
- }
- else if (value instanceof TestElement)
- {
- property= new TestElementProperty();
- }
- else throw new Error("Ouch!");
+ JMeterProperty property;
+ if (value == null)
+ {
+ property= new NullProperty();
+ }
+ else if (value instanceof Boolean)
+ {
+ property= new BooleanProperty();
+ }
+ else if (value instanceof Double)
+ {
+ property= new DoubleProperty();
+ }
+ else if (value instanceof Float)
+ {
+ property= new FloatProperty();
+ }
+ else if (value instanceof Integer)
+ {
+ property= new IntegerProperty();
+ }
+ else if (value instanceof Long)
+ {
+ property= new LongProperty();
+ }
+ else if (value instanceof String)
+ {
+ property= new StringProperty();
+ }
+ else if (value instanceof TestElement)
+ {
+ property= new TestElementProperty();
+ }
+ else throw new Error("Ouch!");
- property.setObjectValue(value);
+ property.setObjectValue(value);
- return property;
- }
+ return property;
+ }
/* (non-Javadoc)
* @see org.apache.jmeter.gui.JMeterGUIComponent#createPopupMenu()
*/
public JPopupMenu createPopupMenu()
{
- // TODO: this menu is too wide (allows, e.g. to add controllers, no matter the
- // type of the element). Change to match the actual bean's capabilities.
+ // TODO: this menu is too wide (allows, e.g. to add controllers, no matter the
+ // type of the element). Change to match the actual bean's capabilities.
return MenuFactory.getDefaultControllerMenu();
}
/* (non-Javadoc)
+ * @see org.apache.jmeter.gui.JMeterGUIComponent#configure(org.apache.jmeter.testelement.TestElement)
+ */
+ public void configure(TestElement element)
+ {
+ if (! initialized) init();
+
+ super.configure(element);
+
+ // Copy all property values into the map:
+ propertyMap.clear();
+ for (PropertyIterator jprops= element.propertyIterator(); jprops.hasNext(); )
+ {
+ JMeterProperty jprop= jprops.next();
+ propertyMap.put(jprop.getName(), jprop.getObjectValue());
+ }
+
+ if (customizer != null)
+ {
+ customizer.setObject(propertyMap);
+ }
+ else
+ {
+ if (initialized) remove(customizerIndexInPanel);
+ Customizer c= (Customizer)customizers.get(element);
+ if (c == null)
+ {
+ c= createCustomizer();
+ c.setObject(propertyMap);
+ customizers.put(element, c);
+ }
+ add((Component)c, BorderLayout.CENTER);
+ }
+
+ initialized= true;
+ }
+
+ /* (non-Javadoc)
* @see org.apache.jmeter.gui.JMeterGUIComponent#getMenuCategories()
*/
public Collection getMenuCategories()
@@ -679,225 +427,25 @@
}
return menuCategories;
}
-
- /**
- * Initialize the GUI.
- */
+
private void init()
{
- // TODO: add support for Bean Customizers
-
setLayout(new BorderLayout(0, 5));
setBorder(makeBorder());
add(makeTitlePanel(), BorderLayout.NORTH);
- JPanel mainPanel = new JPanel(new GridBagLayout());
-
- GridBagConstraints cl= new GridBagConstraints(); // for labels
- cl.gridx= 0;
- cl.anchor= GridBagConstraints.EAST;//JDK1.4: was LINE_END
- cl.insets= new Insets(0, 1, 0, 1);
-
- GridBagConstraints ce= new GridBagConstraints(); // for editors
- ce.fill= GridBagConstraints.BOTH;
- ce.gridx= 1;
- ce.weightx= 1.0;
- ce.insets= new Insets(0, 1, 0, 1);
-
- GridBagConstraints cp= new GridBagConstraints(); // for panels
- cp.fill= GridBagConstraints.BOTH;
- cp.gridx= 1;
- cp.gridy= GridBagConstraints.RELATIVE;
- cp.gridwidth= 2;
- cp.weightx= 1.0;
-
- JPanel currentPanel= mainPanel;
- String currentGroup= DEFAULT_GROUP;
- int y=0;
-
- for (int i=0; i<editors.length; i++)
- {
- if (editors[i] == null) continue;
-
- if (log.isDebugEnabled())
- {
- log.debug("Laying property "+descriptors[i].getName());
- }
-
- String g= group(descriptors[i]);
- if (! currentGroup.equals(g))
- {
- if (currentPanel != mainPanel)
- {
- mainPanel.add(currentPanel, cp);
- }
- currentGroup= g;
- currentPanel= new JPanel(new GridBagLayout());
- currentPanel.setBorder(
- BorderFactory.createTitledBorder(
- BorderFactory.createEtchedBorder(),
- groupDisplayName(g)));
- cp.weighty= 0.0;
- y= 0;
- }
-
- Component customEditor= editors[i].getCustomEditor();
-
- boolean multiLineEditor= false;
- if (customEditor.getPreferredSize().height > 50)
- {
- // TODO: the above works in the current situation, but it's
- // just a hack. How to get each editor to report whether it
- // wants to grow bigger? Whether the property label should
- // be at the left or at the top of the editor? ...?
- multiLineEditor= true;
- }
-
- JLabel label= createLabel(descriptors[i]);
- label.setLabelFor(customEditor);
-
- cl.gridy= y;
- cl.gridwidth= multiLineEditor ? 2 : 1;
- cl.anchor= multiLineEditor
- ? GridBagConstraints.CENTER
- : GridBagConstraints.EAST;//JDK1.4: was LINE_END
- currentPanel.add(label, cl);
-
- ce.gridx= multiLineEditor ? 0 : 1;
- ce.gridy= multiLineEditor ? ++y : y;
- ce.gridwidth= multiLineEditor ? 2 : 1;
- ce.weighty= multiLineEditor ? 1.0 : 0.0;
-
- cp.weighty+= ce.weighty;
-
- currentPanel.add(customEditor, ce);
-
- y++;
- }
- if (currentPanel != mainPanel)
- {
- mainPanel.add(currentPanel, cp);
- }
-
- // Add a 0-sized invisible component that will take all the vertical
- // space that nobody wants:
- GridBagConstraints cs= new GridBagConstraints(); // for strut
- cs.gridx= 1;
- cs.gridy= y++;
- cs.gridwidth= 2;
- cs.weighty= 0.0001;
-
- mainPanel.add(Box.createHorizontalStrut(0), cs);
-
- // Done: add the panel to the GUI:
- add(mainPanel, BorderLayout.CENTER);
- }
+ customizerIndexInPanel= getComponentCount();
- private JLabel createLabel(PropertyDescriptor desc)
- {
- String text= desc.getDisplayName();
- if (! "".equals(text))
- {
- text= propertyFieldLabelMessage.format(
- new Object[] { desc.getDisplayName() } );
- }
- // if the displayName is the empty string, leave it like that.
- JLabel label = new JLabel(text);
- label.setHorizontalAlignment(JLabel.TRAILING);
- text= propertyToolTipMessage.format(
- new Object[] { desc.getName(), desc.getShortDescription() } );
- label.setToolTipText(text);
-
- return label;
- }
-
-
- /**
- * Obtain a property descriptor's group.
- *
- * @param descriptor
- * @return the group String.
- */
- private String group(PropertyDescriptor d)
- {
- String group= (String)d.getValue(GROUP);
- if (group == null) group= DEFAULT_GROUP;
- return group;
- }
-
- /**
- * Obtain a group's display name
- */
- private String groupDisplayName(String group)
- {
- try {
- ResourceBundle b= (ResourceBundle)
- beanInfo.getBeanDescriptor().getValue(RESOURCE_BUNDLE);
- if (b == null) return group;
- else return b.getString(group+".displayName");
- }
- catch (MissingResourceException e)
- {
- return group;
- }
- }
-
- /**
- * Comparator used to sort properties for presentation in the GUI.
- */
- private class PropertyComparator implements Comparator
- {
- public int compare(Object o1, Object o2)
- {
- return compare((PropertyDescriptor)o1, (PropertyDescriptor)o2);
- }
-
- private int compare(PropertyDescriptor d1, PropertyDescriptor d2)
- {
- int result;
-
- String g1= group(d1), g2= group(d2);
- Integer go1= groupOrder(g1), go2= groupOrder(g2);
-
- result= go1.compareTo(go2);
- if (result != 0) return result;
-
- result= g1.compareTo(g2);
- if (result != 0) return result;
-
- Integer po1= propertyOrder(d1), po2= propertyOrder(d2);
- result= po1.compareTo(po2);
- if (result != 0) return result;
-
- return d1.getName().compareTo(d2.getName());
- }
-
- /**
- * Obtain a group's order.
- *
- * @param group group name
- * @return the group's order (zero by default)
- */
- private Integer groupOrder(String group)
- {
- Integer order= (Integer)beanInfo.getBeanDescriptor()
- .getValue(ORDER(group));
- if (order == null) order= new Integer(0);
- return order;
- }
-
- /**
- * Obtain a property's order.
- *
- * @param d
- * @return the property's order attribute (zero by default)
- */
- private Integer propertyOrder(PropertyDescriptor d)
- {
- Integer order= (Integer)d.getValue(ORDER);
- if (order == null) order= new Integer(0);
- return order;
- }
+ if (customizerClass == null)
+ {
+ customizer= new GenericTestBeanCustomizer(beanInfo);
+ }
+ else if (SharedCustomizer.class.isAssignableFrom(customizerClass))
+ {
+ customizer= createCustomizer();
+ }
+
+ if (customizer != null) add((Component)customizer, BorderLayout.CENTER);
}
}
1.3 +9 -6 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/TestElementEditor.java
Index: TestElementEditor.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/TestElementEditor.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TestElementEditor.java 14 Jan 2004 23:10:30 -0000 1.2
+++ TestElementEditor.java 28 Jan 2004 10:35:14 -0000 1.3
@@ -71,9 +71,10 @@
*/
public abstract class TestElementEditor extends PropertyEditorSupport {
- Class guiClass;
- JMeterGUIComponent guiComponent;
-
+ private Class guiClass;
+ private JMeterGUIComponent guiComponent;
+ private TestElement element;
+
/**
* Create a property editor from a given Component subclass implementing
* JMeterGuiComponent -- most often an AbstractJMeterGuiComponent subclass.
@@ -99,7 +100,8 @@
* @see java.beans.PropertyEditor#getValue()
*/
public Object getValue() {
- return guiComponent.createTestElement();
+ guiComponent.modifyTestElement(element);
+ return element;
}
/**
@@ -109,7 +111,8 @@
public void setValue(Object value) {
if (value != null)
{
- guiComponent.configure((TestElement)value);
+ element= (TestElement)value;
+ guiComponent.configure(element);
firePropertyChange();
}
}
1.5 +18 -11 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/BeanInfoSupport.java
Index: BeanInfoSupport.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/BeanInfoSupport.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- BeanInfoSupport.java 16 Jan 2004 02:28:08 -0000 1.4
+++ BeanInfoSupport.java 28 Jan 2004 10:35:15 -0000 1.5
@@ -103,13 +103,19 @@
private static transient Logger log = LoggingManager.getLoggerForClass();
- // Some known attribute names, just for convenience:
- public static final String TAGS= TestBeanGUI.TAGS;
- public static final String NOT_UNDEFINED= TestBeanGUI.NOT_UNDEFINED;
- public static final String NOT_EXPRESSION= TestBeanGUI.NOT_EXPRESSION;
- public static final String NOT_OTHER= TestBeanGUI.NOT_OTHER;
- public static final String DEFAULT= TestBeanGUI.DEFAULT;
- public static final String RESOURCE_BUNDLE= TestBeanGUI.RESOURCE_BUNDLE;
+ // Some known attribute names, just for convenience:
+ public static final String TAGS=
+ GenericTestBeanCustomizer.TAGS;
+ public static final String NOT_UNDEFINED=
+ GenericTestBeanCustomizer.NOT_UNDEFINED;
+ public static final String NOT_EXPRESSION=
+ GenericTestBeanCustomizer.NOT_EXPRESSION;
+ public static final String NOT_OTHER=
+ GenericTestBeanCustomizer.NOT_OTHER;
+ public static final String DEFAULT=
+ GenericTestBeanCustomizer.DEFAULT;
+ public static final String RESOURCE_BUNDLE=
+ GenericTestBeanCustomizer.RESOURCE_BUNDLE;
/**
* The class for which we're providing the bean info.
@@ -233,11 +239,12 @@
for (int i=0; i<names.length; i++)
{
PropertyDescriptor p= property(names[i]);
- p.setValue(TestBeanGUI.GROUP, group);
- p.setValue(TestBeanGUI.ORDER, new Integer(i));
+ p.setValue(GenericTestBeanCustomizer.GROUP, group);
+ p.setValue(GenericTestBeanCustomizer.ORDER, new Integer(i));
}
numCreatedGroups++;
- getBeanDescriptor().setValue(TestBeanGUI.ORDER(group),
+ getBeanDescriptor().setValue(
+ GenericTestBeanCustomizer.ORDER(group),
new Integer(numCreatedGroups));
}
1.6 +1 -16 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/WrapperEditor.java
Index: WrapperEditor.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/WrapperEditor.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- WrapperEditor.java 27 Jan 2004 13:22:24 -0000 1.5
+++ WrapperEditor.java 28 Jan 2004 10:35:16 -0000 1.6
@@ -186,21 +186,6 @@
acceptsOther, defaultValue);
}
- /**
- * Constructor for backward compatibility -- will soon be removed.
- */
- WrapperEditor(
- PropertyEditor typeEditor,
- PropertyEditor guiEditor,
- boolean acceptsNull,
- boolean acceptsExpressions,
- boolean acceptsOther)
- {
- super();
- initialize(typeEditor, guiEditor, acceptsNull, acceptsExpressions,
- acceptsOther, null);
- }
-
private void initialize(
PropertyEditor typeEditor,
PropertyEditor guiEditor,
1.1 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/GenericTestBeanCustomizer.java
Index: GenericTestBeanCustomizer.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author <a href="mailto:jsalvata@apache.org">Jordi Salvat i Alabart</a>
* @version $Id: GenericTestBeanCustomizer.java,v 1.1 2004/01/28 10:35:12 jsalvata Exp $
*/
package org.apache.jmeter.testbeans.gui;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.beans.BeanInfo;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
/**
* The GenericTestBeanCustomizer is designed to provide developers with a
* mechanism to quickly implement GUIs for new components.
* <p>
* It allows editing each of the public exposed properties of the
* edited type 'a la JavaBeans': as far as the types of those properties
* have an associated editor, there's no GUI development required.
* <p>
* This class understands the following PropertyDescriptor attributes:
* <dl>
* <dt>group: String</dt>
* <dd>Group under which the property should be shown in the GUI. The string is
* also used as a group title (but see comment on resourceBundle below). The
* default group is "".</dd>
* <dt>order: Integer</dt>
* <dd>Order in which the property will be shown in its group. A smaller
* integer means higher up in the GUI. The default order is 0. Properties
* of equal order are sorted alphabetically.</dd>
* <dt>tags: String[]</dt>
* <dd>List of values to be offered for the property in addition to those
* offered by its property editor.</dd>
* <dt>notUndefined: Boolean</dt>
* <dd>If true, the property should not be left undefined. A <b>default</b>
* attribute must be provided if this is set.</dd>
* <dd>notExpression: Boolean</dd>
* <dd>If true, the property content should always be constant: JMeter
* 'expressions' (strings using ${var}, etc...) can't be used.</dt>
* <dd>notOther: Boolean</dd>
* <dd>If true, the property content must always be one of the tags values or
* null.</dt>
* <dt>default: Object</dt>
* <dd>Initial value for the property's GUI. Must be provided and be non-null
* if <b>notUndefined</b> is set. Must be one of the provided tags (or null) if
* <b>notOther</b> is set.
* </dl>
* <p>
* The following BeanDescriptor attributes are also understood:
* <dl>
* <dt>group.<i>group</i>.order: Integer</dt>
* <dd>where <b><i>group</i></b> is a group name used in a <b>group</b>
* attribute in one or more PropertyDescriptors. Defines the order in which
* the group will be shown in the GUI. A smaller integer means higher up
* in the GUI. The default order is 0. Groups of equal order are sorted
* alphabetically.</dd>
* <dt>resourceBundle: ResourceBundle</dt>
* <dd>A resource bundle to be used for GUI localization: group display names
* will be obtained from property "<b><i>group</i>.displayName</b>" if
* available (where <b><i>group</i></b> is the group name).
* </dl>
*/
class GenericTestBeanCustomizer extends JPanel
implements SharedCustomizer, PropertyChangeListener
{
private static Logger log = LoggingManager.getLoggerForClass();
public static final String GROUP= "group";
public static final String ORDER= "order";
public static final String TAGS= "tags";
public static final String NOT_UNDEFINED= "notUndefined";
public static final String NOT_EXPRESSION= "notExpression";
public static final String NOT_OTHER= "notOther";
public static final String DEFAULT= "default";
public static final String RESOURCE_BUNDLE= "resourceBundle";
public static final String ORDER(String group) {
return "group."+group+".order";
}
public static final String DEFAULT_GROUP= "";
/**
* BeanInfo object for the class of the objects being edited.
*/
private BeanInfo beanInfo;
/**
* Property descriptors from the beanInfo.
*/
private PropertyDescriptor[] descriptors;
/**
* Property editors -- or null if the property can't be edited.
* Unused if customizerClass==null.
*/
private PropertyEditor[] editors;
/**
* Message format for property field labels:
*/
private MessageFormat propertyFieldLabelMessage;
/**
* Message format for property tooltips:
*/
private MessageFormat propertyToolTipMessage;
/**
* The Map we're currently customizing. Set by setObject().
*/
private Map propertyMap;
/**
* Create a customizer for a given test bean type.
*
* @param testBeanClass a subclass of TestBean
* @see org.apache.jmeter.testbeans.TestBean
*/
GenericTestBeanCustomizer(BeanInfo beanInfo)
{
super();
this.beanInfo= beanInfo;
// Get and sort the property descriptors:
descriptors= beanInfo.getPropertyDescriptors();
Arrays.sort(descriptors, new PropertyComparator());
// Obtain the propertyEditors:
editors= new PropertyEditor[descriptors.length];
for (int i=0; i<descriptors.length; i++)
{
String name= descriptors[i].getName();
// Don't get editors for hidden or non-read-write properties:
if (descriptors[i].isHidden()
|| descriptors[i].getReadMethod() == null
|| descriptors[i].getWriteMethod() == null)
{
log.debug("No editor for property "+name);
editors[i]= null;
continue;
}
PropertyEditor propertyEditor;
Class editorClass= descriptors[i].getPropertyEditorClass();
if (log.isDebugEnabled())
{
log.debug("Property "+name
+" has editor class "+editorClass);
}
if (editorClass != null)
{
try
{
propertyEditor= (PropertyEditor)editorClass.newInstance();
}
catch (InstantiationException e)
{
log.error("Can't create property editor.", e);
throw new Error(e.toString());
}
catch (IllegalAccessException e)
{
log.error("Can't create property editor.", e);
throw new Error(e.toString());
}
}
else
{
Class c= descriptors[i].getPropertyType();
propertyEditor= PropertyEditorManager.findEditor(c);
}
if (log.isDebugEnabled())
{
log.debug("Property "+name
+" has property editor "+propertyEditor);
}
if (propertyEditor == null)
{
log.debug("No editor for property "+name);
editors[i]= null;
continue;
}
if (! propertyEditor.supportsCustomEditor())
{
propertyEditor= createWrapperEditor(
propertyEditor, descriptors[i]);
if (log.isDebugEnabled())
{
log.debug("Editor for property "+name
+" is wrapped in "+propertyEditor);
}
}
editors[i]= propertyEditor;
// Initialize the editor with the provided default value or null:
setEditorValue(i, descriptors[i].getValue(DEFAULT));
// Now subscribe as a listener (we didn't want to receive the event
// for the setEditorValue above!)
propertyEditor.addPropertyChangeListener(this);
}
// Obtain message formats:
propertyFieldLabelMessage= new MessageFormat(
JMeterUtils.getResString("property_as_field_label"));
propertyToolTipMessage= new MessageFormat(
JMeterUtils.getResString("property_tool_tip"));
// Initialize the GUI:
init();
}
/**
* Find the default typeEditor and a suitable guiEditor for the given
* property descriptor, and combine them in a WrapperEditor.
*
* @param typeEditor
* @param descriptor
* @return
*/
private WrapperEditor createWrapperEditor(
PropertyEditor typeEditor, PropertyDescriptor descriptor)
{
String[] editorTags= typeEditor.getTags();
String[] additionalTags= (String[])descriptor.getValue(TAGS);
String[] tags= null;
if (editorTags == null) tags= additionalTags;
else if (additionalTags == null) tags= editorTags;
else {
tags= new String[editorTags.length+additionalTags.length];
int j= 0;
for (int i=0; i<editorTags.length; i++) tags[j++]= editorTags[i];
for (int i=0; i<additionalTags.length; i++) tags[j++]= additionalTags[i];
}
boolean notNull=
Boolean.TRUE.equals(descriptor.getValue(NOT_UNDEFINED));
boolean notExpression=
Boolean.TRUE.equals(descriptor.getValue(NOT_EXPRESSION));
boolean notOther=
Boolean.TRUE.equals(descriptor.getValue(NOT_OTHER));
PropertyEditor guiEditor;
if (notNull && tags==null)
{
guiEditor= new FieldStringEditor();
}
else
{
ComboStringEditor e= new ComboStringEditor();
e.setNoUndefined(notNull);
e.setNoEdit(notExpression && notOther);
e.setTags(tags);
guiEditor= e;
}
WrapperEditor wrapper= new WrapperEditor(
typeEditor, guiEditor,
!notNull, // acceptsNull
!notExpression, // acceptsExpressions
!notOther, // acceptsOther
descriptor.getValue(DEFAULT)
);
return wrapper;
}
/**
* Set the value of the i-th property, properly reporting a possible failure.
*
* @param i the index of the property in the descriptors and editors arrays
* @param value the value to be stored in the editor
*
* @throws IllegalArgumentException if the editor refuses the value
*/
private void setEditorValue(int i, Object value)
throws IllegalArgumentException
{
try
{
editors[i].setValue(value);
}
catch (IllegalArgumentException e)
{
log.error("Could not set value "
+ ( value == null ? "NULL" : value.getClass().getName() )
+ ":" + value
+" for property "+descriptors[i].getName());
throw e;
}
}
/* (non-Javadoc)
* @see org.apache.jmeter.gui.JMeterGUIComponent#configure(org.apache.jmeter.testelement.TestElement)
*/
public void setObject(Object map)
{
propertyMap= (Map)map;
if (propertyMap.size() == 0)
{
// Uninitialized -- set it to the defaults:
for (int i=0; i<editors.length; i++)
{
Object value= descriptors[i].getValue(DEFAULT);
String name= descriptors[i].getName();
if (value != null)
{
propertyMap.put(name, value);
log.debug("Set "+name+"= "+value);
}
firePropertyChange(name, null, value);
}
}
// Now set the editors to the element's values:
for (int i=0; i<editors.length; i++)
{
if (editors[i] == null) continue;
try
{
setEditorValue(i, propertyMap.get(descriptors[i].getName()));
}
catch (IllegalArgumentException e)
{
// I guess this can happen as a result of a bad
// file read? In this case, it would be better to replace the
// incorrect value with anything valid, e.g. the default value
// for the property.
// But for the time being, I just prefer to be aware of any
// problems occuring here, most likely programming errors,
// so I'll bail out.
throw new Error("Bad property value."+e);
// TODO: review this and possibly change to:
// setEditorValue(i, descriptors[i].getValue(DEFAULT);
}
}
}
/**
* Find the index of the property of the given name.
*
* @param name the name of the property
* @return the index of that property in the descriptors array, or -1 if
* there's no property of this name.
*/
private int descriptorIndex(String name) //NOTUSED
{
for (int i=0; i<descriptors.length; i++)
{
if (descriptors[i].getName().equals(name))
{
return i;
}
}
return -1;
}
/**
* Initialize the GUI.
*/
private void init()
{
setLayout(new GridBagLayout());
GridBagConstraints cl= new GridBagConstraints(); // for labels
cl.gridx= 0;
cl.anchor= GridBagConstraints.EAST;//JDK1.4: was LINE_END
cl.insets= new Insets(0, 1, 0, 1);
GridBagConstraints ce= new GridBagConstraints(); // for editors
ce.fill= GridBagConstraints.BOTH;
ce.gridx= 1;
ce.weightx= 1.0;
ce.insets= new Insets(0, 1, 0, 1);
GridBagConstraints cp= new GridBagConstraints(); // for panels
cp.fill= GridBagConstraints.BOTH;
cp.gridx= 1;
cp.gridy= GridBagConstraints.RELATIVE;
cp.gridwidth= 2;
cp.weightx= 1.0;
JPanel currentPanel= this;
String currentGroup= DEFAULT_GROUP;
int y=0;
for (int i=0; i<editors.length; i++)
{
if (editors[i] == null) continue;
if (log.isDebugEnabled())
{
log.debug("Laying property "+descriptors[i].getName());
}
String g= group(descriptors[i]);
if (! currentGroup.equals(g))
{
if (currentPanel != this)
{
add(currentPanel, cp);
}
currentGroup= g;
currentPanel= new JPanel(new GridBagLayout());
currentPanel.setBorder(
BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(),
groupDisplayName(g)));
cp.weighty= 0.0;
y= 0;
}
Component customEditor= editors[i].getCustomEditor();
boolean multiLineEditor= false;
if (customEditor.getPreferredSize().height > 50)
{
// TODO: the above works in the current situation, but it's
// just a hack. How to get each editor to report whether it
// wants to grow bigger? Whether the property label should
// be at the left or at the top of the editor? ...?
multiLineEditor= true;
}
JLabel label= createLabel(descriptors[i]);
label.setLabelFor(customEditor);
cl.gridy= y;
cl.gridwidth= multiLineEditor ? 2 : 1;
cl.anchor= multiLineEditor
? GridBagConstraints.CENTER
: GridBagConstraints.EAST;//JDK1.4: was LINE_END
currentPanel.add(label, cl);
ce.gridx= multiLineEditor ? 0 : 1;
ce.gridy= multiLineEditor ? ++y : y;
ce.gridwidth= multiLineEditor ? 2 : 1;
ce.weighty= multiLineEditor ? 1.0 : 0.0;
cp.weighty+= ce.weighty;
currentPanel.add(customEditor, ce);
y++;
}
if (currentPanel != this)
{
add(currentPanel, cp);
}
// Add a 0-sized invisible component that will take all the vertical
// space that nobody wants:
GridBagConstraints cs= new GridBagConstraints(); // for strut
cs.gridx= 1;
cs.gridy= y++;
cs.gridwidth= 2;
cs.weighty= 0.0001;
add(Box.createHorizontalStrut(0), cs);
}
private JLabel createLabel(PropertyDescriptor desc)
{
String text= desc.getDisplayName();
if (! "".equals(text))
{
text= propertyFieldLabelMessage.format(
new Object[] { desc.getDisplayName() } );
}
// if the displayName is the empty string, leave it like that.
JLabel label = new JLabel(text);
label.setHorizontalAlignment(JLabel.TRAILING);
text= propertyToolTipMessage.format(
new Object[] { desc.getName(), desc.getShortDescription() } );
label.setToolTipText(text);
return label;
}
/**
* Obtain a property descriptor's group.
*
* @param descriptor
* @return the group String.
*/
private String group(PropertyDescriptor d)
{
String group= (String)d.getValue(GROUP);
if (group == null) group= DEFAULT_GROUP;
return group;
}
/**
* Obtain a group's display name
*/
private String groupDisplayName(String group)
{
try {
ResourceBundle b= (ResourceBundle)
beanInfo.getBeanDescriptor().getValue(RESOURCE_BUNDLE);
if (b == null) return group;
else return b.getString(group+".displayName");
}
catch (MissingResourceException e)
{
return group;
}
}
/**
* Comparator used to sort properties for presentation in the GUI.
*/
private class PropertyComparator implements Comparator
{
public int compare(Object o1, Object o2)
{
return compare((PropertyDescriptor)o1, (PropertyDescriptor)o2);
}
private int compare(PropertyDescriptor d1, PropertyDescriptor d2)
{
int result;
String g1= group(d1), g2= group(d2);
Integer go1= groupOrder(g1), go2= groupOrder(g2);
result= go1.compareTo(go2);
if (result != 0) return result;
result= g1.compareTo(g2);
if (result != 0) return result;
Integer po1= propertyOrder(d1), po2= propertyOrder(d2);
result= po1.compareTo(po2);
if (result != 0) return result;
return d1.getName().compareTo(d2.getName());
}
/**
* Obtain a group's order.
*
* @param group group name
* @return the group's order (zero by default)
*/
private Integer groupOrder(String group)
{
Integer order= (Integer)beanInfo.getBeanDescriptor()
.getValue(ORDER(group));
if (order == null) order= new Integer(0);
return order;
}
/**
* Obtain a property's order.
*
* @param d
* @return the property's order attribute (zero by default)
*/
private Integer propertyOrder(PropertyDescriptor d)
{
Integer order= (Integer)d.getValue(ORDER);
if (order == null) order= new Integer(0);
return order;
}
}
/* (non-Javadoc)
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt)
{
for (int i=0; i<editors.length; i++)
{
if (editors[i] == evt.getSource())
{
Object value= editors[i].getValue();
String name= descriptors[i].getName();
if (value == null)
{
propertyMap.remove(name);
log.debug("Unset "+name);
}
else {
propertyMap.put(name, value);
log.debug("Set "+name+"= "+value);
}
firePropertyChange(name, evt.getOldValue(), value);
return;
}
}
throw new Error("Unexpected propertyChange event received: "+evt);
}
}
1.1 jakarta-jmeter/src/core/org/apache/jmeter/testbeans/gui/SharedCustomizer.java
Index: SharedCustomizer.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2004 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author <a href="mailto:jsalvata@apache.org">Jordi Salvat i Alabart</a>
* @version $Id: SharedCustomizer.java,v 1.1 2004/01/28 10:35:17 jsalvata Exp $
*/
package org.apache.jmeter.testbeans.gui;
import java.beans.Customizer;
/**
* Tagging interface to mark a customizer class as shareable among elements
* of the same type.
* <p>
* The interface is equivalent to Customizer -- the only difference is that setElement
* can be called multiple times to change the element it works on.
*/
public interface SharedCustomizer extends Customizer
{
}
---------------------------------------------------------------------
To unsubscribe, e-mail: jmeter-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jmeter-dev-help@jakarta.apache.org