package de.ugoe.cs.eventbench.jfc.data; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections15.CollectionUtils; /** *

* This class implements a comparator for target string for JFC GUIs. It * internally maintains a collection of all targets that have been compared, to * ensure transitivity of the equals relation. This memory can always be deleted * by calling {@link #reset()}. *

* * @author Steffen Herbold * @version 1.0 */ public class JFCTargetComparator { private static boolean mutable = true; private static Set knownTargets = new LinkedHashSet(); private static Map> equalTargets; public static void setMutable(boolean mutable) { if( JFCTargetComparator.mutable==true && mutable == false ) { equalTargets = new HashMap>(); for( String target1 : knownTargets ) { Set curEqualTargets = new HashSet(); for( String target2 : knownTargets ) { if( compare(target1, target2) ) { curEqualTargets.add(target2); } } equalTargets.put(target1, curEqualTargets); } } JFCTargetComparator.mutable = mutable; } /** *

* Compares to target strings. The strings are equal, if *

    *
  • the class, index, and text of all widgets are equal
  • *
  • either the title or the hashCode of all widgets are equal
  • *
  • either the title or the hashCode has been observed in one equal * instance of a widget, for all widgets.
  • *
*

*

* All target strings are remembered internally, to be able to test for the * third property. *

* * @param target1 * first target string * @param target2 * second target string * @return true, if both targets are equal; false otherwise */ public static boolean compare(String target1, String target2) { boolean result = false; if( mutable ) { instance.addTarget(target1); instance.addTarget(target2); knownTargets.add(target1); knownTargets.add(target2); JFCWidget widget1 = instance.find(target1); JFCWidget widget2 = instance.find(target2); result = (widget1==widget2); } if( !mutable ) { Set curEquals = equalTargets.get(target1); if( curEquals!=null ) { result = curEquals.contains(target2); } } return result; } /** *

* Resets the internal memory of targets. *

*/ public static void reset() { instance = new JFCTargetComparator(); } /** *

* Internal handle to the instance of this class (implemented as * Singleton!). *

*/ private static JFCTargetComparator instance = new JFCTargetComparator(); /** *

* Private Constructor. Creates a new instance of the class and prevents * instantiation from outside of this class. *

*/ private JFCTargetComparator() { } /** *

* List of the root widgets found in the target string. *

*/ private List rootWidgets = new ArrayList(); /** *

* Adds a target to the memory. *

* * @param target * target to be added */ private void addTarget(String target) { if (target != null) { String[] targetParts = target.split("\\]\\.\\["); if (targetParts.length == 1) { addWidget(target.substring(1, target.length() - 1), null); } else { JFCWidget parent = null; for (int i = 0; i < targetParts.length; i++) { if (i == 0) { parent = addWidget(targetParts[i].substring(1), parent); } else if (i == targetParts.length - 1) { parent = addWidget( targetParts[i].substring(0, targetParts[i].length() - 1), parent); } else { parent = addWidget(targetParts[i], parent); } } } } } /** *

* Adds a widget extracted from a target to the memory. The widget is placed * as a child/parent of other widget according to the GUI hierarchy of the * application. *

*

* In case the widget already exists, the existing widget is returned and * the known targets and hashCodes of the existing widget are updated. *

* * @param widgetString * string identifying the widget * @param parent * parent widget; if null, it is a root widget and added to * {@link #rootWidgets} * @return the created widget. */ private JFCWidget addWidget(String widgetString, JFCWidget parent) { String[] widgetInfo = widgetString.split("','"); JFCWidget widget = generateWidget(widgetString); if (parent == null) { int index = rootWidgets.indexOf(widget); if (index >= 0) { widget = rootWidgets.get(index); widget.titles.add(widgetInfo[0]); widget.hashCodes.add(widgetInfo[4]); } else { rootWidgets.add(widget); } } else { int index = parent.children.indexOf(widget); if (index >= 0) { widget = parent.children.get(index); widget.titles.add(widgetInfo[0]); widget.hashCodes.add(widgetInfo[4]); } else { parent.children.add(widget); } } return widget; } /** *

* Creates a new {@link JFCWidget} from a widget string. *

* * @param widgetString * string describing the widget * @return created {@link JFCWidget} */ private JFCWidget generateWidget(String widgetString) { String[] widgetInfo = widgetString.split("','"); JFCWidget widget = new JFCWidget(); if (widgetInfo[0].startsWith("'Pos(")) { widget.titles.add("Pos"); } else { widget.titles.add(widgetInfo[0]); } widget.widgetClass = widgetInfo[1]; widget.text = widgetInfo[2]; widget.index = widgetInfo[3]; widget.hashCodes.add(widgetInfo[4]); return widget; } /** *

* Tries to find the {@link JFCWidget} that the target string identifies in * the known GUI hierarchy, by traversing the known widgets starting with * the {@link #rootWidgets}. * * @param target * target string whose widget is searched for * @return respective {@link JFCWidget} instance if it is found; null * otherwise */ private JFCWidget find(String target) { JFCWidget widget = null; if (target != null) { String[] targetParts = target.split("\\]\\.\\["); if (targetParts.length == 1) { JFCWidget generatedWidget = generateWidget(target.substring(1, target.length() - 1)); int index = rootWidgets.indexOf(generatedWidget); if (index >= 0) { widget = rootWidgets.get(index); } else { return null; } } else { JFCWidget parent = null; for (int i = 0; i < targetParts.length; i++) { if (i == 0) { JFCWidget generatedWidget = generateWidget(targetParts[i] .substring(1)); int index = rootWidgets.indexOf(generatedWidget); if (index >= 0) { parent = rootWidgets.get(index); } else { return null; } } else if (i == targetParts.length - 1) { JFCWidget generatedWidget = generateWidget(targetParts[i] .substring(0, targetParts[i].length() - 1)); int index = parent.children.indexOf(generatedWidget); if (index >= 0) { widget = parent.children.get(index); } else { return null; } } else { JFCWidget generatedWidget = generateWidget(targetParts[i]); int index = parent.children.indexOf(generatedWidget); if (index >= 0) { parent = parent.children.get(index); } else { return null; } } } } } return widget; } /** *

* Internal class used to store JFCWidgets. The implementation is more like * a C-style structure, than a actual class. *

* * @author Steffen Herbold * @version 1.0 */ private static class JFCWidget { /** *

* Set of all known title strings of the widget. *

*/ Set titles = new LinkedHashSet(); /** *

* Set of all known hashCodes of the widget. *

*/ Set hashCodes = new LinkedHashSet(); /** *

* Class of the widget. *

*/ String widgetClass; /** *

* Index of the widget. *

*/ String index; /** *

* Text of the widget. *

*/ String text; int hashCode=0; /** *

* List of children of the widget. *

*/ List children = new ArrayList(); /** *

* Two widgets are equal, if {@link #widgetClass}, {@link #index}, and * {@link #text} are equal and the intersection of either the * {@link #hashCodes}, the {@link #titles}, or both of them is not * empty. *

* * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof JFCWidget) { JFCWidget other = (JFCWidget) obj; boolean titleEqual = CollectionUtils.containsAny(titles, other.titles); boolean hashEqual = CollectionUtils.containsAny(hashCodes, other.hashCodes); boolean retVal; if (widgetClass.equals("Class")) { retVal = (widgetClass.equals(other.widgetClass) && text.equals(other.text) && (titleEqual || hashEqual)); } else { retVal = (widgetClass.equals(other.widgetClass) && index.equals(other.index) && text.equals(other.text) && (titleEqual || hashEqual)); } return retVal; } return false; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { if( hashCode==0 ) { int multiplier = 7; hashCode = multiplier * hashCode + widgetClass.hashCode(); if (!widgetClass.equals("Class")) { hashCode = multiplier * hashCode + index.hashCode(); } hashCode = multiplier * hashCode + text.hashCode(); } return hashCode; } } }