
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}...
