package org.fluentlenium.core.components; import org.fluentlenium.core.FluentControl; import org.fluentlenium.core.proxy.LocatorProxies; import org.fluentlenium.core.proxy.ProxyElementListener; import org.openqa.selenium.WebElement; import org.openqa.selenium.internal.WrapsElement; import org.openqa.selenium.support.pagefactory.ElementLocator; import java.util.ArrayList; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Manage living components for a WebDriver instance. * <p> * A component is an Object implementing no particular interface, but capable a wrapping * a {@link org.openqa.selenium.WebElement}. * <p> * {@link org.fluentlenium.core.domain.FluentWebElement} is the most common component. */ public class ComponentsManager extends AbstractComponentInstantiator implements ComponentInstantiator, ComponentsAccessor, ProxyElementListener { private final FluentControl control; private final DefaultComponentInstantiator instantiator; private final Map<WebElement, Set<Object>> components = new IdentityHashMap<>(); private final List<ComponentsListener> listeners = new ArrayList<>(); /** * Creates a new components manager. * * @param control control interface */ public ComponentsManager(FluentControl control) { this.control = control; instantiator = new DefaultComponentInstantiator(this.control, this); } /** * Get the component instantiator used by this components manager. * * @return component instantiator */ public ComponentInstantiator getInstantiator() { return instantiator; } @Override public Set<Object> getComponents(WebElement element) { return components.get(unwrapElement(element)); } @Override public boolean isComponentClass(Class<?> componentClass) { return instantiator.isComponentClass(componentClass); } @Override public boolean isComponentListClass(Class<? extends List<?>> componentListClass) { return instantiator.isComponentListClass(componentListClass); } @Override public <T> T newComponent(Class<T> componentClass, WebElement element) { T component = instantiator.newComponent(componentClass, element); register(element, component); return component; } @Override public boolean addComponentsListener(ComponentsListener listener) { synchronized (this) { return listeners.add(listener); } } @Override public boolean removeComponentsListener(ComponentsListener listener) { synchronized (this) { return listeners.remove(listener); } } /** * Fire component registered event. * * @param element underlying element * @param component registered component */ protected void fireComponentRegistered(WebElement element, Object component) { synchronized (this) { for (ComponentsListener listener : listeners) { listener.componentRegistered(element, component); } } } /** * Fire component released event. * * @param element underlying element * @param component released component */ protected void fireComponentReleased(WebElement element, Object component) { synchronized (this) { for (ComponentsListener listener : listeners) { listener.componentReleased(element, component); } } } private <T> void register(WebElement element, T component) { WebElement webElement = unwrapElement(element); LocatorProxies.addProxyListener(webElement, this); synchronized (this) { Set<Object> elementComponents = components.get(webElement); if (elementComponents == null) { elementComponents = new HashSet<>(); components.put(webElement, elementComponents); } elementComponents.add(component); fireComponentRegistered(element, component); } } @Override public <L extends List<T>, T> L newComponentList(Class<L> listClass, Class<T> componentClass, List<T> componentsList) { return instantiator.newComponentList(listClass, componentClass, componentsList); } @Override public <L extends List<T>, T> L asComponentList(Class<L> listClass, Class<T> componentClass, Iterable<WebElement> elementList) { L componentList = instantiator.asComponentList(listClass, componentClass, elementList); int i = 0; for (WebElement element : elementList) { register(element, componentList.get(i)); i++; } return componentList; } @Override public void proxyElementSearch(Object proxy, ElementLocator locator) { // Do nothing. } @Override public void proxyElementFound(Object proxy, ElementLocator locator, List<WebElement> elements) { synchronized (this) { for (WebElement element : elements) { Set<Object> proxyComponents = components.remove(proxy); if (proxyComponents != null) { components.put(unwrapElement(element), proxyComponents); } } } } private WebElement unwrapElement(WebElement element) { if (element instanceof WrapsElement) { WebElement wrappedElement = ((WrapsElement) element).getWrappedElement(); if (wrappedElement != element && wrappedElement != null) { // NOPMD CompareObjectsWithEquals return unwrapElement(wrappedElement); } } return element; } /** * Release this manager. */ public void release() { for (Map.Entry<WebElement, Set<Object>> elementEntry : components.entrySet()) { LocatorProxies.removeProxyListener(elementEntry.getKey(), this); for (Object component : elementEntry.getValue()) { fireComponentRegistered(elementEntry.getKey(), component); } } components.clear(); } }