/*
* JBoss, Home of Professional Open Source
* Copyright 2016, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.arquillian.drone.webdriver.factory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.arquillian.drone.configuration.ConfigurationMapper;
import org.jboss.arquillian.drone.configuration.mapping.ValueMapper;
import org.jboss.arquillian.drone.webdriver.utils.StringUtils;
import org.jboss.arquillian.drone.webdriver.utils.Validate;
import org.openqa.selenium.remote.DesiredCapabilities;
/**
*
*/
public class CapabilitiesOptionsMapper {
private static final Gson GSON = new Gson();
/**
* Parses capabilities set in {@link DesiredCapabilities} and according to set-method names it sets the values into
* corresponding variables of the given Object instance. It is expected that the parameters defined in arquillian.xml
* file have a specific browserPrefix; after this prefix then there is the parameter name itself
* (whole string has to be in camelcase)
*
* @param object
* An instance of an object the values should be set into
* @param capabilities
* A {@link DesiredCapabilities} that contains parameters and its values set in arquillian.xml
* @param browserPrefix
* A prefix the should the mapped parameters should start with
*/
static void mapCapabilities(Object object, DesiredCapabilities capabilities, String browserPrefix) {
Method[] methods = object.getClass().getMethods();
List<String> processedMethods = new ArrayList<String>();
for (Method method : methods) {
if (isSetter(method)) {
method.setAccessible(true);
String methodName = method.getName();
if (processedMethods.contains(methodName)) {
continue;
}
String propertyName = browserPrefix + methodName.substring(3);
propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
String capability = (String) capabilities.getCapability(propertyName);
if (Validate.nonEmpty(capability)) {
try {
if (method.getParameterTypes().length == 1) {
Object converted = convert(method, capability);
if (converted != null) {
method.invoke(object, converted);
processedMethods.add(methodName);
}
} else if (shouldContainDictionaries(method)) {
handleDictionaries(object, method, capability);
processedMethods.add(methodName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private static void handleDictionaries(Object object, Method method, String capability)
throws InvocationTargetException, IllegalAccessException {
String trimmedCapability = StringUtils.trimMultiline(capability);
JsonObject json = new JsonParser().parse(trimmedCapability).getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entries = json.entrySet();
final Type type = new TypeToken<Map<String, String>>() {
}.getType();
for (Map.Entry<String, JsonElement> entry : entries) {
String key = entry.getKey();
Map<String, String> values = GSON.fromJson(entry.getValue(), type);
method.invoke(object, key, values);
}
}
private static boolean shouldContainDictionaries(Method method) {
return
(method.getParameterTypes().length == 2)
&&
(method.getParameterTypes()[0].isAssignableFrom(String.class)
|| method.getParameterTypes()[0].isAssignableFrom(Object.class))
&&
(method.getParameterTypes()[1].isAssignableFrom(Object.class)
|| method.getParameterTypes()[1].isAssignableFrom(Map.class));
}
private static Object convert(Method method, String capability) {
Class<?> parameterType = method.getParameterTypes()[0];
Object converted;
if ((converted = convertToBooleanNumberStringOrFile(parameterType, capability)) != null) {
return converted;
} else if (parameterType.isArray()) {
return handleArray(parameterType.getComponentType(), capability);
} else if (parameterType.isAssignableFrom(List.class)) {
return handleList(method, capability);
}
return null;
}
private static <T> T[] handleArray(Class<T> parameterType, String capability) {
List<T> convertedList = getConvertedList(parameterType, capability);
T[] array = (T[]) Array.newInstance(parameterType, convertedList.size());
return convertedList.toArray(array);
}
private static Object handleList(Method method, String capability) {
Type[] genericParameterTypes = method.getGenericParameterTypes();
if (genericParameterTypes.length == 1) {
Type type = genericParameterTypes[0];
if (type instanceof ParameterizedType) {
Type[] parameters = ((ParameterizedType) type).getActualTypeArguments();
if (parameters.length == 1) {
return getConvertedList((Class<Object>) parameters[0], capability);
}
}
}
return null;
}
private static <T> List<T> getConvertedList(Class<T> parameter, String capability) {
List<String> values = StringUtils.tokenize(capability);
List<T> convertedList = new ArrayList<T>(values.size());
for (String value : values) {
convertedList.add(convertToBooleanNumberStringOrFile(parameter, value));
}
return convertedList;
}
private static <T> T convertToBooleanNumberStringOrFile(Class<T> clazz, String value) {
for (ValueMapper<?> mapper : ConfigurationMapper.VALUE_MAPPERS) {
if (mapper.handles(clazz)) {
return (T) mapper.transform(value);
}
}
return null;
}
private static boolean isSetter(Method candidate) {
return candidate.getName().matches("^(set|add)[A-Z].*") && candidate.getReturnType().equals(Void.TYPE)
&& candidate.getParameterTypes().length > 0;
}
}