/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.common; import java.net.URLClassLoader; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import org.apache.commons.lang.Validate; import org.eclipse.jubula.rc.common.businessprocess.ReflectionBP; import org.eclipse.jubula.rc.common.classloader.ImplClassClassLoader; import org.eclipse.jubula.rc.common.exception.UnsupportedComponentException; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.tools.internal.exception.ConfigXmlException; import org.eclipse.jubula.tools.internal.i18n.CompSystemI18n; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.jubula.tools.internal.objects.MappingConstants; import org.eclipse.jubula.tools.internal.xml.businessmodell.Component; import org.eclipse.jubula.tools.internal.xml.businessmodell.ComponentClass; import org.eclipse.jubula.tools.internal.xml.businessmodell.ConcreteComponent; import org.eclipse.jubula.tools.internal.xml.businessmodell.Profile; /** * This class contains the configuration for the AUTServer at runtime. It's a * singleton. <br> * * It comprises the supported components (with their implementation classes). * <br> * The implementation classes are cached. * * @author BREDEX GmbH * @created 26.08.2004 */ public class AUTServerConfiguration { /** the logger */ private static AutServerLogger log = new AutServerLogger( AUTServerConfiguration.class); /** the singleton instance */ private static AUTServerConfiguration instance = null; /** the map for the implementation class names: * key = component class name, value = implementation class name */ private Map<String, String> m_implClassNames; /** * A cache for implementation class instances * key = implementation class name value = implementation class instance */ private Map<String, Object> m_implClassCache; /** * profile for fuzzy logic */ private Profile m_profile; /** * keyboard shortcut for starting Mapping/Records Mode Modifier */ private int m_mappingKeyMod; /** * keyboard shortcut for mapping of components together with their parents */ private int m_mappingWithParentsKeyMod; /** * keyboard shortcut for starting Mapping/Record Mode */ private int m_mappingKey; /** * keyboard shortcut for mapping of components together with their parents */ private int m_mappingWithParentsKey; /** * mouse button for collecting a UI element in Mapping Mode */ private int m_mappingMouseButton; /** * mouse button for collecting a UI element together with its parents in Mapping Mode */ private int m_mappingWithParentsMouseButton; /** * keyboard shortcut for starting Mapping/Records Mode Modifier */ private int m_key2Mod; /** * keyboard shortcut for starting Mapping/Record Mode */ private int m_key2; /** * keyboard shortcut for starting Check Mode */ private int m_checkModeKeyMod; /** * keyboard shortcut for starting Check Mode */ private int m_checkModeKey; /** * keyboard shortcut for check current component */ private int m_checkCompKeyMod; /** * keyboard shortcut for check current component */ private int m_checkCompKey; /** * singleLineTrigger for Observation Mode */ private SortedSet m_singleLineTrigger; /** * multiLineTrigger for Observation Mode */ private SortedSet m_multiLineTrigger; /** * the complete list of supported components, what actions are supported etc. */ private Set<ConcreteComponent> m_components; /** * the set of actually supported component class names */ private Set<ComponentClass> m_supportedComponentTypes; /** * private constructor (singleton) <br> * initializes the cache */ private AUTServerConfiguration() { m_implClassNames = new HashMap<String, String>(); m_implClassCache = new HashMap<String, Object>(); m_components = new HashSet<ConcreteComponent>(); } /** * @return Returns the instance. */ public static AUTServerConfiguration getInstance() { if (instance == null) { instance = new AUTServerConfiguration(); } return instance; } /** * returns true if the type of <code>component</code> is supported * * @param graphicsComponent * any instance of the component to check, must not be null * @throws IllegalArgumentException * if component is null * @return true if the type of <code>component</code> is supported, false * otherwise */ public boolean isSupported(Object graphicsComponent) throws IllegalArgumentException { Validate.notNull(graphicsComponent, "graphics component must not be null"); //$NON-NLS-1$ final String className = graphicsComponent.getClass().getName(); if (className.equals(MappingConstants .SWING_MENU_DEFAULT_MAPPING_CLASSNAME)) { return true; } return m_implClassNames.containsKey(className); } /** * Registers an instance of the implementation class for a component. * * @param componentClassName * The name of the component, e.g. * <code>javax.swing.JButton</code> * @param implClassName * The name of the implementation class * @throws IllegalArgumentException * if the <code>componentClassName</code> or the * <code>implClassName</code> is <code>null</code>. */ private void registerImplementationClass(String componentClassName, String implClassName) throws IllegalArgumentException { Validate.notNull(componentClassName); Validate.notNull(implClassName); m_implClassNames.put(componentClassName, implClassName); } /** * Returns an instance of the implementation class for * <code>componentClassName</code>. * * @param componentClass * the class name of the component, e.g javax.swing.JButton * @throws UnsupportedComponentException * If the <code>componentClassName</code> has no registered * implementation class. * @throws IllegalArgumentException * if the <code>componentClassName</code> is <code>null</code>. * @return An instance of the implementation class. */ public Object getImplementationClass(Class componentClass) throws UnsupportedComponentException, IllegalArgumentException { Validate.notNull(componentClass); Class currentClass = componentClass; String implClassName = m_implClassNames.get( currentClass.getName()); while (implClassName == null && currentClass.getSuperclass() != null) { currentClass = currentClass.getSuperclass(); implClassName = m_implClassNames.get( currentClass.getName()); } return createInstance(componentClass.getName(), implClassName, currentClass.getClassLoader()); } /** * Returns an instance of the implementation class for * <code>componentClassName</code>. * * @param componentClassName * the class name of the component, e.g javax.swing.JButton * @throws UnsupportedComponentException * If the <code>componentClassName</code> has no registered * implementation class. * @throws IllegalArgumentException * if the <code>componentClassName</code> is <code>null</code>. * @return An instance of the implementation class. */ public Object getImplementationClass(String componentClassName) throws UnsupportedComponentException, IllegalArgumentException { Validate.notNull(componentClassName); String implClassName = m_implClassNames.get( componentClassName); return createInstance(componentClassName, implClassName, null); } /** * * @param componentClassName * the class name of the component * @param implClassName * the tester class name of the component * @param classLoader * the classloader which should be used * @return an instance of the tester class, either from the cache or newly * instantiated * @throws UnsupportedComponentException */ private Object createInstance(String componentClassName, String implClassName, ClassLoader classLoader) throws UnsupportedComponentException { if (implClassName != null) { Class implClass = null; Object implInstance = m_implClassCache.get(implClassName); if (implInstance != null) { return implInstance; } try { if (log.isDebugEnabled()) { log.debug("Loading ImplementationClass: " //$NON-NLS-1$ + "'" + String.valueOf(implClassName) + "'" //$NON-NLS-1$ //$NON-NLS-2$ + " with ClassLoader: " //$NON-NLS-1$ + "'" + String.valueOf(classLoader) + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } implClass = loadImplementationClass(implClassName, classLoader); implInstance = implClass.newInstance(); if (!m_implClassCache.containsKey(implClassName)) { m_implClassCache.put(implClassName, implInstance); } } catch (InstantiationException ie) { log.error(ie); throw new UnsupportedComponentException( "component '" + implClass.getName() //$NON-NLS-1$ + "' could not be instantiated", MessageIDs.E_INSTANTIATION); //$NON-NLS-1$ } catch (IllegalAccessException iae) { log.error(iae); throw new UnsupportedComponentException( "component '" + implClass.getName() //$NON-NLS-1$ + "' could not be accessed", MessageIDs.E_ILLEGAL_ACCESS); //$NON-NLS-1$ } catch (ClassNotFoundException cnfe) { log.error(cnfe); throw new UnsupportedComponentException( "component '" + componentClassName //$NON-NLS-1$ + "' is not supported: implementation class '" //$NON-NLS-1$ + implClassName + "' not found", MessageIDs.E_COMPONENT_UNSUPPORTED); //$NON-NLS-1$ } return implInstance; } throw new UnsupportedComponentException( "component '" + componentClassName //$NON-NLS-1$ + "' is not supported", MessageIDs.E_COMPONENT_UNSUPPORTED); //$NON-NLS-1$ } /** * Loads the ImplementationClass * @param implClassName the name of the ImplementationClass * @param componentClassLoader the ClassLoader of the Component * @return the loaded ImplementationClass * @throws ClassNotFoundException in case of class not found */ private Class loadImplementationClass(String implClassName, ClassLoader componentClassLoader) throws ClassNotFoundException { if (AUTServer.getInstance().isRcpAccessible()) { return this.getClass().getClassLoader().loadClass(implClassName); } URLClassLoader thisCL = (URLClassLoader)this.getClass() .getClassLoader(); ClassLoader implCL = new ImplClassClassLoader(thisCL, componentClassLoader); return implCL.loadClass(implClassName); } /** * Returns the testable class for a given component class * <code>componentClassName</code>. * * @param component the component, e.g javax.swing.JButton * @throws IllegalArgumentException If the <code>componentClassName</code> has no registered implementation class. * @return An instance of the implementation class. */ public Class getTestableClass( Class component) throws IllegalArgumentException { Validate.notNull(component); Class componentClass = component; while (componentClass.getSuperclass() != null) { String componentClassName = componentClass.getName(); if (m_implClassNames.containsKey(componentClassName)) { return componentClass; } componentClass = componentClass.getSuperclass(); } throw new IllegalArgumentException( "component '" + component.getName() //$NON-NLS-1$ + "' is not supported"); //$NON-NLS-1$ } /** * Prepares the implementation class, which has been registered for the * passed component class name, by setting the passed graphics component * instance. After this call, the action method of the returned * implementation class can be invoked. * @param graphicsComponent The graphics component. * @param componentClass the class name of the component, e.g javax.swing.JButton * @return The implementation class. * @throws UnsupportedComponentException If the component identified by the * <code>componentClassName</code> is not supported, that * means, if there is no mapped implementation class. * @throws IllegalArgumentException If the <code>graphicsComponent</code> or * <code>componentClassName</code> is <code>null</code>. */ public Object prepareImplementationClass(Object graphicsComponent, Class componentClass) throws UnsupportedComponentException, IllegalArgumentException { Validate.notNull(graphicsComponent); Object implClass = getImplementationClass(componentClass); ReflectionBP.invokeMethod("setComponent", implClass, //$NON-NLS-1$ new Class[] {Object.class}, new Object[] {graphicsComponent}); return implClass; } /** * @return Returns the components. */ public Set<ConcreteComponent> getComponents() { return m_components; } /** * Registers a component. An implementation class instance is created and registered. * * @param c The component to register. * @throws IllegalArgumentException if the <code>componentClassName</code> or the * <code>implClassName</code> is <code>null</code>. */ public void registerComponent(ConcreteComponent c) throws IllegalArgumentException { m_components.add(c); registerImplementationClass( c.getComponentClass().getName(), c.getTesterClass()); } /** * Returns the component with the specified typeName. * * @param typeName * Name of the specified component. * @return the specified Component. */ public Component findComponent(String typeName) { Validate.notNull(typeName); Set<ConcreteComponent> list = getComponents(); for (ConcreteComponent cc : list) { if (cc.getComponentClass() != null && cc.getComponentClass().getName().equals(typeName)) { return cc; } } String message = "Component " + typeName //$NON-NLS-1$ + " does not exist"; //$NON-NLS-1$ log.error(message); throw new ConfigXmlException(message, MessageIDs.E_NO_COMPONENT); } /** * Returns the components with the specified typeName. * @param typeName Name of the specified component. * @return the specified Components. */ public List<ConcreteComponent> findComponents(String typeName) { Validate.notNull(typeName); Set<ConcreteComponent> list = getComponents(); List<ConcreteComponent> comps = new LinkedList<ConcreteComponent>(); for (ConcreteComponent cc : list) { if (cc.getComponentClass() != null && cc.getComponentClass().getName().equals(typeName)) { comps.add(cc); } } if (!(comps.isEmpty())) { return comps; } String message = "Component " + typeName //$NON-NLS-1$ + " does not exist"; //$NON-NLS-1$ log.error(message); throw new ConfigXmlException(message, MessageIDs.E_NO_COMPONENT); } /** * gets the jubula component name for a java component * @param component java component * @return jubula component type */ public String getComponentName (Object component) { String gdCompName = null; try { String compType = getTestableClass(component.getClass()).getName(); Component gdComp = findComponent(compType); while (gdComp != null && (!gdComp.isVisible() || !gdComp.isObservable()) && !gdComp.getRealized().isEmpty()) { List realizedComponents = gdComp.getRealized(); gdComp = (Component)realizedComponents.get(0); } if (gdComp.getType() != null) { gdCompName = CompSystemI18n.getString(gdComp.getType()); } } catch (IllegalArgumentException iae) { log.error("componentClassname has no registrered implementation class", //$NON-NLS-1$ iae); } return gdCompName; } /** * @return Returns the profile. */ public Profile getProfile() { return m_profile; } /** * @param p The profile to set. */ public void setProfile(Profile p) { m_profile = p; } /** * @return Returns the key for mapping components . */ public int getMappingKey() { return m_mappingKey; } /** * @param key The key for mapping components to set. */ public void setMappingKey(int key) { m_mappingKey = key; } /** * @return Returns the key for mapping components . */ public int getMappingWithParentsKey() { return m_mappingWithParentsKey; } /** * @param key The key for mapping components to set. */ public void setMappingWithParentsKey(int key) { m_mappingWithParentsKey = key; } /** * @param mouseButton the mouseButton to set */ public void setMappingMouseButton(int mouseButton) { m_mappingMouseButton = mouseButton; } /** * @return the mouseButton */ public int getMappingMouseButton() { return m_mappingMouseButton; } /** * @param mouseButton the mouseButton to set */ public void setMappingWithParentsMouseButton(int mouseButton) { m_mappingWithParentsMouseButton = mouseButton; } /** * @return the mouseButton */ public int getMappingWithParentsMouseButton() { return m_mappingWithParentsMouseButton; } /** * @return Returns the keyMod. */ public int getMappingKeyMod() { return m_mappingKeyMod; } /** * @param keyMod The keyMod to set. */ public void setMappingKeyMod(int keyMod) { m_mappingKeyMod = keyMod; } /** * @return Returns the keyMod. */ public int getMappingWithParentsKeyMod() { return m_mappingWithParentsKeyMod; } /** * @param keyMod The keyMod to set. */ public void setMappingWithParentsKeyMod(int keyMod) { m_mappingWithParentsKeyMod = keyMod; } /** * @return Returns the key for Application record */ public int getKey2() { return m_key2; } /** * @param key2 The key to set. */ public void setKey2(int key2) { m_key2 = key2; } /** * @return Returns the keyMod for Application component. */ public int getKey2Mod() { return m_key2Mod; } /** * @param mod the keyMod for Application component to set. */ public void setKey2Mod(int mod) { m_key2Mod = mod; } /** * @return Returns the key for checkMode */ public int getCheckModeKey() { return m_checkModeKey; } /** * @param checkModeKey The checkModeKey to set. */ public void setCheckModeKey(int checkModeKey) { m_checkModeKey = checkModeKey; } /** * @return Returns the checkModeKeyMod for checkMode. */ public int getCheckModeKeyMod() { return m_checkModeKeyMod; } /** * @param checkModeKeyMod the checkModeKeyMod to set. */ public void setCheckModeKeyMod(int checkModeKeyMod) { m_checkModeKeyMod = checkModeKeyMod; } /** * @return the checkCompKeyMod */ public int getCheckCompKeyMod() { return m_checkCompKeyMod; } /** * @param checkCompKeyMod the checkCompKeyMod to set */ public void setCheckCompKeyMod(int checkCompKeyMod) { m_checkCompKeyMod = checkCompKeyMod; } /** * @return the checkCompKey */ public int getCheckCompKey() { return m_checkCompKey; } /** * @param checkCompKey the checkCompKey to set */ public void setCheckCompKey(int checkCompKey) { m_checkCompKey = checkCompKey; } /** * @return the singleLineTrigger for Observation Mode */ public SortedSet getSingleLineTrigger() { return m_singleLineTrigger; } /** * @param singleLineTrigger singleLineTrigger for Observation Mode */ public void setSingleLineTrigger(SortedSet singleLineTrigger) { m_singleLineTrigger = singleLineTrigger; } /** * @return the multiLineTrigger for Observation Mode */ public SortedSet getMultiLineTrigger() { return m_multiLineTrigger; } /** * @param multiLineTrigger multiLineTrigger for Observation Mode */ public void setMultiLineTrigger(SortedSet multiLineTrigger) { m_multiLineTrigger = multiLineTrigger; } /** * @return a set of supported type identifier */ public Set<ComponentClass> getSupportedTypes() { if (m_supportedComponentTypes != null) { return m_supportedComponentTypes; } m_supportedComponentTypes = new HashSet<ComponentClass>(); Set<ConcreteComponent> supportedComponents = AUTServerConfiguration .getInstance().getComponents(); Iterator<ConcreteComponent> iterator = supportedComponents.iterator(); while (iterator.hasNext()) { ConcreteComponent c = iterator.next(); if (!c.hasDefaultMapping()) { List ccl = c.getCompClass(); Iterator compClassIterator = ccl.iterator(); while (compClassIterator.hasNext()) { ComponentClass cc = (ComponentClass) compClassIterator.next(); m_supportedComponentTypes.add(cc); } } } return m_supportedComponentTypes; } }