Skip to content

Commit

Permalink
Remove dependency on guava
Browse files Browse the repository at this point in the history
  • Loading branch information
shs96c committed Jul 3, 2019
1 parent 0d47cc2 commit 593c17a
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 145 deletions.
1 change: 0 additions & 1 deletion java/client/src/org/openqa/selenium/json/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ java_library(
deps = [
"//java/client/src/org/openqa/selenium:core",
"//java/client/src/org/openqa/selenium/remote:api",
"//third_party/java/guava:guava",
],
visibility = [
"//java/client/src/org/openqa/...",
Expand Down
1 change: 0 additions & 1 deletion java/client/src/org/openqa/selenium/json/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ java_library(
deps = [
"//java/client/src/org/openqa/selenium:core",
"//java/client/src/org/openqa/selenium/remote:api",
"//third_party/java/guava",
],
)
2 changes: 0 additions & 2 deletions java/client/src/org/openqa/selenium/json/Json.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

package org.openqa.selenium.json;

import com.google.common.reflect.TypeToken;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
Expand Down
138 changes: 69 additions & 69 deletions java/client/src/org/openqa/selenium/json/JsonOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@

package org.openqa.selenium.json;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

import com.google.common.collect.ImmutableMap;

import org.openqa.selenium.logging.LogLevelMapping;
import org.openqa.selenium.remote.SessionId;

Expand All @@ -32,8 +28,10 @@
import java.net.URL;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
Expand All @@ -44,6 +42,8 @@
import java.util.logging.Logger;
import java.util.stream.Stream;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class JsonOutput implements Closeable {
private static final Logger LOG = Logger.getLogger(JsonOutput.class.getName());
private static final int MAX_DEPTH = 5;
Expand All @@ -69,7 +69,7 @@ public class JsonOutput implements Closeable {
// we'll also escape "<" and "&"
private static final Map<Integer, String> ESCAPES;
static {
ImmutableMap.Builder<Integer, String> builder = ImmutableMap.builder();
Map<Integer, String> builder = new LinkedHashMap<>();

for (int i = 0; i <= 0x1f; i++) {
// We want nice looking escapes for these, which are called out
Expand All @@ -91,7 +91,7 @@ public class JsonOutput implements Closeable {
builder.put((int) '\u2028', "\\u2028");
builder.put((int) '<', String.format("\\u%04x", (int) '<'));
builder.put((int) '&', String.format("\\u%04x", (int) '&'));
ESCAPES = builder.build();
ESCAPES = Collections.unmodifiableMap(builder);
}

private final Map<Predicate<Class<?>>, SafeBiConsumer<Object, Integer>> converters;
Expand Down Expand Up @@ -119,69 +119,69 @@ public class JsonOutput implements Closeable {

// Order matters, since we want to handle null values first to avoid exceptions, and then then
// common kinds of inputs next.
this.converters = ImmutableMap.<Predicate<Class<?>>, SafeBiConsumer<Object, Integer>>builder()
.put(Objects::isNull, (obj, depth) -> append("null"))
.put(CharSequence.class::isAssignableFrom, (obj, depth) -> append(asString(obj)))
.put(Number.class::isAssignableFrom, (obj, depth) -> append(obj.toString()))
.put(Boolean.class::isAssignableFrom, (obj, depth) -> append((Boolean) obj ? "true" : "false"))
.put(Date.class::isAssignableFrom, (obj, depth) -> append(String.valueOf(MILLISECONDS.toSeconds(((Date) obj).getTime()))))
.put(Enum.class::isAssignableFrom, (obj, depth) -> append(asString(obj)))
.put(File.class::isAssignableFrom, (obj, depth) -> append(((File) obj).getAbsolutePath()))
.put(URI.class::isAssignableFrom, (obj, depth) -> append(asString((obj).toString())))
.put(URL.class::isAssignableFrom, (obj, depth) -> append(asString(((URL) obj).toExternalForm())))
.put(UUID.class::isAssignableFrom, (obj, depth) -> append(asString(((UUID) obj).toString())))
.put(Level.class::isAssignableFrom, (obj, depth) -> append(asString(LogLevelMapping.getName((Level) obj))))
.put(SessionId.class::isAssignableFrom, (obj, depth) -> append(asString(obj)))
.put(
GSON_ELEMENT,
(obj, depth) -> {
LOG.log(
Level.WARNING,
"Attempt to convert JsonElement from GSON. This functionality is deprecated. "
+ "Diagnostic stacktrace follows",
new JsonException("Stack trace to determine cause of warning"));
append(obj.toString());
})
// Special handling of asMap and toJson
.put(
cls -> getMethod(cls, "toJson") != null,
(obj, depth) -> convertUsingMethod("toJson", obj, depth))
.put(
cls -> getMethod(cls, "asMap") != null,
(obj, depth) -> convertUsingMethod("asMap", obj, depth))
.put(
cls -> getMethod(cls, "toMap") != null,
(obj, depth) -> convertUsingMethod("toMap", obj, depth))

// And then the collection types
.put(
Collection.class::isAssignableFrom,
(obj, depth) -> {
beginArray();
((Collection<?>) obj).forEach(o -> write(o, depth - 1));
endArray();
})

.put(
Map.class::isAssignableFrom,
(obj, depth) -> {
beginObject();
((Map<?, ?>) obj).forEach(
(key, value) -> name(String.valueOf(key)).write(value, depth - 1));
endObject();
})
.put(
Class::isArray,
(obj, depth) -> {
beginArray();
Stream.of((Object[]) obj).forEach(o -> write(o, depth - 1));
endArray();
})

// Finally, attempt to convert as an object
.put(cls -> true, (obj, depth) -> mapObject(obj, depth - 1))

.build();
Map<Predicate<Class<?>>, SafeBiConsumer<Object, Integer>> builder = new LinkedHashMap<>();
builder.put(Objects::isNull, (obj, depth) -> append("null"));
builder.put(CharSequence.class::isAssignableFrom, (obj, depth) -> append(asString(obj)));
builder.put(Number.class::isAssignableFrom, (obj, depth) -> append(obj.toString()));
builder.put(Boolean.class::isAssignableFrom, (obj, depth) -> append((Boolean) obj ? "true" : "false"));
builder.put(Date.class::isAssignableFrom, (obj, depth) -> append(String.valueOf(MILLISECONDS.toSeconds(((Date) obj).getTime()))));
builder.put(Enum.class::isAssignableFrom, (obj, depth) -> append(asString(obj)));
builder.put(File.class::isAssignableFrom, (obj, depth) -> append(((File) obj).getAbsolutePath()));
builder.put(URI.class::isAssignableFrom, (obj, depth) -> append(asString((obj).toString())));
builder.put(URL.class::isAssignableFrom, (obj, depth) -> append(asString(((URL) obj).toExternalForm())));
builder.put(UUID.class::isAssignableFrom, (obj, depth) -> append(asString(((UUID) obj).toString())));
builder.put(Level.class::isAssignableFrom, (obj, depth) -> append(asString(LogLevelMapping.getName((Level) obj))));
builder.put(SessionId.class::isAssignableFrom, (obj, depth) -> append(asString(obj)));
builder.put(
GSON_ELEMENT,
(obj, depth) -> {
LOG.log(
Level.WARNING,
"Attempt to convert JsonElement from GSON. This functionality is deprecated. "
+ "Diagnostic stacktrace follows",
new JsonException("Stack trace to determine cause of warning"));
append(obj.toString());
});
// Special handling of asMap and toJson
builder.put(
cls -> getMethod(cls, "toJson") != null,
(obj, depth) -> convertUsingMethod("toJson", obj, depth));
builder.put(
cls -> getMethod(cls, "asMap") != null,
(obj, depth) -> convertUsingMethod("asMap", obj, depth));
builder.put(
cls -> getMethod(cls, "toMap") != null,
(obj, depth) -> convertUsingMethod("toMap", obj, depth));

// And then the collection types
builder.put(
Collection.class::isAssignableFrom,
(obj, depth) -> {
beginArray();
((Collection<?>) obj).forEach(o -> write(o, depth - 1));
endArray();
});

builder.put(
Map.class::isAssignableFrom,
(obj, depth) -> {
beginObject();
((Map<?, ?>) obj).forEach(
(key, value) -> name(String.valueOf(key)).write(value, depth - 1));
endObject();
});
builder.put(
Class::isArray,
(obj, depth) -> {
beginArray();
Stream.of((Object[]) obj).forEach(o -> write(o, depth - 1));
endArray();
});

// Finally, attempt to convert as an object
builder.put(cls -> true, (obj, depth) -> mapObject(obj, depth - 1));

this.converters = Collections.unmodifiableMap(builder);
}

public JsonOutput setPrettyPrint(boolean enablePrettyPrinting) {
Expand Down
140 changes: 71 additions & 69 deletions java/client/src/org/openqa/selenium/json/JsonTypeCoercer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,15 @@

package org.openqa.selenium.json;

import static java.util.stream.Collector.Characteristics.CONCURRENT;
import static java.util.stream.Collector.Characteristics.UNORDERED;
import static org.openqa.selenium.json.Types.narrow;

import com.google.common.collect.ImmutableSet;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.MutableCapabilities;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -38,6 +34,14 @@
import java.util.function.BiFunction;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static java.util.stream.Collector.Characteristics.CONCURRENT;
import static java.util.stream.Collector.Characteristics.UNORDERED;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toSet;
import static org.openqa.selenium.json.Types.narrow;

class JsonTypeCoercer {

Expand All @@ -46,76 +50,74 @@ class JsonTypeCoercer {
private final Map<Type, BiFunction<JsonInput, PropertySetting, Object>> knownCoercers = new ConcurrentHashMap<>();

JsonTypeCoercer() {
this(ImmutableSet.of());
this(Stream.of());
}

JsonTypeCoercer(JsonTypeCoercer coercer, Iterable<TypeCoercer<?>> coercers) {
this(
ImmutableSet.<TypeCoercer<?>>builder()
.addAll(coercers)
.addAll(coercer.additionalCoercers)
.build());
Stream.concat(StreamSupport.stream(coercers.spliterator(), false), coercer.additionalCoercers.stream()));
}

JsonTypeCoercer(Iterable<TypeCoercer<?>> coercers) {
this.additionalCoercers = ImmutableSet.copyOf(coercers);

private JsonTypeCoercer(Stream<TypeCoercer<?>> coercers) {
this.additionalCoercers = coercers.collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

// Note: we call out when ordering matters.
Set<TypeCoercer<?>> builder = new LinkedHashSet<>(additionalCoercers);

// Types that don't contain other types first
// From java
builder.add(new BooleanCoercer());

builder.add(new NumberCoercer<>(Byte.class, Number::byteValue));
builder.add(new NumberCoercer<>(Double.class, Number::doubleValue));
builder.add(new NumberCoercer<>(Float.class, Number::floatValue));
builder.add(new NumberCoercer<>(Integer.class, Number::intValue));
builder.add(new NumberCoercer<>(Long.class, Number::longValue));
builder.add(
new NumberCoercer<>(
Number.class,
num -> {
if (num.doubleValue() % 1 != 0) {
return num.doubleValue();
}
return num.longValue();
}));
builder.add(new NumberCoercer<>(Short.class, Number::shortValue));
builder.add(new StringCoercer());
builder.add(new EnumCoercer());
builder.add(new UriCoercer());
builder.add(new UrlCoercer());
builder.add(new UuidCoercer());

// From Selenium
builder.add(new MapCoercer<>(
Capabilities.class,
this,
Collector.of(MutableCapabilities::new, (caps, entry) -> caps.setCapability((String) entry.getKey(), entry.getValue()), MutableCapabilities::merge, UNORDERED)));
builder.add(new CommandCoercer());
builder.add(new ResponseCoercer(this));
builder.add(new SessionIdCoercer());

// Container types
//noinspection unchecked
this.coercers =
// Note: we call out when ordering matters.
ImmutableSet.<TypeCoercer>builder()
.addAll(coercers)
// Types that don't contain other types first
// From java
.add(new BooleanCoercer())

.add(new NumberCoercer<>(Byte.class, Number::byteValue))
.add(new NumberCoercer<>(Double.class, Number::doubleValue))
.add(new NumberCoercer<>(Float.class, Number::floatValue))
.add(new NumberCoercer<>(Integer.class, Number::intValue))
.add(new NumberCoercer<>(Long.class, Number::longValue))
.add(
new NumberCoercer<>(
Number.class,
num -> {
if (num.doubleValue() % 1 != 0) {
return num.doubleValue();
}
return num.longValue();
}))
.add(new NumberCoercer<>(Short.class, Number::shortValue))
.add(new StringCoercer())
.add(new EnumCoercer())
.add(new UriCoercer())
.add(new UrlCoercer())
.add(new UuidCoercer())

// From Selenium
.add(new MapCoercer<>(
Capabilities.class,
this,
Collector.of(MutableCapabilities::new, (caps, entry) -> caps.setCapability((String) entry.getKey(), entry.getValue()), MutableCapabilities::merge, UNORDERED)))
.add(new CommandCoercer())
.add(new ResponseCoercer(this))
.add(new SessionIdCoercer())

// Container types
.add(new CollectionCoercer<>(List.class, this, Collectors.toCollection(ArrayList::new)))
.add(new CollectionCoercer<>(Set.class, this, Collectors.toCollection(HashSet::new)))

.add(new MapCoercer<>(
Map.class,
this,
Collector.of(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), (l, r) -> { l.putAll(r); return l; }, UNORDERED, CONCURRENT)))

// If the requested type is exactly "Object", do some guess work
.add(new ObjectCoercer(this))

.add(new StaticInitializerCoercer())

// Order matters here: we want this to be the last called coercer
.add(new InstanceCoercer(this))
.build();
builder.add(new CollectionCoercer<>(List.class, this, Collectors.toCollection(ArrayList::new)));
//noinspection unchecked
builder.add(new CollectionCoercer<>(Set.class, this, Collectors.toCollection(HashSet::new)));

builder.add(new MapCoercer<>(
Map.class,
this,
Collector.of(LinkedHashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), (l, r) -> { l.putAll(r); return l; }, UNORDERED, CONCURRENT)));

// If the requested type is exactly "Object", do some guess work
builder.add(new ObjectCoercer(this));

builder.add(new StaticInitializerCoercer());

// Order matters here: we want this to be the last called coercer
builder.add(new InstanceCoercer(this));

this.coercers = Collections.unmodifiableSet(builder);
}

<T> T coerce(JsonInput json, Type typeOfT, PropertySetting setter) {
Expand Down
Loading

0 comments on commit 593c17a

Please sign in to comment.