001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jmeter.testbeans.gui;
018:
019: import java.awt.Component;
020: import java.awt.event.ItemEvent;
021: import java.awt.event.ItemListener;
022: import java.beans.PropertyEditorSupport;
023: import java.util.Arrays;
024:
025: import javax.swing.DefaultComboBoxModel;
026: import javax.swing.JComboBox;
027: import javax.swing.text.JTextComponent;
028:
029: import org.apache.jmeter.util.JMeterUtils;
030:
031: /**
032: * This class implements a property editor for possibly null String properties
033: * that supports custom editing (i.e.: provides a GUI component) based on a
034: * combo box.
035: * <p>
036: * The provided GUI is a combo box with:
037: * <ul>
038: * <li>An option for "undefined" (corresponding to the null value), unless the
039: * <b>noUndefined</b> property is set.
040: * <li>An option for each value in the <b>tags</b> property.
041: * <li>The possibility to write your own value, unless the <b>noEdit</b>
042: * property is set.
043: * </ul>
044: *
045: */
046: class ComboStringEditor extends PropertyEditorSupport implements
047: ItemListener {
048:
049: /**
050: * The list of options to be offered by this editor.
051: */
052: private String[] tags = new String[0];
053:
054: /**
055: * True iif the editor should not accept (nor produce) a null value.
056: */
057: private boolean noUndefined = false;
058:
059: /**
060: * True iif the editor should not accept (nor produce) any non-null values
061: * different from the provided tags.
062: */
063: private boolean noEdit = false;
064:
065: /**
066: * The edited property's default value.
067: */
068: private String initialEditValue;
069:
070: private JComboBox combo;
071:
072: private DefaultComboBoxModel model;
073:
074: private boolean startingEdit = false;
075:
076: /*
077: * True iif we're currently processing an event triggered by the user
078: * selecting the "Edit" option. Used to prevent reverting the combo to
079: * non-editable during processing of secondary events.
080: */
081:
082: // Needs to be visible to test cases
083: static final Object UNDEFINED = new UniqueObject(JMeterUtils
084: .getResString("property_undefined")); //$NON-NLS-1$
085:
086: private static final Object EDIT = new UniqueObject(JMeterUtils
087: .getResString("property_edit")); //$NON-NLS-1$
088:
089: ComboStringEditor() {
090: // Create the combo box we will use to edit this property:
091:
092: model = new DefaultComboBoxModel();
093: model.addElement(UNDEFINED);
094: model.addElement(EDIT);
095:
096: combo = new JComboBox(model);
097: combo.addItemListener(this );
098: combo.setEditable(false);
099: }
100:
101: /*
102: * (non-Javadoc)
103: *
104: * @see java.beans.PropertyEditor#supportsCustomEditor()
105: */
106: public boolean supportsCustomEditor() {
107: return true;
108: }
109:
110: /*
111: * (non-Javadoc)
112: *
113: * @see java.beans.PropertyEditor#getCustomEditor()
114: */
115: public Component getCustomEditor() {
116: return combo;
117: }
118:
119: /*
120: * (non-Javadoc)
121: *
122: * @see java.beans.PropertyEditor#getValue()
123: */
124: public Object getValue() {
125: return getAsText();
126: }
127:
128: /*
129: * (non-Javadoc)
130: *
131: * @see java.beans.PropertyEditor#getAsText()
132: */
133: public String getAsText() {
134: Object value = combo.getSelectedItem();
135:
136: if (value == UNDEFINED)
137: return null;
138: else
139: return (String) value;
140: }
141:
142: /*
143: * (non-Javadoc)
144: *
145: * @see java.beans.PropertyEditor#setValue()
146: */
147: public void setValue(Object value) {
148: setAsText((String) value);
149: }
150:
151: /*
152: * (non-Javadoc)
153: *
154: * @see java.beans.PropertyEditor#setAsText()
155: */
156: public void setAsText(String value) {
157: combo.setEditable(true);
158:
159: if (value == null)
160: combo.setSelectedItem(UNDEFINED);
161: else
162: combo.setSelectedItem(value);
163:
164: if (!startingEdit && combo.getSelectedIndex() >= 0) {
165: combo.setEditable(false);
166: }
167: }
168:
169: /*
170: * (non-Javadoc)
171: *
172: * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
173: */
174: public void itemStateChanged(ItemEvent e) {
175: if (e.getStateChange() == ItemEvent.SELECTED) {
176: if (e.getItem() == EDIT) {
177: startingEdit = true;
178: startEditing();
179: startingEdit = false;
180: } else {
181: if (!startingEdit && combo.getSelectedIndex() >= 0) {
182: combo.setEditable(false);
183: }
184:
185: firePropertyChange();
186: }
187: }
188: }
189:
190: private void startEditing() {
191: JTextComponent textField = (JTextComponent) combo.getEditor()
192: .getEditorComponent();
193:
194: combo.setEditable(true);
195:
196: textField.requestFocus();
197:
198: String text = initialEditValue;
199: if (initialEditValue == null)
200: text = ""; // will revert to last valid value if invalid
201:
202: combo.setSelectedItem(text);
203:
204: int i = text.indexOf("${}");
205: if (i != -1)
206: textField.setCaretPosition(i + 2);
207: else
208: textField.selectAll();
209: }
210:
211: public String getInitialEditValue() {
212: return initialEditValue;
213: }
214:
215: public boolean getNoEdit() {
216: return noEdit;
217: }
218:
219: public boolean getNoUndefined() {
220: return noUndefined;
221: }
222:
223: public String[] getTags() {
224: return tags;
225: }
226:
227: /**
228: * @param object
229: */
230: public void setInitialEditValue(String object) {
231: initialEditValue = object;
232: }
233:
234: /**
235: * @param b
236: */
237: public void setNoEdit(boolean b) {
238: if (noEdit == b)
239: return;
240: noEdit = b;
241:
242: if (noEdit)
243: model.removeElement(EDIT);
244: else
245: model.addElement(EDIT);
246: }
247:
248: /**
249: * @param b
250: */
251: public void setNoUndefined(boolean b) {
252: if (noUndefined == b)
253: return;
254: noUndefined = b;
255:
256: if (noUndefined)
257: model.removeElement(UNDEFINED);
258: else
259: model.insertElementAt(UNDEFINED, 0);
260: }
261:
262: /**
263: * @param strings
264: */
265: public void setTags(String[] strings) {
266: if (Arrays.equals(tags, strings))
267: return;
268:
269: for (int i = 0; i < tags.length; i++)
270: model.removeElement(tags[i]);
271:
272: tags = strings == null ? new String[0] : strings;
273:
274: int b = noUndefined ? 0 : 1; // base index for tags
275: for (int i = 0; i < tags.length; i++)
276: model.insertElementAt(tags[i], b + i);
277: }
278:
279: /**
280: * This is a funny hack: if you use a plain String, entering the text of the
281: * string in the editor will make the combo revert to that option -- which
282: * actually amounts to making that string 'reserved'. I preferred to avoid
283: * this by using a different type having a controlled .toString().
284: */
285: private static class UniqueObject {
286: private String s;
287:
288: UniqueObject(String s) {
289: this .s = s;
290: }
291:
292: public String toString() {
293: return s;
294: }
295: }
296: }
|