| /* |
| * 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.gui; |
| |
| import static org.apache.jmeter.util.JMeterUtils.labelFor; |
| import static org.apiguardian.api.API.Status.DEPRECATED; |
| import static org.apiguardian.api.API.Status.EXPERIMENTAL; |
| import static org.apiguardian.api.API.Status.INTERNAL; |
| |
| import java.awt.Component; |
| import java.awt.Container; |
| import java.util.Locale; |
| |
| import javax.swing.BorderFactory; |
| import javax.swing.JComponent; |
| import javax.swing.JLabel; |
| import javax.swing.JPanel; |
| import javax.swing.JScrollPane; |
| import javax.swing.JTextArea; |
| import javax.swing.JTextField; |
| import javax.swing.border.Border; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.jmeter.gui.util.VerticalPanel; |
| import org.apache.jmeter.testelement.TestElement; |
| import org.apache.jmeter.testelement.TestElementSchema; |
| import org.apache.jmeter.util.JMeterUtils; |
| import org.apache.jmeter.visualizers.Printable; |
| import org.apache.jorphan.gui.JFactory; |
| import org.apiguardian.api.API; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import net.miginfocom.swing.MigLayout; |
| |
| /** |
| * This abstract class takes care of the most basic functions necessary to |
| * create a viable JMeter GUI component. It extends JPanel and implements |
| * JMeterGUIComponent. This abstract class is, in turn, extended by several |
| * other abstract classes that create different classes of GUI components for |
| * JMeter (Visualizers, Timers, Samplers, Modifiers, Controllers, etc). |
| * |
| * @see org.apache.jmeter.gui.JMeterGUIComponent |
| * @see org.apache.jmeter.config.gui.AbstractConfigGui |
| * @see org.apache.jmeter.assertions.gui.AbstractAssertionGui |
| * @see org.apache.jmeter.control.gui.AbstractControllerGui |
| * @see org.apache.jmeter.timers.gui.AbstractTimerGui |
| * @see org.apache.jmeter.visualizers.gui.AbstractVisualizer |
| * @see org.apache.jmeter.samplers.gui.AbstractSamplerGui |
| * |
| */ |
| public abstract class AbstractJMeterGuiComponent extends JPanel implements JMeterGUIComponent, Printable { |
| private static final long serialVersionUID = 241L; |
| |
| /** Logging */ |
| private static final Logger log = LoggerFactory.getLogger(AbstractJMeterGuiComponent.class); |
| |
| /** Flag indicating whether this component is enabled. */ |
| private boolean enabled = true; |
| |
| /** |
| * A GUI panel containing the name of this component. |
| * @deprecated use {@link #getName()} or {@link AbstractJMeterGuiComponent#createTitleLabel()} for better alignment of the fields |
| **/ |
| @API(status = INTERNAL, since = "5.2.0") |
| @Deprecated |
| @SuppressWarnings("DeprecatedIsStillUsed") |
| protected NamePanel namePanel; |
| |
| private final JTextArea commentField = JFactory.tabMovesFocus(new JTextArea()); |
| |
| /** |
| * Stores a collection of property editors, so GuiCompoenent can have default implementations that |
| * update the UI fields based on {@link TestElement} properties and vice versa. |
| */ |
| @API(status = EXPERIMENTAL, since = "5.6.3") |
| protected final BindingGroup bindingGroup = new BindingGroup(); |
| |
| /** |
| * When constructing a new component, this takes care of basic tasks like |
| * setting up the Name Panel and assigning the class's static label as the |
| * name to start. |
| */ |
| protected AbstractJMeterGuiComponent() { |
| namePanel = new NamePanel(); |
| init(); |
| } |
| |
| /** |
| * Provides a default implementation for setting the name property. It's unlikely |
| * developers will need to override. |
| */ |
| @Override |
| public void setName(String name) { |
| namePanel.setName(name); |
| } |
| |
| /** |
| * Provides a default implementation for setting the comment property. It's |
| * unlikely developers will need to override. |
| * |
| * @param comment |
| * The comment for the property |
| */ |
| public void setComment(String comment) { |
| commentField.setText(comment); |
| } |
| |
| /** |
| * Provides a default implementation for the enabled property. It's unlikely |
| * developers will need to override. |
| */ |
| @Override |
| public boolean isEnabled() { |
| return enabled; |
| } |
| |
| /** |
| * Provides a default implementation for the enabled property. It's unlikely |
| * developers will need to override. |
| */ |
| @Override |
| public void setEnabled(boolean enabled) { |
| log.debug("Setting enabled: {}", enabled); |
| this.enabled = enabled; |
| } |
| |
| /** |
| * Provides a default implementation for the name property. It's unlikely |
| * developers will need to override. |
| */ |
| @Override |
| public String getName() { |
| NamePanel namePanel = getNamePanel(); |
| // Check is mandatory because some LAFs (Nimbus) call getName() from |
| // super constructor (so can happen before namePanel field is initialized) |
| if (namePanel != null) { |
| return namePanel.getName(); |
| } |
| return ""; // $NON-NLS-1$ |
| } |
| |
| /** |
| * Provides a default implementation for the comment property. It's unlikely |
| * developers will need to override. |
| * |
| * @return The comment for the property |
| */ |
| public String getComment() { |
| return commentField.getText(); |
| } |
| |
| /** |
| * Provides the Name Panel for extending classes. Extending classes are free |
| * to place it as desired within the component, or not at all. Most |
| * components place the NamePanel automatically by calling |
| * {@link #makeTitlePanel()} instead of directly calling this method. |
| * |
| * @return a NamePanel containing the name of this component |
| * @deprecated use {@link #getName()} or {@link AbstractJMeterGuiComponent#createTitleLabel()} for better alignment of the fields |
| */ |
| @API(status = DEPRECATED, since = "5.2.0") |
| @Deprecated |
| protected NamePanel getNamePanel() { |
| return namePanel; |
| } |
| |
| /** |
| * Provides a label containing the title for the component. Subclasses |
| * typically place this label at the top of their GUI. The title is set to |
| * the name returned from the component's |
| * {@link JMeterGUIComponent#getStaticLabel() getStaticLabel()} method. Most |
| * components place this label automatically by calling |
| * {@link #makeTitlePanel()} instead of directly calling this method. |
| * |
| * @return a JLabel which subclasses can add to their GUI |
| */ |
| protected Component createTitleLabel() { |
| return JFactory.big(new JLabel(getStaticLabel())); |
| } |
| |
| /** |
| * A newly created gui component can be initialized with the contents of a |
| * Test Element object by calling this method. The component is responsible |
| * for querying the Test Element object for the relevant information to |
| * display in its GUI. |
| * <p> |
| * AbstractJMeterGuiComponent provides a partial implementation of this |
| * method, setting the name of the component and its enabled status. |
| * Subclasses should override this method, performing their own |
| * configuration as needed, but also calling this super-implementation. |
| * |
| * @param element |
| * the TestElement to configure |
| */ |
| @Override |
| public void configure(TestElement element) { |
| setName(element.getName()); |
| enabled = element.isEnabled(); |
| commentField.setText(element.getComment()); |
| bindingGroup.updateUi(element); |
| } |
| |
| /** |
| * Provides a default implementation that resets the name field to the value of |
| * getStaticLabel(), reset comment and sets enabled to true. Your GUI may need more things |
| * cleared, in which case you should override, clear the extra fields, and |
| * still call super.clearGui(). |
| */ |
| @Override |
| public void clearGui() { |
| initGui(); |
| enabled = true; |
| } |
| |
| private void initGui() { |
| setName(getStaticLabel()); |
| commentField.setText(""); |
| } |
| |
| private void init() { |
| initGui(); |
| } |
| |
| @Override |
| @API(status = EXPERIMENTAL, since = "5.6.3") |
| public void modifyTestElement(TestElement element) { |
| JMeterGUIComponent.super.modifyTestElement(element); |
| modifyTestElementEnabledAndComment(element); |
| bindingGroup.updateElement(element); |
| } |
| |
| /** |
| * This provides a convenience for extenders when they implement the |
| * {@link JMeterGUIComponent#modifyTestElement(TestElement)} method. This |
| * method will set the name, gui class, and test class for the created Test |
| * Element. It should be called by every extending class when |
| * creating/modifying Test Elements, as that will best assure consistent |
| * behavior. |
| * <p>Deprecation notice: most likely you do not need the method, and you should |
| * override {@link #modifyTestElement(TestElement)} instead</p> |
| * |
| * @param mc |
| * the TestElement being created. |
| */ |
| @API(status = DEPRECATED, since = "5.6.3") |
| protected void configureTestElement(TestElement mc) { |
| mc.setName(StringUtils.defaultIfEmpty(getName(), null)); |
| TestElementSchema schema = TestElementSchema.INSTANCE; |
| mc.set(schema.getGuiClass(), getClass()); |
| mc.set(schema.getTestClass(), mc.getClass()); |
| modifyTestElementEnabledAndComment(mc); |
| } |
| |
| /** |
| * Assigns basic fields from UI to the test element: name, comments, gui class, and the registered editors. |
| * |
| * @param mc test element |
| */ |
| private void modifyTestElementEnabledAndComment(TestElement mc) { |
| // This stores the state of the TestElement |
| log.debug("setting element to enabled: {}", enabled); |
| // We can skip storing "enabled" state if it's true, as it's default value. |
| // JMeter removes disabled elements early from the tree, so configuration elements |
| // with enabled=false (~HTTP Request Defaults) can't unexpectedly override the regular ones |
| // like HTTP Request. |
| mc.set(TestElementSchema.INSTANCE.getEnabled(), enabled ? null : Boolean.FALSE); |
| // Note: we can't use editors for "comments" as getComments() is not a final method, so plugins might |
| // override it and provide a different implementation. |
| mc.setComment(StringUtils.defaultIfEmpty(getComment(), null)); |
| } |
| |
| /** |
| * Create a standard title section for JMeter components. This includes the |
| * title for the component and the Name Panel allowing the user to change |
| * the name for the component. This method is typically added to the top of |
| * the component at the beginning of the component's init method. |
| * |
| * @return a panel containing the component title and name panel |
| */ |
| protected Container makeTitlePanel() { |
| JPanel titlePanel = new JPanel(new MigLayout("fillx, wrap 2, insets 0", "[][fill,grow]")); |
| titlePanel.add(createTitleLabel(), "span 2"); |
| |
| JTextField nameField = namePanel.getNameField(); |
| titlePanel.add(labelFor(nameField, "name")); |
| titlePanel.add(nameField); |
| |
| titlePanel.add(labelFor(nameField, "testplan_comments")); |
| commentField.setWrapStyleWord(true); |
| commentField.setLineWrap(true); |
| titlePanel.add(commentField); |
| |
| // Note: VerticalPanel has a workaround for Box layout which aligns elements, so we can't |
| // use trivial JPanel. |
| // Extra wrapper is often required to ensure that further additions to the panel would be vertical |
| // For instance AbstractVisualizer adds "browse file" panel |
| // If it calls just ..add(browseFilePanel), then it will go to |
| return wrapTitlePanel(titlePanel); |
| } |
| |
| @API(status = EXPERIMENTAL, since = "5.2.0") |
| protected Container wrapTitlePanel(Container titlePanel) { |
| VerticalPanel vp = new VerticalPanel(); |
| vp.add(titlePanel); |
| return vp; |
| } |
| |
| /** |
| * Create a top-level Border which can be added to JMeter components. |
| * Components typically set this as their border in their init method. It |
| * simply provides a nice spacing between the GUI components used and the |
| * edges of the window in which they appear. |
| * |
| * @return a Border for JMeter components |
| */ |
| protected Border makeBorder() { |
| return BorderFactory.createEmptyBorder(10, 10, 5, 10); |
| } |
| |
| /** |
| * Create a scroll panel that sets it's preferred size to it's minimum size. |
| * Explicitly for scroll panes that live inside other scroll panes, or |
| * within containers that stretch components to fill the area they exist in. |
| * Use this for any component you would put in a scroll pane (such as |
| * TextAreas, tables, JLists, etc). It is here for convenience and to avoid |
| * duplicate code. JMeter displays best if you follow this custom. |
| * |
| * @param comp |
| * the component which should be placed inside the scroll pane |
| * @return a JScrollPane containing the specified component |
| */ |
| protected JScrollPane makeScrollPane(Component comp) { |
| JScrollPane pane = new JScrollPane(comp); |
| pane.setPreferredSize(pane.getMinimumSize()); |
| return pane; |
| } |
| |
| /** |
| * Create a scroll panel that sets it's preferred size to it's minimum size. |
| * Explicitly for scroll panes that live inside other scroll panes, or |
| * within containers that stretch components to fill the area they exist in. |
| * Use this for any component you would put in a scroll pane (such as |
| * TextAreas, tables, JLists, etc). It is here for convenience and to avoid |
| * duplicate code. JMeter displays best if you follow this custom. |
| * |
| * @see javax.swing.ScrollPaneConstants |
| * |
| * @param comp |
| * the component which should be placed inside the scroll pane |
| * @param verticalPolicy |
| * the vertical scroll policy |
| * @param horizontalPolicy |
| * the horizontal scroll policy |
| * @return a JScrollPane containing the specified component |
| */ |
| protected JScrollPane makeScrollPane(Component comp, int verticalPolicy, int horizontalPolicy) { |
| JScrollPane pane = new JScrollPane(comp, verticalPolicy, horizontalPolicy); |
| pane.setPreferredSize(pane.getMinimumSize()); |
| return pane; |
| } |
| |
| @Override |
| public String getStaticLabel() { |
| return JMeterUtils.getResString(getLabelResource()); |
| } |
| |
| /** |
| * Compute Anchor value to find reference in documentation for a particular component |
| * @return String anchor |
| */ |
| @Override |
| public String getDocAnchor() { |
| // Ensure we use default bundle |
| String label = JMeterUtils.getResString(getLabelResource(), new Locale("","")); |
| return label.replace(' ', '_'); |
| } |
| |
| /** |
| * Subclasses need to over-ride this method, if they wish to return |
| * something other than the Visualizer itself. |
| * |
| * @return this object |
| */ |
| @Override |
| public JComponent getPrintableComponent() { |
| return this; |
| } |
| } |