1package net.thucydides.core.annotations.locators;2import com.google.common.collect.ImmutableList;3import net.thucydides.core.annotations.findby.FindBy;4import net.thucydides.core.pages.PageObject;5import net.thucydides.core.pages.WebElementFacade;6import org.openqa.selenium.WebDriver;7import org.openqa.selenium.WebElement;8import org.openqa.selenium.internal.Locatable;9import org.openqa.selenium.internal.WrapsElement;10import org.openqa.selenium.support.FindBys;11import org.openqa.selenium.support.pagefactory.ElementLocator;12import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;13import org.openqa.selenium.support.pagefactory.FieldDecorator;14import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;15import org.openqa.selenium.support.pagefactory.internal.LocatingElementListHandler;16import java.lang.annotation.Annotation;17import java.lang.reflect.*;18import java.util.List;19public class SmartFieldDecorator implements FieldDecorator {20 protected ElementLocatorFactory factory;21 protected WebDriver driver;22 protected PageObject pageObject;23 public SmartFieldDecorator(ElementLocatorFactory factory, WebDriver driver,24 PageObject pageObject) {25 this.driver = driver;26 this.factory = factory;27 this.pageObject = pageObject;28 }29 public Object decorate(ClassLoader loader, Field field) {30 if (!(WebElement.class.isAssignableFrom(field.getType()) || isDecoratableList(field))) {31 return null;32 }33 ElementLocator locator = factory.createLocator(field);34 if (locator == null) {35 return null;36 }37 Class<?> fieldType = field.getType();38 if (WebElement.class.isAssignableFrom(fieldType)) {39 return proxyForLocator(loader, fieldType, locator);40 } else if (List.class.isAssignableFrom(fieldType)) {41 Class<?> erasureClass = getErasureClass(field);42 return proxyForListLocator(loader, erasureClass, locator);43 } else {44 return null;45 }46 }47 @SuppressWarnings("rawtypes")48 private Class getErasureClass(Field field) {49 Type genericType = field.getGenericType();50 if (!(genericType instanceof ParameterizedType)) {51 return null;52 }53 return (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0];54 }55 @SuppressWarnings("rawtypes")56 private boolean isDecoratableList(Field field) {57 if (!List.class.isAssignableFrom(field.getType())) {58 return false;59 }60 Class erasureClass = getErasureClass(field);61 if (erasureClass == null || !WebElement.class.isAssignableFrom(erasureClass)) {62 return false;63 }64 return annotatedByLegalFindByAnnotation(field);65 }66 private final static List<Class<? extends Annotation>> LEGAL_ANNOTATIONS67 = ImmutableList.of(FindBy.class,68 org.openqa.selenium.support.FindBy.class,69 FindBys.class);70 private boolean annotatedByLegalFindByAnnotation(Field field) {71 for (Annotation annotation : field.getAnnotations()) {72 if (LEGAL_ANNOTATIONS.contains(annotation.annotationType())) {73 return true;74 }75 }76 return false;77 }78 /* Generate a type-parameterized locator proxy for the element in question. */79 @SuppressWarnings("unchecked")80 protected <T> T proxyForLocator(ClassLoader loader, Class<T> interfaceType, ElementLocator locator) {81 InvocationHandler handler;82 T proxy = null;83 if (WebElementFacade.class.isAssignableFrom(interfaceType)) {84 handler = new SmartElementHandler(interfaceType, locator, driver, pageObject.waitForTimeoutInMilliseconds());85 proxy = (T) Proxy.newProxyInstance(loader, new Class[]{interfaceType}, handler);86 } else {87 handler = new LocatingElementHandler(locator);88 proxy = (T) Proxy.newProxyInstance(loader,89 new Class[]{WebElement.class, WrapsElement.class, Locatable.class}, handler);90 }91 return proxy;92 }93 /* generates a proxy for a list of elements to be wrapped. */94 @SuppressWarnings("unchecked")95 protected <T> List<T> proxyForListLocator(ClassLoader loader, Class<T> interfaceType, ElementLocator locator) {96 InvocationHandler handler = new LocatingElementListHandler(locator);97 List<T> proxy;98 proxy = (List<T>) Proxy.newProxyInstance(99 loader, new Class[]{List.class}, handler);100 return proxy;101 }102}...