0001: /*
0002: * Jacareto Copyright (c) 2002-2005
0003: * Applied Computer Science Research Group, Darmstadt University of
0004: * Technology, Institute of Mathematics & Computer Science,
0005: * Ludwigsburg University of Education, and Computer Based
0006: * Learning Research Group, Aachen University. All rights reserved.
0007: *
0008: * Jacareto is free software; you can redistribute it and/or
0009: * modify it under the terms of the GNU General Public
0010: * License as published by the Free Software Foundation; either
0011: * version 2 of the License, or (at your option) any later version.
0012: *
0013: * Jacareto is distributed in the hope that it will be useful,
0014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016: * General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU General Public
0019: * License along with Jacareto; if not, write to the Free
0020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
0021: *
0022: */
0023:
0024: package jacareto.comp;
0025:
0026: import jacareto.catching.EventListener;
0027: import jacareto.catching.PopUpCatcherObserver;
0028: import jacareto.comp.glasspanes.GlassPaneManager;
0029: import jacareto.comp.select.ComponentSelection;
0030: import jacareto.system.Environment;
0031: import jacareto.system.EnvironmentMember;
0032: import jacareto.system.Language;
0033: import jacareto.toolkit.EnhancedHashtable;
0034: import jacareto.toolkit.PriorityList;
0035: import jacareto.toolkit.StringToolkit;
0036: import jacareto.toolkit.WindowUtilities;
0037:
0038: import org.apache.log4j.Logger;
0039:
0040: import java.awt.Component;
0041: import java.awt.Dialog;
0042: import java.awt.Frame;
0043: import java.awt.Window;
0044:
0045: import java.lang.reflect.Constructor;
0046: import java.lang.reflect.InvocationTargetException;
0047:
0048: import java.util.Collection;
0049: import java.util.Enumeration;
0050: import java.util.Hashtable;
0051: import java.util.Iterator;
0052: import java.util.Set;
0053: import java.util.TreeSet;
0054: import java.util.Vector;
0055:
0056: import javax.swing.JPopupMenu;
0057: import javax.swing.SwingUtilities;
0058:
0059: /**
0060: * This class provides access mechanisms and utilities for sets of component trees. You can add
0061: * whole component trees by calling the <code>addTree()</code>-methods. If a component has been
0062: * added, it is <code>known</code>. Known components can be <code>included</code> or not. Excluded
0063: * components will never be considered by this instance. If a component is included, it can be
0064: * enabled for capturing or not. That means, only events on known, included and enabled components
0065: * will be handled during a capture process. The different attributes have the following sense:
0066: * all components which are new (because they have popped up recently) are not known by the
0067: * <code>Components</code> instance. They must be added first. Those components can belong to the
0068: * capture&replay application itself and should therefore be ignored (you do not want to
0069: * capture events on the capture&replay application like CleverPHL). Those components must be
0070: * excluded. Some components of the application whose events you are capturing should perhaps not
0071: * be considered during the capture process. Those components are known, included, but disabled
0072: * for capturing.
0073: *
0074: * <p>
0075: * You can hide all known, included components by calling the method {@link #hideAllComponents()}.
0076: * It does not matter if the component is enabled or disabled; all known, included components will
0077: * be hidden. Before you call this method, you should call the method {@link #saveVisibility()} to
0078: * store the visibility status of all known, included components. After hiding them you can
0079: * restore the state before hiding by calling the method {@link #restoreVisibility()}.
0080: * </p>
0081: *
0082: * <p>
0083: * All components which are known and included will get a name. This name is the name of the
0084: * component's parent, succeeded by a dot, the name of the component delivered by its handler, and
0085: * a number surrounded by a prefix and a suffix string.
0086: * </p>
0087: *
0088: * @author Thomas Leonhard
0089: * @author <a href="mailto:cspannagel@web.de">Christian Spannagel</a> (Version 1.1)
0090: * @version 1.21
0091: */
0092: public class Components extends EnvironmentMember implements
0093: PopUpCatcherObserver {
0094: /** Load the component handlers from the customization at the beginning. */
0095: public static final int INIT_CUSTOM = 0;
0096:
0097: /** The components instance should have no handlers at the beginning. */
0098: public static final int INIT_EMPTY = 1;
0099:
0100: /** ATTRIBUTES */
0101: /** The hashtable which maps components to comp items (only included components). */
0102: private Hashtable compToItem;
0103:
0104: /** The hashtable which maps item names to comp items (only included components). */
0105: private Hashtable nameToItem;
0106:
0107: /** The hashtable of the included root components. */
0108: private Hashtable includedRoots;
0109:
0110: /** The hashtable of the excluded root components. */
0111: private Hashtable excludedRoots;
0112:
0113: /**
0114: * The hashtable of all known roots. The known roots should be the union of the included and
0115: * excluded roots.
0116: */
0117: private Hashtable knownRoots;
0118:
0119: /** Hashtable which maps old to new names. */
0120: private Hashtable renamedComponents;
0121:
0122: /** The hashtable which maps component names to user defined component names. */
0123: private Hashtable compNameToUserDefinedName;
0124:
0125: /** The hashtable which maps user defined names to component names. */
0126: private Hashtable userDefinedNameToCompName;
0127:
0128: /** The hashtable of duplicate user defined names. */
0129: private Hashtable duplicateUserDefinedNames;
0130:
0131: /** The component listener for events which are not dispatched by the AWTEventQueue. */
0132: private ComponentListener componentListener;
0133:
0134: /** The list of the component handlers. */
0135: private PriorityList compHandlers;
0136:
0137: /** Whether this instance should listen to window pop ups or not. */
0138: private boolean isListeningToPopUps;
0139:
0140: /** The number prefix. */
0141: private String numberPrefix;
0142:
0143: /** The number suffix. */
0144: private String numberSuffix;
0145:
0146: /** The collection of components listeners. */
0147: private Collection componentsListeners;
0148:
0149: /** Whether or not components events should be fired. */
0150: private boolean firesComponentsEvents;
0151:
0152: /** Whether or not debug messages should be printed. */
0153: private boolean isDebugPrinting;
0154:
0155: /** The glasspane compound manager. */
0156: private GlassPaneManager glassPaneManager;
0157:
0158: /** The component selection. */
0159: private ComponentSelection componentSelection;
0160:
0161: /** The component mode. */
0162: private ComponentMode mode;
0163:
0164: /** The component watcher. */
0165: private ComponentWatcher componentWatcher;
0166:
0167: /**
0168: * Creates a new Components instance. The component handlers are loaded by the given class
0169: * loader.
0170: *
0171: * @param env the environment.
0172: * @param init defines where to get the initial values from ({@link #INIT_CUSTOM} or {@link
0173: * #INIT_EMPTY})
0174: * @param classLoader the class loader which loads the component handlers
0175: */
0176: public Components(Environment env, int init, ClassLoader classLoader) {
0177: super (env);
0178: compToItem = new Hashtable();
0179: nameToItem = new Hashtable();
0180: includedRoots = new Hashtable();
0181: excludedRoots = new Hashtable();
0182: knownRoots = new Hashtable();
0183: renamedComponents = new Hashtable();
0184: userDefinedNameToCompName = new Hashtable();
0185: compNameToUserDefinedName = new Hashtable();
0186: duplicateUserDefinedNames = new Hashtable();
0187:
0188: mode = new ComponentMode(env);
0189:
0190: numberPrefix = customization.getString(
0191: "Components.Tree.NumberPrefix", "_(");
0192: numberSuffix = customization.getString(
0193: "Components.Tree.NumberSuffix", ")");
0194:
0195: compHandlers = new PriorityList();
0196:
0197: if (init == INIT_CUSTOM) {
0198: loadComponentHandlers(classLoader);
0199: }
0200:
0201: componentListener = new ComponentListener(env);
0202: setListeningToPopUps(true);
0203:
0204: componentsListeners = new Vector(5, 5);
0205: firesComponentsEvents = true;
0206: isDebugPrinting = true;
0207: glassPaneManager = new GlassPaneManager(env, this );
0208: componentSelection = new ComponentSelection(env, this );
0209: componentWatcher = new ComponentWatcher(env, this );
0210: }
0211:
0212: /**
0213: * Creates a new Components instance. The component handlers are loaded by the default class
0214: * loader.
0215: *
0216: * @param env the environment.
0217: * @param init defines where to get the initial values from ({@link #INIT_CUSTOM} or {@link
0218: * #INIT_EMPTY})
0219: */
0220: public Components(Environment env, int init) {
0221: this (env, init, null);
0222: }
0223:
0224: /**
0225: * Sets the component mode.
0226: *
0227: * @param mode the component mode.
0228: */
0229: public void setMode(ComponentMode mode) {
0230: this .mode = mode;
0231: }
0232:
0233: /**
0234: * Returns the component mode.
0235: *
0236: * @return the mode
0237: */
0238: public ComponentMode getMode() {
0239: return mode;
0240: }
0241:
0242: // ADDING Methods which add components
0243:
0244: /**
0245: * Adds the whole component tree the specified component belongs to. You can choose whether or
0246: * not the added components should be included. If they are included, you can specify whether
0247: * or not they should be enabled for capturing. If they are excluded, the value of
0248: * <code>isEnabled</code> will not be taken into account.
0249: *
0250: * @param component the component
0251: * @param isIncluded whether or not the components should be included
0252: * @param isEnabled <code>true</code> if all added components should be enabled, otherwise
0253: * <code>false</code>.
0254: */
0255: public synchronized void addTree(Component component,
0256: boolean isIncluded, boolean isEnabled) {
0257: if (component != null) {
0258: // get the root of the component's tree
0259: Component root = getRoot(component);
0260:
0261: if ((root != null) && root instanceof Window) {
0262: if (isDebugPrinting) {
0263: logger
0264: .debug(language
0265: .getString("Components.Msg.ComponentAndRoot")
0266: + " "
0267: + component.getClass().getName()
0268: + "; " + root.getClass().getName());
0269: }
0270:
0271: //
0272: // insert a root which is not know yet
0273: //
0274: if (!isKnown(root)) {
0275: // exclude jacareto components or components with ! isIncluded
0276: if (!isIncluded
0277: || StringToolkit
0278: .areEqual(
0279: root.getName(),
0280: getCustomization()
0281: .getString(
0282: "Components.JacaretoComponent",
0283: "JACARETO_COMPONENT"))) {
0284: excludedRoots.put(root, "");
0285:
0286: if (isDebugPrinting) {
0287: logger
0288: .debug(language
0289: .getString("Components.Msg.AddedComponentsExcluded")
0290: + " "
0291: + component.getClass());
0292: }
0293: }
0294: // include that root
0295: else {
0296: insertRootAndSubtree(root, isEnabled);
0297:
0298: if (!compToItem.containsKey(component)) {
0299: // component not reachable from root; insert it at the correct position
0300: // and with the attributes of its parent
0301: insertNotReachable(component);
0302: }
0303:
0304: if (isDebugPrinting) {
0305: logger
0306: .debug(language
0307: .getString("Components.Msg.AddedComponentsIncluded")
0308: + " ["
0309: + getName_Internal(component)
0310: + "]");
0311: }
0312:
0313: if (root instanceof Dialog) {
0314: setDialogModal((Dialog) root);
0315: }
0316: }
0317:
0318: knownRoots.put(root, "");
0319: }
0320: //
0321: // component is a new component of an already known root component
0322: // refresh the tree of the root component
0323: //
0324: else {
0325: if (isIncluded(root)) {
0326: if (isIncluded) {
0327: refreshTree(root);
0328:
0329: if (!compToItem.containsKey(component)) {
0330: // component not reachable from root; insert it at the correct position
0331: // and with the attributes of its parent
0332: insertNotReachable(component);
0333: } else {
0334: setSubTreeEnabled(component, isEnabled);
0335: }
0336:
0337: // a new component of an already known root component should be added
0338: if (isDebugPrinting) {
0339: logger
0340: .debug(language
0341: .getString("Components.Msg.RefreshedTree")
0342: + " ["
0343: + getName_Internal(component)
0344: + "]");
0345: }
0346: } else {
0347: // to be continued
0348: }
0349: }
0350: }
0351:
0352: fireComponentsEvent(new ComponentsEvent(this ,
0353: ComponentsEvent.TREE_CHANGED, null, null));
0354: }
0355: }
0356: }
0357:
0358: /**
0359: * Adds the whole component tree the specified component belongs to. Afterwards all added
0360: * components will be known, included and enabled for capturing.
0361: *
0362: * @param component the component
0363: */
0364: public synchronized void addTree(Component component) {
0365: addTree(component, true, true);
0366: }
0367:
0368: /**
0369: * Adds a component and its subtree as "included".
0370: *
0371: * @param root the root of the subtree to add
0372: * @param isEnabled whether or not the root and its subtree should be enabled for capturing
0373: */
0374: private void insertRootAndSubtree(Component root, boolean isEnabled) {
0375: ComponentHandler compHandler = getFirstComponentHandler(root);
0376:
0377: if (root instanceof Window) {
0378: glassPaneManager.createGlassPaneCompound((Window) root);
0379: }
0380:
0381: if (compHandler != null) {
0382: // add the whole component tree
0383: String rootName = "";
0384:
0385: if (mode.containsNewRootName() && (root instanceof Window)) {
0386: rootName = mode.getNewRootName(0);
0387: mode.removeNewRootName(0);
0388: } else {
0389: rootName = getNewName(root, compHandler, "");
0390: }
0391:
0392: if (nameToItem.containsKey(rootName)) {
0393: CompItem item = (CompItem) nameToItem.get(rootName);
0394: Component rootToRemove = item.getComponent();
0395: removeSubTree(rootToRemove);
0396: }
0397:
0398: addSubTree(root, compHandler, rootName);
0399:
0400: includedRoots.put(root, "");
0401: setSubTreeEnabled(root, isEnabled);
0402: }
0403: }
0404:
0405: /**
0406: * This method inserts a component which is not reachable from its parent. For this reason, the
0407: * method <code>refreshTree</code> called with the root of the component's tree has not found
0408: * the component. Therefore, we must insert the component "bottom up" and not "top down"
0409: *
0410: * @param component DOCUMENT ME!
0411: */
0412: private void insertNotReachable(Component component) {
0413: if (isDebugPrinting) {
0414: logger.debug(language
0415: .getString("Components.Msg.ComponentNotReachable")
0416: + " [" + component.getClass().getName() + "]");
0417: }
0418:
0419: ComponentHandler compHandler = getFirstComponentHandler(component);
0420: Component parent = compHandler.getParent(component);
0421:
0422: if (parent != null) {
0423: if (!compToItem.containsKey(parent)) {
0424: insertNotReachable(parent);
0425: } else {
0426: CompItem parentItem = (CompItem) compToItem.get(parent);
0427: String parentName = parentItem.getName();
0428: String compName = getNewName(component, compHandler,
0429: parentName + ".");
0430:
0431: boolean result = addSubTree(component, compHandler,
0432: compName);
0433:
0434: if (result == true) {
0435: setSubTreeEnabled(component, parentItem.isEnabled());
0436:
0437: Component[] parentChildren = new Component[parentItem
0438: .getChildrenCount() + 1];
0439: Component[] oldChildren = parentItem.getChildren();
0440:
0441: for (int i = 0; i < (parentChildren.length - 1); i++) {
0442: parentChildren[i] = oldChildren[i];
0443: }
0444:
0445: parentChildren[parentChildren.length - 1] = component;
0446: parentItem.setChildren(parentChildren);
0447: }
0448: }
0449: }
0450:
0451: if (isKnown(component) && isDebugPrinting) {
0452: logger.debug(language
0453: .getString("Components.Msg.NotReachableSuccess")
0454: + " [" + getName_Internal(component) + "]");
0455: } else {
0456: logger.debug(language
0457: .getString("Components.Msg.NotReachableFailed")
0458: + " [" + component.getClass().getName() + "]");
0459: }
0460: }
0461:
0462: /**
0463: * Adds a component and all subcomponents of this component. Just used for included components.
0464: * All added components will be included by default. This method returns a root tree node of
0465: * the whole subtree If a component is already included, it will be automatically removed
0466: * first.
0467: *
0468: * @param component the component
0469: * @param compHandler the component handler
0470: * @param name the name for the component
0471: *
0472: * @return the root tree node of this subtree
0473: */
0474: private boolean addSubTree(Component component,
0475: ComponentHandler compHandler, String name) {
0476: boolean result = false;
0477:
0478: // Removes known components which are known with another name
0479: if (isDebugPrinting) {
0480: logger.debug(language
0481: .getString("Components.Msg.AddingSubtree")
0482: + " [" + name + "]");
0483: }
0484:
0485: // CHANGED
0486: // For all other component types, too?
0487:
0488: /*if ((component instanceof JPopupMenu) && compToItem.containsKey(component) && ! getName_Internal(component).equals(name)) {
0489: System.out.println ("!!!!!!!! REMOVING SUBTREE");
0490: removeSubTree (component);
0491: }
0492:
0493: if (component instanceof JPopupMenu) {
0494: System.out.println ("YES: " + component.getClass().getName());
0495: }*/
0496: if (compHandler != null) {
0497: result = true;
0498:
0499: if (!compToItem.containsKey(component)
0500: || !getName_Internal(component).equals(name)) {
0501: addComponent(component, name, compHandler);
0502: }
0503:
0504: Component[] compChildren = compHandler
0505: .getChildren(component);
0506: int counter = 0;
0507:
0508: if ((compChildren != null) && (compChildren.length > 0)) {
0509: for (int i = 0; i < compChildren.length; i++) {
0510: Component child = compChildren[i];
0511: ComponentHandler childHandler = getFirstComponentHandler(compChildren[i]);
0512: String childName = getNewName(compChildren[i],
0513: compHandler, name + ".");
0514:
0515: addSubTree(child, childHandler, childName);
0516:
0517: counter++;
0518: }
0519: }
0520:
0521: CompItem item = ((CompItem) compToItem.get(component));
0522: item.setChildrenCount(counter);
0523: item.setChildren(compChildren);
0524:
0525: if (isDebugPrinting) {
0526: logger
0527: .debug(language
0528: .getString("Components.Msg.AddingSubtreeFinished")
0529: + " ["
0530: + getName_Internal(component)
0531: + "]");
0532: }
0533: } else {
0534: getLogger().warn(
0535: getLanguage().getString(
0536: "Components.Error.NoHandler")
0537: + ": " + component.getClass().getName());
0538: }
0539:
0540: return result;
0541: }
0542:
0543: /**
0544: * Adds a known, included component.
0545: *
0546: * @param component the component to add
0547: * @param componentName the name for the component
0548: * @param compHandler the component handler
0549: */
0550: private void addComponent(Component component,
0551: String componentName, ComponentHandler compHandler) {
0552: if (compToItem.containsKey(component)) {
0553: if (!getName(component).equals(componentName)) {
0554: // component has changed the location in the component tree
0555: // and must be renamed
0556: // CHANGED
0557: //System.out.println ("Removing subtree before adding: " + getName(component));
0558: //compToItem.remove(component);
0559: if (isDebugPrinting) {
0560: logger
0561: .debug(language
0562: .getString("Components.Msg.RenamingComponent")
0563: + " ["
0564: + getName(component)
0565: + "]["
0566: + componentName + "]");
0567: }
0568:
0569: renamedComponents
0570: .put(getName(component), componentName);
0571: removeComponent(component);
0572:
0573: //removeSubTree (component);
0574: }
0575: }
0576:
0577: // Just add the component if it has not been added before
0578: if (!compToItem.containsKey(component)) {
0579: CompItem item = new CompItem(env, component, componentName);
0580: compToItem.put(component, item);
0581: nameToItem.put(componentName, item);
0582:
0583: String userDefinedName = component.getName();
0584:
0585: if ((userDefinedName != null)
0586: && !userDefinedName.equals("")) {
0587: if (!userDefinedNameToCompName
0588: .containsKey(userDefinedName)) {
0589: userDefinedNameToCompName.put(component.getName(),
0590: componentName);
0591: compNameToUserDefinedName.put(componentName,
0592: component.getName());
0593: } else {
0594: duplicateUserDefinedNames.put(userDefinedName, "");
0595: logger
0596: .debug(language
0597: .getString("Components.Msg.DuplicateUserDefined")
0598: + " [" + userDefinedName + "]");
0599: }
0600: }
0601:
0602: item.setAlwaysDisabled(compHandler.isAlwaysDisabled());
0603: compHandler.init(component);
0604: }
0605: }
0606:
0607: /**
0608: * REMOVAL Methods which remove components
0609: */
0610: /**
0611: * Disposes all components known by this instance which are included and clears the tree model.
0612: * After this method has been executed, the instance knows all system wide windows; those
0613: * windows are ignored.
0614: */
0615: public synchronized void removeAll() {
0616: hideAllComponents();
0617:
0618: // remove all included components
0619: Enumeration e = includedRoots.keys();
0620:
0621: while (e.hasMoreElements()) {
0622: Object nextRoot = e.nextElement();
0623:
0624: if (nextRoot instanceof Component) {
0625: Component component = (Component) nextRoot;
0626:
0627: removeSubTree(component);
0628:
0629: //System.out.println ("Disposing window: " + component.getClass().getName());
0630: if (component instanceof Window) {
0631: ((Window) component).setVisible(false);
0632: ((Window) component).dispose();
0633: }
0634: } else {
0635: logger.error(language
0636: .getString("Components.Error.NoComponent"));
0637: }
0638: }
0639:
0640: // remove all known roots and elements
0641: includedRoots.clear();
0642: excludedRoots.clear();
0643: knownRoots.clear();
0644: compToItem.clear();
0645: nameToItem.clear();
0646: renamedComponents.clear();
0647: userDefinedNameToCompName.clear();
0648: compNameToUserDefinedName.clear();
0649: duplicateUserDefinedNames.clear();
0650: glassPaneManager.removeAllGlassPaneCompounds();
0651:
0652: componentSelection.clear();
0653: componentWatcher.clear();
0654: componentListener = new ComponentListener(env);
0655:
0656: // Call the garbage collector
0657: // (hope that he comes...)
0658: System.gc();
0659:
0660: // detect all components and exclude them
0661: detectComponents(false, false);
0662: }
0663:
0664: /**
0665: * Removes a component and its subtree.
0666: *
0667: * @param component DOCUMENT ME!
0668: */
0669: private void removeSubTree(Component component) {
0670: if (isKnown(component)) {
0671: String componentName = getName_Internal(component);
0672:
0673: if (isDebugPrinting) {
0674: logger.debug(language
0675: .getString("Components.Msg.RemoveSubtree")
0676: + " [" + componentName + "]");
0677: }
0678:
0679: // Getting all components whose names start with the name of the given component
0680: int count = 0;
0681: Enumeration names = nameToItem.keys();
0682: Vector nameVector = new Vector(20, 20);
0683:
0684: while (names.hasMoreElements()) {
0685: String name = (String) names.nextElement();
0686:
0687: if (name.startsWith(componentName)) {
0688: count++;
0689: nameVector.add(name);
0690: }
0691: }
0692:
0693: for (int i = 0; i < count; i++) {
0694: CompItem item = (CompItem) nameToItem.get(nameVector
0695: .get(i));
0696: removeComponent(item.getComponent());
0697: }
0698:
0699: if (isDebugPrinting) {
0700: logger
0701: .debug(language
0702: .getString("Components.Msg.RemoveSubtreeFinished")
0703: + " [" + componentName + "]");
0704: }
0705: }
0706: }
0707:
0708: /**
0709: * Removes a component.
0710: *
0711: * @param component DOCUMENT ME!
0712: */
0713: private void removeComponent(Component component) {
0714: String componentName = getName_Internal(component);
0715:
0716: if (isDebugPrinting) {
0717: logger.debug(language
0718: .getString("Components.Msg.RemoveComponent")
0719: + " [" + componentName + "]");
0720: }
0721:
0722: nameToItem.remove(componentName);
0723: compToItem.remove(component);
0724:
0725: String userDefinedName = component.getName();
0726:
0727: if ((userDefinedName != null)
0728: && !userDefinedName.equals("")
0729: && userDefinedNameToCompName
0730: .containsKey(userDefinedName)) {
0731: userDefinedNameToCompName.remove(component.getName());
0732: compNameToUserDefinedName.remove(componentName);
0733: }
0734:
0735: if (includedRoots.containsKey(component)) {
0736: includedRoots.remove(component);
0737: }
0738:
0739: if (isDebugPrinting) {
0740: logger
0741: .debug(language
0742: .getString("Components.Msg.RemoveComponentFinished")
0743: + " [" + componentName + "]");
0744: }
0745: }
0746:
0747: /**
0748: * UPDATING Methods which update, refresh, rename or detect components automatically
0749: */
0750: /**
0751: * Updates the information hold by this instance top down. Updates the component trees already
0752: * known and detects and adds new components. The added components will be included and
0753: * enabled for capturing.
0754: */
0755: public synchronized void update() {
0756: if (isDebugPrinting) {
0757: logger.debug(language
0758: .getString("Components.Msg.UpdateComponents"));
0759: }
0760:
0761: Enumeration i = includedRoots.keys();
0762:
0763: while (i.hasMoreElements()) {
0764: refreshTree((Component) i.nextElement());
0765: }
0766:
0767: detectComponents(true, true);
0768:
0769: componentSelection.update();
0770: componentWatcher.update();
0771:
0772: if (isDebugPrinting) {
0773: logger
0774: .debug(language
0775: .getString("Components.Msg.UpdateComponentsFinished"));
0776: }
0777: }
0778:
0779: /**
0780: * This method tries to detect new components which are not known yet. All components belonging
0781: * to the windows will be added with the specified attributes.
0782: *
0783: * @param isIncluded whether or not the new components will be included
0784: * @param isEnabled whether or not the new components will be enabled for capturing. (only
0785: * taken into account if they ae included)
0786: */
0787: public synchronized void detectComponents(boolean isIncluded,
0788: boolean isEnabled) {
0789: if (isDebugPrinting) {
0790: logger.debug(language
0791: .getString("Components.Msg.DetectComponents"));
0792: }
0793:
0794: Frame[] newFrames = Frame.getFrames();
0795:
0796: for (int i = 0; i < newFrames.length; i++) {
0797: if (!isKnown(newFrames[i])) {
0798: firesComponentsEvents = false;
0799: addTree(newFrames[i], isIncluded, isEnabled);
0800:
0801: Window[] ownedWindows = newFrames[i].getOwnedWindows();
0802:
0803: for (int j = 0; j < ownedWindows.length; j++) {
0804: if (!isKnown(ownedWindows[j])) {
0805: addTree(ownedWindows[j], isIncluded, isEnabled);
0806: }
0807: }
0808: }
0809:
0810: if (!firesComponentsEvents) {
0811: firesComponentsEvents = true;
0812: fireComponentsEvent(new ComponentsEvent(this ,
0813: ComponentsEvent.TREE_CHANGED, null, null));
0814: }
0815: }
0816:
0817: if (isDebugPrinting) {
0818: logger
0819: .debug(language
0820: .getString("Components.Msg.DetectComponentsFinished"));
0821: }
0822: }
0823:
0824: /**
0825: * Refreshs the tree of a given root component; it adds new components which have not yet been
0826: * known. The given root component must already be known and included.
0827: *
0828: * @param rootComponent the given root component
0829: */
0830: private void refreshTree(Component rootComponent) {
0831: if (isDebugPrinting) {
0832: logger.debug(language
0833: .getString("Components.Msg.RefreshTree")
0834: + " [" + getName_Internal(rootComponent) + "]");
0835: }
0836:
0837: ComponentHandler compHandler = getFirstComponentHandler(rootComponent);
0838:
0839: if (compHandler != null) {
0840: CompItem item = (CompItem) compToItem.get(rootComponent);
0841:
0842: if (item != null) {
0843: String componentName = item.getName();
0844:
0845: // The stored and the actual children must be tested
0846: // There can be stored children which are not reachable from the component,
0847: // and there may be new children which have not yet been stored
0848: Vector testChildren = new Vector(5, 5);
0849: Component[] storedChildren = item.getChildren();
0850:
0851: if (storedChildren != null) {
0852: for (int i = 0; i < storedChildren.length; i++) {
0853: if (storedChildren[i] != null) {
0854: testChildren.add(storedChildren[i]);
0855: }
0856: }
0857: }
0858:
0859: Component[] compChildren = compHandler
0860: .getChildren(rootComponent);
0861:
0862: if (compChildren != null) {
0863: for (int i = 0; i < compChildren.length; i++) {
0864: if ((compChildren[i] != null)
0865: && !testChildren
0866: .contains(compChildren[i])) {
0867: testChildren.add(compChildren[i]);
0868: }
0869: }
0870: }
0871:
0872: boolean childAdded = false;
0873:
0874: if (testChildren.size() > 0) {
0875: int counter = item.getChildrenCount();
0876:
0877: for (int i = 0; i < testChildren.size(); i++) {
0878: Component child = (Component) testChildren
0879: .get(i);
0880:
0881: if (!isKnown(child)) {
0882: ComponentHandler childHandler = getFirstComponentHandler(child);
0883: String childName = getNewName(child,
0884: compHandler, componentName + ".");
0885:
0886: boolean result = addSubTree(child,
0887: childHandler, childName);
0888:
0889: if (result == true) {
0890: setSubTreeEnabled(child, item
0891: .isEnabled());
0892: childAdded = true;
0893: }
0894:
0895: counter++;
0896: } else {
0897: refreshTree(child);
0898: }
0899: }
0900:
0901: // Actualize the children of the component
0902: if ((item.getChildrenCount() != counter)
0903: || childAdded) {
0904: item.setChildrenCount(counter);
0905:
0906: Component[] newChildren = new Component[testChildren
0907: .size()];
0908:
0909: for (int i = 0; i < newChildren.length; i++) {
0910: newChildren[i] = (Component) testChildren
0911: .get(i);
0912: }
0913:
0914: item.setChildren(newChildren);
0915: }
0916: }
0917:
0918: if (isDebugPrinting) {
0919: logger
0920: .debug(language
0921: .getString("Components.Msg.RefreshTreeFinished")
0922: + " ["
0923: + getName_Internal(rootComponent)
0924: + "]");
0925: }
0926: } else {
0927: getLogger().error(
0928: getLanguage().getString(
0929: "Components.Error.Nitem")
0930: + ": "
0931: + rootComponent.getClass().getName());
0932: }
0933: } else {
0934: getLogger()
0935: .warn(
0936: getLanguage().getString(
0937: "Components.Error.NoHandler")
0938: + ": "
0939: + rootComponent.getClass()
0940: .getName());
0941: }
0942: }
0943:
0944: /**
0945: * Tries to find a window for a window's Jacareto name which has the same class and is the only
0946: * one of this class which is visible. This method should only be used when a window must not
0947: * be visible and all other component finding mechanism have failed. It should only be used by
0948: * WindowReplayers. If just one visible component is found with this class, its component tree
0949: * is renamed to the new one.
0950: *
0951: * @param targetName DOCUMENT ME!
0952: */
0953: public void renameOnlyVisibleWindowMatchingTo(String targetName) {
0954: Enumeration enumeration = includedRoots.keys();
0955: Vector rootsFound = new Vector(5, 5);
0956:
0957: // does not work with components which have been renamed with
0958: // the component mode item
0959: while (enumeration.hasMoreElements()) {
0960: Component root = (Component) enumeration.nextElement();
0961: String rootName = getName_Internal(root);
0962:
0963: if ((rootName.lastIndexOf("_") > -1)
0964: && (targetName.lastIndexOf("_") > -1)
0965: && rootName.substring(0, rootName.lastIndexOf("_"))
0966: .equals(
0967: targetName.substring(0, targetName
0968: .lastIndexOf("_")))) {
0969: if (root.isVisible()) {
0970: rootsFound.add(root);
0971: }
0972: }
0973: }
0974:
0975: if (rootsFound.size() == 1) {
0976: logger.debug(language
0977: .getString("Components.Msg.RenamingOnlyVisible")
0978: + " " + targetName);
0979:
0980: Component targetRoot = (Component) rootsFound.get(0);
0981: addSubTree(targetRoot,
0982: getFirstComponentHandler(targetRoot), targetName);
0983: }
0984: }
0985:
0986: /**
0987: * Validates a path from a component to its root from bottom up. This method should be called
0988: * if a component could not be found after this instance has been udpated with the
0989: * update()-method. This method searches from the location of the component not found upwards
0990: * until a known parent has been found. Then the whole tree of the parent's first child
0991: * component which is also parent of the desired component will be renamed.
0992: *
0993: * @param componentName DOCUMENT ME!
0994: */
0995: public synchronized void validatePath(String componentName) {
0996: logger.debug(language.getString("Components.Msg.Validating")
0997: + " [" + componentName + "]");
0998:
0999: // Search bottom up until a known parent has been found. Store the path from
1000: // the component to the parent.
1001: Vector path = new Vector(5, 5);
1002: String parentName = componentName;
1003: path.add(parentName);
1004:
1005: while (!isKnown(parentName)
1006: && (parentName.lastIndexOf(".") > -1)) {
1007: parentName = parentName.substring(0, parentName
1008: .lastIndexOf("."));
1009: path.add(parentName);
1010: }
1011:
1012: // Has a known parent been found?
1013: if (isKnown(parentName)) {
1014: logger.info(language
1015: .getString("Components.Msg.ValidatingParentFound")
1016: + " [" + parentName + "]");
1017:
1018: String pathRootName = parentName;
1019:
1020: // Calculate possible paths which match the path found above
1021: Vector possiblePaths = getPossiblePaths(parentName, path,
1022: path.size() - 1);
1023:
1024: // Possible paths must have the same length as the desired path
1025: for (int i = possiblePaths.size() - 1; i >= 0; i--) {
1026: if (((Vector) possiblePaths.get(i)).size() != path
1027: .size()) {
1028: possiblePaths.remove(i);
1029: }
1030: }
1031:
1032: // Pfade unterschiedlicher Laenge noch rausfiltern
1033: if (possiblePaths.size() == 1) {
1034: Vector possiblePath = (Vector) possiblePaths.get(0);
1035:
1036: if (possiblePath.size() > 1) {
1037: logger
1038: .debug(language
1039: .getString("Components.Msg.ValidatingPathFound"));
1040:
1041: String oldName = (String) possiblePath
1042: .get(possiblePath.size() - 2);
1043: String newName = (String) path.get(path.size() - 2);
1044: Component compToRename = getComponent(oldName);
1045: addSubTree(compToRename,
1046: getFirstComponentHandler(compToRename),
1047: newName);
1048: }
1049: } else if (possiblePaths.size() > 0) {
1050: logger
1051: .debug(language
1052: .getString("Components.Msg.ValidatingTooManyPathsFound")
1053: + " [" + possiblePaths.size() + "]");
1054: } else {
1055: logger
1056: .debug(language
1057: .getString("Components.Msg.ValidatingNoPathFound"));
1058: }
1059: } else {
1060: logger
1061: .debug(language
1062: .getString("Components.Msg.ValidatingParentNotFound"));
1063: }
1064: }
1065:
1066: /**
1067: * Internal method which search from a component with parentName downwards and collects paths
1068: * which match the given path.
1069: *
1070: * @param parentName DOCUMENT ME!
1071: * @param path DOCUMENT ME!
1072: * @param index DOCUMENT ME!
1073: *
1074: * @return DOCUMENT ME!
1075: */
1076: private Vector getPossiblePaths(String parentName, Vector path,
1077: int index) {
1078: Vector result = new Vector(5, 5);
1079:
1080: if (getPrefix(parentName).equals(
1081: getPrefix((String) path.get(index)))) {
1082: if (index > 0) {
1083: CompItem parentItem = (CompItem) nameToItem
1084: .get(parentName);
1085: Component[] children = parentItem.getChildren();
1086:
1087: for (int i = 0; i < children.length; i++) {
1088: Vector childResult = getPossiblePaths(
1089: getName_Internal(children[i]), path,
1090: index - 1);
1091:
1092: if (childResult.size() > 0) {
1093: Iterator it = childResult.iterator();
1094:
1095: while (it.hasNext()) {
1096: Vector tmp = (Vector) it.next();
1097: tmp.add(parentName);
1098: result.add(tmp);
1099: }
1100: }
1101: }
1102: } else {
1103: Vector resultChild = new Vector(5, 5);
1104: resultChild.add(parentName);
1105: result.add(resultChild);
1106: }
1107: }
1108:
1109: return result;
1110: }
1111:
1112: /**
1113: * ENABLING Methods which enable or disable components for capture processes.
1114: *
1115: * @param component DOCUMENT ME!
1116: * @param isEnabled DOCUMENT ME!
1117: */
1118: /**
1119: * Enabled or disabled a component and its whole subtree. If the component is not known, it
1120: * will be added to the components first (and the whole tree it belongs to, too). In this case
1121: * all added components will be included and enabled for capturing at first; the given
1122: * component and its subtree will be then set to the given enabled state.
1123: *
1124: * @param component the component to be enabled/disabled
1125: * @param isEnabled <code>true</code> if the component and its subtree should be enabled;
1126: * otherwise <code>false</code>
1127: */
1128: public synchronized void setSubTreeEnabled(Component component,
1129: boolean isEnabled) {
1130: if (component != null) {
1131: if (!isKnown(component)) {
1132: addTree(component);
1133: }
1134:
1135: ComponentHandler compHandler = getFirstComponentHandler(component);
1136:
1137: if (compHandler != null) {
1138: setEnabled(component, isEnabled);
1139:
1140: Component[] compChildren = compHandler
1141: .getChildren(component);
1142:
1143: if ((compChildren != null) && (compChildren.length > 0)) {
1144: for (int i = 0; i < compChildren.length; i++) {
1145: Component child = compChildren[i];
1146: setSubTreeEnabled(child, isEnabled);
1147: }
1148: }
1149: } else {
1150: getLogger()
1151: .warn(
1152: getLanguage().getString(
1153: "Components.Error.NoHandler")
1154: + ": "
1155: + component.getClass()
1156: .getName());
1157: }
1158: }
1159: }
1160:
1161: /**
1162: * Enables or disabled a component and its whole subtree specified by the component's name.
1163: *
1164: * @param componentName the name of the component to be enabled/disabled
1165: * @param isEnabled <code>true</code> if the component and its subtree should be enabled;
1166: * otherwise <code>false</code>
1167: */
1168: public synchronized void setSubTreeEnabled(String componentName,
1169: boolean isEnabled) {
1170: if (isKnown(componentName)) {
1171: setSubTreeEnabled(
1172: ((CompItem) nameToItem.get(componentName))
1173: .getComponent(), isEnabled);
1174: } else {
1175: getLogger().error(
1176: getLanguage().getString(
1177: "Components.Error.UnknownItemName")
1178: + ": " + componentName);
1179: }
1180: }
1181:
1182: /**
1183: * COMPONENT STATE Methods to inspect the state of a component, or to change the state of a
1184: * component
1185: *
1186: * @param component DOCUMENT ME!
1187: *
1188: * @return DOCUMENT ME!
1189: */
1190: /**
1191: * Returns whether the specified component is known or not.
1192: *
1193: * @param component the component
1194: *
1195: * @return <code>true</code> if it is known, otherwise <code>false</code>
1196: */
1197: public synchronized boolean isKnown(Component component) {
1198: if (component != null) {
1199: Component root = getRoot(component);
1200:
1201: return excludedRoots.containsKey(root)
1202: || compToItem.containsKey(component);
1203: } else {
1204: return false;
1205: }
1206: }
1207:
1208: /**
1209: * Returns whether the component with the specified component's name is known or not.
1210: *
1211: * @param componentName the component's name
1212: *
1213: * @return <code>true</code> if it is known, otherwise <code>false</code>
1214: */
1215: public synchronized boolean isKnown(String componentName) {
1216: return nameToItem.containsKey(componentName);
1217: }
1218:
1219: /**
1220: * Returns whether or not a given component is included.
1221: *
1222: * @param component DOCUMENT ME!
1223: *
1224: * @return DOCUMENT ME!
1225: */
1226: public synchronized boolean isIncluded(Component component) {
1227: if (component != null) {
1228: return compToItem.containsKey(component);
1229:
1230: //return includedRoots.containsKey(getRoot(component));
1231: } else {
1232: return false;
1233: }
1234: }
1235:
1236: /**
1237: * Returns whether the component specified by its name is included or not.
1238: *
1239: * @param componentName the name of the component to test
1240: *
1241: * @return <code>true</code> if it is included, <code>false</code> if it is excluded or if the
1242: * component's name is unknown
1243: */
1244: public synchronized boolean isIncluded(String componentName) {
1245: if (isKnown(componentName)) {
1246: return isIncluded(((CompItem) nameToItem.get(componentName))
1247: .getComponent());
1248: } else {
1249: getLogger().warn(
1250: getLanguage().getString(
1251: "Components.Error.UnknownItemName")
1252: + ": " + componentName);
1253:
1254: return false;
1255: }
1256: }
1257:
1258: /**
1259: * Enables or disables a component for capturing. If the component is not known, it will be
1260: * added to the components first (and the whole component tree it belongs to). In this case
1261: * the added components will be included and enabled for capturing afterwards.
1262: *
1263: * @param component the component to be enabled/disabled
1264: * @param isEnabled <code>true</code> if the component should be enabled; otherwise
1265: * <code>false</code>
1266: */
1267: public synchronized void setEnabled(Component component,
1268: boolean isEnabled) {
1269: if (component != null) {
1270: if (!isKnown(component)) {
1271: addTree(component);
1272: }
1273:
1274: if (isIncluded(component)) {
1275: ((CompItem) compToItem.get(component))
1276: .setEnabled(isEnabled);
1277: }
1278: }
1279: }
1280:
1281: /**
1282: * Enables or disabled a component for capturing specified by its name.
1283: *
1284: * @param componentName the name of the component to be enabled/disabled
1285: * @param isEnabled <code>true</code> if the component should be enabled; otherwise
1286: * <code>false</code>
1287: */
1288: public synchronized void setEnabled(String componentName,
1289: boolean isEnabled) {
1290: if (isKnown(componentName)) {
1291: ((CompItem) nameToItem.get(componentName))
1292: .setEnabled(isEnabled);
1293: } else {
1294: getLogger().error(
1295: getLanguage().getString(
1296: "Components.Error.UnknownItemName")
1297: + ": " + componentName);
1298: }
1299: }
1300:
1301: /**
1302: * Returns whether the specified component is enabled or not. If the component is not known, it
1303: * will be added to the components before (and the whole tree it belongs to, too). All those
1304: * components will be initially included and enabled.
1305: *
1306: * @param component the component to test
1307: *
1308: * @return <code>true</code> if it is enabled, <code>false</code> if it is excluded
1309: */
1310: public synchronized boolean isEnabled(Component component) {
1311: if (component != null) {
1312: if (!isKnown(component)) {
1313: addTree(component);
1314: }
1315:
1316: return isIncluded(component)
1317: && ((CompItem) compToItem.get(component))
1318: .isEnabled();
1319: } else {
1320: return false;
1321: }
1322: }
1323:
1324: /**
1325: * Returns whether the component specified by its name is enabled or not.
1326: *
1327: * @param componentName the name of the component to test
1328: *
1329: * @return <code>true</code> if it is enabled, <code>false</code> if it is enabled or if the
1330: * component's name is unknown
1331: */
1332: public synchronized boolean isEnabled(String componentName) {
1333: if (isKnown(componentName)) {
1334: return isEnabled(((CompItem) nameToItem.get(componentName))
1335: .getComponent());
1336: } else {
1337: logger.warn(getLanguage().getString(
1338: "Components.Error.UnknownItemName")
1339: + ": " + componentName);
1340:
1341: return false;
1342: }
1343: }
1344:
1345: /**
1346: * Makes a component visible or not.
1347: *
1348: * @param component the component
1349: * @param isVisible whether the component is visible or not
1350: */
1351: public synchronized void setVisible(Component component,
1352: boolean isVisible) {
1353: if (component != null) {
1354: if (!isKnown(component)) {
1355: addTree(component);
1356: }
1357:
1358: component.setVisible(isVisible);
1359: }
1360: }
1361:
1362: /**
1363: * POPUP HANDLING Methods which handle window popups
1364: *
1365: * @param isListeningToPopUps DOCUMENT ME!
1366: */
1367: /**
1368: * Defines whether this instance should listen to window pop ups or not.
1369: *
1370: * @param isListeningToPopUps DOCUMENT ME!
1371: */
1372: public void setListeningToPopUps(boolean isListeningToPopUps) {
1373: this .isListeningToPopUps = isListeningToPopUps;
1374:
1375: if (isDebugPrinting) {
1376: logger.debug(getLanguage().getString(
1377: "Components.Msg.ListenToPopUps")
1378: + " " + isListeningToPopUps);
1379: }
1380: }
1381:
1382: /**
1383: * This function will be called by the pop up catcher when a window has been opened. The
1384: * specified window will be added if it is not already known. All components belonging to the
1385: * window will also be added. All added components will be initially enabled for capturing.
1386: *
1387: * @param window the window which has popped up
1388: */
1389: public void handlePopUp(Window window) {
1390: if (isListeningToPopUps && !isKnown(window)) {
1391: // Adds a window and all its subcomponents as included.
1392: if (isDebugPrinting) {
1393: logger.debug(getLanguage().getString(
1394: "Components.Msg.PopUpAdd"));
1395: }
1396:
1397: addTree(window);
1398:
1399: if (isDebugPrinting) {
1400: logger.debug(getLanguage().getString(
1401: "Components.Msg.PopUpAdded")
1402: + " [" + getName_Internal(window) + "]");
1403: }
1404: }
1405: }
1406:
1407: /**
1408: * VISIBILITY HANDLING Methods which show or hide components
1409: */
1410: /**
1411: * Saves the visibility status of all included components (whether they are visible or not).
1412: * This status will be restored with the method {@link #restoreVisibility()}.
1413: */
1414: public synchronized void saveVisibility() {
1415: Enumeration e = includedRoots.keys();
1416:
1417: while (e.hasMoreElements()) {
1418: Component component = (Component) e.nextElement();
1419: CompItem item = (CompItem) compToItem.get(component);
1420: item.setVisible(component.isVisible());
1421: }
1422: }
1423:
1424: /**
1425: * Restores the visibility status of all included components (whether they are visible or not)
1426: * stored with the method {@link #saveVisibility()}. If that method has never been invoked
1427: * before, the components will be visible if and only if they had been visible at the moment
1428: * they've been added to the components instance. Windows which are made visible by this
1429: * method will also be opened. This method will not touch forbidden components.
1430: */
1431: public synchronized void restoreVisibility() {
1432: Enumeration e = includedRoots.keys();
1433:
1434: while (e.hasMoreElements()) {
1435: Component component = (Component) e.nextElement();
1436:
1437: // Just for frames, because dialog are always in front of their frames
1438: // With setting modal dialogs unmodal, putting them to front would cause
1439: // a deadlock between them.
1440: if (component instanceof Frame) {
1441: CompItem item = (CompItem) compToItem.get(component);
1442: component.setVisible(item.isVisible());
1443:
1444: if (item.isVisible()) {
1445: component.repaint();
1446: }
1447: }
1448: }
1449: }
1450:
1451: /**
1452: * Hides all known components (except the excluded ones). If you want to restore the actual
1453: * visibility status later on with the method {@link #restoreVisibility()}, you should call
1454: * the method {@link #saveVisibility()} before hiding the components.
1455: */
1456: public synchronized void hideAllComponents() {
1457: Enumeration e = includedRoots.keys();
1458:
1459: while (e.hasMoreElements()) {
1460: ((Component) e.nextElement()).setVisible(false);
1461: }
1462: }
1463:
1464: /**
1465: * Puts all included, visible components to the front.
1466: */
1467: public synchronized void allToFront() {
1468: Enumeration e = includedRoots.keys();
1469:
1470: while (e.hasMoreElements()) {
1471: Component component = (Component) e.nextElement();
1472:
1473: // Just for frames, because dialog are always in front of their frames
1474: // With setting modal dialogs unmodal, putting them to front would cause
1475: // a deadlock between them.
1476: if (component.isVisible() && (component instanceof Frame)) {
1477: toFront(component);
1478: }
1479: }
1480: }
1481:
1482: /**
1483: * Puts a component and the whole tree this component is contained in to the front. The given
1484: * component must not be a window.
1485: *
1486: * @param comp the component to put to the front.
1487: */
1488: public void toFront(Component comp) {
1489: try {
1490: Window window = null;
1491:
1492: window = SwingUtilities.getWindowAncestor(comp);
1493:
1494: if (window == null) {
1495: window = getWindow(comp);
1496: }
1497:
1498: if (window != null) {
1499: window.toFront();
1500: }
1501: } catch (Exception exception) {
1502: getLogger().error(
1503: getLanguage().getString("Components.Error.ToFront")
1504: + " " + getName(comp), exception);
1505: }
1506: }
1507:
1508: /**
1509: * Returns the glass pane manager
1510: *
1511: * @return the manager for glass panes
1512: */
1513: public GlassPaneManager getGlassPaneManager() {
1514: return glassPaneManager;
1515: }
1516:
1517: /**
1518: * Returns the component selection
1519: *
1520: * @return the component selection
1521: */
1522: public ComponentSelection getComponentSelection() {
1523: return componentSelection;
1524: }
1525:
1526: /**
1527: * Returns the component watcher
1528: *
1529: * @return the component watcher
1530: */
1531: public ComponentWatcher getComponentWatcher() {
1532: return componentWatcher;
1533: }
1534:
1535: /**
1536: * STANDARD METHODS Just standard.
1537: *
1538: * @return DOCUMENT ME!
1539: */
1540: /**
1541: * Returns a string representation of this instance.
1542: *
1543: * @return a string containing information of this instance.
1544: */
1545: public String toString() {
1546: return getNames().toString();
1547: }
1548:
1549: /**
1550: * Sets whether or not debug messages should be printed.
1551: *
1552: * @param isDebugPrinting DOCUMENT ME!
1553: */
1554: public void setDebugPrinting(boolean isDebugPrinting) {
1555: this .isDebugPrinting = isDebugPrinting;
1556: logger.debug(language.getString("Components.Msg.DebugPrinting")
1557: + " " + isDebugPrinting);
1558: }
1559:
1560: /**
1561: * NAME HANDLING Methods which set or return names of components
1562: *
1563: * @param jacaretoName DOCUMENT ME!
1564: * @param userDefinedName DOCUMENT ME!
1565: */
1566: /**
1567: * Sets the user defined name for a given component.
1568: *
1569: * @param jacaretoName DOCUMENT ME!
1570: * @param userDefinedName DOCUMENT ME!
1571: */
1572: public void setUserDefinedName(String jacaretoName,
1573: String userDefinedName) {
1574: if (compNameToUserDefinedName.containsKey(jacaretoName)) {
1575: String oldUserName = (String) compNameToUserDefinedName
1576: .get(jacaretoName);
1577: compNameToUserDefinedName.remove(jacaretoName);
1578: userDefinedNameToCompName.remove(oldUserName);
1579:
1580: // TODO: duplicate names should be tested if they are duplicate anymore
1581: }
1582:
1583: if (!userDefinedNameToCompName.containsKey(userDefinedName)) {
1584: userDefinedNameToCompName
1585: .put(userDefinedName, jacaretoName);
1586: compNameToUserDefinedName
1587: .put(jacaretoName, userDefinedName);
1588: fireComponentsEvent(new ComponentsEvent(this ,
1589: ComponentsEvent.USER_DEFINED_NAME_SET,
1590: jacaretoName, userDefinedName));
1591: } else {
1592: duplicateUserDefinedNames.put(userDefinedName, "");
1593: logger.debug(language
1594: .getString("Components.Msg.DuplicateUserDefined")
1595: + " [" + userDefinedName + "]");
1596: }
1597:
1598: Component component = getComponent(jacaretoName);
1599: component.setName(userDefinedName);
1600: }
1601:
1602: /**
1603: * Returns the name of a given component. If the component is not known by this instance, it
1604: * will be added to it (and the whole component tree the component belongs to.) In this case
1605: * all added components will be included and enabled for capturing afterwards.
1606: *
1607: * @param component the component
1608: *
1609: * @return the component's name, or <code>null</code> if the component's name cannot be
1610: * determined
1611: */
1612: public synchronized String getName(Component component) {
1613: // just use this method from outside
1614: // for internal use, take getName_Internal
1615: try {
1616: if (component != null) {
1617: if (!isKnown(component)) {
1618: addTree(component);
1619: }
1620:
1621: // Ist danach nicht known.
1622: // Second isKnown call is necessary, because sometime
1623: // there is no component item
1624: if (isKnown(component)) {
1625: return ((CompItem) compToItem.get(component))
1626: .getName();
1627: } else {
1628: logger
1629: .warn(language
1630: .getString("Components.Error.GetNameNoItem")
1631: + " ["
1632: + component.getClass().getName()
1633: + "]");
1634:
1635: return null;
1636: }
1637: } else {
1638: return null;
1639: }
1640: } catch (NullPointerException npe) {
1641: if (!compToItem.containsKey(component)) {
1642: logger.error(language
1643: .getString("Components.Error.GetNameNoItem")
1644: + " [" + component.getClass().getName() + "]");
1645: } else if (compToItem.get(component) == null) {
1646: logger
1647: .error(language
1648: .getString("Components.Error.GetNameEmptyEntry")
1649: + " ["
1650: + component.getClass().getName()
1651: + "]");
1652: } else {
1653: logger.error(language
1654: .getString("Components.Error.UnknownNPEReason")
1655: + " [" + component.getClass().getName() + "]");
1656: }
1657:
1658: logger.error(npe.getMessage());
1659:
1660: return null;
1661: }
1662: }
1663:
1664: /**
1665: * Returns the an indicator string of a given component. This is a string which allows
1666: * for identifiing the component more easily (for example, the text of the menu item or
1667: * anything like this). If the component is not known by this instance, it
1668: * will be added to it (and the whole component tree the component belongs to.) In this case
1669: * all added components will be included and enabled for capturing afterwards.
1670: *
1671: * @param component the component
1672: *
1673: * @return the indicator string, or <code>null</code> if the component's name cannot be
1674: * determined or if the component does not have an indicator
1675: */
1676: public synchronized String getIndicatorString(Component component) {
1677: // just use this method from outside
1678: // for internal use, take getName_Internal
1679: try {
1680: if (component != null) {
1681: if (!isKnown(component)) {
1682: addTree(component);
1683: }
1684:
1685: // Ist danach nicht known.
1686: // Second isKnown call is necessary, because sometime
1687: // there is no component item
1688: if (isKnown(component)) {
1689: ComponentHandler handler = getFirstComponentHandler(component);
1690: if (handler != null) {
1691: return handler.getIndicatorString(component);
1692: } else {
1693: return null;
1694: }
1695: } else {
1696: logger
1697: .warn(language
1698: .getString("Components.Error.GetNameNoItem")
1699: + " ["
1700: + component.getClass().getName()
1701: + "]");
1702:
1703: return null;
1704: }
1705: } else {
1706: return null;
1707: }
1708: } catch (NullPointerException npe) {
1709: if (!compToItem.containsKey(component)) {
1710: logger.error(language
1711: .getString("Components.Error.GetNameNoItem")
1712: + " [" + component.getClass().getName() + "]");
1713: } else if (compToItem.get(component) == null) {
1714: logger
1715: .error(language
1716: .getString("Components.Error.GetNameEmptyEntry")
1717: + " ["
1718: + component.getClass().getName()
1719: + "]");
1720: } else {
1721: logger.error(language
1722: .getString("Components.Error.UnknownNPEReason")
1723: + " [" + component.getClass().getName() + "]");
1724: }
1725:
1726: logger.error(npe.getMessage());
1727:
1728: return null;
1729: }
1730: }
1731:
1732: /**
1733: * Returns the name of a given component. Does not add the component if it is not known; just
1734: * for internal use
1735: *
1736: * @param component the component
1737: *
1738: * @return the component's name
1739: */
1740: private String getName_Internal(Component component) {
1741: if (component != null) {
1742: if (isKnown(component)) {
1743: return ((CompItem) compToItem.get(component)).getName();
1744: } else {
1745: if (isDebugPrinting) {
1746: logger.debug(language
1747: .getString("Components.Error.NameNotKnown")
1748: + " [" + component.getClass().getName());
1749: }
1750:
1751: return "NotKnownComponent";
1752: }
1753: } else {
1754: return null;
1755: }
1756: }
1757:
1758: /**
1759: * Returns a name for a new component to be added.
1760: *
1761: * @param component the component to name
1762: * @param compHandler the component handler for the given component
1763: * @param prefix the prefix of the name
1764: *
1765: * @return DOCUMENT ME!
1766: */
1767: private String getNewName(Component component,
1768: ComponentHandler compHandler, String prefix) {
1769: int number = 0;
1770: String result;
1771: String compName = compHandler.getName(component);
1772:
1773: if ((component instanceof JPopupMenu)
1774: && !mode.getNumberPopupMenues()) {
1775: result = prefix + compName;
1776: System.out.println("*******Not numbered JPopupMenu: "
1777: + result);
1778: } else {
1779: do {
1780: number++;
1781: result = prefix + compName + numberPrefix + number
1782: + numberSuffix;
1783: } while (nameToItem.containsKey(result)
1784: && (getComponent(result) != component));
1785: }
1786:
1787: return result;
1788: }
1789:
1790: /**
1791: * Returns the names of all known and included components as set.
1792: *
1793: * @return DOCUMENT ME!
1794: */
1795: public Set getNames() {
1796: Set set = nameToItem.keySet();
1797:
1798: return new TreeSet(set);
1799: }
1800:
1801: /**
1802: * Returns a hashtable which maps Jacareto names to user defined names.
1803: *
1804: * @return DOCUMENT ME!
1805: */
1806: public Hashtable getUserDefinedNames() {
1807: return compNameToUserDefinedName;
1808: }
1809:
1810: /**
1811: * Returns a user defined name for a given Jacareto name, or <code>null</code> if there is no
1812: * user defined name.
1813: *
1814: * @param jacaretoName DOCUMENT ME!
1815: *
1816: * @return DOCUMENT ME!
1817: */
1818: public String getUserDefinedName(String jacaretoName) {
1819: if (compNameToUserDefinedName.containsKey(jacaretoName)) {
1820: return (String) compNameToUserDefinedName.get(jacaretoName);
1821: } else {
1822: return null;
1823: }
1824: }
1825:
1826: /**
1827: * Returns all Jacareto names.
1828: *
1829: * @return DOCUMENT ME!
1830: */
1831: public Set getJacaretoNames() {
1832: return nameToItem.keySet();
1833: }
1834:
1835: /**
1836: * Prints all root names.
1837: */
1838: public void printRootNames() {
1839: Enumeration enumeration = includedRoots.keys();
1840:
1841: while (enumeration.hasMoreElements()) {
1842: Component comp = (Component) enumeration.nextElement();
1843: System.out.println(getName_Internal(comp));
1844: }
1845: }
1846:
1847: /**
1848: * Returns the component name with no tree information and no number.
1849: *
1850: * @param componentName DOCUMENT ME!
1851: *
1852: * @return DOCUMENT ME!
1853: */
1854: private String getPrefix(String componentName) {
1855: String result = componentName;
1856: int lastPrefixIndex = componentName.lastIndexOf(numberPrefix);
1857:
1858: if (lastPrefixIndex >= 0) {
1859: result = componentName.substring(0, lastPrefixIndex);
1860:
1861: if (result.lastIndexOf(".") > -1) {
1862: result = result.substring(result.lastIndexOf("."));
1863: }
1864: }
1865:
1866: return result;
1867: }
1868:
1869: /**
1870: * COMPONENT RETRIEVAL Methods which return components
1871: *
1872: * @param componentName DOCUMENT ME!
1873: *
1874: * @return DOCUMENT ME!
1875: */
1876: /**
1877: * Returns the component belonging to a given component's name.
1878: *
1879: * @param componentName the name
1880: *
1881: * @return the component belonging to the specified name, or <code>null</code> if there is no
1882: * component with that name.
1883: */
1884: public synchronized Component getComponent(String componentName) {
1885: String componentToGet = componentName;
1886:
1887: // Test if the component is not known and has been renamed
1888: while (!compToItem.containsKey(componentToGet)
1889: && renamedComponents.containsKey(componentToGet)) {
1890: componentToGet = (String) renamedComponents
1891: .get(componentToGet);
1892:
1893: if (isDebugPrinting) {
1894: logger.debug(language
1895: .getString("Components.Msg.TakingRenamed")
1896: + " ["
1897: + componentName
1898: + "] ["
1899: + componentToGet
1900: + "]");
1901: }
1902: }
1903:
1904: if (isKnown(componentToGet)) {
1905: return ((CompItem) nameToItem.get(componentToGet))
1906: .getComponent();
1907: } else {
1908: return null;
1909: }
1910: }
1911:
1912: /**
1913: * This method can be used if the method getComponent (String) has failed. This method tries to
1914: * find the component by the user defined name.
1915: *
1916: * @param userDefinedName DOCUMENT ME!
1917: *
1918: * @return the component, or <code>null</code> if the component could not be found
1919: */
1920: public synchronized Component getComponentByUserDefinedName(
1921: String userDefinedName) {
1922: Component result = null;
1923:
1924: if ((userDefinedName != null) && !userDefinedName.equals("")) {
1925: logger.debug(language
1926: .getString("Components.Msg.SearchingUserDefined")
1927: + " [" + userDefinedName + "]");
1928:
1929: if (userDefinedNameToCompName.containsKey(userDefinedName)) {
1930: if (!duplicateUserDefinedNames
1931: .containsKey(userDefinedName)) {
1932: String componentName = (String) userDefinedNameToCompName
1933: .get(userDefinedName);
1934: logger.debug(language
1935: .getString("Components.Msg.InternalName")
1936: + " [" + componentName + "]");
1937: result = getComponent(componentName);
1938: } else {
1939: logger
1940: .debug(language
1941: .getString("Components.Msg.DuplicateUserDefinedRetrieval")
1942: + " [" + userDefinedName + "]");
1943: }
1944: }
1945: }
1946:
1947: if (result != null) {
1948: logger.debug(language
1949: .getString("Components.Msg.FoundUserDefined")
1950: + " [" + userDefinedName + "]");
1951: }
1952:
1953: return result;
1954: }
1955:
1956: /**
1957: * Returns the root component of the component tree of a given component. This method uses the
1958: * {@link jacareto.comp.ComponentHandler#getParent(Component)} method for determing the root
1959: * component.
1960: *
1961: * @param component The component
1962: *
1963: * @return the root
1964: */
1965: public Component getRoot(Component component) {
1966: Component result = component;
1967: Component parent = null;
1968:
1969: do {
1970: ComponentHandler compHandler = getFirstComponentHandler(result);
1971:
1972: if (compHandler != null) {
1973: parent = compHandler.getParent(result);
1974:
1975: if (parent != null) {
1976: result = parent;
1977: }
1978: } else {
1979: // just to avoid an endless loop
1980: parent = null;
1981: }
1982: } while (parent != null);
1983:
1984: return result;
1985: }
1986:
1987: /**
1988: * Returns the window a given component belongs to. If the specified component is a window
1989: * itself, the method will return the component.
1990: *
1991: * @param comp The component
1992: *
1993: * @return the window, or <code>null</code> if there is no window (can this be?)
1994: */
1995: public Window getWindow(Component comp) {
1996: while ((comp != null) && !(comp instanceof Window)) {
1997: comp = comp.getParent();
1998: }
1999:
2000: return (Window) comp;
2001: }
2002:
2003: /**
2004: * Returns the included roots.
2005: *
2006: * @return DOCUMENT ME!
2007: */
2008: public Set getIncludedRoots() {
2009: return includedRoots.keySet();
2010: }
2011:
2012: /**
2013: * Returns the active included root.
2014: *
2015: * @return the active included root, or <code>null</code> if no included root is active
2016: */
2017: public Window getActiveIncludedRoot() {
2018: Iterator it = getIncludedRoots().iterator();
2019:
2020: while (it.hasNext()) {
2021: Object root = it.next();
2022:
2023: if (root instanceof Window && ((Window) root).isActive()) {
2024: return (Window) root;
2025: }
2026: }
2027:
2028: return null;
2029: }
2030:
2031: /**
2032: * Returns an enumeration on all included roots.
2033: *
2034: * @return DOCUMENT ME!
2035: */
2036: public Enumeration includedRoots() {
2037: return includedRoots.keys();
2038: }
2039:
2040: /**
2041: * Changes the modality of a dialog if it should be changed according to the actual mode.
2042: *
2043: * @param dialog the dialog
2044: */
2045: private void setDialogModal(Dialog dialog) {
2046: if (mode.getApplyNewDialogsModal()) {
2047: boolean isActuallyModal = dialog.isModal();
2048: boolean newModalState = mode.getNewDialogsModal();
2049:
2050: EventListener.setIgnoringEvents(true);
2051:
2052: if (isActuallyModal != newModalState) {
2053: WindowUtilities.setModal(dialog, newModalState);
2054: }
2055:
2056: EventListener.setIgnoringEvents(false);
2057: }
2058: }
2059:
2060: /**
2061: * COMPONENT HANDLERS Methods for component handlers
2062: *
2063: * @param compHandler DOCUMENT ME!
2064: * @param priority DOCUMENT ME!
2065: */
2066: /**
2067: * Registers a component handler with the specified priority. A greater value of priority
2068: * symbolizes a higher priority.
2069: *
2070: * @param compHandler the component handler to add
2071: * @param priority the priority
2072: */
2073: public void addComponentHandler(ComponentHandler compHandler,
2074: int priority) {
2075: if (compHandler != null) {
2076: if (priority >= 0) {
2077: compHandlers.add(compHandler, priority);
2078: } else {
2079: compHandlers.add(compHandler);
2080: }
2081: }
2082: }
2083:
2084: /**
2085: * Registers a component handler with the lowest priority.
2086: *
2087: * @param compHandler the component handler to add
2088: */
2089: public void addComponentHandler(ComponentHandler compHandler) {
2090: addComponentHandler(compHandler, -1);
2091: }
2092:
2093: /**
2094: * Returns the first component handler which feels responsible for the given component. If no
2095: * component handler is responsible, <code>null</code> will be returned.
2096: *
2097: * @param comp the component
2098: *
2099: * @return the first handler which feels responsible, or <code>null</code> if no handler is
2100: * responsible.
2101: */
2102: public ComponentHandler getFirstComponentHandler(Component comp) {
2103: ComponentHandler compHandler;
2104: Iterator i = compHandlers.iterator();
2105:
2106: while (i.hasNext()) {
2107: compHandler = (ComponentHandler) i.next();
2108:
2109: if (compHandler.handlesComponent(comp)) {
2110: return compHandler;
2111: }
2112: }
2113:
2114: return null;
2115: }
2116:
2117: /**
2118: * Loads the component handlers from the {@link jacareto.system.Customization} instance of the
2119: * {@link jacareto.system.Environment} object with the default class loader.
2120: */
2121: private void loadComponentHandlers() {
2122: loadComponentHandlers(null);
2123: }
2124:
2125: /**
2126: * Loads the component handlers from the {@link jacareto.system.Customization} instance of the
2127: * {@link jacareto.system.Environment} object with the given class loader.
2128: *
2129: * @param classLoader DOCUMENT ME!
2130: */
2131: private void loadComponentHandlers(ClassLoader classLoader) {
2132: Logger logger = getLogger();
2133: Language language = getLanguage();
2134:
2135: if (classLoader == null) {
2136: classLoader = ClassLoader.getSystemClassLoader();
2137: }
2138:
2139: try {
2140: // some reflection instances
2141: Class componentHandlerClass = Class
2142: .forName("jacareto.comp.ComponentHandler", true,
2143: classLoader);
2144: Class[] parameterTypes = new Class[2];
2145: parameterTypes[0] = Class.forName(
2146: "jacareto.system.Environment", true, classLoader);
2147: parameterTypes[1] = Class.forName(
2148: "jacareto.comp.Components", true, classLoader);
2149:
2150: Object[] parameters = new Object[2];
2151: parameters[0] = env;
2152: parameters[1] = this ;
2153:
2154: // load the map of specific event masks
2155: EnhancedHashtable compHandlersMap = getCustomization()
2156: .getMap("ComponentHandlers",
2157: new EnhancedHashtable());
2158: Enumeration enumeration = compHandlersMap.keys();
2159:
2160: while (enumeration.hasMoreElements()) {
2161: String compHandlerClassName = (String) enumeration
2162: .nextElement();
2163: Class compHandlerClass = Class.forName(
2164: compHandlerClassName, true, classLoader);
2165: Constructor compHandlerConstructor = compHandlerClass
2166: .getConstructor(parameterTypes);
2167:
2168: if (componentHandlerClass
2169: .isAssignableFrom(compHandlerClass)) {
2170: try {
2171: int priority = compHandlersMap.getInt(
2172: compHandlerClassName, -1);
2173:
2174: if (isDebugPrinting) {
2175: logger
2176: .debug(language
2177: .getString("Components.Msg.Loading")
2178: + " "
2179: + compHandlerClassName
2180: + ", "
2181: + language
2182: .getString("General.Priority")
2183: + ": " + priority);
2184: }
2185:
2186: addComponentHandler(
2187: (ComponentHandler) compHandlerConstructor
2188: .newInstance(parameters),
2189: priority);
2190: } catch (InstantiationException inst) {
2191: logger
2192: .error(
2193: language
2194: .getString("Components.Error.Construction"),
2195: inst);
2196: } catch (IllegalAccessException ill) {
2197: logger
2198: .error(
2199: language
2200: .getString("Components.Error.Construction"),
2201: ill);
2202: } catch (IllegalArgumentException ila) {
2203: logger
2204: .error(
2205: language
2206: .getString("Components.Error.Construction"),
2207: ila);
2208: } catch (InvocationTargetException inv) {
2209: logger
2210: .error(
2211: language
2212: .getString("Components.Error.Construction"),
2213: inv);
2214: }
2215: } else {
2216: logger.warn(language
2217: .getString("Components.Msg.NotSubclass")
2218: + ": " + compHandlerClassName);
2219: }
2220: }
2221: } catch (ClassNotFoundException c) {
2222: logger.error(language
2223: .getString("Components.Error.MissingClass"), c);
2224: } catch (NoSuchMethodException n) {
2225: logger.error(language
2226: .getString("Components.Error.MissingConstructor"),
2227: n);
2228: }
2229: }
2230:
2231: /**
2232: * ??? ???
2233: *
2234: * @return DOCUMENT ME!
2235: */
2236: /**
2237: * Returns the component listener.
2238: *
2239: * @return DOCUMENT ME!
2240: */
2241: public ComponentListener getComponentListener() {
2242: return componentListener;
2243: }
2244:
2245: /**
2246: * Returns the source class name belonging to a component. If the component is an instance of
2247: * an inner class, the superclass will be taken if there is one.
2248: *
2249: * @param component DOCUMENT ME!
2250: *
2251: * @return DOCUMENT ME!
2252: */
2253: public static String getClassName(Object component) {
2254: Class compClass = component.getClass();
2255: String result = compClass.getName();
2256:
2257: if (result.indexOf("$") >= 0) {
2258: compClass = compClass.getSuperclass();
2259: result = compClass.getName();
2260: }
2261:
2262: return result;
2263: }
2264:
2265: /**
2266: * DOCUMENT ME!
2267: *
2268: * @return HashTable
2269: */
2270: public Hashtable getNameToItem() {
2271: return nameToItem;
2272: }
2273:
2274: /**
2275: * DOCUMENT ME!
2276: *
2277: * @param hashtable
2278: */
2279: public void setNameToItem(Hashtable hashtable) {
2280: nameToItem = hashtable;
2281: }
2282:
2283: /**
2284: * LISTENER HANDLING Methods which handle and notify listeners
2285: *
2286: * @param listener DOCUMENT ME!
2287: */
2288: /**
2289: * Adds a components listeners.
2290: *
2291: * @param listener DOCUMENT ME!
2292: */
2293: public void addComponentsListener(ComponentsListener listener) {
2294: if (!componentsListeners.contains(listener)) {
2295: componentsListeners.add(listener);
2296: }
2297: }
2298:
2299: /**
2300: * Removes a components listeners.
2301: *
2302: * @param listener DOCUMENT ME!
2303: */
2304: public void removeComponentsListener(ComponentsListener listener) {
2305: if (componentsListeners.contains(listener)) {
2306: componentsListeners.remove(listener);
2307: }
2308: }
2309:
2310: /**
2311: * Fires a components event to all registered listeners.
2312: *
2313: * @param event DOCUMENT ME!
2314: */
2315: public void fireComponentsEvent(ComponentsEvent event) {
2316: if (firesComponentsEvents) {
2317: Iterator it = componentsListeners.iterator();
2318:
2319: while (it.hasNext()) {
2320: ComponentsListener listener = (ComponentsListener) it
2321: .next();
2322:
2323: if (event.getID() == ComponentsEvent.TREE_CHANGED) {
2324: listener.componentTreeChanged(event);
2325: } else if (event.getID() == ComponentsEvent.USER_DEFINED_NAME_SET) {
2326: listener.userDefinedNameSet(event);
2327: }
2328: }
2329: }
2330: }
2331: }
|