Skip to content

Commit

Permalink
Refactor code related to proxies into a dedicated @UtilityClass
Browse files Browse the repository at this point in the history
  • Loading branch information
Toilal committed Aug 23, 2016
1 parent a5989a6 commit e88c5f5
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 93 deletions.
@@ -1,37 +1,28 @@
package org.fluentlenium.core.inject; package org.fluentlenium.core.inject;


import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import org.fluentlenium.core.FluentContainer; import org.fluentlenium.core.FluentContainer;
import org.fluentlenium.core.FluentControl; import org.fluentlenium.core.FluentControl;
import org.fluentlenium.core.annotation.AjaxElement; import org.fluentlenium.core.annotation.AjaxElement;
import org.fluentlenium.core.annotation.Page; import org.fluentlenium.core.annotation.Page;
import org.fluentlenium.core.components.ComponentsManager; import org.fluentlenium.core.components.ComponentsManager;
import org.fluentlenium.core.domain.FluentList; import org.fluentlenium.core.domain.FluentList;
import org.fluentlenium.core.domain.FluentListImpl;
import org.fluentlenium.core.domain.FluentWebElement; import org.fluentlenium.core.domain.FluentWebElement;
import org.fluentlenium.core.events.ContainerAnnotationsEventsRegistry; import org.fluentlenium.core.events.ContainerAnnotationsEventsRegistry;
import org.fluentlenium.core.proxy.Proxies;
import org.fluentlenium.utils.ReflectionUtils; import org.fluentlenium.utils.ReflectionUtils;
import org.openqa.selenium.WebElement; import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.events.EventFiringWebDriver; import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory; import org.openqa.selenium.support.pagefactory.AjaxElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory; import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;


import javax.inject.Inject; import javax.inject.Inject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
Expand Down Expand Up @@ -151,17 +142,13 @@ private <T extends FluentControl> void initFluentElements(Object container) {
for (Field fieldFromPage : cls.getDeclaredFields()) { for (Field fieldFromPage : cls.getDeclaredFields()) {
if (isSupported(container, fieldFromPage)) { if (isSupported(container, fieldFromPage)) {
AjaxElement elem = fieldFromPage.getAnnotation(AjaxElement.class); AjaxElement elem = fieldFromPage.getAnnotation(AjaxElement.class);
ElementLocatorFactory locatorFactory;
if (elem == null) { if (elem == null) {
initFieldElements( locatorFactory = new DefaultElementLocatorFactory(this.fluentControl.getDriver());
new DefaultElementLocatorFactory(this.fluentControl.getDriver()),
container,
fieldFromPage);
} else { } else {
initFieldElements( locatorFactory = new AjaxElementLocatorFactory(this.fluentControl.getDriver(), elem.timeOutInSeconds());
new AjaxElementLocatorFactory(this.fluentControl.getDriver(), elem.timeOutInSeconds()),
container,
fieldFromPage);
} }
initFieldElements(locatorFactory, container, fieldFromPage);
} }
} }
} }
Expand Down Expand Up @@ -229,90 +216,20 @@ private Object initFieldElements(ElementLocatorFactory factory, Object container
} }


private Object initFieldAsElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException { private Object initFieldAsElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException {
final InvocationHandler handler = new LocatingElementHandler(locator); Object proxy = Proxies.createComponent(locator, field.getType(), componentsManager);
WebElement proxy = (WebElement) Proxy.newProxyInstance(container.getClass().getClassLoader(), new Class[]{WebElement.class, Locatable.class}, handler); ReflectionUtils.set(field, container, proxy);
Object proxyWrapper = newComponent(proxy, field.getType()); return proxy;
ReflectionUtils.set(field, container, proxyWrapper);
return proxyWrapper;
} }


private List<?> initFieldAsList(ElementLocator locator, Object container, Field field) throws IllegalAccessException { private List<?> initFieldAsList(ElementLocator locator, Object container, Field field) throws IllegalAccessException {
final InvocationHandler handler = new ArrayListInvocationHandler(locator, getFirstGenericType(field)); List<?> proxy = Proxies.createComponentList(locator, getFirstGenericType(field), componentsManager);
List<?> proxy = (List<?>) Proxy.newProxyInstance(
container.getClass().getClassLoader(), new Class[]{List.class}, handler);
ReflectionUtils.set(field, container, proxy); ReflectionUtils.set(field, container, proxy);
return proxy; return proxy;
} }


private FluentList<? extends FluentWebElement> initFieldAsListOfFluentWebElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException { private FluentList<? extends FluentWebElement> initFieldAsListOfFluentWebElement(ElementLocator locator, Object container, Field field) throws IllegalAccessException {
final InvocationHandler handler = new FluentListInvocationHandler(locator, getFirstGenericType(field)); FluentList<? extends FluentWebElement> proxy = Proxies.createFluentList(locator, (Class<? extends FluentWebElement>) getFirstGenericType(field), componentsManager);
FluentList<? extends FluentWebElement> proxy = (FluentList<? extends FluentWebElement>) Proxy.newProxyInstance(
container.getClass().getClassLoader(), new Class[]{FluentList.class}, handler);
ReflectionUtils.set(field, container, proxy); ReflectionUtils.set(field, container, proxy);
return proxy; return proxy;
} }

private <T> T newComponent(WebElement element, Class<T> fluentElementClass) {
return this.componentsManager.newComponent(fluentElementClass, element);
}

private class FluentListInvocationHandler<T> implements InvocationHandler {

private final ElementLocator elementLocator;

private final Class<T> fluentElementClass;

public FluentListInvocationHandler(ElementLocator elementLocator, Class<T> fluentElementClass) {
this.elementLocator = elementLocator;
this.fluentElementClass = fluentElementClass;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
List<WebElement> elements = elementLocator.findElements();
FluentListImpl list = new FluentListImpl(FluentIterable.from(elements).transform(new Function<WebElement, T>() {

@Override
public T apply(WebElement input) {
return newComponent(input, fluentElementClass);
}
}).toList());
try {
return ReflectionUtils.invoke(method, list, args);
} catch (InvocationTargetException e) {
// Unwrap the underlying exception
throw e.getCause();
}
}
}

private class ArrayListInvocationHandler<T> implements InvocationHandler {

private final ElementLocator elementLocator;

private final Class<T> fluentElementClass;

public ArrayListInvocationHandler(ElementLocator elementLocator,
Class<T> fluentElementClass) {
this.elementLocator = elementLocator;
this.fluentElementClass = fluentElementClass;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
List<WebElement> elements = elementLocator.findElements();
List<T> list = new ArrayList<T>(FluentIterable.from(elements).transform(new Function<WebElement, T>() {
@Override
public T apply(WebElement input) {
return newComponent(input, fluentElementClass);
}
}).toList());
try {
return ReflectionUtils.invoke(method, list, args);
} catch (InvocationTargetException e) {
// Unwrap the underlying exception
throw e.getCause();
}
}
}
} }
@@ -0,0 +1,39 @@
package org.fluentlenium.core.inject;

import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.pagefactory.ElementLocator;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class LocatingElementChainHandler implements InvocationHandler {
private final ElementLocator locator;

public LocatingElementChainHandler(ElementLocator locator) {
this.locator = locator;
}

public Object invoke(Object object, Method method, Object[] objects) throws Throwable {
WebElement element;
try {
element = locator.findElement();
} catch (NoSuchElementException e) {
if ("toString".equals(method.getName())) {
return "Proxy element for: " + locator.toString();
} else throw e;
}

if ("getWrappedElement".equals(method.getName())) {
return element;
}

try {
return method.invoke(element, objects);
} catch (InvocationTargetException e) {
// Unwrap the underlying exception
throw e.getCause();
}
}
}
@@ -0,0 +1,114 @@
package org.fluentlenium.core.proxy;

import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import lombok.experimental.UtilityClass;
import org.fluentlenium.core.components.ComponentInstantiator;
import org.fluentlenium.core.domain.FluentList;
import org.fluentlenium.core.domain.FluentListImpl;
import org.fluentlenium.core.domain.FluentWebElement;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
* Utility class to create proxies of WebElement, Component, FluentList and List of Components.
*/
@UtilityClass
public class Proxies {
public WebElement createWebElement(ElementLocator locator) {
final InvocationHandler handler = new LocatingElementHandler(locator);
WebElement proxy = (WebElement) Proxy.newProxyInstance(locator.getClass().getClassLoader(), new Class[]{WebElement.class, Locatable.class}, handler);
return proxy;
}

public <T> T createComponent(ElementLocator locator, Class<T> componentClass, ComponentInstantiator instantiator) {
return instantiator.newComponent(componentClass, createWebElement(locator));
}

public <T extends FluentWebElement> FluentList<T> createFluentList(ElementLocator locator, Class<T> componentClass, ComponentInstantiator instantiator) {
final InvocationHandler handler = new FluentListInvocationHandler(locator, componentClass, instantiator);
return (FluentList<T>) Proxy.newProxyInstance(
locator.getClass().getClassLoader(), new Class[]{FluentList.class}, handler);
}

public <T> List<T> createComponentList(ElementLocator locator, Class<T> componentClass, ComponentInstantiator instantiator) {
final InvocationHandler handler = new ArrayListInvocationHandler(locator, componentClass, instantiator);
return (List<T>) Proxy.newProxyInstance(
locator.getClass().getClassLoader(), new Class[]{List.class}, handler);
}

private class ArrayListInvocationHandler<T> implements InvocationHandler {

private final ElementLocator elementLocator;

private final Class<T> componentClass;

private final ComponentInstantiator instantiator;

public ArrayListInvocationHandler(ElementLocator elementLocator,
Class<T> componentClass, ComponentInstantiator instantiator) {
this.elementLocator = elementLocator;
this.componentClass = componentClass;
this.instantiator = instantiator;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
List<WebElement> elements = elementLocator.findElements();
List<T> list = new ArrayList<T>(FluentIterable.from(elements).transform(new Function<WebElement, T>() {
@Override
public T apply(WebElement input) {
return instantiator.newComponent(componentClass, input);
}
}).toList());
try {
return method.invoke(list, args);
} catch (InvocationTargetException e) {
// Unwrap the underlying exception
throw e.getCause();
}
}
}

private class FluentListInvocationHandler<T extends FluentWebElement> implements InvocationHandler {

private final ElementLocator elementLocator;

private final Class<T> fluentElementClass;

private final ComponentInstantiator instantiator;

public FluentListInvocationHandler(ElementLocator elementLocator, Class<T> fluentElementClass, ComponentInstantiator instantiator) {
this.elementLocator = elementLocator;
this.fluentElementClass = fluentElementClass;
this.instantiator = instantiator;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
List<WebElement> elements = elementLocator.findElements();
FluentListImpl list = new FluentListImpl(FluentIterable.from(elements).transform(new Function<WebElement, T>() {

@Override
public T apply(WebElement input) {
return instantiator.newComponent(fluentElementClass, input);
}
}).toList());
try {
return method.invoke(list, args);
} catch (InvocationTargetException e) {
// Unwrap the underlying exception
throw e.getCause();
}
}
}
}

0 comments on commit e88c5f5

Please sign in to comment.