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

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