net.amygdalum.testrecorder.deserializers.DefaultDeserializerContext Maven / Gradle / Ivy
package net.amygdalum.testrecorder.deserializers;
import static java.util.Collections.emptyList;
import static net.amygdalum.testrecorder.deserializers.Templates.callMethod;
import static net.amygdalum.testrecorder.deserializers.Templates.cast;
import static net.amygdalum.testrecorder.util.Types.assignableTypes;
import static net.amygdalum.testrecorder.util.Types.baseType;
import static net.amygdalum.testrecorder.util.Types.boxingEquivalentTypes;
import static net.amygdalum.testrecorder.util.Types.isGeneric;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import net.amygdalum.testrecorder.runtime.Wrapped;
import net.amygdalum.testrecorder.types.Computation;
import net.amygdalum.testrecorder.types.DeserializationException;
import net.amygdalum.testrecorder.types.DeserializerContext;
import net.amygdalum.testrecorder.types.LocalVariable;
import net.amygdalum.testrecorder.types.LocalVariableDefinition;
import net.amygdalum.testrecorder.types.LocalVariableNameGenerator;
import net.amygdalum.testrecorder.types.ReferenceTypeVisitor;
import net.amygdalum.testrecorder.types.RoleVisitor;
import net.amygdalum.testrecorder.types.SerializedImmutableType;
import net.amygdalum.testrecorder.types.SerializedReferenceType;
import net.amygdalum.testrecorder.types.SerializedRole;
import net.amygdalum.testrecorder.types.SerializedValue;
import net.amygdalum.testrecorder.types.TypeManager;
public class DefaultDeserializerContext implements DeserializerContext {
private static final SerializedReferenceType GLOBAL_REFERRER = new GlobalRoot();
private Map> backReferences;
private Map> closures;
private HintManager hints;
private LocalVariableNameGenerator locals;
private TypeManager types;
private Map defined;
private Set computed;
private Deque stack;
public DefaultDeserializerContext(TypeManager types, LocalVariableNameGenerator locals) {
this(new IdentityHashMap<>(), new IdentityHashMap<>(), new HintManager(), types, locals);
}
public DefaultDeserializerContext() {
this.backReferences = new IdentityHashMap<>();
this.closures = new IdentityHashMap<>();
this.hints = new HintManager();
this.types = new DeserializerTypeManager();
this.locals = new LocalVariableNameGenerator();
this.defined = new IdentityHashMap<>();
this.computed = new HashSet<>();
this.stack = new LinkedList<>();
}
private DefaultDeserializerContext(Map> backReferences, Map> closures,
HintManager hints, TypeManager types, LocalVariableNameGenerator locals) {
this.backReferences = backReferences;
this.closures = closures;
this.hints = hints;
this.types = types;
this.locals = locals;
this.defined = new IdentityHashMap<>();
this.computed = new HashSet<>();
this.stack = new LinkedList<>();
}
public static DefaultDeserializerContext empty() {
return new DefaultDeserializerContext();
}
@Override
public void clear() {
this.backReferences.clear();
this.closures.clear();
this.hints = new HintManager();
this.types = new DeserializerTypeManager();
this.locals = new LocalVariableNameGenerator();
this.defined.clear();
this.computed.clear();
this.stack.clear();
}
@Override
public LocalVariableNameGenerator getLocals() {
return locals;
}
@Override
public TypeManager getTypes() {
return types;
}
@Override
public String temporaryLocal() {
return locals.fetchName("temp");
}
@Override
public String newLocal(String name) {
return locals.fetchName(name);
}
@Override
public LocalVariable localVariable(SerializedValue value, Type type) {
String name = locals.fetchName(type);
LocalVariable definition = new LocalVariable(name, type);
defined.put(value, definition);
return definition;
}
@Override
public void finishVariable(SerializedValue value) {
defined.computeIfPresent(value, (val, def) -> def.finish());
}
@Override
public void resetVariable(SerializedValue value) {
LocalVariable variable = defined.remove(value);
locals.freeName(variable.getName());
}
@Override
public boolean defines(SerializedValue value) {
return defined.containsKey(value);
}
@Override
public LocalVariable getDefinition(SerializedValue value) {
return defined.get(value);
}
@Override
public DefaultDeserializerContext newIsolatedContext(TypeManager types, LocalVariableNameGenerator locals) {
return new DefaultDeserializerContext(backReferences, closures, hints, types, locals);
}
@Override
public void addHint(AnnotatedElement role, Object hint) {
hints.addHint(role, hint);
}
@Override
public Optional getHint(SerializedRole role, Class clazz) {
return fetchHints(role, clazz)
.findFirst();
}
@Override
public Stream getHints(SerializedRole role, Class clazz) {
return fetchHints(role, clazz);
}
private Stream fetchHints(SerializedRole role, Class clazz) {
Stream roles = findRoles(role);
return roles.flatMap(r -> hints.fetch(clazz, r));
}
private Stream findRoles(SerializedRole role) {
Iterator iterator = stack.iterator();
if (!iterator.hasNext() || iterator.next() != role) {
return Stream.of(role);
}
if (iterator.hasNext()) {
SerializedRole parent = iterator.next();
if (parent instanceof SerializedValue) {
return Stream.of(role);
}
return Stream.of(parent, role);
}
return Stream.of(role);
}
@Override
public Optional getHint(AnnotatedElement element, Class clazz) {
return fetchHints(element, clazz)
.findFirst();
}
@Override
public Stream getHints(AnnotatedElement element, Class clazz) {
return fetchHints(element, clazz);
}
private Stream fetchHints(AnnotatedElement element, Class clazz) {
return hints.fetch(clazz, element);
}
@Override
public int refCount(SerializedValue value) {
int size = 0;
Set references = backReferences.get(value);
if (references != null) {
size += references.size();
}
return size;
}
@Override
public void ref(SerializedReferenceType value, SerializedValue referencedValue) {
backReferences.computeIfAbsent(referencedValue, key -> new HashSet<>())
.add(value);
}
@Override
public void staticRef(SerializedValue referencedValue) {
backReferences.computeIfAbsent(referencedValue, key -> new HashSet<>())
.add(GLOBAL_REFERRER);
}
@Override
public Set closureOf(SerializedValue value) {
Set closure = closures.get(value);
if (closure != null) {
return closure;
}
if (value instanceof SerializedImmutableType) {
return Collections.singleton(value);
} else if (value instanceof SerializedReferenceType) {
closures.put(value, Collections.singleton(value));
closure = new HashSet<>();
closure.add(value);
value.referencedValues().stream()
.flatMap(val -> closureOf(val).stream())
.forEach(closure::add);
closures.put(value, closure);
return closure;
} else {
return Collections.singleton(value);
}
}
@Override
public String adapt(String expression, Type resultType, Type type) {
if (needsUnwrapping(resultType, type)) {
if (types.isHidden(resultType) || baseType(resultType) == Object.class) {
type = Object.class;
expression = callMethod(expression, "value");
} else {
type = baseType(resultType);
expression = callMethod(expression, "value", types.getRawClass(type));
}
}
if ((!assignableTypes(resultType, type) || types.isHidden(type))
&& !boxingEquivalentTypes(resultType, type)
&& baseType(resultType) != Wrapped.class) {
if (isGeneric(resultType) && isGeneric(type)) {
expression = cast(types.getRawTypeName(resultType), expression);
}
expression = cast(types.getVariableTypeName(resultType), expression);
}
return expression;
}
private boolean needsUnwrapping(Type resultType, Type type) {
return (baseType(resultType) != Wrapped.class && type == Wrapped.class)
|| (baseType(resultType) != Wrapped.class && types.isHidden(type));
}
@Override
public boolean needsAdaptation(Type resultType, Type type) {
if (resultType == null || type == null) {
return false;
} else if (baseType(resultType) != Wrapped.class && type == Wrapped.class) {
return true;
} else if (baseType(resultType) != Wrapped.class && types.isHidden(type)) {
return true;
}
if ((!assignableTypes(resultType, type) || types.isHidden(type))
&& !boxingEquivalentTypes(resultType, type)
&& baseType(resultType) != Wrapped.class) {
return true;
}
return false;
}
@Override
public Computation forVariable(SerializedValue value, Type type, LocalVariableDefinition computation) {
LocalVariable local = localVariable(value, type);
try {
Computation definition = computation.define(local);
finishVariable(value);
return definition;
} catch (DeserializationException e) {
resetVariable(value);
throw e;
}
}
@Override
public boolean isComputed(SerializedValue value) {
boolean changed = computed.add(value);
return !changed;
}
@Override
public Optional resolve(int id) {
return defined.keySet().stream()
.filter(value -> (value instanceof SerializedReferenceType) && ((SerializedReferenceType) value).getId() == id)
.findFirst();
}
@Override
public S withRole(T role, Function continuation) {
try {
stack.push(role);
return continuation.apply(role);
} finally {
stack.pop();
}
}
private static class GlobalRoot implements SerializedReferenceType {
GlobalRoot() {
}
@Override
public T accept(RoleVisitor visitor) {
return null;
}
@Override
public T accept(ReferenceTypeVisitor visitor) {
return null;
}
@Override
public void useAs(Type resultType) {
}
@Override
public Type[] getUsedTypes() {
return new Type[] { Class.class };
}
@Override
public Class> getType() {
return Class.class;
}
@Override
public List referencedValues() {
return emptyList();
}
@Override
public void setId(int id) {
}
@Override
public int getId() {
return 0;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy