/*
* Copyright 2004-2016 EPAM Systems
*
* This file is part of JDI project.
*
* JDI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JDI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JDI. If not, see <http://www.gnu.org/licenses/>.
*/
package com.epam.jdi.uitests.web.selenium.elements;
import com.epam.commons.LinqUtils;
import com.epam.commons.pairs.Pairs;
import com.epam.jdi.uitests.core.interfaces.base.IBaseElement;
import com.epam.jdi.uitests.core.interfaces.base.IComposite;
import com.epam.jdi.uitests.web.selenium.elements.apiInteract.ContextType;
import com.epam.jdi.uitests.web.selenium.elements.composite.Site;
import com.epam.jdi.uitests.web.selenium.elements.composite.WebPage;
import com.epam.jdi.uitests.web.selenium.elements.pageobjects.annotations.Frame;
import com.epam.jdi.uitests.web.selenium.elements.pageobjects.annotations.JFindBy;
import com.epam.jdi.uitests.web.selenium.elements.pageobjects.annotations.JPage;
import com.epam.jdi.uitests.web.selenium.elements.pageobjects.annotations.WebAnnotationsUtil;
import org.openqa.selenium.By;
import org.openqa.selenium.support.FindBy;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.epam.commons.LinqUtils.foreach;
import static com.epam.commons.ReflectionUtils.*;
import static com.epam.commons.StringUtils.LINE_BREAK;
import static com.epam.commons.TryCatchUtil.tryGetResult;
import static com.epam.jdi.uitests.core.settings.JDIData.APP_VERSION;
import static com.epam.jdi.uitests.core.settings.JDISettings.exception;
import static java.lang.String.format;
import static java.lang.reflect.Modifier.isStatic;
/**
* Created by Roman_Iovlev on 6/10/2015.
*/
public abstract class CascadeInit implements IBaseElement {
public synchronized static void InitElements(Object parent, String driverName) {
if (parent.getClass().getName().contains("$")) return;
Class<?> parentType = parent.getClass();
initSubElements(parent, driverName);
if (isClass(parentType, WebPage.class) && parentType.isAnnotationPresent(JPage.class))
WebAnnotationsUtil.fillPageFromAnnotaiton((WebPage) parent, parentType.getAnnotation(JPage.class), null);
}
private static void initSubElements(Object parent, String driverName) {
foreach(deepGetFields(parent, IBaseElement.class),
field -> setElement(parent, field, driverName));
}
private static List<Field> deepGetFields(Class<?> clazz) {
List<Field> result = new ArrayList<>();
if (isInterface(clazz, IBaseElement.class))
result.addAll(deepGetFields(clazz.getSuperclass()));
result.addAll(Arrays.asList(clazz.getDeclaredFields()));
return result;
}
private static List<Field> deepGetFields(Object obj, Class<?> type) {
return LinqUtils.where(deepGetFields(obj.getClass()), field -> !isStatic(field.getModifiers()) && (isClass(field, type) || isInterface(field, type)));
}
public synchronized static void initStaticPages(Class<?> parentType, String driverName) {
foreach(getStaticFields(parentType, BaseElement.class),
field -> setElement(parentType, field, driverName));
}
public synchronized static <T extends Site> T initPages(Class<T> site, String driverName) {
T instance = tryGetResult(site::newInstance);
instance.setDriverName(driverName);
InitElements(instance, driverName);
return instance;
}
private static void setElement(Class<?> parentType, Field field, String driverName) {
try {
Class<?> type = field.getType();
BaseElement instance;
if (isClass(type, WebPage.class)) {
instance = (BaseElement) getValueField(field, null);
if (instance == null)
instance = (BaseElement) type.newInstance();
fillPage(instance, field, parentType);
} else {
instance = createChildFromFieldStatic(parentType, field, type, driverName);
instance.function = WebAnnotationsUtil.getFunction(field);
}
instance.setName(field);
if (instance.getClass().getSimpleName().equals(""))
instance.setTypeName(type.getSimpleName());
instance.setParentName(parentType.getClass().getSimpleName());
field.set(null, instance);
if (isInterface(field, IComposite.class))
InitElements(instance, driverName);
} catch (Exception ex) {
throw exception("Error in setElement for field '%s' with parent '%s'", field.getName(), parentType.getClass().getSimpleName() + LINE_BREAK + ex.getMessage());
}
}
private static String getClassName(Object obj) {
return obj == null ? "NULL Class" : obj.getClass().getSimpleName();
}
private static void setElement(Object parent, Field field, String driverName) {
try {
Class<?> type = field.getType();
BaseElement instance;
if (isClass(type, WebPage.class)) {
instance = (BaseElement) getValueField(field, parent);
if (instance == null)
instance = (BaseElement) type.newInstance();
fillPage(instance, field, parent != null ? parent.getClass() : null);
} else {
instance = createChildFromField(parent, field, type, driverName);
instance.function = WebAnnotationsUtil.getFunction(field);
}
instance.setName(field);
instance.avatar.setDriverName(driverName);
if (instance.getClass().getSimpleName().equals(""))
instance.setTypeName(type.getSimpleName());
instance.setParentName(getClassName(parent));
field.set(parent, instance);
if (isInterface(field, IComposite.class))
InitElements(instance, driverName);
} catch (Exception ex) {
throw exception("Error in setElement for field '%s' with parent '%s'", field.getName(),
getClassName(parent) + LINE_BREAK + ex.getMessage());
}
}
private static void fillPage(BaseElement instance, Field field, Class<?> parentType) {
if (field.isAnnotationPresent(JPage.class))
WebAnnotationsUtil.fillPageFromAnnotaiton((WebPage) instance, field.getAnnotation(JPage.class), parentType);
}
private static BaseElement createChildFromFieldStatic(Class<?> parentClass, Field field, Class<?> type, String driverName) {
BaseElement instance = (BaseElement) getValueField(field, null);
if (instance == null)
try {
instance = getElementInstance(type, field.getName(), getNewLocator(field), driverName);
} catch (Exception ex) {
throw exception(format("Can't create child for parent '%s' with type '%s'",
parentClass.getSimpleName(), field.getType().getSimpleName()));
}
else if (instance.getLocator() == null)
instance.avatar.byLocator = getNewLocator(field);
instance.avatar.context = new Pairs<>();
By frameBy = WebAnnotationsUtil.getFrame(field.getDeclaredAnnotation(Frame.class));
if (frameBy != null)
instance.avatar.context.add(ContextType.Frame, frameBy);
return instance;
}
private static BaseElement createChildFromField(Object parent, Field field, Class<?> type, String driverName) {
BaseElement instance = (BaseElement) getValueField(field, parent);
if (instance == null)
try {
instance = getElementInstance(type, field.getName(), getNewLocator(field), driverName);
} catch (Exception ex) {
throw exception(
format("Can't create child for parent '%s' with type '%s'",
parent.getClass().getSimpleName(), field.getType().getSimpleName()));
}
else if (instance.getLocator() == null)
instance.avatar.byLocator = getNewLocator(field);
instance.avatar.context = (isBaseElement(parent))
? ((BaseElement) parent).avatar.context.copy()
: new Pairs<>();
if (type != null) {
By frameBy = WebAnnotationsUtil.getFrame(type.getDeclaredAnnotation(Frame.class));
if (frameBy != null)
instance.avatar.context.add(ContextType.Frame, frameBy);
}
if (isBaseElement(parent)) {
By parentLocator = ((BaseElement) parent).getLocator();
if (parentLocator != null)
instance.avatar.context.add(ContextType.Locator, parentLocator);
}
return instance;
}
private static boolean isBaseElement(Object obj) {
return isClass(obj.getClass(), BaseElement.class);
}
private static BaseElement getElementInstance(Class<?> type, String fieldName, By newLocator, String driverName) {
try {
if (!type.isInterface()) {
BaseElement instance = (BaseElement) type.newInstance();
instance.avatar.byLocator = newLocator;
instance.avatar.setDriverName(driverName);
return instance;
}
Class classType = MapInterfaceToElement.getClassFromInterface(type);
if (classType != null)
return (BaseElement) classType.getDeclaredConstructor(By.class).newInstance(newLocator);
throw exception("Unknown interface: " + type +
". Add relation interface -> class in VIElement.InterfaceTypeMap");
} catch (Exception ex) {
throw exception("Error in getElementInstance for field '%s' with type '%s'", fieldName, type.getSimpleName() +
LINE_BREAK + ex.getMessage());
}
}
private static By getNewLocator(Field field) {
try {
By byLocator = null;
String locatorGroup = APP_VERSION;
if (locatorGroup != null) {
JFindBy jFindBy = field.getAnnotation(JFindBy.class);
if (jFindBy != null && locatorGroup.equals(jFindBy.group()))
byLocator = WebAnnotationsUtil.getFindByLocator(jFindBy);
}
return (byLocator != null)
? byLocator
: WebAnnotationsUtil.getFindByLocator(field.getAnnotation(FindBy.class));
} catch (Exception ex) {
throw exception("Error in get locator for type '%s'", field.getType().getName() +
LINE_BREAK + ex.getMessage());
}
}
}