]> source.dussan.org Git - vaadin-framework.git/commitdiff
Implement TestBench4 features in debug window (#12694)
authorPatrik Lindström <patrik@vaadin.com>
Tue, 21 Jan 2014 22:25:33 +0000 (00:25 +0200)
committerTeemu Suo-Anttila <teemusa@vaadin.com>
Wed, 29 Jan 2014 08:29:33 +0000 (08:29 +0000)
Done:
- VaadinFinderLocatorStrategy returns optimal paths for any Widget
selectable by it.
- TestBenchSection of the Debug Window picks and clears as expected.
- Debug Window no longer presents user with a widget hierarchy
- Translation from VaadinFinderLocatorStrategy query strings to
ElementQuery API calls (should be done in SelectorPath.java)
- Make SelectorPaths change background color when hovered

Change-Id: Ie122f962a319ddf560fa9ac4f6bc57f32a120f91

WebContent/VAADIN/themes/base/debug/debug.scss
client/src/com/vaadin/client/ConnectorMap.java
client/src/com/vaadin/client/componentlocator/ComponentLocator.java
client/src/com/vaadin/client/componentlocator/LocatorUtil.java [new file with mode: 0644]
client/src/com/vaadin/client/componentlocator/SelectorPredicate.java [new file with mode: 0644]
client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
client/src/com/vaadin/client/debug/internal/SelectorPath.java
client/src/com/vaadin/client/debug/internal/TestBenchSection.java

index 0992f19bb921ba81d612786b462756794a290434..b50245a7be09d38e80321c9211628be043ffb839 100644 (file)
                width: 100%;
        }
        
+       .v-debugwindow-selector > span.value {
+          width: 100%;
+       }
+       
+       .v-debugwindow-selector :hover {
+          background: rgba(255,32,32,0.5);
+       }
+       
        /* LOG */
        .v-debugwindow-log {
                font-family: monospace;
index 810f12824a520925ecce8ed90cb1ca6c7e86a178..c2f1eda21df33d5c3d17fdf2c653a9d7cd9011b3 100644 (file)
@@ -116,7 +116,7 @@ public class ConnectorMap {
      *         no connector was found
      */
     public ComponentConnector getConnector(Widget widget) {
-        return getConnector(widget.getElement());
+        return widget == null ? null : getConnector(widget.getElement());
     }
 
     public void registerConnector(String id, ServerConnector connector) {
index aa841ce5b05bda793e5789c0ecac1761167e1f99..d2a89c00d5c6f8edf0485b2d70791b8a38817b9a 100644 (file)
@@ -201,4 +201,20 @@ public class ComponentLocator {
         return client;
     }
 
+    /**
+     * Check if a given selector is valid for LegacyLocatorStrategy.
+     * 
+     * @param path
+     *            Vaadin selector path
+     * @return true if passes path validation with LegacyLocatorStrategy
+     */
+    public boolean isValidForLegacyLocator(String path) {
+        for (LocatorStrategy ls : locatorStrategies) {
+            if (ls instanceof LegacyLocatorStrategy) {
+                return ls.validatePath(path);
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/client/src/com/vaadin/client/componentlocator/LocatorUtil.java b/client/src/com/vaadin/client/componentlocator/LocatorUtil.java
new file mode 100644 (file)
index 0000000..0462492
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+/**
+ * Common String manipulator utilities used in VaadinFinderLocatorStrategy and
+ * SelectorPredicates.
+ * 
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class LocatorUtil {
+
+    /**
+     * Find first occurrence of character that's not inside quotes starting from
+     * specified index.
+     * 
+     * @param str
+     *            Full string for searching
+     * @param find
+     *            Character we want to find
+     * @param startingAt
+     *            Index where we start
+     * @return Index of character. -1 if character not found
+     */
+    protected static int indexOfIgnoringQuoted(String str, char find,
+            int startingAt) {
+        boolean quote = false;
+        String quoteChars = "'\"";
+        char currentQuote = '"';
+        for (int i = startingAt; i < str.length(); ++i) {
+            char cur = str.charAt(i);
+            if (quote) {
+                if (cur == currentQuote) {
+                    quote = !quote;
+                }
+                continue;
+            } else if (cur == find) {
+                return i;
+            } else {
+                if (quoteChars.indexOf(cur) >= 0) {
+                    currentQuote = cur;
+                    quote = !quote;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Find first occurrence of character that's not inside quotes starting from
+     * the beginning of string.
+     * 
+     * @param str
+     *            Full string for searching
+     * @param find
+     *            Character we want to find
+     * @return Index of character. -1 if character not found
+     */
+    protected static int indexOfIgnoringQuoted(String str, char find) {
+        return indexOfIgnoringQuoted(str, find, 0);
+    }
+}
diff --git a/client/src/com/vaadin/client/componentlocator/SelectorPredicate.java b/client/src/com/vaadin/client/componentlocator/SelectorPredicate.java
new file mode 100644 (file)
index 0000000..32b3300
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.componentlocator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SelectorPredicates are statements about the state of different components
+ * that VaadinFinderLocatorStrategy is finding. SelectorPredicates also provide
+ * useful information of said components to debug window by giving means to
+ * provide better variable naming.
+ * 
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class SelectorPredicate {
+    private String name = "";
+    private String value = "";
+    private boolean wildcard = false;
+    private int index = -1;
+
+    public static List<SelectorPredicate> extractPostFilterPredicates(
+            String path) {
+        if (path.startsWith("(")) {
+            return extractPredicates(path.substring(path.lastIndexOf(')')));
+        }
+        return new ArrayList<SelectorPredicate>();
+    }
+
+    /**
+     * Generate a list of predicates from a single predicate string
+     * 
+     * @param str
+     *            a comma separated string of predicates
+     * @return a List of Predicate objects
+     */
+    public static List<SelectorPredicate> extractPredicates(String path) {
+        List<SelectorPredicate> predicates = new ArrayList<SelectorPredicate>();
+
+        String predicateStr = extractPredicateString(path);
+        if (null == predicateStr || predicateStr.length() == 0) {
+            return predicates;
+        }
+
+        // Extract input strings
+        List<String> input = readPredicatesFromString(predicateStr);
+
+        // Process each predicate into proper predicate descriptor
+        for (String s : input) {
+            SelectorPredicate p = new SelectorPredicate();
+            s = s.trim();
+
+            try {
+                // If we can parse out the predicate as a pure index argument,
+                // stop processing here.
+                p.index = Integer.parseInt(s);
+                predicates.add(p);
+
+                continue;
+            } catch (Exception e) {
+                p.index = -1;
+            }
+
+            int idx = LocatorUtil.indexOfIgnoringQuoted(s, '=');
+            if (idx < 0) {
+                continue;
+            }
+            p.name = s.substring(0, idx);
+            p.value = s.substring(idx + 1);
+
+            if (p.value.equals("?")) {
+                p.wildcard = true;
+                p.value = null;
+            } else {
+                // Only unquote predicate value once we're sure it's a proper
+                // value...
+
+                p.value = unquote(p.value);
+            }
+
+            predicates.add(p);
+        }
+        // Move any (and all) index predicates to last place in the list.
+        for (int i = 0, l = predicates.size(); i < l - 1; ++i) {
+            if (predicates.get(i).index > -1) {
+                predicates.add(predicates.remove(i));
+                --i;
+                --l;
+            }
+        }
+
+        return predicates;
+    }
+
+    /**
+     * Splits the predicate string to list of predicate strings.
+     * 
+     * @param predicateStr
+     *            Comma separated predicate strings
+     * @return List of predicate strings
+     */
+    private static List<String> readPredicatesFromString(String predicateStr) {
+        List<String> predicates = new ArrayList<String>();
+        int prevIdx = 0;
+        int idx = LocatorUtil.indexOfIgnoringQuoted(predicateStr, ',', prevIdx);
+
+        while (idx > -1) {
+            predicates.add(predicateStr.substring(prevIdx, idx));
+            prevIdx = idx + 1;
+            idx = LocatorUtil.indexOfIgnoringQuoted(predicateStr, ',', prevIdx);
+        }
+        predicates.add(predicateStr.substring(prevIdx));
+
+        return predicates;
+    }
+
+    /**
+     * Returns the predicate string, i.e. the string between the brackets in a
+     * path fragment. Examples: <code>
+     * VTextField[0] => 0
+     * VTextField[caption='foo'] => caption='foo'
+     * </code>
+     * 
+     * @param pathFragment
+     *            The path fragment from which to extract the predicate string.
+     * @return The predicate string for the path fragment or empty string if not
+     *         found.
+     */
+    private static String extractPredicateString(String pathFragment) {
+        int ixOpenBracket = LocatorUtil
+                .indexOfIgnoringQuoted(pathFragment, '[');
+        if (ixOpenBracket >= 0) {
+            int ixCloseBracket = LocatorUtil.indexOfIgnoringQuoted(
+                    pathFragment, ']', ixOpenBracket);
+            return pathFragment.substring(ixOpenBracket + 1, ixCloseBracket);
+        }
+        return "";
+    }
+
+    /**
+     * Removes the surrounding quotes from a string if it is quoted.
+     * 
+     * @param str
+     *            the possibly quoted string
+     * @return an unquoted version of str
+     */
+    private static String unquote(String str) {
+        if ((str.startsWith("\"") && str.endsWith("\""))
+                || (str.startsWith("'") && str.endsWith("'"))) {
+            return str.substring(1, str.length() - 1);
+        }
+        return str;
+    }
+
+    /**
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @param name
+     *            the name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return the value
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * @param value
+     *            the value to set
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * @return the index
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * @param index
+     *            the index to set
+     */
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    /**
+     * @return the wildcard
+     */
+    public boolean isWildcard() {
+        return wildcard;
+    }
+
+    /**
+     * @param wildcard
+     *            the wildcard to set
+     */
+    public void setWildcard(boolean wildcard) {
+        this.wildcard = wildcard;
+    }
+}
index 2bb08a52c918cecc6c1f0d4f0d3549b3cfaea068..49090b66db1c38a053d3adef10c239ef21295ec9 100644 (file)
@@ -19,22 +19,18 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import com.google.gwt.core.client.JsArrayString;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.FastStringSet;
 import com.vaadin.client.HasComponentsConnector;
 import com.vaadin.client.Util;
-import com.vaadin.client.metadata.NoDataException;
 import com.vaadin.client.metadata.Property;
 import com.vaadin.client.metadata.TypeDataStore;
 import com.vaadin.client.ui.AbstractConnector;
 import com.vaadin.client.ui.SubPartAware;
 import com.vaadin.client.ui.VNotification;
-import com.vaadin.shared.AbstractComponentState;
 
 /**
  * The VaadinFinder locator strategy implements an XPath-like syntax for
@@ -60,15 +56,11 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
     private final ApplicationConnection client;
 
     /**
-     * Internal container/descriptor for search predicates
-     * 
-     * @author Vaadin Ltd
+     * Internal descriptor for connector/element/widget name combinations
      */
-    private static final class Predicate {
-        private String name = "";
-        private String value = "";
-        private boolean wildcard = false;
-        private int index = -1;
+    private static final class ConnectorPath {
+        private String name;
+        private ComponentConnector connector;
     }
 
     public VaadinFinderLocatorStrategy(ApplicationConnection clientConnection) {
@@ -80,11 +72,176 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      */
     @Override
     public String getPathForElement(Element targetElement) {
-        // Path generation functionality is not yet implemented as there is no
-        // current need for it. This might be implemented in the future if the
-        // need arises. Until then, all locator generation is handled by
-        // LegacyLocatorStrategy.
-        return null;
+        if (targetElement == null) {
+            return "";
+        }
+
+        List<ConnectorPath> hierarchy = getConnectorHierarchyForElement(targetElement);
+        List<String> path = new ArrayList<String>();
+
+        // Assemble longname path components back-to-forth with useful
+        // predicates - first try ID, then caption.
+        for (int i = 0; i < hierarchy.size(); ++i) {
+            ConnectorPath cp = hierarchy.get(i);
+            String pathFragment = cp.name;
+            String identifier = getPropertyValue(cp.connector, "id");
+
+            if (identifier != null) {
+                pathFragment += "[id=\"" + identifier + "\"]";
+            } else {
+                identifier = getPropertyValue(cp.connector, "caption");
+                if (identifier != null) {
+                    pathFragment += "[caption=\"" + identifier + "\"]";
+                }
+            }
+            path.add(pathFragment);
+        }
+
+        if (path.size() == 0) {
+            // If we didn't find a single element, return null..
+            return null;
+        }
+
+        return getBestSelector(generateQueries(path), targetElement);
+    }
+
+    /**
+     * Search different queries for the best one. Use the fact that the lowest
+     * possible index is with the last selector. Last selector is the full
+     * search path containing the complete Component hierarchy.
+     * 
+     * @param selectors
+     *            List of selectors
+     * @param target
+     *            Target element
+     * @return Best selector string formatted with a post filter
+     */
+    private String getBestSelector(List<String> selectors, Element target) {
+        // The last selector gives us smallest list index for target element.
+        String bestSelector = selectors.get(selectors.size() - 1);
+        int min = getElementsByPath(bestSelector).indexOf(target);
+        if (selectors.size() > 1
+                && min == getElementsByPath(selectors.get(0)).indexOf(target)) {
+            // The first selector has same index as last. It's much shorter.
+            bestSelector = selectors.get(0);
+        } else if (selectors.size() > 2) {
+            // See if we get minimum from second last. If not then we already
+            // have the best one.. Second last one contains almost full
+            // component hierarchy.
+            if (getElementsByPath(selectors.get(selectors.size() - 2)).indexOf(
+                    target) == min) {
+                for (int i = 1; i < selectors.size() - 2; ++i) {
+                    // Loop through the remaining selectors and look for one
+                    // with the same index
+                    if (getElementsByPath(selectors.get(i)).indexOf(target) == min) {
+                        bestSelector = selectors.get(i);
+                        break;
+                    }
+                }
+
+            }
+        }
+        return "(" + bestSelector + ")[" + min + "]";
+
+    }
+
+    /**
+     * Function to generate all possible search paths for given component list.
+     * Function strips out all the com.vaadin.ui. prefixes from elements as this
+     * functionality makes generating a query later on easier.
+     * 
+     * @param components
+     *            List of components
+     * @return List of Vaadin selectors
+     */
+    private List<String> generateQueries(List<String> components) {
+        // Prepare to loop through all the elements.
+        List<String> paths = new ArrayList<String>();
+        int compIdx = 0;
+        String basePath = components.get(compIdx).replace("com.vaadin.ui.", "");
+        // Add a basic search for the first element (eg. //Button)
+        paths.add((components.size() == 1 ? "/" : "//") + basePath);
+        while (++compIdx < components.size()) {
+            // Loop through the remaining components
+            for (int i = components.size() - 1; i >= compIdx; --i) {
+                boolean recursive = false;
+                if (i > compIdx) {
+                    recursive = true;
+                }
+                paths.add((i == components.size() - 1 ? "/" : "//")
+                        + components.get(i).replace("com.vaadin.ui.", "")
+                        + (recursive ? "//" : "/") + basePath);
+            }
+            // Add the element at index compIdx to the basePath so it is
+            // included in all the following searches.
+            basePath = components.get(compIdx).replace("com.vaadin.ui.", "")
+                    + "/" + basePath;
+        }
+
+        return paths;
+    }
+
+    /**
+     * Helper method to get the string-form value of a named property of a
+     * component connector
+     * 
+     * @since 7.2
+     * @param c
+     *            any ComponentConnector instance
+     * @param propertyName
+     *            property name to test for
+     * @return a string, if the property is found, or null, if the property does
+     *         not exist on the object (or some other error is encountered).
+     */
+    private String getPropertyValue(ComponentConnector c, String propertyName) {
+        Property prop = AbstractConnector.getStateType(c).getProperty(
+                propertyName);
+        try {
+            return prop.getValue(c.getState()).toString();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Generate a list representing the top-to-bottom connector hierarchy for
+     * any given element. ConnectorPath element provides long- and short names,
+     * as well as connector and widget root element references.
+     * 
+     * @since 7.2
+     * @param elem
+     *            any Element that is part of a widget hierarchy
+     * @return a list of ConnectorPath objects, in descending order towards the
+     *         common root container.
+     */
+    private List<ConnectorPath> getConnectorHierarchyForElement(Element elem) {
+        Element e = elem;
+        ComponentConnector c = Util.findPaintable(client, e);
+        List<ConnectorPath> connectorHierarchy = new ArrayList<ConnectorPath>();
+
+        while (c != null) {
+
+            for (String id : getIDsForConnector(c)) {
+                ConnectorPath cp = new ConnectorPath();
+                cp.name = getFullClassName(id);
+                cp.connector = c;
+
+                // We want to make an exception for the UI object, since it's
+                // our default search context (and can't be found inside itself)
+                if (!cp.name.equals("com.vaadin.ui.UI")) {
+                    connectorHierarchy.add(cp);
+                }
+            }
+
+            e = (Element) e.getParentElement();
+            if (e != null) {
+                c = Util.findPaintable(client, e);
+                e = c != null ? c.getWidget().getElement() : null;
+            }
+
+        }
+
+        return connectorHierarchy;
     }
 
     private boolean isNotificationExpression(String path) {
@@ -118,21 +275,41 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      */
     @Override
     public List<Element> getElementsByPath(String path) {
+        List<SelectorPredicate> postFilters = SelectorPredicate
+                .extractPostFilterPredicates(path);
+        if (postFilters.size() > 0) {
+            path = path.substring(1, path.lastIndexOf(')'));
+        }
 
+        List<Element> elements = new ArrayList<Element>();
         if (isNotificationExpression(path)) {
-            List<Element> elements = new ArrayList<Element>();
 
             for (VNotification n : findNotificationsByPath(path)) {
                 elements.add(n.getElement());
             }
 
-            return elements;
+        } else {
+
+            elements.addAll(eliminateDuplicates(getElementsByPathStartingAtConnector(
+                    path, client.getUIConnector())));
         }
 
-        List<Element> elems = eliminateDuplicates(getElementsByPathStartingAtConnector(
-                path, client.getUIConnector()));
+        for (SelectorPredicate p : postFilters) {
+            // Post filtering supports only indexes and follows instruction
+            // blindly. Index that is outside of our list results into an empty
+            // list and multiple indexes are likely to ruin a search completely
+            if (p.getIndex() >= 0) {
+                if (p.getIndex() >= elements.size()) {
+                    elements.clear();
+                } else {
+                    Element e = elements.get(p.getIndex());
+                    elements.clear();
+                    elements.add(e);
+                }
+            }
+        }
 
-        return elems;
+        return elements;
     }
 
     /**
@@ -140,90 +317,56 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      */
     @Override
     public Element getElementByPath(String path) {
-        if (isNotificationExpression(path)) {
-            return findNotificationsByPath(path).get(0).getElement();
+        List<Element> elements = getElementsByPath(path);
+        if (elements.isEmpty()) {
+            return null;
         }
-        return getElementByPathStartingAtConnector(path,
-                client.getUIConnector());
+        return elements.get(0);
     }
 
     /**
-     * Generate a list of predicates from a single predicate string
-     * 
-     * @param str
-     *            a comma separated string of predicates
-     * @return a List of Predicate objects
+     * {@inheritDoc}
      */
-    private List<Predicate> extractPredicates(String path) {
-        List<Predicate> predicates = new ArrayList<Predicate>();
-
-        String str = extractPredicateString(path);
-        if (null == str || str.length() == 0) {
-            return predicates;
-        }
-
-        // Extract input strings
-        List<String> input = new ArrayList<String>();
-        {
-            int idx = indexOfIgnoringQuotes(str, ',', 0), p = 0;
-            if (idx == -1) {
-                input.add(str);
-            } else {
-                do {
-                    input.add(str.substring(p, idx));
-                    p = idx + 1;
-                    idx = indexOfIgnoringQuotes(str, ',', p);
-                } while (idx > -1);
-                input.add(str.substring(p));
-            }
+    @Override
+    public Element getElementByPathStartingAt(String path, Element root) {
+        List<Element> elements = getElementsByPathStartingAt(path, root);
+        if (elements.isEmpty()) {
+            return null;
         }
+        return elements.get(0);
 
-        // Process each predicate into proper predicate descriptor
-        for (String s : input) {
-            Predicate p = new Predicate();
-            s = s.trim();
-
-            try {
-                // If we can parse out the predicate as a pure index argument,
-                // stop processing here.
-                p.index = Integer.parseInt(s);
-                predicates.add(p);
-
-                continue;
-            } catch (Exception e) {
-                p.index = -1;
-            }
-
-            int idx = indexOfIgnoringQuotes(s, '=');
-            if (idx < 0) {
-                continue;
-            }
-            p.name = s.substring(0, idx);
-            p.value = s.substring(idx + 1);
-
-            if (p.value.equals("?")) {
-                p.wildcard = true;
-                p.value = null;
-            } else {
-                // Only unquote predicate value once we're sure it's a proper
-                // value...
-
-                p.value = unquote(p.value);
-            }
+    }
 
-            predicates.add(p);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Element> getElementsByPathStartingAt(String path, Element root) {
+        List<SelectorPredicate> postFilters = SelectorPredicate
+                .extractPostFilterPredicates(path);
+        if (postFilters.size() > 0) {
+            path = path.substring(1, path.lastIndexOf(')'));
         }
 
-        // Move any (and all) index predicates to last place in the list.
-        for (int i = 0, l = predicates.size(); i < l - 1; ++i) {
-            if (predicates.get(i).index > -1) {
-                predicates.add(predicates.remove(i));
-                --i;
-                --l;
+        List<Element> elements = getElementsByPathStartingAtConnector(path,
+                Util.findPaintable(client, root));
+
+        for (SelectorPredicate p : postFilters) {
+            // Post filtering supports only indexes and follows instruction
+            // blindly. Index that is outside of our list results into an empty
+            // list and multiple indexes are likely to ruin a search completely
+            if (p.getIndex() >= 0) {
+                if (p.getIndex() >= elements.size()) {
+                    elements.clear();
+                } else {
+                    Element e = elements.get(p.getIndex());
+                    elements.clear();
+                    elements.add(e);
+                }
             }
         }
 
-        return predicates;
+        return elements;
     }
 
     /**
@@ -245,11 +388,12 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
             }
         }
 
-        List<Predicate> predicates = extractPredicates(path);
-        for (Predicate p : predicates) {
+        List<SelectorPredicate> predicates = SelectorPredicate
+                .extractPredicates(path);
+        for (SelectorPredicate p : predicates) {
 
-            if (p.index > -1) {
-                VNotification n = notifications.get(p.index);
+            if (p.getIndex() > -1) {
+                VNotification n = notifications.get(p.getIndex());
                 notifications.clear();
                 if (n != null) {
                     notifications.add(n);
@@ -261,59 +405,6 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return eliminateDuplicates(notifications);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Element getElementByPathStartingAt(String path, Element root) {
-        return getElementByPathStartingAtConnector(path,
-                Util.findPaintable(client, root));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public List<Element> getElementsByPathStartingAt(String path, Element root) {
-        List<Element> elements = getElementsByPathStartingAtConnector(path,
-                Util.findPaintable(client, root));
-        return elements;
-    }
-
-    /**
-     * Finds an element by the specified path, starting traversal of the
-     * connector hierarchy from the specified root.
-     * 
-     * @param path
-     *            the locator path
-     * @param root
-     *            the root connector
-     * @return the element identified by path or null if not found.
-     */
-    private Element getElementByPathStartingAtConnector(String path,
-            ComponentConnector root) {
-        String[] pathComponents = path.split(SUBPART_SEPARATOR);
-        ComponentConnector connector;
-        if (pathComponents[0].length() > 0) {
-            connector = findConnectorByPath(pathComponents[0], root);
-        } else {
-            connector = root;
-        }
-        if (connector != null) {
-            if (pathComponents.length > 1) {
-                // We have subparts
-                if (connector.getWidget() instanceof SubPartAware) {
-                    return ((SubPartAware) connector.getWidget())
-                            .getSubPartElement(pathComponents[1]);
-                } else {
-                    return null;
-                }
-            }
-            return connector.getWidget().getElement();
-        }
-        return null;
-    }
-
     /**
      * Finds a list of elements by the specified path, starting traversal of the
      * connector hierarchy from the specified root.
@@ -355,41 +446,6 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return eliminateDuplicates(output);
     }
 
-    /**
-     * Recursively finds a connector for the element identified by the provided
-     * path by traversing the connector hierarchy starting from the
-     * {@code parent} connector.
-     * 
-     * @param path
-     *            The path identifying an element.
-     * @param parent
-     *            The connector to start traversing from.
-     * @return The connector identified by {@code path} or null if no such
-     *         connector could be found.
-     */
-    private ComponentConnector findConnectorByPath(String path,
-            ComponentConnector parent) {
-        boolean findRecursively = path.startsWith("//");
-        // Strip away the one or two slashes from the beginning of the path
-        path = path.substring(findRecursively ? 2 : 1);
-
-        String[] fragments = splitFirstFragmentFromTheRest(path);
-        List<ComponentConnector> potentialMatches = collectPotentialMatches(
-                parent, fragments[0], findRecursively);
-
-        List<ComponentConnector> connectors = filterMatches(potentialMatches,
-                extractPredicates(fragments[0]));
-
-        if (!connectors.isEmpty()) {
-            if (fragments.length > 1) {
-                return findConnectorByPath(fragments[1], connectors.get(0));
-            } else {
-                return connectors.get(0);
-            }
-        }
-        return null;
-    }
-
     /**
      * Recursively finds connectors for the elements identified by the provided
      * path by traversing the connector hierarchy starting from {@code parents}
@@ -414,7 +470,8 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         for (ComponentConnector parent : parents) {
             connectors.addAll(filterMatches(
                     collectPotentialMatches(parent, fragments[0],
-                            findRecursively), extractPredicates(fragments[0])));
+                            findRecursively), SelectorPredicate
+                            .extractPredicates(fragments[0])));
         }
 
         if (!connectors.isEmpty() && fragments.length > 1) {
@@ -423,28 +480,6 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return eliminateDuplicates(connectors);
     }
 
-    /**
-     * Returns the predicate string, i.e. the string between the brackets in a
-     * path fragment. Examples: <code>
-     * VTextField[0] => 0
-     * VTextField[caption='foo'] => caption='foo'
-     * </code>
-     * 
-     * @param pathFragment
-     *            The path fragment from which to extract the predicate string.
-     * @return The predicate string for the path fragment or empty string if not
-     *         found.
-     */
-    private String extractPredicateString(String pathFragment) {
-        int ixOpenBracket = indexOfIgnoringQuotes(pathFragment, '[');
-        if (ixOpenBracket >= 0) {
-            int ixCloseBracket = indexOfIgnoringQuotes(pathFragment, ']',
-                    ixOpenBracket);
-            return pathFragment.substring(ixOpenBracket + 1, ixCloseBracket);
-        }
-        return "";
-    }
-
     /**
      * Go through a list of potentially matching components, modifying that list
      * until all elements that remain in that list match the complete list of
@@ -458,13 +493,13 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      */
     private List<ComponentConnector> filterMatches(
             List<ComponentConnector> potentialMatches,
-            List<Predicate> predicates) {
+            List<SelectorPredicate> predicates) {
 
-        for (Predicate p : predicates) {
+        for (SelectorPredicate p : predicates) {
 
-            if (p.index > -1) {
+            if (p.getIndex() > -1) {
                 try {
-                    ComponentConnector v = potentialMatches.get(p.index);
+                    ComponentConnector v = potentialMatches.get(p.getIndex());
                     potentialMatches.clear();
                     potentialMatches.add(v);
                 } catch (IndexOutOfBoundsException e) {
@@ -476,20 +511,11 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
 
             for (int i = 0, l = potentialMatches.size(); i < l; ++i) {
 
-                ComponentConnector c = potentialMatches.get(i);
-                Property property = AbstractConnector.getStateType(c)
-                        .getProperty(p.name);
-
-                Object propData;
-                try {
-                    propData = property.getValue(c.getState());
-                } catch (NoDataException e) {
-                    propData = null;
-                }
+                String propData = getPropertyValue(potentialMatches.get(i),
+                        p.getName());
 
-                if ((p.wildcard && propData == null)
-                        || (!p.wildcard && !valueEqualsPropertyValue(p.value,
-                                property, c.getState()))) {
+                if ((p.isWildcard() && propData == null)
+                        || (!p.isWildcard() && !p.getValue().equals(propData))) {
                     potentialMatches.remove(i);
                     --l;
                     --i;
@@ -501,44 +527,6 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return eliminateDuplicates(potentialMatches);
     }
 
-    /**
-     * Returns true if the value matches the value of the property in the state
-     * object.
-     * 
-     * @param value
-     *            The value to compare against.
-     * @param property
-     *            The property, whose value to check.
-     * @param state
-     *            The connector, whose state object contains the property.
-     * @return true if the values match.
-     */
-    private boolean valueEqualsPropertyValue(String value, Property property,
-            AbstractComponentState state) {
-        try {
-            return value.equals(property.getValue(state));
-        } catch (Exception e) {
-            // The property doesn't exist in the state object, so they aren't
-            // equal.
-            return false;
-        }
-    }
-
-    /**
-     * Removes the surrounding quotes from a string if it is quoted.
-     * 
-     * @param str
-     *            the possibly quoted string
-     * @return an unquoted version of str
-     */
-    private String unquote(String str) {
-        if ((str.startsWith("\"") && str.endsWith("\""))
-                || (str.startsWith("'") && str.endsWith("'"))) {
-            return str.substring(1, str.length() - 1);
-        }
-        return str;
-    }
-
     /**
      * Collects all connectors that match the widget class name of the path
      * fragment. If the {@code collectRecursively} parameter is true, a
@@ -579,6 +567,15 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return eliminateDuplicates(potentialMatches);
     }
 
+    private List<String> getIDsForConnector(ComponentConnector connector) {
+        Class<?> connectorClass = connector.getClass();
+        List<String> ids = new ArrayList<String>();
+
+        TypeDataStore.get().findIdentifiersFor(connectorClass).addAllTo(ids);
+
+        return ids;
+    }
+
     /**
      * Determines whether a connector matches a path fragment. This is done by
      * comparing the path fragment to the name of the widget type of the
@@ -593,16 +590,8 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      */
     private boolean connectorMatchesPathFragment(ComponentConnector connector,
             String widgetName) {
-        Class<?> connectorClass = connector.getClass();
-        List<String> ids = new ArrayList<String>();
 
-        FastStringSet identifiers = TypeDataStore.get().findIdentifiersFor(
-                connectorClass);
-        JsArrayString str = identifiers.dump();
-
-        for (int j = 0; j < str.length(); ++j) {
-            ids.add(str.get(j));
-        }
+        List<String> ids = getIDsForConnector(connector);
 
         Integer[] widgetTags = client.getConfiguration()
                 .getTagsForServerSideClassName(getFullClassName(widgetName));
@@ -677,7 +666,7 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
      *         the path.
      */
     private String[] splitFirstFragmentFromTheRest(String path) {
-        int ixOfSlash = indexOfIgnoringQuotes(path, '/');
+        int ixOfSlash = LocatorUtil.indexOfIgnoringQuoted(path, '/');
         if (ixOfSlash > 0) {
             return new String[] { path.substring(0, ixOfSlash),
                     path.substring(ixOfSlash) };
@@ -685,33 +674,6 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
         return new String[] { path };
     }
 
-    private int indexOfIgnoringQuotes(String str, char find) {
-        return indexOfIgnoringQuotes(str, find, 0);
-    }
-
-    private int indexOfIgnoringQuotes(String str, char find, int startingAt) {
-        boolean quote = false;
-        String quoteChars = "'\"";
-        char currentQuote = '"';
-        for (int i = startingAt; i < str.length(); ++i) {
-            char cur = str.charAt(i);
-            if (quote) {
-                if (cur == currentQuote) {
-                    quote = !quote;
-                }
-                continue;
-            } else if (cur == find) {
-                return i;
-            } else {
-                if (quoteChars.indexOf(cur) >= 0) {
-                    currentQuote = cur;
-                    quote = !quote;
-                }
-            }
-        }
-        return -1;
-    }
-
     private String getSimpleClassName(String s) {
         String[] parts = s.split("\\.");
         if (s.endsWith(".class")) {
index 2ad77a246b9605ea2a6d5115738f7a56a3880763..2f425ee1a7e0810188c41718c4a990f8e318f6b9 100644 (file)
 
 package com.vaadin.client.debug.internal;
 
-import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ApplicationConnection;
-import com.vaadin.client.ComponentConnector;
-import com.vaadin.client.FastStringSet;
 import com.vaadin.client.ServerConnector;
-import com.vaadin.client.Util;
 import com.vaadin.client.componentlocator.ComponentLocator;
-import com.vaadin.client.componentlocator.VaadinFinderLocatorStrategy;
-import com.vaadin.client.metadata.NoDataException;
-import com.vaadin.client.metadata.Property;
-import com.vaadin.client.metadata.TypeDataStore;
-import com.vaadin.client.ui.AbstractConnector;
-import com.vaadin.client.ui.SubPartAware;
+import com.vaadin.client.componentlocator.SelectorPredicate;
 
 /**
- * A single segment of a selector path with optional parent.
- * <p>
- * The static method {@link #findTestBenchSelector(ServerConnector, Element)}
- * permits looking up a selector chain for an element (a selector and its
- * parents, each selector relative to its parent).
- * <p>
- * The method {@link #findElement()} can be used to locate the element
- * referenced by a {@link SelectorPath}. {@link #getJUnitSelector(String)} can
- * be used to obtain the string to add to a JUnit test to refer to the element
- * identified by the path.
+ * A single segment of a selector path pointing to an Element.
  * <p>
  * This class should be considered internal to the framework and may change at
  * any time.
+ * <p>
  * 
  * @since 7.1.x
  */
-public abstract class SelectorPath {
-    private final SelectorPath parent;
+public class SelectorPath {
+    private final String path;
+    private final Element element;
     private final ComponentLocator locator;
+    private static Map<String, Integer> counter = new HashMap<String, Integer>();
+    private static Map<String, String> legacyNames = new HashMap<String, String>();
 
-    private static final String SUBPART_SEPARATOR = VaadinFinderLocatorStrategy.SUBPART_SEPARATOR;
-
-    /**
-     * Creates a {@link SelectorPath} from the root of the UI (without a parent)
-     * to identify an element.
-     * <p>
-     * The {@link ComponentLocator} is used to locate the corresponding
-     * {@link Element} in the context of a UI. If there are multiple UIs on a
-     * single page, the locator should correspond to the correct
-     * {@link ApplicationConnection}.
-     * 
-     * @param locator
-     *            {@link ComponentLocator} to use
-     */
-    protected SelectorPath(ComponentLocator locator) {
-        this(null, locator);
-    }
-
-    /**
-     * Creates a {@link SelectorPath} which is relative to another
-     * {@link SelectorPath}. to identify an element.
-     * <p>
-     * The {@link ComponentLocator} is used to locate the corresponding
-     * {@link Element} in the context of a UI. If there are multiple UIs on a
-     * single page, the locator should correspond to the correct
-     * {@link ApplicationConnection}.
-     * 
-     * @param parent
-     *            parent {@link SelectorPath} or null for root paths
-     * @param locator
-     *            {@link ComponentLocator} to use
-     */
-    protected SelectorPath(SelectorPath parent, ComponentLocator locator) {
-        this.parent = parent;
-        this.locator = locator;
+    static {
+        legacyNames.put("FilterSelect", "ComboBox");
+        legacyNames.put("ScrollTable", "Table");
     }
 
-    /**
-     * Returns the parent {@link SelectorPath} to which this path is relative.
-     * 
-     * @return parent path
-     */
-    public SelectorPath getParent() {
-        return parent;
+    protected SelectorPath(ServerConnector c, Element e) {
+        element = e;
+        locator = new ComponentLocator(c.getConnection());
+        path = locator.getPathForElement(e);
     }
 
-    @Override
-    public String toString() {
-        return "SelectorPath: " + getJUnitSelector("...");
+    public String getPath() {
+        return path;
     }
 
-    /**
-     * Returns the JUnit test fragment which can be used to refer to the element
-     * in a test.
-     * 
-     * @param context
-     *            the context to use (usually a variable name) or null for
-     *            default
-     * @return string to add in a JUnit test
-     */
-    public abstract String getJUnitSelector(String context);
-
-    /**
-     * Returns the {@link Element} that this {@link SelectorPath} points to in
-     * the context of the {@link ComponentLocator} of the {@link SelectorPath}.
-     * 
-     * @return Element identified by the path in the current UI
-     */
-    public abstract Element findElement();
-
-    /**
-     * Returns the path to an element/connector, including separate intermediate
-     * paths and the final path segment.
-     * 
-     * @param connector
-     *            the connector to find
-     * @param element
-     *            sub-element inside connector or null to use connector root
-     *            element
-     * @return Vaadin locator path
-     */
-    public static SelectorPath findTestBenchSelector(ServerConnector connector,
-            Element element) {
-        // TODO there should be a better way to locate and refer to captions -
-        // now using domChild in layout
-        SelectorPath selectorPath = null;
-        ApplicationConnection connection = connector.getConnection();
-        if (connection != null) {
-            if (null == element) {
-                element = findConnectorRootElement(connector);
-            }
-            if (null != element) {
-                ComponentLocator locator = new ComponentLocator(connection);
-                String path = locator.getPathForElement(element);
-                SelectorPath parent = null;
-
-                if (!path.isEmpty()) {
-                    selectorPath = extractIdSelectorPath(path, locator);
-                    if (null == selectorPath) {
-                        // parent paths first if not rooted on an ID
-                        if (connector.getParent() != null) {
-                            parent = findTestBenchSelector(
-                                    connector.getParent(), null);
-                        }
-
-                        if (parent != null) {
-                            // update path to be relative to parent
-                            Element parentElement = parent.findElement();
-                            if (null != parentElement) {
-                                String parentPath = locator
-                                        .getPathForElement(parentElement);
-                                if (path.startsWith(parentPath)) {
-                                    // remove path of parent to look for the
-                                    // children
-                                    path = path.substring(parentPath.length());
-                                }
-                            }
-                        }
-
-                        selectorPath = extractVaadinSelectorPath(path, parent,
-                                locator);
-                    }
-                    if (null == selectorPath) {
-                        if (path.startsWith("/V")) {
-                            // fall-back: Vaadin
-                            // this branch is needed for /VTabsheetPanel etc.
-                            selectorPath = SelectorPath.vaadinPath(path,
-                                    parent, locator);
-                        } else {
-                            // fall-back: XPath
-                            selectorPath = SelectorPath.xpath(path, parent,
-                                    locator);
-                        }
-                    }
-                }
-            }
-        }
-        return selectorPath;
-    }
-
-    private static SelectorPath extractIdSelectorPath(String path,
-            ComponentLocator locator) {
-        SelectorPath selectorPath = null;
-        if (path.startsWith("PID_S")) {
-            // remove internal prefix
-            path = path.substring(5);
-
-            // no parent for an ID selector
-            String pid = path;
-            String rest = null;
-            // split at first slash that is not in the subpart (if any)
-            int slashPos = path.indexOf("/");
-            int subPartPos = path.indexOf(SUBPART_SEPARATOR);
-            if (subPartPos >= 0 && slashPos > subPartPos) {
-                // ignore slashes in subpart
-                slashPos = -1;
-            } else if (slashPos >= 0 && subPartPos > slashPos) {
-                // ignore subpart after slashes - handled as a part of rest
-                subPartPos = -1;
-            }
-            // split the ID part and any relative path after it
-            if (slashPos > 0) {
-                pid = path.substring(0, slashPos);
-                rest = path.substring(slashPos);
-            }
-
-            // if there is a subpart directly after the id, need to use a Vaadin
-            // selector
-            SelectorPath pidSelector = null;
-            if (subPartPos > 0) {
-                String id = pid.substring(0, subPartPos);
-                // include the subpart separator
-                String subPart = pid.substring(subPartPos);
-                Element element = locator.getElementByPath("PID_S" + pid);
-                ComponentConnector connector = Util.findPaintable(
-                        locator.getClient(), element);
-                if (null != connector && null != connector.getWidget()) {
-                    String type = connector.getWidget().getClass()
-                            .getSimpleName();
-                    pidSelector = SelectorPath.vaadinPath("//" + type
-                            + "[id=\\\"" + id + "\\\"]" + subPart, null,
-                            locator);
-                } else {
-                    // no valid connector for the subpart
-                    return null;
-                }
-            } else {
-                pidSelector = SelectorPath.id(pid, locator);
-            }
-            if (null != rest && !rest.isEmpty()) {
-                selectorPath = extractVaadinSelectorPath(path, pidSelector,
-                        locator);
-                if (selectorPath == null) {
-                    selectorPath = SelectorPath.xpath(rest, pidSelector,
-                            locator);
-                }
-            } else {
-                selectorPath = pidSelector;
-            }
-        }
-        return selectorPath;
+    public Element getElement() {
+        return element;
     }
 
-    private static SelectorPath extractVaadinSelectorPath(String path,
-            SelectorPath parent, ComponentLocator locator) {
-        SelectorPath selectorPath = null;
-
-        String xpathPart = null;
-        int xpathPos = Math.min(path.indexOf("/div"), path.indexOf("/span"));
-        if (xpathPos >= 0) {
-            xpathPart = path.substring(xpathPos);
-            path = path.substring(0, xpathPos);
-        }
-
-        String subPartPart = null;
-        int subPartPos = path.indexOf("#");
-        if (subPartPos >= 0) {
-            subPartPart = path.substring(subPartPos + 1);
-            path = path.substring(0, subPartPos);
-        }
-
-        String domChildPart = null;
-        int domChildPos = path.indexOf("/domChild");
-        if (domChildPos >= 0) {
-            // include the slash
-            domChildPart = path.substring(domChildPos);
-            path = path.substring(0, domChildPos);
-        }
-
-        // is it something VaadinSelectorPath can handle?
-        String widgetClass = null;
-        // first cases in a layout slot
-        RegExp widgetInSlotMatcher = RegExp
-                .compile("^/(Slot\\[(\\d+)\\]/)([a-zA-Z]+)(\\[0\\])?$");
-        MatchResult matchResult = widgetInSlotMatcher.exec(path);
-        if (null != matchResult) {
-            if (matchResult.getGroupCount() >= 3) {
-                widgetClass = matchResult.getGroup(3);
-            }
-        }
-        // handle cases without intervening slot
-        if (null == widgetClass) {
-            RegExp widgetDirectlyMatcher = RegExp
-                    .compile("^//?([a-zA-Z]+)(\\[(\\d+)\\])?$");
-            matchResult = widgetDirectlyMatcher.exec(path);
-            if (null != matchResult) {
-                if (matchResult.getGroupCount() >= 1) {
-                    widgetClass = matchResult.getGroup(1);
-                }
-            }
-        }
-        if (null != widgetClass && !widgetClass.isEmpty()) {
-            selectorPath = findVaadinSelectorInParent(path, widgetClass,
-                    parent, locator);
-            if (null != subPartPart
-                    && selectorPath instanceof VaadinSelectorPath) {
-                ((VaadinSelectorPath) selectorPath).setSubPart(subPartPart);
-            } else if (null != xpathPart
-                    && selectorPath instanceof VaadinSelectorPath) {
-                // try to find sub-part if supported
-                ComponentConnector connector = Util.findPaintable(
-                        locator.getClient(), selectorPath.findElement());
-                if (connector != null
-                        && connector.getWidget() instanceof SubPartAware) {
-                    // for SubPartAware, skip the XPath fall-back path
-                    Element element = locator.getElementByPathStartingAt(path,
-                            selectorPath.findElement());
-                    SubPartAware subPartAware = (SubPartAware) connector
-                            .getWidget();
-                    String subPart = subPartAware.getSubPartName(element);
-                    if (null != subPart) {
-                        // type checked above
-                        ((VaadinSelectorPath) selectorPath).setSubPart(subPart);
-                    }
-                } else {
-                    // fall-back to XPath for the last part of the path
-                    selectorPath = SelectorPath.xpath(xpathPart, selectorPath,
-                            locator);
-                }
-            }
-
-            // the whole /domChild[i]/domChild[j]... part as a single selector
-            if (null != domChildPart
-                    && selectorPath instanceof VaadinSelectorPath) {
-                selectorPath = SelectorPath.vaadinPath(domChildPart,
-                        selectorPath, locator);
-            }
-        } else if (null != domChildPart) {
-            // cases with domChild path only (parent contains rest)
-            selectorPath = SelectorPath.vaadinPath(domChildPart, parent,
-                    locator);
-        }
-        return selectorPath;
+    public ComponentLocator getLocator() {
+        return locator;
     }
 
     /**
-     * Find the zero-based index of the widget of type widgetClass identified by
-     * path within its parent and returns the corresponding Vaadin path (if
-     * any). For instance, the second button in a layout has index 1 regardless
-     * of non-button components in the parent.
-     * <p>
-     * The approach used internally is to try to find the caption of the element
-     * inside its parent and check whether it is sufficient to identify the
-     * element correctly. If not, possible indices are looped through to see if
-     * the component of the specified type within the specified parent
-     * identifies the correct element. This is inefficient but more reliable
-     * than some alternative approaches, and does not require special cases for
-     * various layouts etc.
+     * Generate ElementQuery code for Java. Fallback to By.vaadin(path) if
+     * dealing with LegacyLocator
      * 
-     * @param path
-     *            relative path for the widget of interest
-     * @param widgetClass
-     *            type of the widget of interest
-     * @param parent
-     *            parent component to which the path is relative
-     * @param locator
-     *            ComponentLocator used to map paths to elements
-     * @return selector path for the element, null if none found
+     * @return String containing Java code for finding the element described by
+     *         path
      */
-    private static SelectorPath findVaadinSelectorInParent(String path,
-            String widgetClass, SelectorPath parent, ComponentLocator locator) {
-        if (null == parent) {
-            SelectorPath selectorPath = SelectorPath.vaadin(widgetClass, 0,
-                    null, locator);
-            if (selectorPath.findElement() == locator.getElementByPath(path)) {
-                return selectorPath;
+    public String getElementQuery() {
+        if (locator.isValidForLegacyLocator(path)) {
+            return getLegacyLocatorQuery();
+        }
+
+        String[] fragments;
+        String tmpPath = path;
+        List<SelectorPredicate> postFilters = SelectorPredicate
+                .extractPostFilterPredicates(path);
+        if (postFilters.size() > 0) {
+            tmpPath = tmpPath.substring(1, tmpPath.lastIndexOf(')'));
+        }
+
+        // Generate an ElementQuery
+        fragments = tmpPath.split("/");
+        String elementQueryString;
+        int index = 0;
+        for (SelectorPredicate p : postFilters) {
+            if (p.getIndex() > 0) {
+                index = p.getIndex();
+            }
+        }
+        if (index > 0) {
+            elementQueryString = ".get(" + index + ");";
+        } else {
+            elementQueryString = ".fist();";
+        }
+        for (int i = 1; i < fragments.length; ++i) {
+            if (fragments[i].isEmpty()) {
+                // Recursive search has occasional empty fragments
+                continue;
+            }
+
+            // Get Element.class -name
+            String queryFragment = "";
+            String elementClass = getComponentName(fragments[i])
+                    + "Element.class";
+            for (SelectorPredicate p : SelectorPredicate
+                    .extractPredicates(fragments[i])) {
+                // Add in predicates like .caption and .id
+                queryFragment += "." + p.getName() + "(\"" + p.getValue()
+                        + "\")";
+            }
+            if (i == fragments.length - 1) {
+                // Last element in path.
+                queryFragment = "$(" + elementClass + ")" + queryFragment;
             } else {
-                return null;
-            }
-        }
-        // This method uses an inefficient brute-force approach but its
-        // results should match what is used by the TestBench selectors.
-        Element parentElement = parent.findElement();
-        String parentPathString = locator.getPathForElement(parentElement);
-        if (null == parentPathString) {
-            parentPathString = "";
-        }
-        Element elementToFind = locator.getElementByPath(parentPathString
-                + path);
-        if (null == elementToFind) {
-            return null;
-        }
-        // if the connector has a caption, first try if the element can be
-        // located in parent with it; if that fails, use the index in parent
-        String caption = getCaptionForElement(elementToFind, locator);
-        if (null != caption) {
-            SelectorPath testPath = SelectorPath.vaadin(widgetClass, caption,
-                    parent, locator);
-            Element testElement = testPath.findElement();
-            // TODO in theory could also iterate upwards into parents, using
-            // "//" before the caption to find the shortest matching path that
-            // identifies the correct element
-            if (testElement == elementToFind) {
-                return testPath;
-            }
-        }
-
-        // Assumes that the number of logical child elements is at most the
-        // number of direct children of the DOM element - e.g. layouts have a
-        // single component per slot.
-        for (int i = 0; i < parentElement.getChildCount(); ++i) {
-            SelectorPath testPath = SelectorPath.vaadin(widgetClass, i, parent,
-                    locator);
-            Element testElement = testPath.findElement();
-            if (testElement == elementToFind) {
-                return testPath;
-            }
-        }
-        return null;
-    }
-
-    private static String getCaptionForElement(Element element,
-            ComponentLocator locator) {
-        String caption = null;
-        ComponentConnector connector = Util.findPaintable(locator.getClient(),
-                element);
-        if (null != connector) {
-            Property property = AbstractConnector.getStateType(connector)
-                    .getProperty("caption");
-            try {
-                Object value = property.getValue(connector.getState());
-                if (null != value) {
-                    caption = String.valueOf(value);
+                // If followed by an empty fragment search is recursive
+                boolean recursive = fragments[i + 1].isEmpty();
+                if (recursive) {
+                    queryFragment = ".in(" + elementClass + ")" + queryFragment;
+                } else {
+                    queryFragment = ".childOf(" + elementClass + ")"
+                            + queryFragment;
                 }
-            } catch (NoDataException e) {
-                // skip the caption based selection and use index below
             }
+            elementQueryString = queryFragment + elementQueryString;
         }
-        return caption;
-    }
 
-    private static Element findConnectorRootElement(ServerConnector connector) {
-        Element element = null;
-        // try to find the root element of the connector
-        if (connector instanceof ComponentConnector) {
-            Widget widget = ((ComponentConnector) connector).getWidget();
-            if (widget != null) {
-                element = widget.getElement();
-            }
-        }
-        return element;
-    }
-
-    public ComponentLocator getLocator() {
-        return locator;
-    }
-
-    @Override
-    public int hashCode() {
-        return getJUnitSelector("context").hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        SelectorPath other = (SelectorPath) obj;
-        if (parent == null) {
-            if (other.parent != null) {
-                return false;
-            }
-        } else if (!parent.equals(other.parent)) {
-            return false;
-        }
-        if (!other.getJUnitSelector("context").equals(
-                getJUnitSelector("context"))) {
-            return false;
-        }
-        return true;
-    }
-
-    protected static SelectorPath xpath(String path, SelectorPath parent,
-            ComponentLocator locator) {
-        return new XPathSelectorPath(path, parent, locator);
-    }
-
-    protected static SelectorPath id(String id, ComponentLocator locator) {
-        return new IdSelectorPath(id, locator);
-    }
-
-    protected static SelectorPath vaadin(String widgetClass,
-            String widgetCaption, SelectorPath parent, ComponentLocator locator) {
-        return new VaadinSelectorPath(widgetClass, widgetCaption, 0, parent,
-                locator);
-    }
-
-    protected static SelectorPath vaadin(String widgetClass, int widgetIndex,
-            SelectorPath parent, ComponentLocator locator) {
-        return new VaadinSelectorPath(widgetClass, null, widgetIndex, parent,
-                locator);
-    }
-
-    protected static SelectorPath vaadinPath(String vaadinPath,
-            SelectorPath parent, ComponentLocator locator) {
-        return new ByVaadinSelectorPath(vaadinPath, parent, locator);
+        // Return full Java variable assignment and eQuery
+        return generateJavaVariable(fragments[fragments.length - 1])
+                + elementQueryString;
     }
 
     /**
-     * Selector path for finding an {@link Element} based on an XPath (relative
-     * to the parent {@link SelectorPath}).
+     * @since
+     * @param frags
+     * @param i
+     * @return
      */
-    private static class XPathSelectorPath extends SelectorPath {
-        // path segment relative to parent
-        private final String path;
-
-        /**
-         * Creates a relative XPath based component selector path.
-         * 
-         * @param path
-         *            XPath
-         * @param parent
-         *            {@link SelectorPath} to which the XPath is relative, null
-         *            if from the root
-         * @param locator
-         *            ComponentLocator to use to find the element
-         */
-        public XPathSelectorPath(String path, SelectorPath parent,
-                ComponentLocator locator) {
-            super(parent, locator);
-            this.path = path;
-        }
-
-        /**
-         * Returns the XPath relative to the parent element.
-         * 
-         * @return relative path string
-         */
-        public String getPath() {
-            return path;
-        }
-
-        @Override
-        public String getJUnitSelector(String context) {
-            // use driver by default
-            String contextString = null != context ? context : "getDriver()";
-            return contextString + ".findElement(By.xpath(\"" + getPath()
-                    + "\"))";
-        }
-
-        @Override
-        public Element findElement() {
-            if (null != getParent()) {
-                Element parentElement = getParent().findElement();
-                if (null == parentElement) {
-                    // broken path - possibly removed parent
-                    return null;
-                }
-                Element element = getLocator().getElementByPathStartingAt(
-                        getPath(), parentElement);
-                return element;
-            } else {
-                Element element = getLocator().getElementByPath(getPath());
-                return element;
-            }
-        }
+    protected String getComponentName(String fragment) {
+        return fragment.split("\\[")[0];
     }
 
     /**
-     * Element identifier based locator path.
-     * <p>
-     * Identifier paths never have a parent and the identifiers should be unique
-     * within the context of the {@link ComponentLocator}/page.
+     * Generates a legacy locator for SelectorPath.
+     * 
+     * @return String containing Java code for element search and assignment
      */
-    private static class IdSelectorPath extends SelectorPath {
-        private final String id;
-
-        /**
-         * Creates an identifier based {@link SelectorPath}. The identifier
-         * should not contain the old "PID_S" prefix.
-         * 
-         * @param id
-         * @param locator
-         */
-        public IdSelectorPath(String id, ComponentLocator locator) {
-            super(locator);
-            this.id = id;
-        }
+    private String getLegacyLocatorQuery() {
+        String[] frags = path.split("/");
+        String name = getComponentName(frags[frags.length - 1]).substring(1);
 
-        /**
-         * Returns the ID in the DOM used to identify the element.
-         * 
-         * @return Vaadin debug ID or equivalent
-         */
-        public String getId() {
-            return id;
+        if (legacyNames.containsKey(name)) {
+            name = legacyNames.get(name);
         }
 
-        @Override
-        public String getJUnitSelector(String context) {
-            String contextPart = null != context ? ", " + context : "";
-            return "getElementById(\"" + getId() + "\"" + contextPart + ")";
-        }
+        name = getNameWithCount(name);
 
-        @Override
-        public Element findElement() {
-            // this also works for IDs
-            return getLocator().getElementByPath("PID_S" + getId());
-        }
+        // Use direct path and elementX naming style.
+        return "WebElement " + name.substring(0, 1).toLowerCase()
+                + name.substring(1) + " = getDriver().findElement(By.vaadin(\""
+                + path + "\"));";
     }
 
     /**
-     * Common base class for Vaadin selector paths (By.vaadin(...)).
+     * Get variable name with counter for given component name.
+     * 
+     * @param name
+     *            Component name
+     * @return name followed by count
      */
-    private static abstract class AbstractVaadinSelectorPath extends
-            SelectorPath {
-
-        protected AbstractVaadinSelectorPath(SelectorPath parent,
-                ComponentLocator locator) {
-            super(parent, locator);
+    protected String getNameWithCount(String name) {
+        if (!counter.containsKey(name)) {
+            counter.put(name, 0);
         }
-
-        /**
-         * Returns the {@link ComponentLocator} path of the element relative to
-         * the parent path.
-         * 
-         * @return path of the element for By.vaadin(...)
-         */
-        protected abstract String getPath();
-
-        @Override
-        public Element findElement() {
-            if (null != getParent()) {
-                Element parentElement = getParent().findElement();
-                Element element = getLocator().getElementByPathStartingAt(
-                        getPath(), parentElement);
-                return element;
-            } else {
-                return getLocator().getElementByPath(getPath());
-            }
-        }
-
+        counter.put(name, counter.get(name) + 1);
+        name += counter.get(name);
+        return name;
     }
 
     /**
-     * TestBench selector path for Vaadin widgets. These selectors are based on
-     * the widget class and either the index among the widgets of that type in
-     * the parent or the widget caption.
+     * Generate Java variable assignment from given selector fragment
+     * 
+     * @param pathFragment
+     *            Selector fragment
+     * @return piece of java code
      */
-    private static class VaadinSelectorPath extends AbstractVaadinSelectorPath {
-        private final String widgetClass;
-        private final String widgetCaption;
-        // negative for no index
-        private final int widgetIndex;
-        private String subPart;
-
-        /**
-         * Creates a Vaadin {@link SelectorPath}. The path identifies an element
-         * of a given type under its parent based on either its caption or its
-         * index (if both are given, only the caption is used). See also
-         * {@link ComponentLocator} for more details.
-         * 
-         * @param widgetClass
-         *            client-side widget class
-         * @param widgetCaption
-         *            caption of the widget - null to use the index instead
-         * @param widgetIndex
-         *            index of the widget of the type within its parent, used
-         *            only if the caption is not given
-         * @param parent
-         *            parent {@link SelectorPath} or null
-         * @param locator
-         *            component locator to use to find the corresponding
-         *            {@link Element}
-         */
-        public VaadinSelectorPath(String widgetClass, String widgetCaption,
-                int widgetIndex, SelectorPath parent, ComponentLocator locator) {
-            super(parent, locator);
-            this.widgetClass = widgetClass;
-            this.widgetCaption = widgetCaption;
-            this.widgetIndex = widgetIndex;
-        }
-
-        /**
-         * Returns the widget type used to identify the element.
-         * 
-         * @return Vaadin widget class
-         */
-        public String getWidgetClass() {
-            return widgetClass;
-        }
-
-        /**
-         * Returns the widget caption to look for or null if index is used
-         * instead.
-         * 
-         * @return widget caption to match
-         */
-        public String getWidgetCaption() {
-            return widgetCaption;
-        }
-
-        /**
-         * Returns the index of the widget of that type within its parent - only
-         * used if caption is null.
-         * 
-         * @return widget index
-         */
-        public int getWidgetIndex() {
-            return widgetIndex;
-        }
-
-        /**
-         * Returns the sub-part string (e.g. row and column identifiers within a
-         * table) used to identify a part of a component. See
-         * {@link ComponentLocator} and especially Vaadin selectors for more
-         * information.
-         * 
-         * @return sub-part string or null if none
-         */
-        public String getSubPart() {
-            return subPart;
-        }
-
-        /**
-         * Sets the sub-part string (e.g. row and column identifiers within a
-         * table) used to identify a part of a component. See
-         * {@link ComponentLocator} and especially Vaadin selectors for more
-         * information.
-         * 
-         * @param subPart
-         *            sub-part string to use or null for none
-         */
-        public void setSubPart(String subPart) {
-            this.subPart = subPart;
-        }
-
-        @Override
-        public String getJUnitSelector(String context) {
-            String componentClass = getComponentClass();
-            String contextPart = null != context ? ", " + context : "";
-            // TODO update after subpart API finished
-            if (null != getSubPart() || null == componentClass) {
-                return "getElementByPath(\"" + getPath() + "\"" + contextPart
-                        + ")";
-            } else if (null != getWidgetCaption()) {
-                return "getElementByCaption(" + componentClass + ".class, \""
-                        + getWidgetCaption() + "\"" + contextPart + ")";
-            } else if (getWidgetIndex() >= 0) {
-                return "getElementByIndex(" + componentClass + ".class, "
-                        + getWidgetIndex() + contextPart + ")";
-            } else {
-                return "getElement(" + componentClass + ".class" + contextPart
-                        + ")";
-            }
-        }
+    private String generateJavaVariable(String pathFragment) {
+        // Get element type and predicates from fragment
+        List<SelectorPredicate> predicates = SelectorPredicate
+                .extractPredicates(pathFragment);
+        String elementType = pathFragment.split("\\[")[0];
+        String name = getNameFromPredicates(predicates, elementType);
 
-        /**
-         * Returns the Vaadin server side component class to use for a widget
-         * class.
-         * 
-         * @return fully qualified server side class name, null if unable to
-         *         determine it
-         */
-        private String getComponentClass() {
-            ComponentConnector connector = Util.findPaintable(getLocator()
-                    .getClient(), findElement());
-            Class<? extends ServerConnector> connectorClass = connector
-                    .getClass();
-            FastStringSet identifiers = TypeDataStore.get().findIdentifiersFor(
-                    connectorClass);
-            JsArrayString ids = identifiers.dump();
-            if (ids.length() == 1) {
-                return ids.get(0);
-            } else {
-                return null;
-            }
+        if (name.equals(elementType)) {
+            name = getNameWithCount(name);
         }
 
-        // these are used only to locate components on the client side by path
-
-        @Override
-        protected String getPath() {
-            return "/" + getWidgetClass() + getIndexString(false)
-                    + getSubPartPostfix();
-        }
+        // Replace unusable characters
+        name = name.replaceAll("\\W", "");
 
-        private String getIndexString(boolean escapeQuotes) {
-            if (null != getWidgetCaption()) {
-                if (escapeQuotes) {
-                    return "[caption=\\\"" + widgetCaption + "\\\"]";
-                } else {
-                    return "[caption=\"" + widgetCaption + "\"]";
-                }
-            } else if (widgetIndex >= 0) {
-                return "[" + getWidgetIndex() + "]";
-            } else {
-                return "";
-            }
-        }
-
-        private String getSubPartPostfix() {
-            String subPartString = "";
-            if (null != getSubPart()) {
-                subPartString = SUBPART_SEPARATOR + getSubPart();
-            }
-            return subPartString;
-        }
+        // Lowercase the first character of name
+        return elementType + "Element " + name.substring(0, 1).toLowerCase()
+                + name.substring(1) + " = ";
     }
 
     /**
-     * TestBench selector path for Vaadin widgets, always using a
-     * By.vaadin(path) rather than other convenience methods.
+     * Get variable name based on predicates. Fallback to elementType
+     * 
+     * @param predicates
+     *            Predicates related to element
+     * @param elementType
+     *            Element type
+     * @return name for Variable
      */
-    private static class ByVaadinSelectorPath extends
-            AbstractVaadinSelectorPath {
-        private final String path;
-
-        /**
-         * Vaadin selector path for an exact path (including any preceding
-         * slash).
-         * 
-         * @param path
-         *            path of the element (normally with a leading slash), not
-         *            null
-         * @param parent
-         *            parent selector path or null if none
-         * @param locator
-         *            ComponentLocator to use to find the corresponding element
-         */
-        public ByVaadinSelectorPath(String path, SelectorPath parent,
-                ComponentLocator locator) {
-            super(parent, locator);
-            this.path = path;
-        }
-
-        @Override
-        public String getJUnitSelector(String context) {
-            String contextPart = null != context ? ", " + context : "";
-            return "getElementByPath(\"" + getPath() + "\"" + contextPart + ")";
-        }
-
-        /**
-         * Returns the By.vaadin(...) path relative to the parent element.
-         * 
-         * @return relative path string
-         */
-        @Override
-        public String getPath() {
-            return path;
-        }
+    private String getNameFromPredicates(List<SelectorPredicate> predicates,
+            String elementType) {
+        String name = elementType;
+        for (SelectorPredicate p : predicates) {
+            if ("caption".equals(p.getName())) {
+                // Caption + elementType is a suitable name
+                name = p.getValue() + elementType;
+            } else if ("id".equals(p.getName())) {
+                // Just id. This is unique, use it.
+                return p.getValue();
+            }
+        }
+        return name;
     }
 }
\ No newline at end of file
index 462309768f43b19ba03617a3ccd5ee7ba150fbf3..35c0d7abe8c446def2bc7bb1ec52eac1599f3a92 100644 (file)
@@ -55,25 +55,14 @@ public class TestBenchSection implements Section {
      */
     private static class SelectorWidget extends HTML implements
             MouseOverHandler, MouseOutHandler {
-        private static int selectorCounter = 1;
+        private final SelectorPath path;
 
-        final private SelectorPath path;
-        final private SelectorWidget parent;
-        final private int selectorIndex = selectorCounter++;
-
-        public SelectorWidget(final SelectorPath path,
-                final SelectorWidget parent) {
+        public SelectorWidget(final SelectorPath path) {
             this.path = path;
-            this.parent = parent;
 
-            String parentString = (parent != null) ? ("element" + parent.selectorIndex)
-                    : null;
-            String html = "<div class=\""
-                    + VDebugWindow.STYLENAME
+            String html = "<div class=\"" + VDebugWindow.STYLENAME
                     + "-selector\"><span class=\"tb-selector\">"
-                    + Util.escapeHTML("WebElement element" + selectorIndex
-                            + " = " + path.getJUnitSelector(parentString) + ";")
-                    + "</span></div>";
+                    + Util.escapeHTML(path.getElementQuery()) + "</span></div>";
             setHTML(html);
 
             addMouseOverHandler(this);
@@ -82,10 +71,10 @@ public class TestBenchSection implements Section {
 
         @Override
         public void onMouseOver(MouseOverEvent event) {
-            ApplicationConnection a = path.getLocator().getClient();
-            Element element = path.findElement();
+            Highlight.hideAll();
+
+            Element element = path.getElement();
             if (null != element) {
-                Highlight.hideAll();
                 Highlight.show(element);
             }
         }
@@ -96,13 +85,11 @@ public class TestBenchSection implements Section {
         }
     }
 
-    private final DebugButton tabButton = new DebugButton(Icon.SELECTOR,
+    private final DebugButton tabButton = new DebugButton(Icon.WARNING,
             "Pick Vaadin TestBench selectors");
 
     private final FlowPanel content = new FlowPanel();
 
-    private final HierarchyPanel hierarchyPanel = new HierarchyPanel();
-
     private final FlowPanel selectorPanel = new FlowPanel();
     // map from full path to SelectorWidget to enable reuse of old selectors
     private Map<SelectorPath, SelectorWidget> selectorWidgets = new HashMap<SelectorPath, SelectorWidget>();
@@ -110,21 +97,14 @@ public class TestBenchSection implements Section {
     private final FlowPanel controls = new FlowPanel();
 
     private final Button find = new DebugButton(Icon.HIGHLIGHT,
-            "Select a component on the page to inspect it");
-    private final Button refreshHierarchy = new DebugButton(Icon.HIERARCHY,
-            "Refresh the connector hierarchy tree");
+            "Pick an element and generate a query for it");
+
+    private final Button clear = new DebugButton(Icon.CLEAR,
+            "Clear current elements");
 
     private HandlerRegistration highlightModeRegistration = null;
 
     public TestBenchSection() {
-        controls.add(refreshHierarchy);
-        refreshHierarchy.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
-        refreshHierarchy.addClickHandler(new ClickHandler() {
-            @Override
-            public void onClick(ClickEvent event) {
-                hierarchyPanel.update();
-            }
-        });
 
         controls.add(find);
         find.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
@@ -135,15 +115,16 @@ public class TestBenchSection implements Section {
             }
         });
 
-        hierarchyPanel.addListener(new SelectConnectorListener() {
+        controls.add(clear);
+        clear.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
+        clear.addClickHandler(new ClickHandler() {
             @Override
-            public void select(ServerConnector connector, Element element) {
-                pickSelector(connector, element);
+            public void onClick(ClickEvent event) {
+                clearResults();
             }
         });
 
         content.setStylePrimaryName(VDebugWindow.STYLENAME + "-testbench");
-        content.add(hierarchyPanel);
         content.add(selectorPanel);
     }
 
@@ -209,33 +190,19 @@ public class TestBenchSection implements Section {
             highlightModeRegistration = null;
             find.removeStyleDependentName(VDebugWindow.STYLENAME_ACTIVE);
         }
+        Highlight.hideAll();
     }
 
     private void pickSelector(ServerConnector connector, Element element) {
-        SelectorPath path = SelectorPath.findTestBenchSelector(connector,
-                element);
 
-        if (null != path) {
-            addSelectorWidgets(path);
-        }
-    }
+        SelectorPath p = new SelectorPath(connector, Util
+                .findPaintable(connector.getConnection(), element).getWidget()
+                .getElement());
+        SelectorWidget w = new SelectorWidget(p);
 
-    private SelectorWidget addSelectorWidgets(SelectorPath path) {
-        // add selector widgets recursively from root towards children, reusing
-        // old ones
-        SelectorPath parent = path.getParent();
-        SelectorWidget parentWidget = null;
-        if (null != parent) {
-            parentWidget = addSelectorWidgets(parent);
-        }
-        SelectorWidget widget = selectorWidgets.get(path);
-        if (null == widget) {
-            // the parent has already been added above
-            widget = new SelectorWidget(path, parentWidget);
-            selectorWidgets.put(path, widget);
-            selectorPanel.add(widget);
-        }
-        return widget;
+        content.add(w);
+
+        stopFind();
     }
 
     private final NativePreviewHandler highlightModeHandler = new NativePreviewHandler() {
@@ -260,6 +227,7 @@ public class TestBenchSection implements Section {
 
                 // make sure that not finding the highlight element only
                 Highlight.hideAll();
+
                 eventTarget = Util.getElementFromPoint(event.getNativeEvent()
                         .getClientX(), event.getNativeEvent().getClientY());
                 ComponentConnector connector = findConnector(eventTarget);
@@ -304,4 +272,8 @@ public class TestBenchSection implements Section {
         return null;
     }
 
+    private void clearResults() {
+        content.clear();
+    }
+
 }