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: */
018:
019: package org.apache.jmeter.gui.util;
020:
021: import java.io.IOException;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.Iterator;
027: import java.util.LinkedList;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031:
032: import javax.swing.JMenu;
033: import javax.swing.JMenuItem;
034: import javax.swing.JPopupMenu;
035: import javax.swing.KeyStroke;
036: import javax.swing.MenuElement;
037:
038: import org.apache.jmeter.control.Controller;
039: import org.apache.jmeter.gui.GuiPackage;
040: import org.apache.jmeter.gui.JMeterGUIComponent;
041: import org.apache.jmeter.gui.action.ActionNames;
042: import org.apache.jmeter.gui.action.ActionRouter;
043: import org.apache.jmeter.gui.action.KeyStrokes;
044: import org.apache.jmeter.gui.tree.JMeterTreeNode;
045: import org.apache.jmeter.samplers.Sampler;
046: import org.apache.jmeter.testbeans.TestBean;
047: import org.apache.jmeter.testbeans.gui.TestBeanGUI;
048: import org.apache.jmeter.testelement.TestElement;
049: import org.apache.jmeter.testelement.TestPlan;
050: import org.apache.jmeter.testelement.WorkBench;
051: import org.apache.jmeter.util.JMeterUtils;
052: import org.apache.jmeter.visualizers.Printable;
053: import org.apache.jorphan.logging.LoggingManager;
054: import org.apache.jorphan.reflect.ClassFinder;
055: import org.apache.jorphan.util.JOrphanUtils;
056: import org.apache.log.Logger;
057:
058: public final class MenuFactory {
059: private static final Logger log = LoggingManager
060: .getLoggerForClass();
061:
062: /*
063: * Predefined strings for makeMenu().
064: * These are used as menu categories in the menuMap Hashmap,
065: * and also for resource lookup in messages.properties
066: */
067: public final static String TIMERS = "menu_timer"; //$NON-NLS-1$
068:
069: public final static String CONTROLLERS = "menu_logic_controller"; //$NON-NLS-1$
070:
071: public final static String SAMPLERS = "menu_generative_controller"; //$NON-NLS-1$
072:
073: public final static String CONFIG_ELEMENTS = "menu_config_element"; //$NON-NLS-1$
074:
075: public final static String POST_PROCESSORS = "menu_post_processors"; //$NON-NLS-1$
076:
077: public final static String PRE_PROCESSORS = "menu_pre_processors"; //$NON-NLS-1$
078:
079: public final static String ASSERTIONS = "menu_assertions"; //$NON-NLS-1$
080:
081: public final static String NON_TEST_ELEMENTS = "menu_non_test_elements"; //$NON-NLS-1$
082:
083: public final static String LISTENERS = "menu_listener"; //$NON-NLS-1$
084:
085: private static Map menuMap = new HashMap();
086:
087: private static Set elementsToSkip = new HashSet();
088:
089: // MENU_ADD_xxx - controls which items are in the ADD menu
090: // MENU_PARENT_xxx - controls which items are in the Insert Parent menu
091: private static final String[] MENU_ADD_CONTROLLER = new String[] {
092: MenuFactory.CONTROLLERS, MenuFactory.CONFIG_ELEMENTS,
093: MenuFactory.TIMERS, MenuFactory.PRE_PROCESSORS,
094: MenuFactory.SAMPLERS, MenuFactory.ASSERTIONS,
095: MenuFactory.POST_PROCESSORS, MenuFactory.LISTENERS, };
096:
097: private static final String[] MENU_PARENT_CONTROLLER = new String[] { MenuFactory.CONTROLLERS };
098:
099: private static final String[] MENU_ADD_SAMPLER = new String[] {
100: MenuFactory.CONFIG_ELEMENTS, MenuFactory.TIMERS,
101: MenuFactory.PRE_PROCESSORS, MenuFactory.ASSERTIONS,
102: MenuFactory.POST_PROCESSORS, MenuFactory.LISTENERS, };
103:
104: private static final String[] MENU_PARENT_SAMPLER = new String[] { MenuFactory.CONTROLLERS };
105:
106: private static List timers, controllers, samplers, configElements,
107: assertions, listeners, nonTestElements, postProcessors,
108: preProcessors;
109:
110: static {
111: try {
112: String[] classesToSkip = JOrphanUtils.split(JMeterUtils
113: .getPropDefault("not_in_menu", ""), ","); //$NON-NLS-1$
114: for (int i = 0; i < classesToSkip.length; i++) {
115: elementsToSkip.add(classesToSkip[i].trim());
116: }
117:
118: initializeMenus();
119: } catch (Throwable e) {
120: log.error("", e);
121: }
122: }
123:
124: /**
125: * Private constructor to prevent instantiation.
126: */
127: private MenuFactory() {
128: }
129:
130: public static void addEditMenu(JPopupMenu menu, boolean removable) {
131: addSeparator(menu);
132: if (removable) {
133: menu.add(makeMenuItem(JMeterUtils.getResString("cut"), //$NON-NLS-1$
134: "Cut", ActionNames.CUT, //$NON-NLS-1$
135: KeyStrokes.CUT));
136: }
137: menu.add(makeMenuItem(JMeterUtils.getResString("copy"), //$NON-NLS-1$
138: "Copy", ActionNames.COPY, //$NON-NLS-1$
139: KeyStrokes.COPY));
140: menu.add(makeMenuItem(JMeterUtils.getResString("paste"), //$NON-NLS-1$
141: "Paste", ActionNames.PASTE, //$NON-NLS-1$
142: KeyStrokes.PASTE));
143: menu.add(makeMenuItem(JMeterUtils.getResString("reset_gui"), //$NON-NLS-1$
144: "Reset", ActionNames.RESET_GUI //$NON-NLS-1$
145: ));
146: if (removable) {
147: menu.add(makeMenuItem(JMeterUtils.getResString("remove"), //$NON-NLS-1$
148: "Remove", ActionNames.REMOVE, //$NON-NLS-1$
149: KeyStrokes.REMOVE));
150: }
151: }
152:
153: public static void addPasteResetMenu(JPopupMenu menu) {
154: addSeparator(menu);
155: menu.add(makeMenuItem(JMeterUtils.getResString("paste"), //$NON-NLS-1$
156: "Paste", ActionNames.PASTE, //$NON-NLS-1$
157: KeyStrokes.PASTE));
158: menu.add(makeMenuItem(JMeterUtils.getResString("reset_gui"), //$NON-NLS-1$
159: "Reset", ActionNames.RESET_GUI //$NON-NLS-1$
160: ));
161: }
162:
163: public static void addFileMenu(JPopupMenu menu) {
164: addSeparator(menu);
165: menu.add(makeMenuItem(JMeterUtils.getResString("open"),// $NON-NLS-1$
166: "Open", ActionNames.OPEN));// $NON-NLS-1$
167: menu.add(makeMenuItem(JMeterUtils.getResString("menu_merge"),// $NON-NLS-1$
168: "Merge", ActionNames.MERGE));// $NON-NLS-1$
169: menu.add(makeMenuItem(JMeterUtils.getResString("save_as"),// $NON-NLS-1$
170: "Save As", ActionNames.SAVE_AS));// $NON-NLS-1$
171:
172: addSeparator(menu);
173: JMenuItem savePicture = makeMenuItem(JMeterUtils
174: .getResString("save_as_image"),// $NON-NLS-1$
175: "Save Image", ActionNames.SAVE_GRAPHICS,// $NON-NLS-1$
176: KeyStrokes.SAVE_GRAPHICS);
177: menu.add(savePicture);
178: if (!(GuiPackage.getInstance().getCurrentGui() instanceof Printable)) {
179: savePicture.setEnabled(false);
180: }
181:
182: JMenuItem savePictureAll = makeMenuItem(JMeterUtils
183: .getResString("save_as_image_all"),// $NON-NLS-1$
184: "Save Image All", ActionNames.SAVE_GRAPHICS_ALL,// $NON-NLS-1$
185: KeyStrokes.SAVE_GRAPHICS_ALL);
186: menu.add(savePictureAll);
187:
188: addSeparator(menu);
189:
190: JMenuItem disabled = makeMenuItem(JMeterUtils
191: .getResString("disable"),// $NON-NLS-1$
192: "Disable", ActionNames.DISABLE);// $NON-NLS-1$
193: JMenuItem enabled = makeMenuItem(JMeterUtils
194: .getResString("enable"),// $NON-NLS-1$
195: "Enable", ActionNames.ENABLE);// $NON-NLS-1$
196: boolean isEnabled = GuiPackage.getInstance().getTreeListener()
197: .getCurrentNode().isEnabled();
198: if (isEnabled) {
199: disabled.setEnabled(true);
200: enabled.setEnabled(false);
201: } else {
202: disabled.setEnabled(false);
203: enabled.setEnabled(true);
204: }
205: menu.add(enabled);
206: menu.add(disabled);
207: addSeparator(menu);
208: menu.add(makeMenuItem(JMeterUtils.getResString("help"), // $NON-NLS-1$
209: "Help", ActionNames.HELP));// $NON-NLS-1$
210: }
211:
212: public static JMenu makeMenus(String[] categories, String label,
213: String actionCommand) {
214: JMenu addMenu = new JMenu(label);
215: for (int i = 0; i < categories.length; i++) {
216: addMenu.add(makeMenu(categories[i], actionCommand));
217: }
218: return addMenu;
219: }
220:
221: public static JPopupMenu getDefaultControllerMenu() {
222: JPopupMenu pop = new JPopupMenu();
223: pop.add(MenuFactory.makeMenus(MENU_ADD_CONTROLLER, JMeterUtils
224: .getResString("add"),// $NON-NLS-1$
225: ActionNames.ADD));
226: pop.add(makeMenus(MENU_PARENT_CONTROLLER, JMeterUtils
227: .getResString("insert_parent"),// $NON-NLS-1$
228: ActionNames.ADD_PARENT));
229: MenuFactory.addEditMenu(pop, true);
230: MenuFactory.addFileMenu(pop);
231: return pop;
232: }
233:
234: public static JPopupMenu getDefaultSamplerMenu() {
235: JPopupMenu pop = new JPopupMenu();
236: pop.add(MenuFactory.makeMenus(MENU_ADD_SAMPLER, JMeterUtils
237: .getResString("add"),// $NON-NLS-1$
238: ActionNames.ADD));
239: pop.add(makeMenus(MENU_PARENT_SAMPLER, JMeterUtils
240: .getResString("insert_parent"),// $NON-NLS-1$
241: ActionNames.ADD_PARENT));
242: MenuFactory.addEditMenu(pop, true);
243: MenuFactory.addFileMenu(pop);
244: return pop;
245: }
246:
247: public static JPopupMenu getDefaultConfigElementMenu() {
248: JPopupMenu pop = new JPopupMenu();
249: MenuFactory.addEditMenu(pop, true);
250: MenuFactory.addFileMenu(pop);
251: return pop;
252: }
253:
254: public static JPopupMenu getDefaultVisualizerMenu() {
255: JPopupMenu pop = new JPopupMenu();
256: MenuFactory.addEditMenu(pop, true);
257: MenuFactory.addFileMenu(pop);
258: return pop;
259: }
260:
261: public static JPopupMenu getDefaultTimerMenu() {
262: JPopupMenu pop = new JPopupMenu();
263: MenuFactory.addEditMenu(pop, true);
264: MenuFactory.addFileMenu(pop);
265: return pop;
266: }
267:
268: public static JPopupMenu getDefaultAssertionMenu() {
269: JPopupMenu pop = new JPopupMenu();
270: MenuFactory.addEditMenu(pop, true);
271: MenuFactory.addFileMenu(pop);
272: return pop;
273: }
274:
275: public static JPopupMenu getDefaultExtractorMenu() {
276: JPopupMenu pop = new JPopupMenu();
277: MenuFactory.addEditMenu(pop, true);
278: MenuFactory.addFileMenu(pop);
279: return pop;
280: }
281:
282: /**
283: * Create a menu from a menu category.
284: *
285: * @param category - predefined string (used as key for menuMap HashMap and messages.properties lookup)
286: * @param actionCommand - predefined string, e.g. ActionNames.ADD
287: * @see org.apache.jmeter.gui.action.ActionNames
288: * @return the menu
289: */
290: public static JMenu makeMenu(String category, String actionCommand) {
291: return makeMenu((Collection) menuMap.get(category),
292: actionCommand, JMeterUtils.getResString(category));
293: }
294:
295: /**
296: * Create a menu from a collection of items.
297: *
298: * @param menuInfo - collection of MenuInfo items
299: * @param actionCommand - predefined string, e.g. ActionNames.ADD
300: * @see org.apache.jmeter.gui.action.ActionNames
301: * @param menuName
302: * @return the menu
303: */
304: public static JMenu makeMenu(Collection menuInfo,
305: String actionCommand, String menuName) {
306: Iterator iter = menuInfo.iterator();
307: JMenu menu = new JMenu(menuName);
308: while (iter.hasNext()) {
309: MenuInfo info = (MenuInfo) iter.next();
310: menu.add(makeMenuItem(info.label, info.className,
311: actionCommand));
312: }
313: return menu;
314: }
315:
316: public static void setEnabled(JMenu menu) {
317: if (menu.getSubElements().length == 0) {
318: menu.setEnabled(false);
319: }
320: }
321:
322: /**
323: * Create a single menu item
324: *
325: * @param label for the MenuItem
326: * @param name for the MenuItem
327: * @param actionCommand - predefined string, e.g. ActionNames.ADD
328: * @see org.apache.jmeter.gui.action.ActionNames
329: * @return the menu item
330: */
331: public static JMenuItem makeMenuItem(String label, String name,
332: String actionCommand) {
333: JMenuItem newMenuChoice = new JMenuItem(label);
334: newMenuChoice.setName(name);
335: newMenuChoice.addActionListener(ActionRouter.getInstance());
336: if (actionCommand != null) {
337: newMenuChoice.setActionCommand(actionCommand);
338: }
339:
340: return newMenuChoice;
341: }
342:
343: public static JMenuItem makeMenuItem(String label, String name,
344: String actionCommand, KeyStroke accel) {
345: JMenuItem item = makeMenuItem(label, name, actionCommand);
346: item.setAccelerator(accel);
347: return item;
348: }
349:
350: private static void initializeMenus() {
351: try {
352: List guiClasses = ClassFinder.findClassesThatExtend(
353: JMeterUtils.getSearchPaths(), new Class[] {
354: JMeterGUIComponent.class, TestBean.class });
355: timers = new LinkedList();
356: controllers = new LinkedList();
357: samplers = new LinkedList();
358: configElements = new LinkedList();
359: assertions = new LinkedList();
360: listeners = new LinkedList();
361: postProcessors = new LinkedList();
362: preProcessors = new LinkedList();
363: nonTestElements = new LinkedList();
364: menuMap.put(TIMERS, timers);
365: menuMap.put(ASSERTIONS, assertions);
366: menuMap.put(CONFIG_ELEMENTS, configElements);
367: menuMap.put(CONTROLLERS, controllers);
368: menuMap.put(LISTENERS, listeners);
369: menuMap.put(NON_TEST_ELEMENTS, nonTestElements);
370: menuMap.put(SAMPLERS, samplers);
371: menuMap.put(POST_PROCESSORS, postProcessors);
372: menuMap.put(PRE_PROCESSORS, preProcessors);
373: Collections.sort(guiClasses);
374: Iterator iter = guiClasses.iterator();
375: while (iter.hasNext()) {
376: String name = (String) iter.next();
377:
378: /*
379: * JMeterTreeNode and TestBeanGUI are special GUI classes, and
380: * aren't intended to be added to menus
381: *
382: * TODO: find a better way of checking this
383: */
384: if (name.endsWith("JMeterTreeNode") // $NON-NLS-1$
385: || name.endsWith("TestBeanGUI")) {// $NON-NLS-1$
386: continue;// Don't try to instantiate these
387: }
388:
389: JMeterGUIComponent item;
390: try {
391: Class c = Class.forName(name);
392: if (TestBean.class.isAssignableFrom(c)) {
393: item = new TestBeanGUI(c);
394: } else {
395: item = (JMeterGUIComponent) c.newInstance();
396: }
397: } catch (NoClassDefFoundError e) {
398: log.warn("Missing jar? Could not create " + name
399: + ". " + e);
400: continue;
401: } catch (Throwable e) {
402: log.warn("Could not instantiate " + name, e);
403: continue;
404: }
405: if (elementsToSkip.contains(name)
406: || elementsToSkip.contains(item
407: .getStaticLabel())) {
408: log.info("Skipping " + name);
409: continue;
410: } else {
411: elementsToSkip.add(name);
412: }
413: Collection categories = item.getMenuCategories();
414: if (categories == null) {
415: log.debug(name + " participates in no menus.");
416: continue;
417: }
418: if (categories.contains(TIMERS)) {
419: timers
420: .add(new MenuInfo(item.getStaticLabel(),
421: name));
422: }
423:
424: if (categories.contains(POST_PROCESSORS)) {
425: postProcessors.add(new MenuInfo(item
426: .getStaticLabel(), name));
427: }
428:
429: if (categories.contains(PRE_PROCESSORS)) {
430: preProcessors.add(new MenuInfo(item
431: .getStaticLabel(), name));
432: }
433:
434: if (categories.contains(CONTROLLERS)) {
435: controllers.add(new MenuInfo(item.getStaticLabel(),
436: name));
437: }
438:
439: if (categories.contains(SAMPLERS)) {
440: samplers.add(new MenuInfo(item.getStaticLabel(),
441: name));
442: }
443:
444: if (categories.contains(NON_TEST_ELEMENTS)) {
445: nonTestElements.add(new MenuInfo(item
446: .getStaticLabel(), name));
447: }
448:
449: if (categories.contains(LISTENERS)) {
450: listeners.add(new MenuInfo(item.getStaticLabel(),
451: name));
452: }
453:
454: if (categories.contains(CONFIG_ELEMENTS)) {
455: configElements.add(new MenuInfo(item
456: .getStaticLabel(), name));
457: }
458: if (categories.contains(ASSERTIONS)) {
459: assertions.add(new MenuInfo(item.getStaticLabel(),
460: name));
461: }
462:
463: }
464: } catch (IOException e) {
465: log.error("", e);
466: }
467: }
468:
469: private static void addSeparator(JPopupMenu menu) {
470: MenuElement[] elements = menu.getSubElements();
471: if ((elements.length > 0)
472: && !(elements[elements.length - 1] instanceof JPopupMenu.Separator)) {
473: menu.addSeparator();
474: }
475: }
476:
477: /**
478: * Determine whether or not nodes can be added to this parent.
479: *
480: * Used by Merge
481: *
482: * @param parentNode
483: * @param element - top-level test element to be added
484: *
485: * @return whether it is OK to add the element to this parent
486: */
487: public static boolean canAddTo(JMeterTreeNode parentNode,
488: TestElement element) {
489: JMeterTreeNode node = new JMeterTreeNode(element, null);
490: return canAddTo(parentNode, new JMeterTreeNode[] { node });
491: }
492:
493: /**
494: * Determine whether or not nodes can be added to this parent.
495: *
496: * Used by DragNDrop and Paste.
497: *
498: * @param parentNode
499: * @param nodes - array of nodes that are to be added
500: *
501: * @return whether it is OK to add the dragged nodes to this parent
502: */
503: public static boolean canAddTo(JMeterTreeNode parentNode,
504: JMeterTreeNode nodes[]) {
505: if (null == parentNode) {
506: return false;
507: }
508: if (foundClass(nodes, new Class[] { WorkBench.class })) {// Can't add a Workbench anywhere
509: return false;
510: }
511: if (foundClass(nodes, new Class[] { TestPlan.class })) {// Can't add a TestPlan anywhere
512: return false;
513: }
514: TestElement parent = parentNode.getTestElement();
515: if (parent instanceof WorkBench) {// allow everything else
516: return true;
517: }
518: if (parent instanceof TestPlan) {
519: if (foundClass(nodes, new Class[] { Sampler.class,
520: Controller.class }, // Samplers and Controllers need not apply ...
521: org.apache.jmeter.threads.ThreadGroup.class) // but ThreadGroup (Controller) is OK
522: ) {
523: return false;
524: }
525: return true;
526: }
527: // ThreadGroup is only allowed under a TestPlan
528: if (foundClass(
529: nodes,
530: new Class[] { org.apache.jmeter.threads.ThreadGroup.class })) {
531: return false;
532: }
533: if (parent instanceof Controller) {// Includes thread group; anything goes
534: return true;
535: }
536: if (parent instanceof Sampler) {// Samplers and Controllers need not apply ...
537: if (foundClass(nodes, new Class[] { Sampler.class,
538: Controller.class })) {
539: return false;
540: }
541: return true;
542: }
543: // All other
544: return false;
545: }
546:
547: // Is any node an instance of one of the classes?
548: private static boolean foundClass(JMeterTreeNode nodes[],
549: Class classes[]) {
550: for (int i = 0; i < nodes.length; i++) {
551: JMeterTreeNode node = nodes[i];
552: for (int j = 0; j < classes.length; j++) {
553: if (classes[j].isInstance(node.getUserObject())) {
554: return true;
555: }
556: }
557: }
558: return false;
559: }
560:
561: // Is any node an instance of one of the classes, but not an exception?
562: private static boolean foundClass(JMeterTreeNode nodes[],
563: Class classes[], Class except) {
564: for (int i = 0; i < nodes.length; i++) {
565: JMeterTreeNode node = nodes[i];
566: Object userObject = node.getUserObject();
567: if (!except.isInstance(userObject)) {
568: for (int j = 0; j < classes.length; j++) {
569: if (classes[j].isInstance(userObject)) {
570: return true;
571: }
572: }
573: }
574: }
575: return false;
576: }
577:
578: // Methods used for Test cases
579: static int menuMap_size() {
580: return menuMap.size();
581: }
582:
583: static int assertions_size() {
584: return assertions.size();
585: }
586:
587: static int configElements_size() {
588: return configElements.size();
589: }
590:
591: static int controllers_size() {
592: return controllers.size();
593: }
594:
595: static int listeners_size() {
596: return listeners.size();
597: }
598:
599: static int nonTestElements_size() {
600: return nonTestElements.size();
601: }
602:
603: static int postProcessors_size() {
604: return postProcessors.size();
605: }
606:
607: static int preProcessors_size() {
608: return preProcessors.size();
609: }
610:
611: static int samplers_size() {
612: return samplers.size();
613: }
614:
615: static int timers_size() {
616: return timers.size();
617: }
618:
619: static int elementsToSkip_size() {
620: return elementsToSkip.size();
621: }
622: }
|