package de.ugoe.cs.quest.plugin.jfc.eventcore; 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 { /** ** If mutable is true, new target strings can be added to the internal * memory. This leads to a very expensive {@link #compare(String, String)} * operation. *
** if mutable is set to false, currently possible equal targets are * pre-computed. This pre-computation is expensive and might take a while. * In turn, the {@link #compare(String, String)} operation becomes very * cheap. *
*/ private static boolean mutable = true; /** ** Set of all currently known targets. *
*/ private static Set* Map that contains for all known target strings all equal target strings. * Pre-computed when {@link #mutable} is set to false. *
*/ private static Map* Changes the mutability of the comparator. If the mutability is changed * from true to false, the map {@link #equalTargets} is computed. *
* * @param mutable * new mutability of the comparator */ public static void setMutable(boolean mutable) { if (JFCTargetComparator.mutable == true && mutable == false) { equalTargets = new HashMap* Compares to target strings. The strings are equal, if *
* 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* 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* 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* Set of all known hashCodes of the widget. *
*/ Set* Class of the widget. *
*/ String widgetClass; /** ** Index of the widget. *
*/ String index; /** ** Text of the widget. *
*/ String text; /** ** Pre-computed hash code of the widget. *
*/ int hashCode = 0; /** ** List of children of the widget. *
*/ List* 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; } } }