generictestbeancustomizer.java

来自「测试工具」· Java 代码 · 共 614 行 · 第 1/2 页

JAVA
614
字号
/*
 * 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</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>
 * 
 * @author <a href="mailto:jsalvata@apache.org">Jordi Salvat i Alabart</a>
 */
public class GenericTestBeanCustomizer extends JPanel implements SharedCustomizer {
	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 propertyMap;

    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.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);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?