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;
* 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) {
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;
+ }
+
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
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
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) {
*/
@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) {
*/
@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;
}
/**
*/
@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;
}
/**
}
}
- 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);
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.
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}
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) {
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
*/
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) {
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;
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
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
*/
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));
* 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) };
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")) {
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
*/
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);
@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);
}
}
}
}
- 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>();
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);
}
});
- 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);
}
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() {
// make sure that not finding the highlight element only
Highlight.hideAll();
+
eventTarget = Util.getElementFromPoint(event.getNativeEvent()
.getClientX(), event.getNativeEvent().getClientY());
ComponentConnector connector = findConnector(eventTarget);
return null;
}
+ private void clearResults() {
+ content.clear();
+ }
+
}