All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.remote.JsonToBeanConverter Maven / Gradle / Ivy

Go to download

Selenium automates browsers. That's it! What you do with that power is entirely up to you.

There is a newer version: 4.24.0
Show newest version
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you 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.openqa.selenium.remote;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.WebDriverException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonToBeanConverter {

  public  T convert(Class clazz, Object source) throws JsonException {
    try {
      return convert(clazz, source, 0);
    } catch (JsonSyntaxException e) {
      throw new JsonException(e, source);
    }
  }

  @SuppressWarnings("unchecked")
  private  T convert(Class clazz, Object source, int depth) {
    if (source == null || source instanceof JsonNull) {
      return null;
    }

    if (source instanceof JsonElement) {
      JsonElement json = (JsonElement) source;

      if (json.isJsonPrimitive()) {
        JsonPrimitive jp = json.getAsJsonPrimitive();

        if (String.class.equals(clazz)) {
          return (T) jp.getAsString();
        }

        if (jp.isNumber()) {
          if (Integer.class.isAssignableFrom(clazz) || int.class.equals(clazz)) {
            return (T) Integer.valueOf(jp.getAsNumber().intValue());
          } else if (Long.class.isAssignableFrom(clazz) || long.class.equals(clazz)) {
            return (T) Long.valueOf(jp.getAsNumber().longValue());
          } else if (Float.class.isAssignableFrom(clazz) || float.class.equals(clazz)) {
            return (T) Float.valueOf(jp.getAsNumber().floatValue());
          } else if (Double.class.isAssignableFrom(clazz) || double.class.equals(clazz)) {
            return (T) Double.valueOf(jp.getAsNumber().doubleValue());
          } else {
            return (T) convertJsonPrimitive(jp);
          }
        }
      }
    }

    if (isPrimitive(source.getClass())) {
      return (T) source;
    }

    if (isEnum(clazz, source)) {
      return (T) convertEnum(clazz, source);
    }

    if ("".equals(String.valueOf(source))) {
      return (T) source;
    }

    if (Command.class.equals(clazz)) {
      JsonObject json = new JsonParser().parse((String) source).getAsJsonObject();

      SessionId sessionId = null;
      if (json.has("sessionId") && !json.get("sessionId").isJsonNull()) {
        sessionId = convert(SessionId.class, json.get("sessionId"), depth + 1);
      }

      String name = json.get("name").getAsString();
      if (json.has("parameters")) {
        Map args = (Map) convert(HashMap.class, json.get("parameters"), depth + 1);
        return (T) new Command(sessionId, name, args);
      }

      return (T) new Command(sessionId, name);
    }

    if (Response.class.equals(clazz)) {
      Response response = new Response();
      JsonObject json = source instanceof JsonObject
                        ? (JsonObject) source
                        : new JsonParser().parse((String) source).getAsJsonObject();

      if (json.has("error") && ! json.get("error").isJsonNull()) {
        String state = json.get("error").getAsString();
        response.setState(state);
        response.setStatus(ErrorCodes.toStatus(state));
        response.setValue(convert(Object.class, json.get("message")));
      }
      if (json.has("state") && ! json.get("state").isJsonNull()) {
        String state = json.get("state").getAsString();
        response.setState(state);
        response.setStatus(ErrorCodes.toStatus(state));
      }
      if (json.has("status") && ! json.get("status").isJsonNull()) {
        JsonElement status = json.get("status");
        if (status.getAsJsonPrimitive().isString()) {
          String state = status.getAsString();
          response.setState(state);
          response.setStatus(ErrorCodes.toStatus(state));
        } else {
          int intStatus = status.getAsInt();
          response.setState(ErrorCodes.toState(intStatus));
          response.setStatus(intStatus);
        }
      }
      if (json.has("sessionId") && ! json.get("sessionId").isJsonNull()) {
        response.setSessionId(json.get("sessionId").getAsString());
      }

      if (json.has("value")) {
        response.setValue(convert(Object.class, json.get("value")));
      } else {
        response.setValue(convert(Object.class, json));
      }

      return (T) response;
    }

    if (SessionId.class.equals(clazz)) {
      // Stupid heuristic to tell if we are dealing with a selenium 2 or 3 session id.
      JsonElement json = source instanceof String
          ? new JsonParser().parse((String) source).getAsJsonObject() : (JsonElement) source;
      if (json.isJsonPrimitive()) {
        return (T) new SessionId(json.getAsString());
      }
      return (T) new SessionId(json.getAsJsonObject().get("value").getAsString());
    }

    if (Capabilities.class.isAssignableFrom(clazz)) {
      JsonObject json = source instanceof JsonElement
                        ? ((JsonElement) source).getAsJsonObject()
                        : new JsonParser().parse(source.toString()).getAsJsonObject();
      Map map = convertMap(json.getAsJsonObject(), depth);
      return (T) new DesiredCapabilities(map);
    }

    if (Date.class.equals(clazz)) {
      return (T) new Date(Long.valueOf(String.valueOf(source)));
    }

    if (source instanceof String && !((String) source).startsWith("{") && Object.class.equals(clazz)) {
      return (T) source;
    }

    Method fromJson = getMethod(clazz, "fromJson");
    if (fromJson != null) {
      try {
        return (T) fromJson.invoke(null, source.toString());
      } catch (IllegalArgumentException e) {
        throw new WebDriverException(e);
      } catch (IllegalAccessException e) {
        throw new WebDriverException(e);
      } catch (InvocationTargetException e) {
        throw new WebDriverException(e);
      }
    }

    if (depth == 0) {
      if (source instanceof String) {
        source = new JsonParser().parse((String) source);
      }
    }

    if (source instanceof JsonElement) {
      JsonElement element = (JsonElement) source;

      if (element.isJsonNull()) {
        return null;
      }

      if (element.isJsonPrimitive()) {
        return (T) convertJsonPrimitive(element.getAsJsonPrimitive());
      }

      if (element.isJsonArray()) {
        return (T) convertList(element.getAsJsonArray(), depth);
      }

      if (element.isJsonObject()) {
        if (Map.class.isAssignableFrom(clazz)) {
          return (T) convertMap(element.getAsJsonObject(), depth);
        }

        if (Object.class.equals(clazz)) {
          return (T) convertMap(element.getAsJsonObject(), depth);
        }

        return convertBean(clazz, element.getAsJsonObject(), depth);
      }
    }

    return (T) source; // Crap shoot here; probably a string.
  }

  private Method getMethod(Class clazz, String methodName) {
    try {
      return clazz.getMethod(methodName, String.class);
    } catch (SecurityException e) {
      // fall through
    } catch (NoSuchMethodException e) {
      // fall through
    }

    return null;
  }

  private Object convertJsonPrimitive(JsonElement json) {
    return convertJsonPrimitive(json.getAsJsonPrimitive());
  }

  private Object convertJsonPrimitive(JsonPrimitive json) {
    if (json.isBoolean()) {
      return json.getAsBoolean();
    } else if (json.isNumber()) {
      if (json.getAsLong() == json.getAsDouble()) {
        return json.getAsLong();
      }
      return json.getAsDouble();
    } else if (json.isString()) {
      return json.getAsString();
    } else {
      return null;
    }
  }

  @SuppressWarnings("unchecked")
  private Enum convertEnum(Class clazz, Object text) {
    if (clazz.isEnum()) {
      if (text instanceof JsonElement) {
        return Enum.valueOf(clazz, (String) convertJsonPrimitive((JsonElement) text));
      }
      return Enum.valueOf(clazz, String.valueOf(text));
    }

    Class[] allClasses = clazz.getClasses();
    for (Class current : allClasses) {
      if (current.isEnum()) {
        if (text instanceof JsonElement) {
          return Enum.valueOf(current, (String) convertJsonPrimitive((JsonElement) text));
        }
        return Enum.valueOf(current, String.valueOf(text));
      }
    }

    return null;
  }

  private boolean isEnum(Class clazz, Object text) {
    return clazz.isEnum() || text instanceof Enum;
  }

  private  T convertBean(Class clazz, JsonObject toConvert, int depth) {
    T t = newInstance(clazz);
    SimplePropertyDescriptor[] allProperties =
        SimplePropertyDescriptor.getPropertyDescriptors(clazz);
    for (SimplePropertyDescriptor property : allProperties) {
      if (!toConvert.has(property.getName()))
        continue;

      JsonElement value = toConvert.get(property.getName());

      Method write = property.getWriteMethod();
      if (write == null) {
        continue;
      }

      Class type = write.getParameterTypes()[0];

      try {
        if (value.isJsonNull()) {
          value = null;
        }
        write.invoke(t, convert(type, value, depth + 1));
      } catch (IllegalArgumentException e) {
        throw propertyWriteException(property, value, type, e);
      } catch (IllegalAccessException e) {
        throw propertyWriteException(property, value, type, e);
      } catch (InvocationTargetException e) {
        throw propertyWriteException(property, value, type, e);
      }
    }

    return t;
  }

  private  T newInstance(Class clazz) {
    try {
      return clazz.newInstance();
    } catch (InstantiationException e) {
      throw new WebDriverException(e);
    } catch (IllegalAccessException e) {
      throw new WebDriverException(e);
    }
  }

  private WebDriverException propertyWriteException(
      SimplePropertyDescriptor property, Object value, Class type, Throwable cause) {
    throw new WebDriverException(
        String.format("Property name: %s -> %s on class %s", property.getName(), value, type),
        cause);
  }

  private Map convertMap(JsonObject toConvert, int depth) {
    Map map = new HashMap<>();

    for (Map.Entry entry : toConvert.entrySet()) {
      map.put(entry.getKey(), convert(Object.class, entry.getValue(), depth + 1));
    }

    return map;
  }

  private List convertList(JsonArray toConvert, int depth) {
    List list = new ArrayList<>(toConvert.size());
    for (int i = 0; i < toConvert.size(); i++) {
      list.add(convert(Object.class, toConvert.get(i), depth + 1));
    }
    return list;
  }


  private boolean isPrimitive(Class clazz) {
    if (clazz.isPrimitive()) {
      return true;
    }

    if (Boolean.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Byte.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Character.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Double.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Float.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Integer.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Long.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Short.class.isAssignableFrom(clazz)) {
      return true;
    }

    if (Void.class.isAssignableFrom(clazz)) {
      return true;
    }

    return false;
  }
}