/******************************************************************************* * 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.tools.internal.xml.businessmodell; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.lang.Validate; import org.apache.commons.lang.builder.ToStringBuilder; import org.eclipse.jubula.tools.internal.exception.ConfigXmlException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class represents Components which can be tested by Jubula. * * @author BREDEX GmbH * @created 08.07.2005 */ public abstract class Component { /** * the separator to use when listing action/method arguments */ private static final String ARG_SEPARATOR = ", "; //$NON-NLS-1$ /** * The logger */ private static Logger log = LoggerFactory.getLogger(Component.class); /** * List of directly realized Components * ("direct superclasses"). */ private List<Component> m_realized = new ArrayList<Component>(); /** List of extended Components. */ private List<String> m_extendedTypes = new ArrayList<String>(); /** * List of type names of directly realized AbstractComponents * ("direct superclasses"). */ private List<String> m_realizedTypes = new ArrayList<String>(); /** * Set of all ConcreteComponent classes realizing this * Component. For Concrete Components, this set contains exactly * this ConcreteComponent itself. */ private Set<ConcreteComponent> m_realizers = new HashSet<ConcreteComponent>(); /** * Set of all Component classes realizing this * Component. */ private transient Set<Component> m_allRealizers = new HashSet<Component>(); /** Action list of the component. */ private List<Action> m_actions = new ArrayList<Action>(); /** The name of the component. */ private String m_type; /** a description key for this component */ private String m_descriptionKey; /** The descriptor of the ToolkitPlugin of this Component */ private ToolkitDescriptor m_toolkitDesriptor; /** * <code>m_completionStarted</code> flag indicating * completeActions has started. See also m_completionDone. */ private boolean m_completionStarted = false; /** Whether this Component is deprecated or not */ private boolean m_deprecated = false; /** * <code>m_visible</code> property indicating whether this * Component shall be usable within the client UI */ private boolean m_visible = true; /** * <code>m_completionDone</code> flag indicating * completeActions has been called once before and * is ready. If m_completionStarted is set but not * m_completionDone during a completeActions call, we * found a reference loop. */ private boolean m_completionDone = false; /** * <code>m_observable</code> property indicating whether this * Component is observable */ private boolean m_observable = true; /** * Set this true if the API representation has to be the most concrete class. * This means that the Factory responsible for creating the component will * return the corresponding most concrete component class instead of the most * concrete visible one. */ private boolean m_apiMostConcrete = false; /** * The version of the bundle (important for semantic versions) that's * initially been present for that new component */ private String m_since; /** Default constructor. Do nothing. */ public Component() { super(); } /** * @return Returns the list actions. */ public List<Action> getActions() { return m_actions; } /** * @return the directly realized "super"-components */ public List<Component> getRealized() { return m_realized; } /** * @return the directly realized "super"-component's type names */ public List<String> getRealizedTypes() { return m_realizedTypes; } /** * @return all directly or indirectly realized "super"-components * (excluding this Component itself) */ public Set<Component> getAllRealized() { Set<Component> result = new HashSet<Component>(); result.addAll(m_realized); Iterator<Component> realizedIt = m_realized.iterator(); while (realizedIt.hasNext()) { Component comp = realizedIt.next(); result.addAll(comp.getAllRealized()); } return result; } /** * Checks if the passed type is realized by this component. This method gets * the same result as <code>getRealizedTypes().contains(type)</code>. * * @param type * The type to check * @return <code>true</code> if this component realizes <code>type</code>. */ public boolean isRealizing(String type) { Validate.notNull(type, "The component type name must not be null"); //$NON-NLS-1$ Iterator<Component> realizedIt = getAllRealized().iterator(); while (realizedIt.hasNext()) { Component comp = realizedIt.next(); if (type.equals(comp.getType())) { return true; } } return false; } /** * @return true if this Component is realizing another component, false * otherwise. */ public boolean isRealizer() { return !m_realized.isEmpty(); } /** * Checks if the component can be used for the given type. * * @param type * The type to check (i18n key). * @return <code>true</code> if this component is of the same type as * <code>type</code> or realizes <code>type</code>. */ public boolean isCompatibleWith(String type) { return getType().equals(type) || isRealizing(type); } /** * @return Returns the type. */ public String getType() { return m_type; } /** * @param type The type to set. */ public void setType(String type) { m_type = type; } /** * Returns the size of the actions list. * @return an <code>int</code> value. */ public int getActionsSize() { return m_actions != null ? m_actions.size() : 0; } /** * Adds an action to the action list. * @param action an <code>Action</code> object. */ public void addAction(Action action) { Iterator<Action> actionIt = m_actions.iterator(); while (actionIt.hasNext()) { Action present = actionIt.next(); if (present.equals(action)) { return; // just ignore the second // attempt to add the same // action, may be perfectly legal. } if (present.getName().equals(action.getName())) { final String msg = "duplicate definition of Action " //$NON-NLS-1$ + action.getName() + " at component " + getType() //$NON-NLS-1$ + ": method " + present.getMethod() //$NON-NLS-1$ + " vs. method " + action.getMethod(); //$NON-NLS-1$ log.error(msg); throw new ConfigXmlException(msg, MessageIDs.E_DUPLICATE_ACTION); } } m_actions.add(action); } /** * Returns the action with the specified name. * @param name The name of the specified action * @return the specified action. */ public Action findAction(String name) { Validate.notNull(name); List<Action> actionList = getActions(); Iterator<Action> actionIt = actionList.iterator(); while (actionIt.hasNext()) { Action action = actionIt.next(); if (name.equals(action.getName())) { return action; } } String message = "Action " //$NON-NLS-1$ + name + " not found within component " //$NON-NLS-1$ + m_type; log.debug(message); return new InvalidAction(); } /** * @param methodName * The method name. * @param argTypes * The method's argument types. * @return The action for the passed method signature. */ public Action findActionByMethodSignature(String methodName, String[] argTypes) { Validate.notNull(methodName); List<Action> actionList = getActions(); Iterator<Action> actionIt = actionList.iterator(); while (actionIt.hasNext()) { Action action = actionIt.next(); if (isSignatureMatch(action, methodName, argTypes)) { return action; } } StringBuffer sb = new StringBuffer("Action "); //$NON-NLS-1$ sb.append(methodName).append("("); //$NON-NLS-1$ for (int i = 0; i < argTypes.length; i++) { sb.append(argTypes[i]); if (i < argTypes.length - 1) { sb.append(ARG_SEPARATOR); } } sb.append(") does not exist."); //$NON-NLS-1$ String message = sb.toString(); log.error(message); throw new ConfigXmlException(message, MessageIDs.E_NO_ACTION); } /** * Checks the given method signatures for equality. * * @param action One of the method signatures to check. * @param methodName The name of the second method to check. * @param argTypes The argument types of the second method to check. * @return <code>true</code> if the given method signatures match. * Otherwise, <code>false</code>. */ private boolean isSignatureMatch(Action action, String methodName, String[] argTypes) { if (methodName.equals(action.getMethod())) { // name matches, do arguments match? List actionParams = action.getParams(); if (actionParams.size() != argTypes.length) { return false; } for (int i = 0; i < argTypes.length; i++) { Param param = (Param)actionParams.get(i); if (!param.getType().equals(argTypes[i])) { return false; } } // name and argument types match return true; } return false; } /** * @return true if this Component is of type ConcreteComponent */ public boolean isConcrete() { return false; /* this method is overridden in ConcreteComponent */ } /** * @param realized the type name of a realized AbstractComponent * to add */ public void addRealizedType(String realized) { m_realizedTypes.add(realized); } /** * @param realized a realized Component * to add */ public void addRealized(Component realized) { m_realized.add(realized); } /** * @return Returns the realizers (all ConcreteComponents * that directly or indirectly realize this Component. * For ConcreteComponents themselves, this is a set with exactly * one element, namely itself.) */ public Set<ConcreteComponent> getRealizers() { return m_realizers; } /** * @return Returns the realizers (all Components * that directly or indirectly realize this Component. */ public Set<Component> getAllRealizers() { return m_allRealizers; } /** * @param realizer the realizing ConcreteComponent to add * @return true if the realizer wasn't present before */ boolean addRealizer(ConcreteComponent realizer) { return m_realizers.add(realizer); } /** * @param realizer the realizing Component to add * @return true if the realizer wasn't present before */ boolean addAllRealizer(Component realizer) { return m_allRealizers.add(realizer); } /** * removes the given list of realizers from the "allRealizers" list * @param realizers the realizing Components to remove */ void removeFromAllRealizer(List<? extends Component> realizers) { m_allRealizers.removeAll(realizers); } /** * "pull" all Actions from realized (= "derived from") super-components * * @param compSystem * the component system this component resides in */ void completeActions(CompSystem compSystem) { if (m_completionDone) { return; } if (m_completionStarted) { log.info("realization loop in component configuration for " //$NON-NLS-1$ + getType() + " (may be regarded as an error?)"); //$NON-NLS-1$ } resolveRealized(compSystem); m_completionStarted = true; Iterator<Component> realIt = m_realized.iterator(); while (realIt.hasNext()) { Component realized = realIt.next(); realized.completeActions(compSystem); Iterator<Action> actionIt = realized.getActions().iterator(); while (actionIt.hasNext()) { addAction(actionIt.next()); } } m_completionDone = true; } /** * @param compSystem * the component system this component resides in */ private void resolveRealized(CompSystem compSystem) { if (m_realized.isEmpty() && !m_realizedTypes.isEmpty()) { Iterator<String> typeIt = m_realizedTypes.iterator(); while (typeIt.hasNext()) { String type = typeIt.next(); Component comp = compSystem.findComponent(type); if (!(comp instanceof InvalidComponent)) { addRealized(comp); } } completeActions(compSystem); } } /** * Returns a string representation of the component object. * * @return The string * {@inheritDoc} */ public String toString() { return new ToStringBuilder(this).append("Type", getType()).append(//$NON-NLS-1$ "Actions", getActions()).toString(); //$NON-NLS-1$ } /** * @param obj * The object to compare. * @return <code>true</code> if the passed object is an instance of this * <code>Component</code> and the <code>type</code> s are equal. * * {@inheritDoc} */ public boolean equals(Object obj) { if (!(obj instanceof Component)) { return false; } Component rhs = (Component)obj; if (getType() != null) { return getType().equals(rhs.getType()); } return false; } /** * @return The hash code build from <code>type</code>. */ public int hashCode() { if (getType() != null) { return getType().hashCode(); } return super.hashCode(); } /** * @return the deprecated */ public boolean isDeprecated() { return m_deprecated; } /** * @param deprecated the deprecated to set */ public void setDeprecated(boolean deprecated) { m_deprecated = deprecated; } /** * @param visible the visible to set */ public void setVisible(boolean visible) { m_visible = visible; } /** * @return If <code>true</code>, this <code>Component</code> * shall be visible and usable within the client gui. */ public boolean isVisible() { return m_visible; } /** * @param observable the observable to set */ public void setObservable(boolean observable) { m_observable = observable; } /** * @return If <code>true</code>, this <code>Component</code> * is observable */ public boolean isObservable() { return m_observable; } /** * Gets the valid staus of the Component.<br> * Normally, all Components should return true. Only the InvalidComponent * must return false. * @return true if this component is valid, dalse otherwise. */ public boolean isValid() { return true; } /** * @return the description of the Toolkit of this Component */ public ToolkitDescriptor getToolkitDesriptor() { return m_toolkitDesriptor; } /** * @param toolkitDesriptor the toolkitDesriptor to set */ public void setToolkitDesriptor(ToolkitDescriptor toolkitDesriptor) { m_toolkitDesriptor = toolkitDesriptor; } /** * @return the directly extended "super"-component's type names */ public List<String> getExtendedTypes() { return m_extendedTypes; } /** * @param extended the type name of an extended Component * to add */ public void addExtendedType(String extended) { m_extendedTypes.add(extended); } /** * * @return true if this Component extends another Component. */ public boolean isExtender() { return !m_extendedTypes.isEmpty(); } /** * @return the descriptionKey */ public String getDescriptionKey() { return m_descriptionKey; } /** * @param descriptionKey the descriptionKey to set */ public void setDescriptionKey(String descriptionKey) { m_descriptionKey = descriptionKey; } /** * @return the APIMostConcrete flag */ public boolean isAPIMostConcrete() { return m_apiMostConcrete; } /** * @return the since */ public String getSince() { return m_since; } /** * @param since the since to set */ public void setSince(String since) { m_since = since; } }