aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/com/vaadin/client/ApplicationConfiguration.java25
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java9
-rw-r--r--client/src/com/vaadin/client/componentlocator/ComponentLocator.java78
-rw-r--r--client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java44
-rw-r--r--client/src/com/vaadin/client/componentlocator/LocatorStrategy.java45
-rw-r--r--client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java499
6 files changed, 628 insertions, 72 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java
index a8ae47385a..1a5b0d836f 100644
--- a/client/src/com/vaadin/client/ApplicationConfiguration.java
+++ b/client/src/com/vaadin/client/ApplicationConfiguration.java
@@ -512,6 +512,30 @@ public class ApplicationConfiguration implements EntryPoint {
}
}
+ /**
+ * Returns all tags for given class. Tags are used in
+ * {@link ApplicationConfiguration} to keep track of different classes and
+ * their hierarchy
+ *
+ * @since 7.2
+ * @param classname
+ * name of class which tags we want
+ * @return Integer array of tags pointing to this classname
+ */
+ public Integer[] getTagsForServerSideClassName(String classname) {
+ List<Integer> tags = new ArrayList<Integer>();
+
+ for (Map.Entry<Integer, String> entry : tagToServerSideClassName
+ .entrySet()) {
+ if (classname.equals(entry.getValue())) {
+ tags.add(entry.getKey());
+ }
+ }
+
+ Integer[] out = new Integer[tags.size()];
+ return tags.toArray(out);
+ }
+
public Integer getParentTag(int tag) {
return componentInheritanceMap.get(tag);
}
@@ -762,5 +786,4 @@ public class ApplicationConfiguration implements EntryPoint {
private static final Logger getLogger() {
return Logger.getLogger(ApplicationConfiguration.class.getName());
}
-
}
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index 841e1df96d..68fb57b8c0 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2000-2013 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -64,7 +64,6 @@ import com.google.gwt.user.client.Window.ClosingHandler;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
-import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
@@ -577,6 +576,12 @@ public class ApplicationConnection {
client.getElementByPathStartingAt = $entry(function(id, element) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)(id, element);
});
+ client.getElementsByPath = $entry(function(id) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPath(Ljava/lang/String;)(id);
+ });
+ client.getElementsByPathStartingAt = $entry(function(id, element) {
+ return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getElementsByPathStartingAt(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)(id, element);
+ });
client.getPathForElement = $entry(function(element) {
return componentLocator.@com.vaadin.client.componentlocator.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element);
});
diff --git a/client/src/com/vaadin/client/componentlocator/ComponentLocator.java b/client/src/com/vaadin/client/componentlocator/ComponentLocator.java
index 6f6e52c0e1..aa841ce5b0 100644
--- a/client/src/com/vaadin/client/componentlocator/ComponentLocator.java
+++ b/client/src/com/vaadin/client/componentlocator/ComponentLocator.java
@@ -18,6 +18,8 @@ package com.vaadin.client.componentlocator;
import java.util.Arrays;
import java.util.List;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.Element;
import com.vaadin.client.ApplicationConnection;
@@ -94,15 +96,75 @@ public class ComponentLocator {
*/
public Element getElementByPath(String path) {
for (LocatorStrategy strategy : locatorStrategies) {
- Element element = strategy.getElementByPath(path);
- if (null != element) {
- return element;
+ if (strategy.validatePath(path)) {
+ Element element = strategy.getElementByPath(path);
+ if (null != element) {
+ return element;
+ }
}
}
return null;
}
/**
+ * Locates elements using a String locator (path) which identifies DOM
+ * elements.
+ *
+ * @since 7.2
+ * @param path
+ * The String locator which identifies target elements.
+ * @return The JavaScriptArray of DOM elements identified by {@code path} or
+ * empty array if elements could not be located.
+ */
+ public JsArray<Element> getElementsByPath(String path) {
+ JsArray<Element> jsElements = JavaScriptObject.createArray().cast();
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ List<Element> elements = strategy.getElementsByPath(path);
+ if (elements.size() > 0) {
+ for (Element e : elements) {
+ jsElements.push(e);
+ }
+ return jsElements;
+ }
+ }
+ }
+ return jsElements;
+ }
+
+ /**
+ * Locates elements using a String locator (path) which identifies DOM
+ * elements. The path starts from the specified root element.
+ *
+ * @see #getElementByPath(String)
+ *
+ * @since 7.2
+ * @param path
+ * The path of elements to be found
+ * @param root
+ * The root element where the path is anchored
+ * @return The JavaScriptArray of DOM elements identified by {@code path} or
+ * empty array if elements could not be located.
+ */
+ public JsArray<Element> getElementsByPathStartingAt(String path,
+ Element root) {
+ JsArray<Element> jsElements = JavaScriptObject.createArray().cast();
+ for (LocatorStrategy strategy : locatorStrategies) {
+ if (strategy.validatePath(path)) {
+ List<Element> elements = strategy.getElementsByPathStartingAt(
+ path, root);
+ if (elements.size() > 0) {
+ for (Element e : elements) {
+ jsElements.push(e);
+ }
+ return jsElements;
+ }
+ }
+ }
+ return jsElements;
+ }
+
+ /**
* Locates an element using a String locator (path) which identifies a DOM
* element. The path starts from the specified root element.
*
@@ -117,9 +179,12 @@ public class ComponentLocator {
*/
public Element getElementByPathStartingAt(String path, Element root) {
for (LocatorStrategy strategy : locatorStrategies) {
- Element element = strategy.getElementByPathStartingAt(path, root);
- if (null != element) {
- return element;
+ if (strategy.validatePath(path)) {
+ Element element = strategy.getElementByPathStartingAt(path,
+ root);
+ if (null != element) {
+ return element;
+ }
}
}
return null;
@@ -135,4 +200,5 @@ public class ComponentLocator {
public ApplicationConnection getClient() {
return client;
}
+
}
diff --git a/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java
index 5123a57e5d..4a1b100213 100644
--- a/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java
+++ b/client/src/com/vaadin/client/componentlocator/LegacyLocatorStrategy.java
@@ -20,6 +20,7 @@ import java.util.Iterator;
import java.util.List;
import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HasWidgets;
@@ -73,11 +74,19 @@ public class LegacyLocatorStrategy implements LocatorStrategy {
private final ApplicationConnection client;
+ private static final RegExp validSyntax = RegExp
+ .compile("^((\\w+::)?(PID_S\\w+)?)?(/[a-zA-Z0-9]+\\[\\d+\\])*$");
+
public LegacyLocatorStrategy(ApplicationConnection clientConnection) {
client = clientConnection;
}
@Override
+ public boolean validatePath(String path) {
+ return validSyntax.test(path);
+ }
+
+ @Override
public String getPathForElement(Element targetElement) {
ComponentConnector connector = Util
.findPaintable(client, targetElement);
@@ -178,11 +187,17 @@ public class LegacyLocatorStrategy implements LocatorStrategy {
}
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Element getElementByPath(String path) {
return getElementByPathStartingAt(path, null);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public Element getElementByPathStartingAt(String path, Element baseElement) {
/*
@@ -220,6 +235,34 @@ public class LegacyLocatorStrategy implements LocatorStrategy {
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPath(String path) {
+ // This type of search is not supported in LegacyLocator
+ List<Element> array = new ArrayList<Element>();
+ Element e = getElementByPath(path);
+ if (e != null) {
+ array.add(e);
+ }
+ return array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPathStartingAt(String path, Element root) {
+ // This type of search is not supported in LegacyLocator
+ List<Element> array = new ArrayList<Element>();
+ Element e = getElementByPathStartingAt(path, root);
+ if (e != null) {
+ array.add(e);
+ }
+ return array;
+ }
+
+ /**
* Finds the first widget in the hierarchy (moving upwards) that implements
* SubPartAware. Returns the SubPartAware implementor or null if none is
* found.
@@ -672,4 +715,5 @@ public class LegacyLocatorStrategy implements LocatorStrategy {
return null;
}
+
}
diff --git a/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java
index 56ed396609..e892f43d76 100644
--- a/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java
+++ b/client/src/com/vaadin/client/componentlocator/LocatorStrategy.java
@@ -15,6 +15,8 @@
*/
package com.vaadin.client.componentlocator;
+import java.util.List;
+
import com.google.gwt.user.client.Element;
/**
@@ -28,6 +30,18 @@ import com.google.gwt.user.client.Element;
* @author Vaadin Ltd
*/
public interface LocatorStrategy {
+
+ /**
+ * Test the given input path for formatting errors. If a given path can not
+ * be validated, the locator strategy will not be attempted.
+ *
+ * @param path
+ * a locator path expression
+ * @return true, if the implementing class can process the given path,
+ * otherwise false
+ */
+ boolean validatePath(String path);
+
/**
* Generates a String locator which uniquely identifies the target element.
* The {@link #getElementByPath(String)} method can be used for the inverse
@@ -74,4 +88,35 @@ public interface LocatorStrategy {
* could not be located.
*/
Element getElementByPathStartingAt(String path, Element root);
+
+ /**
+ * Locates all elements that match a String locator (path) which identifies
+ * DOM elements.
+ *
+ * This functionality is limited in {@link LegacyLocatorStrategy}.
+ *
+ * @param path
+ * The String locator which identifies target elements.
+ * @return List that contains all matched elements. Empty list if none
+ * found.
+ */
+ List<Element> getElementsByPath(String path);
+
+ /**
+ * Locates all elements that match a String locator (path) which identifies
+ * DOM elements. The path starts from the specified root element.
+ *
+ * This functionality is limited in {@link LegacyLocatorStrategy}.
+ *
+ * @see #getElementsByPath(String)
+ *
+ * @param path
+ * The String locator which identifies target elements.
+ * @param root
+ * The element that is at the root of the path.
+ * @return List that contains all matched elements. Empty list if none
+ * found.
+ */
+
+ List<Element> getElementsByPathStartingAt(String path, Element root);
}
diff --git a/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java b/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
index 95b2745bf8..6337ca7e8c 100644
--- a/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
+++ b/client/src/com/vaadin/client/componentlocator/VaadinFinderLocatorStrategy.java
@@ -16,18 +16,22 @@
package com.vaadin.client.componentlocator;
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.AbstractHasComponentsConnector;
import com.vaadin.client.ui.SubPartAware;
import com.vaadin.client.ui.VNotification;
import com.vaadin.shared.AbstractComponentState;
@@ -55,6 +59,18 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
private final ApplicationConnection client;
+ /**
+ * Internal container/descriptor for search predicates
+ *
+ * @author Vaadin Ltd
+ */
+ private static final class Predicate {
+ private String name = "";
+ private String value = "";
+ private boolean wildcard = false;
+ private int index = -1;
+ }
+
public VaadinFinderLocatorStrategy(ApplicationConnection clientConnection) {
client = clientConnection;
}
@@ -71,19 +87,146 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
return null;
}
+ private boolean isNotificationExpression(String path) {
+ String[] starts = { "//", "/" };
+
+ String[] frags = { "com.vaadin.ui.Notification.class",
+ "com.vaadin.ui.Notification", "VNotification.class",
+ "VNotification", "Notification.class", "Notification" };
+
+ String[] ends = { "/", "[" };
+
+ for (String s : starts) {
+ for (String f : frags) {
+ if (path.equals(s + f)) {
+ return true;
+ }
+
+ for (String e : ends) {
+ if (path.startsWith(s + f + e)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<Element> getElementsByPath(String path) {
+
+ if (isNotificationExpression(path)) {
+ List<Element> elements = new ArrayList<Element>();
+
+ for (VNotification n : findNotificationsByPath(path)) {
+ elements.add(n.getElement());
+ }
+
+ return elements;
+ }
+
+ List<Element> elems = eliminateDuplicates(getElementsByPathStartingAtConnector(
+ path, client.getUIConnector()));
+
+ return elems;
+ }
+
/**
* {@inheritDoc}
*/
@Override
public Element getElementByPath(String path) {
- if (path.startsWith("//VNotification")) {
- return findNotificationByPath(path);
+ if (isNotificationExpression(path)) {
+ return findNotificationsByPath(path).get(0).getElement();
}
return getElementByPathStartingAtConnector(path,
client.getUIConnector());
}
/**
+ * Generate a list of predicates from a single predicate string
+ *
+ * @param str
+ * a comma separated string of predicates
+ * @return a List of Predicate objects
+ */
+ 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));
+ }
+ }
+
+ // 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);
+ }
+
+ // 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;
+ }
+
+ /**
* Special case for finding notifications as they have no connectors and are
* directly attached to {@link RootPanel}.
*
@@ -93,19 +236,29 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
* brackets.
* @return the notification element or null if not found.
*/
- private Element findNotificationByPath(String path) {
- ArrayList<VNotification> notifications = new ArrayList<VNotification>();
+ private List<VNotification> findNotificationsByPath(String path) {
+
+ List<VNotification> notifications = new ArrayList<VNotification>();
for (Widget w : RootPanel.get()) {
if (w instanceof VNotification) {
notifications.add((VNotification) w);
}
}
- String indexStr = extractPredicateString(path);
- int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
- if (index >= 0 && index < notifications.size()) {
- return notifications.get(index).getElement();
+
+ List<Predicate> predicates = extractPredicates(path);
+ for (Predicate p : predicates) {
+
+ if (p.index > -1) {
+ VNotification n = notifications.get(p.index);
+ notifications.clear();
+ if (n != null) {
+ notifications.add(n);
+ }
+ }
+
}
- return null;
+
+ return eliminateDuplicates(notifications);
}
/**
@@ -118,6 +271,16 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
}
/**
+ * {@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.
*
@@ -130,8 +293,12 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
private Element getElementByPathStartingAtConnector(String path,
ComponentConnector root) {
String[] pathComponents = path.split(SUBPART_SEPARATOR);
- ComponentConnector connector = findConnectorByPath(pathComponents[0],
- root);
+ 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
@@ -148,6 +315,47 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
}
/**
+ * Finds a list of elements 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 list of elements identified by path or empty list if not
+ * found.
+ */
+ private List<Element> getElementsByPathStartingAtConnector(String path,
+ ComponentConnector root) {
+ String[] pathComponents = path.split(SUBPART_SEPARATOR);
+ List<ComponentConnector> connectors;
+ if (pathComponents[0].length() > 0) {
+ connectors = findConnectorsByPath(pathComponents[0],
+ Arrays.asList(root));
+ } else {
+ connectors = Arrays.asList(root);
+ }
+
+ List<Element> output = new ArrayList<Element>();
+ if (null != connectors && !connectors.isEmpty()) {
+ if (pathComponents.length > 1) {
+ // We have subparts
+ for (ComponentConnector connector : connectors) {
+ if (connector.getWidget() instanceof SubPartAware) {
+ output.add(((SubPartAware) connector.getWidget())
+ .getSubPartElement(pathComponents[1]));
+ }
+ }
+ } else {
+ for (ComponentConnector connector : connectors) {
+ output.add(connector.getWidget().getElement());
+ }
+ }
+ }
+ 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.
@@ -156,7 +364,7 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
* The path identifying an element.
* @param parent
* The connector to start traversing from.
- * @return The connector identified by {@code path} or null if it no such
+ * @return The connector identified by {@code path} or null if no such
* connector could be found.
*/
private ComponentConnector findConnectorByPath(String path,
@@ -168,19 +376,56 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
String[] fragments = splitFirstFragmentFromTheRest(path);
List<ComponentConnector> potentialMatches = collectPotentialMatches(
parent, fragments[0], findRecursively);
- ComponentConnector connector = filterPotentialMatches(potentialMatches,
- extractPredicateString(fragments[0]));
- if (connector != null) {
+
+ List<ComponentConnector> connectors = filterMatches(potentialMatches,
+ extractPredicates(fragments[0]));
+
+ if (!connectors.isEmpty()) {
if (fragments.length > 1) {
- return findConnectorByPath(fragments[1], connector);
+ return findConnectorByPath(fragments[1], connectors.get(0));
} else {
- return connector;
+ 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}
+ * connectors.
+ *
+ * @param path
+ * The path identifying elements.
+ * @param parents
+ * The list of connectors to start traversing from.
+ * @return The list of connectors identified by {@code path} or empty list
+ * if no such connectors could be found.
+ */
+ private List<ComponentConnector> findConnectorsByPath(String path,
+ List<ComponentConnector> parents) {
+ 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 = new ArrayList<ComponentConnector>();
+ for (ComponentConnector parent : parents) {
+ potentialMatches.addAll(collectPotentialMatches(parent,
+ fragments[0], findRecursively));
+ }
+
+ List<ComponentConnector> connectors = filterMatches(potentialMatches,
+ extractPredicates(fragments[0]));
+
+ if (!connectors.isEmpty() && fragments.length > 1) {
+ return (findConnectorsByPath(fragments[1], connectors));
+ }
+ return eliminateDuplicates(connectors);
+ }
+
+ /**
* Returns the predicate string, i.e. the string between the brackets in a
* path fragment. Examples: <code>
* VTextField[0] => 0
@@ -189,7 +434,8 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
*
* @param pathFragment
* The path fragment from which to extract the predicate string.
- * @return The predicate string for the path fragment or null if none.
+ * @return The predicate string for the path fragment or empty string if not
+ * found.
*/
private String extractPredicateString(String pathFragment) {
int ixOpenBracket = indexOfIgnoringQuotes(pathFragment, '[');
@@ -198,60 +444,63 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
ixOpenBracket);
return pathFragment.substring(ixOpenBracket + 1, ixCloseBracket);
}
- return null;
+ return "";
}
/**
- * Returns the first ComponentConnector that matches the predicate string
- * from a list of potential matches. If {@code predicateString} is null, the
- * first element in the {@code potentialMatches} list is returned.
+ * Go through a list of potentially matching components, modifying that list
+ * until all elements that remain in that list match the complete list of
+ * predicates.
*
* @param potentialMatches
- * A list of potential matches to check.
- * @param predicateString
- * The predicate that should match. Can be an index or a property
- * name, value pair or null.
- * @return A {@link ComponentConnector} from the {@code potentialMatches}
- * list, which matches the {@code predicateString} or null if no
- * matches are found.
+ * a list of component connectors. Will be changed.
+ * @param predicates
+ * an immutable list of predicates
+ * @return filtered list of component connectors.
*/
- private ComponentConnector filterPotentialMatches(
- List<ComponentConnector> potentialMatches, String predicateString) {
-
- if (potentialMatches.isEmpty()) {
- return null;
- }
-
- if (predicateString != null) {
+ private List<ComponentConnector> filterMatches(
+ List<ComponentConnector> potentialMatches,
+ List<Predicate> predicates) {
+
+ for (Predicate p : predicates) {
+
+ if (p.index > -1) {
+ try {
+ ComponentConnector v = potentialMatches.get(p.index);
+ potentialMatches.clear();
+ potentialMatches.add(v);
+ } catch (IndexOutOfBoundsException e) {
+ potentialMatches.clear();
+ }
- int split_idx = predicateString.indexOf('=');
+ continue;
+ }
- if (split_idx != -1) {
+ for (int i = 0, l = potentialMatches.size(); i < l; ++i) {
- String propertyName = predicateString.substring(0, split_idx)
- .trim();
- String value = unquote(predicateString.substring(split_idx + 1)
- .trim());
+ ComponentConnector c = potentialMatches.get(i);
+ Property property = AbstractConnector.getStateType(c)
+ .getProperty(p.name);
- for (ComponentConnector connector : potentialMatches) {
- Property property = AbstractConnector.getStateType(
- connector).getProperty(propertyName);
- if (valueEqualsPropertyValue(value, property,
- connector.getState())) {
- return connector;
- }
+ Object propData;
+ try {
+ propData = property.getValue(c.getState());
+ } catch (NoDataException e) {
+ propData = null;
}
- return null;
-
- } else {
- int index = Integer.valueOf(predicateString);
- return index < potentialMatches.size() ? potentialMatches
- .get(index) : null;
+ if ((p.wildcard && propData == null)
+ || (!p.wildcard && !valueEqualsPropertyValue(p.value,
+ property, c.getState()))) {
+ potentialMatches.remove(i);
+ --l;
+ --i;
+ }
}
+
}
- return potentialMatches.get(0);
+ return eliminateDuplicates(potentialMatches);
}
/**
@@ -270,7 +519,7 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
AbstractComponentState state) {
try {
return value.equals(property.getValue(state));
- } catch (NoDataException e) {
+ } catch (Exception e) {
// The property doesn't exist in the state object, so they aren't
// equal.
return false;
@@ -315,8 +564,8 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
ComponentConnector parent, String pathFragment,
boolean collectRecursively) {
ArrayList<ComponentConnector> potentialMatches = new ArrayList<ComponentConnector>();
- if (parent instanceof AbstractHasComponentsConnector) {
- List<ComponentConnector> children = ((AbstractHasComponentsConnector) parent)
+ if (parent instanceof HasComponentsConnector) {
+ List<ComponentConnector> children = ((HasComponentsConnector) parent)
.getChildComponents();
for (ComponentConnector child : children) {
String widgetName = getWidgetName(pathFragment);
@@ -329,7 +578,7 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
}
}
}
- return potentialMatches;
+ return eliminateDuplicates(potentialMatches);
}
/**
@@ -346,7 +595,60 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
*/
private boolean connectorMatchesPathFragment(ComponentConnector connector,
String widgetName) {
- return widgetName.equals(Util.getSimpleName(connector.getWidget()));
+ 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));
+ }
+
+ Integer[] widgetTags = client.getConfiguration()
+ .getTagsForServerSideClassName(getFullClassName(widgetName));
+ if (widgetTags.length == 0) {
+ widgetTags = client.getConfiguration()
+ .getTagsForServerSideClassName(
+ getFullClassName("com.vaadin.ui." + widgetName));
+ }
+
+ for (int i = 0, l = ids.size(); i < l; ++i) {
+
+ // Fuzz the connector name, so that the client can provide (for
+ // example: /Button, /Button.class, /com.vaadin.ui.Button,
+ // /com.vaadin.ui.Button.class, etc)
+
+ String name = ids.get(i);
+ final String simpleName = getSimpleClassName(name);
+ final String fullName = getFullClassName(name);
+
+ if (widgetTags.length > 0) {
+ Integer[] foundTags = client.getConfiguration()
+ .getTagsForServerSideClassName(fullName);
+ for (int tag : foundTags) {
+ if (tagsMatch(widgetTags, tag)) {
+ return true;
+ }
+ }
+ }
+
+ // Fallback if something failed before.
+ if (widgetName.equals(fullName + ".class")
+ || widgetName.equals(fullName)
+ || widgetName.equals(simpleName + ".class")
+ || widgetName.equals(simpleName) || widgetName.equals(name)) {
+ return true;
+ }
+ }
+
+ // If the server-side class name didn't match, fall back to testing for
+ // the explicit widget name
+ String widget = Util.getSimpleName(connector.getWidget());
+ return widgetName.equals(widget)
+ || widgetName.equals(widget + ".class");
+
}
/**
@@ -412,4 +714,75 @@ public class VaadinFinderLocatorStrategy implements LocatorStrategy {
return -1;
}
+ private String getSimpleClassName(String s) {
+ String[] parts = s.split("\\.");
+ if (s.endsWith(".class")) {
+ return parts[parts.length - 2];
+ }
+ return parts.length > 0 ? parts[parts.length - 1] : s;
+ }
+
+ private String getFullClassName(String s) {
+ if (s.endsWith(".class")) {
+ return s.substring(0, s.lastIndexOf(".class"));
+ }
+ return s;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.client.componentlocator.LocatorStrategy#validatePath(java.
+ * lang.String)
+ */
+ @Override
+ public boolean validatePath(String path) {
+ // This syntax is so difficult to regexp properly, that we'll just try
+ // to find something with it regardless of the correctness of the
+ // syntax...
+ return true;
+ }
+
+ /**
+ * Go through a list, removing all duplicate elements from it. This method
+ * is used to avoid accumulation of duplicate entries in result lists
+ * resulting from low-context recursion.
+ *
+ * Preserves first entry in list, removes others. Preserves list order.
+ *
+ * @return list passed as parameter, after modification
+ */
+ private final <T> List<T> eliminateDuplicates(List<T> list) {
+
+ int l = list.size();
+ for (int j = 0; j < l; ++j) {
+ T ref = list.get(j);
+
+ for (int i = j + 1; i < l; ++i) {
+ if (list.get(i) == ref) {
+ list.remove(i);
+ --i;
+ --l;
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private boolean tagsMatch(Integer[] targets, Integer tag) {
+ for (int i = 0; i < targets.length; ++i) {
+ if (targets[i].equals(tag)) {
+ return true;
+ }
+ }
+
+ try {
+ return tagsMatch(targets,
+ client.getConfiguration().getParentTag(tag));
+ } catch (Exception e) {
+ return false;
+ }
+ }
}