/******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.runtime; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import com.windowtester.internal.runtime.ClassReference; import com.windowtester.internal.runtime.IWidgetIdentifier; import com.windowtester.internal.runtime.bundle.IBundleReference; import com.windowtester.internal.runtime.finder.FinderFactory; /** * Base class for Widget locators. Widget locators capture hierarchy (containment) relationships between widgets for use * in widget identification. * <p> * For example, a widget identified by class and position relative to its * parent composite could be described so: * <pre> * new WidgetLocator(Text.class, 2, * new WidgetLocator(Group.class, "addressGroup")); * </pre> * * Effectively, this WidgetLocator instance describes the third Text widget in * the Group labeled "addressGroup". * */ public class WidgetLocator implements Serializable, IWidgetIdentifier, IAdaptable, com.windowtester.runtime.locator.IWidgetLocator { /* * NOTE: this class is serializable and uses the default serialization scheme. * This should _not_ be a problem (hierarchies are not too deep); still, we * could consider a custom serialization scheme. */ private static final long serialVersionUID = 7772528976750829834L; /** A sentinel value, indicating an unassigned index */ public static final int UNASSIGNED = -1; /** the reference to the target class */ private ClassReference classRef; /** The target widget's name or label */ private final String nameOrLabel; /** The target's index relative to its parent */ private int index; /** The target's parent info */ private WidgetLocator parentInfo; /** The target's ancestor info */ private WidgetLocator ancestorInfo; /** A map for associated data key-value pairs */ private HashMap<String, String> map; //required for subclass convenience protected WidgetLocator() { this(null); } /** * @since 3.8.1 */ protected WidgetLocator(ClassReference classRef, String nameOrLabel, int index, WidgetLocator parentInfo) { this.classRef = classRef; this.nameOrLabel = nameOrLabel; this.index = index; this.parentInfo = parentInfo; } /** * Create an instance. * @param cls - the target class * @param nameOrLabel - the target's name or label * @param index - the target's index relative to its parent * @param parentInfo - the target's parent info */ public WidgetLocator(Class<?> cls, String nameOrLabel, int index, WidgetLocator parentInfo) { this(ClassReference.forBundleClass(cls), nameOrLabel, index, parentInfo); } /** * Create an instance. * @param cls - the target class * @param index - the target's index relative to its parent * @param parentInfo - the target's parent info */ public WidgetLocator(Class<?> cls, int index, WidgetLocator parentInfo) { this(cls, null, index, parentInfo); } /** * Create an instance. * @param cls - the target class * @param nameOrLabel - the target's name or label * @param parentInfo - the target's parent info */ public WidgetLocator(Class<?> cls, String nameOrLabel, WidgetLocator parentInfo) { this(cls, nameOrLabel, UNASSIGNED, parentInfo); } /** * Create an instance. * @param cls - the target class * @param parentInfo - the target's parent info */ public WidgetLocator(Class<?> cls, WidgetLocator parentInfo) { this(cls, null, UNASSIGNED, parentInfo); } /** * Create an instance. * @param cls - the target class */ public WidgetLocator(Class<?> cls) { this(cls, (WidgetLocator)null); } /** * Create an instance. * @param cls - the target class * @param nameOrLabel - the target's name or label */ public WidgetLocator(Class<?> cls, String nameOrLabel) { this(cls, nameOrLabel, null); } /** * Create an instance. * @param cls - the target class * @param nameOrLabel - the target's name or label * @param index - the target's index relative to its parent */ public WidgetLocator(Class<?> cls, String nameOrLabel, int index) { this(cls, nameOrLabel, index, null); } /** * Create an instance. * @param cls - the target class * @param index - the target's index relative to its parent */ public WidgetLocator(Class<?> cls, int index) { this(cls, null, index, null); } /** * Accept this visitor. * @param visitor */ public void accept(IWidgetLocatorVisitor visitor) { visitor.visit(this); if (parentInfo != null) parentInfo.accept(visitor); } /** * Get the <code>WidgetLocator</code> that describes this widget's parent. * @return the parent's <code>WidgetLocator</code> object. */ public WidgetLocator getParentInfo() { return parentInfo; } /** * Get the <code>WidgetLocator</code> that describes one of the widget's ancestors. * @return the ancestor's <code>WidgetLocator</code> object. */ public WidgetLocator getAncestorInfo() { return ancestorInfo; } /** * Get the name or label String that helps identify this widget. * @return the subject's name or label */ public String getNameOrLabel() { return nameOrLabel; } /** * Get the subject widget's class. * @return the subject's class */ public Class<?> getTargetClass() { return classRef.getClassForName(); } /** * Get the subject widget's class, as a class reference. * @return the subject's class reference */ protected ClassReference getTargetClassRef() { return classRef; } /** * Get the subject widget's class name * @return the subject's class name */ public String getTargetClassName(){ if (classRef != null) return classRef.getName(); else return null; } /** * Set the parent <code>WidgetLocator</code>. * @param parentInfo - the new parent <code>WidgetLocator</code> */ public void setParentInfo(WidgetLocator parentInfo) { this.parentInfo = parentInfo; } /** * Set the ancestor <code>WidgetLocator</code>. * @param ancestorInfo - the new ancestor <code>WidgetLocator</code> */ public void setAncestorInfo(WidgetLocator ancestorInfo) { this.ancestorInfo = ancestorInfo; } /** * Get this widget's index relative to its parent widget. * @return a 0-based relative index or UNASSIGNED if it is not indexed. */ public int getIndex() { return index; } /** * Set this widget's index relative to it's parent. * @param index - the index. */ public void setIndex(int index) { this.index = index; } /** * Get a String representation of this <code>WidgetLocator</code>. * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); String name = getWidgetLocatorStringName(); if (name != null) { sb.append(name).append("("); } else { sb.append("WidgetLocator(").append(classRef.getName()); if (getNameOrLabel() != null) sb.append(", "); } if (getNameOrLabel() != null) { sb.append('\"').append(getNameOrLabel()).append('\"'); if (getIndex() != UNASSIGNED || getParentInfo() != null) sb.append(", "); } if (index != UNASSIGNED) { sb.append(index).append(", "); } if (parentInfo != null) sb.append(parentInfo); sb.append(")"); return sb.toString(); } //Override to customize Locator name used in toString(). protected String getWidgetLocatorStringName() { //default is null return null; } /** * Indicates whether some other object is "equal to" this one. * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { //null check if (o == null) return false; //type check if (!(o instanceof WidgetLocator)) return false; //self check if (o == this) return true; WidgetLocator other = (WidgetLocator)o; //check class //if (!_classRef.getClassForName().equals(other.getTargetClass())) ClassReference targetClassRef = other.getTargetClassRef(); if (classRef == null && targetClassRef != null) return false; if (targetClassRef == null && classRef != null) return false; if (classRef != null && targetClassRef != null && !classRef.equals(targetClassRef)) return false; //check name if (nameOrLabel == null) { if (other.nameOrLabel != null) return false; } else { if (!nameOrLabel.equals(other.nameOrLabel)) return false; } //check index if (index != other.index) return false; //check parent if (parentInfo == null) { if (other.parentInfo != null) return false; } else { if (!parentInfo.equals(other.parentInfo)) return false; } //check data map if (map == null) { if (other.map != null && !other.map.isEmpty()) return false; } else { if (!map.equals(other.map)) return false; } //fall through return true; } /** * Returns a hash code value for this <code>WidgetLocator</code> object. * @see java.lang.Object#hashCode() */ public int hashCode() { int result = 13; result = 37*result + index; //result = 37*result + ((_cls == null) ? 0 : + _cls.hashCode()); result = 37*result + ((classRef == null) ? 0 : + classRef.hashCode()); result = 37*result + ((nameOrLabel == null) ? 0 : + nameOrLabel.hashCode()); result = 37*result + ((parentInfo == null) ? 0 : + parentInfo.hashCode()); result += (map == null) ? 0 : map.hashCode(); return result; } /** * Returns the programmer defined property of the receiver * with the specified name, or null if it has not been set. * <p> * Data mappings allow programmers to associate arbitrary key-value * pairs with locator instances. * </p> * * @param key the name of the property * @return the value of the property or null if it has not been set * * @exception IllegalArgumentException if the key is null * * */ public void setData(String key, String value) { /* * NOTE: keys and values are NOT objects (as in widgets) * this is because locators need to be serializable. * An alternative is to have the be ISerializables... * we could widen the interface if need be. */ if (key == null) throw new IllegalArgumentException("key must not be null"); getDataMap().put(key, value); } /** * Returns the programmer defined property of the receiver * with the specified name, or null if it has not been set. * <p> * Data mappings allow programmers to associate arbitrary key-value * pairs with locator instances. * </p> * * * @param key the name of the property * @return the value of the property or null if it has not been set * * @exception IllegalArgumentException if the key is null */ public String getData (String key) { if (key == null) throw new IllegalArgumentException("key must not be null"); return getDataMap().get(key); } /** * Copies all programmer defined properties of the receiver * into the specified locator. * * @param locator the locator */ public void copyDataTo(WidgetLocator locator) { if (map != null && locator != null) map.putAll(locator.getDataMap()); } private Map<String, String> getDataMap() { if (map == null) map = new HashMap<String, String>(); return map; } /* (non-Javadoc) * @see com.windowtester.runtime.IAdaptable#getAdapter(java.lang.Class) */ public Object getAdapter(Class<?> adapter) { if (adapter == IBundleReference.class) if (classRef instanceof IBundleReference) return (IBundleReference)classRef; return null; } ///////////////////////////////////////////////////////////////////////// // // Widget Finding // ///////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.locator.IWidgetLocator#findAll(com.windowtester.runtime.IUIContext) */ public com.windowtester.runtime.locator.IWidgetLocator[] findAll(IUIContext ui) { //get the appropriate finder for this ui context return FinderFactory.getFinder(ui).findAll(this); } public boolean matches(Object widget) { // TODO Auto-generated method stub return false; } }