|
JMeter example source code file (GenericTestBeanCustomizer.java)
The JMeter GenericTestBeanCustomizer.java source code/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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.PropertyDescriptor; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.Serializable; 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 javax.swing.JScrollPane; 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 * <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 * <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[] * <dd>List of values to be offered for the property in addition to those * offered by its property editor.</dd> * <dt>notUndefined: Boolean * <dd>If true, the property should not be left undefined. A default * attribute must be provided if this is set.</dd> * <dd>notExpression: Boolean * <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>If true, the property content must always be one of the tags values or * null.</dt> * <dt>default: Object * <dd>Initial value for the property's GUI. Must be provided and be non-null * if <b>notUndefined is set. Must be one of the provided tags (or null) if * <b>notOther is set. * </dl> * <p> * The following BeanDescriptor attributes are also understood: * <dl> * <dt>group.group.order: Integer * <dd>where group is a group name used in a group * 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 * <dd>A resource bundle to be used for GUI localization: group display names * will be obtained from property "<b>group.displayName" if * available (where <b>group is the group name). * </dl> */ public class GenericTestBeanCustomizer extends JPanel implements SharedCustomizer { private static final long serialVersionUID = 240L; private static final Logger log = LoggingManager.getLoggerForClass(); public static final String GROUP = "group"; //$NON-NLS-1$ public static final String ORDER = "order"; //$NON-NLS-1$ public static final String TAGS = "tags"; //$NON-NLS-1$ public static final String NOT_UNDEFINED = "notUndefined"; //$NON-NLS-1$ public static final String NOT_EXPRESSION = "notExpression"; //$NON-NLS-1$ public static final String NOT_OTHER = "notOther"; //$NON-NLS-1$ public static final String DEFAULT = "default"; //$NON-NLS-1$ public static final String RESOURCE_BUNDLE = "resourceBundle"; //$NON-NLS-1$ public static final String ORDER(String group) { return "group." + group + ".order"; } public static final String DEFAULT_GROUP = ""; private int scrollerCount = 0; /** * BeanInfo object for the class of the objects being edited. */ private transient BeanInfo beanInfo; /** * Property descriptors from the beanInfo. */ private transient PropertyDescriptor[] descriptors; /** * Property editors -- or null if the property can't be edited. Unused if * customizerClass==null. */ private transient 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<String, Object> propertyMap; /** * @deprecated only for use by test code */ @Deprecated public GenericTestBeanCustomizer(){ log.warn("Constructor only intended for use in testing"); // $NON-NLS-1$ } /** * 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].isExpert() && !JMeterUtils.isExpertMode()) || 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); } } if(propertyEditor instanceof TestBeanPropertyEditor) { ((TestBeanPropertyEditor)propertyEditor).setDescriptor(descriptors[i]); } if (propertyEditor.getCustomEditor() instanceof JScrollPane) { scrollerCount++; } editors[i] = propertyEditor; // Initialize the editor with the provided default value or null: setEditorValue(i, descriptors[i].getValue(DEFAULT)); } // Obtain message formats: propertyFieldLabelMessage = new MessageFormat(JMeterUtils.getResString("property_as_field_label")); //$NON-NLS-1$ propertyToolTipMessage = new MessageFormat(JMeterUtils.getResString("property_tool_tip")); //$NON-NLS-1$ // 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 { editors[i].setValue(value); } /** * {@inheritDoc} * @param map must be an instance of Map<String, Object> */ @SuppressWarnings("unchecked") public void setObject(Object map) { propertyMap = (Map<String, Object>) 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. // (MS Note) Can't bail out - newly create elements have blank // values and must get the defaults. // Also, when loading previous versions of jmeter test scripts, // some values // may not be right, and should get default values - MS // 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; 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 || customEditor instanceof JScrollPane) { // 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; 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: cp.weighty = 0.0001; add(Box.createHorizontalStrut(0), cp); } 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; } 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<PropertyDescriptor>, Serializable { private static final long serialVersionUID = 240L; public 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; } } /** * Save values from the GUI fields into the property map */ void saveGuiFields() { for (int i = 0; i < editors.length; i++) { PropertyEditor propertyEditor=editors[i]; // might be null (e.g. in testing) if (propertyEditor != null) { Object value = propertyEditor.getValue(); String name = descriptors[i].getName(); if (value == null) { propertyMap.remove(name); if (log.isDebugEnabled()) { log.debug("Unset " + name); } } else { propertyMap.put(name, value); if (log.isDebugEnabled()) { log.debug("Set " + name + "= " + value); } } } } } void clearGuiFields() { for (int i = 0; i < editors.length; i++) { PropertyEditor propertyEditor=editors[i]; // might be null (e.g. in testing) if (propertyEditor != null) { try { if (propertyEditor instanceof WrapperEditor){ WrapperEditor we = (WrapperEditor) propertyEditor; String tags[]=we.getTags(); if (tags != null && tags.length > 0) { we.setAsText(tags[0]); } else { we.setValue(""); } } else if (propertyEditor instanceof ComboStringEditor) { ComboStringEditor cse = (ComboStringEditor) propertyEditor; cse.setAsText(cse.getInitialEditValue()); } else { propertyEditor.setAsText(""); } } catch (IllegalArgumentException ex){ log.error("Failed to set field "+descriptors[i].getName(),ex); } } } } } Other JMeter examples (source code examples)Here is a short list of links related to this JMeter GenericTestBeanCustomizer.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.