Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.leeonky.dal.runtime.RuntimeContextBuilder Maven / Gradle / Ivy
package com.github.leeonky.dal.runtime;
import com.github.leeonky.dal.ast.node.DALNode;
import com.github.leeonky.dal.format.Formatter;
import com.github.leeonky.dal.runtime.inspector.Inspector;
import com.github.leeonky.dal.runtime.inspector.InspectorBuilder;
import com.github.leeonky.dal.runtime.inspector.ValueInspector;
import com.github.leeonky.dal.runtime.schema.Expect;
import com.github.leeonky.dal.type.ExtensionName;
import com.github.leeonky.dal.type.Schema;
import com.github.leeonky.interpreter.RuntimeContext;
import com.github.leeonky.interpreter.SyntaxException;
import com.github.leeonky.util.BeanClass;
import com.github.leeonky.util.Converter;
import com.github.leeonky.util.InvocationException;
import com.github.leeonky.util.NumberType;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.github.leeonky.dal.runtime.schema.Actual.actual;
import static com.github.leeonky.dal.runtime.schema.Verification.expect;
import static com.github.leeonky.util.BeanClass.create;
import static java.lang.String.format;
import static java.lang.reflect.Modifier.STATIC;
import static java.util.Arrays.stream;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.joining;
public class RuntimeContextBuilder {
private final ClassKeyMap> propertyAccessors = new ClassKeyMap<>();
private final ClassKeyMap> listAccessors = new ClassKeyMap<>();
private final ClassKeyMap> objectImplicitMapper = new ClassKeyMap<>();
private final Map valueConstructors = new LinkedHashMap<>();
private final Map> schemas = new HashMap<>();
private final Set extensionMethods = new HashSet<>();
private final Map> metaProperties = new HashMap<>();
private final List userDefinedLiterals = new ArrayList<>();
private final NumberType numberType = new NumberType();
private final Map, List>> curryingMethodArgRanges = new HashMap<>();
private final Map textFormatterMap = new LinkedHashMap<>();
private Converter converter = Converter.getInstance();
private final ClassKeyMap equalsCheckers = new ClassKeyMap<>();
private final ClassKeyMap matchesCheckers = new ClassKeyMap<>();
private final ClassKeyMap inspectorBuilders = new ClassKeyMap<>();
public RuntimeContextBuilder registerMetaProperty(Object property, Function function) {
metaProperties.put(property, function);
return this;
}
public RuntimeContextBuilder registerTextFormatter(String name, TextFormatter formatter) {
textFormatterMap.put(name, formatter);
return this;
}
public DALRuntimeContext build(Object inputValue) {
return new DALRuntimeContext(inputValue);
}
public RuntimeContextBuilder registerValueFormat(Formatter, ?> formatter) {
return registerValueFormat(formatter.getFormatterName(), formatter);
}
@SuppressWarnings("unchecked")
public RuntimeContextBuilder registerValueFormat(String name, Formatter, ?> formatter) {
valueConstructors.put(name, (o, c) -> ((Formatter) formatter).transform(o.getInstance()));
return this;
}
public RuntimeContextBuilder registerSchema(Class extends Schema> schema) {
return registerSchema(NameStrategy.SIMPLE_NAME, schema);
}
@SuppressWarnings("unchecked")
public RuntimeContextBuilder registerSchema(String name, Class extends Schema> schema) {
schemas.put(name, create(schema));
return registerSchema(name, (data, context) ->
expect(new Expect(create((Class) schema), null)).verify(context, actual(data)));
}
public RuntimeContextBuilder registerSchema(String name, BiFunction predicate) {
valueConstructors.put(name, (o, context) -> {
if (predicate.apply(o, context))
return o.getInstance();
throw new IllegalTypeException();
});
return this;
}
@SuppressWarnings("unchecked")
public RuntimeContextBuilder registerPropertyAccessor(Class type, PropertyAccessor extends T> propertyAccessor) {
propertyAccessors.put(type, (PropertyAccessor) propertyAccessor);
return this;
}
@SuppressWarnings("unchecked")
public RuntimeContextBuilder registerListAccessor(Class type, ListAccessor extends T> listAccessor) {
listAccessors.put(type, (ListAccessor) listAccessor);
return this;
}
public RuntimeContextBuilder registerSchema(NameStrategy nameStrategy, Class extends Schema> schema) {
return registerSchema(nameStrategy.toName(schema), schema);
}
public RuntimeContextBuilder registerStaticMethodExtension(Class> staticMethodExtensionClass) {
Stream.of(staticMethodExtensionClass.getMethods()).filter(method -> method.getParameterCount() >= 1
&& (STATIC & method.getModifiers()) != 0).forEach(extensionMethods::add);
return this;
}
@SuppressWarnings("unchecked")
public RuntimeContextBuilder registerImplicitData(Class type, Function mapper) {
objectImplicitMapper.put(type, (Function) mapper);
return this;
}
public Converter getConverter() {
return converter;
}
public RuntimeContextBuilder setConverter(Converter converter) {
this.converter = converter;
return this;
}
public RuntimeContextBuilder registerUserDefinedLiterals(UserLiteralRule rule) {
userDefinedLiterals.add(rule);
return this;
}
public RuntimeContextBuilder registerCurryingMethodRange(Method method, BiFunction, List> range) {
curryingMethodArgRanges.put(method, range);
return this;
}
private Set methodToCurrying(Class> type, Object methodName) {
return Stream.of(stream(type.getMethods()).filter(method -> !Modifier.isStatic(method.getModifiers()))
.filter(method -> method.getName().equals(methodName)),
staticMethodsToCurrying(type, methodName, Object::equals),
staticMethodsToCurrying(type, methodName, Class::isAssignableFrom))
.flatMap(Function.identity()).collect(Collectors.toCollection(LinkedHashSet::new));
}
private Stream staticMethodsToCurrying(Class> type, Object property,
BiPredicate, Class>> condition) {
return extensionMethods.stream()
.filter(method -> staticExtensionMethodName(method).equals(property))
.filter(method -> condition.test(method.getParameters()[0].getType(), type));
}
static String staticExtensionMethodName(Method method) {
ExtensionName extensionName = method.getAnnotation(ExtensionName.class);
return extensionName != null ? extensionName.value() : method.getName();
}
BiFunction, List> fetchCurryingMethodArgRange(Method method) {
return curryingMethodArgRanges.get(method);
}
public RuntimeContextBuilder registerMatchesChecker(Class> type, Checker checker) {
matchesCheckers.put(type, checker);
return this;
}
public RuntimeContextBuilder registerEqualsChecker(Class> type, Checker checker) {
equalsCheckers.put(type, checker);
return this;
}
public RuntimeContextBuilder registerValueInspector(Class>... types) {
for (Class> type : types)
registerInspector(type, ValueInspector::new);
return this;
}
public RuntimeContextBuilder registerInspector(Class> type, InspectorBuilder builder) {
inspectorBuilders.put(type, builder);
return this;
}
public class DALRuntimeContext implements RuntimeContext {
private final LinkedList stack = new LinkedList<>();
private final Map partialPropertyStacks;
public DALRuntimeContext(Object inputValue) {
stack.push(wrap(inputValue));
partialPropertyStacks = new HashMap<>();
}
public Data getThis() {
return stack.getFirst();
}
public T newBlockScope(Data data, Supplier supplier) {
try {
stack.push(data);
return supplier.get();
} finally {
stack.pop();
}
}
public Optional searchValueConstructor(String type) {
return Optional.ofNullable(valueConstructors.get(type));
}
public Set findPropertyReaderNames(Object instance) {
return propertyAccessors.getData(instance).getPropertyNames(instance);
}
public Boolean isNull(Object instance) {
return propertyAccessors.tryGetData(instance).map(f -> f.isNull(instance))
.orElseGet(() -> Objects.equals(instance, null));
}
public Object getPropertyValue(Data data, Object property) {
PropertyAccessor propertyAccessor = propertyAccessors.getData(data.getInstance());
try {
return propertyAccessor.getValueByData(data, property);
} catch (InvalidPropertyException e) {
return data.currying(property).orElseThrow(() -> e).resolve();
} catch (InvocationException e) {
throw e;
} catch (Exception e) {
throw new InvocationException(e);
}
}
@SuppressWarnings("unchecked")
public Iterable getList(Object instance) {
return listAccessors.tryGetData(instance).map(l -> (Iterable) l.toIterable(instance))
.orElseGet(() -> arrayIterable(instance));
}
public int getListFirstIndex(Object instance) {
return listAccessors.tryGetData(instance).map(listAccessor -> listAccessor.firstIndex(instance)).orElse(0);
}
private Iterable arrayIterable(Object instance) {
return () -> new Iterator() {
private final int length = Array.getLength(instance);
private int index = 0;
@Override
public boolean hasNext() {
return index < length;
}
@Override
public Object next() {
return Array.get(instance, index++);
}
};
}
public boolean isRegisteredList(Object instance) {
return listAccessors.tryGetData(instance).map(listAccessor -> listAccessor.isList(instance)).orElse(false);
}
public Converter getConverter() {
return converter;
}
public Data wrap(Object instance) {
return new Data(instance, this, SchemaType.createRoot());
}
public Data wrap(Object instance, String schema, boolean isList) {
BeanClass> schemaBeanClass = schemas.get(schema);
if (isList)
schemaBeanClass = create(Array.newInstance(schemaBeanClass.getType(), 0).getClass());
return new Data(instance, this, SchemaType.create(schemaBeanClass));
}
public DALRuntimeContext registerPropertyAccessor(T instance) {
if (!Objects.equals(instance, null) && !propertyAccessors.containsType(instance))
propertyAccessors.put(BeanClass.getClass(instance),
new JavaClassPropertyAccessor<>(BeanClass.createFrom(instance)));
return this;
}
public Optional takeUserDefinedLiteral(String token) {
return userDefinedLiterals.stream().map(userLiteralRule -> userLiteralRule.compile(token))
.filter(Result::hasResult)
.findFirst();
}
public void appendPartialPropertyReference(Data data, Object symbol) {
fetchPartialProperties(data).map(partialProperties -> partialProperties.appendPartialProperties(symbol));
}
private Optional fetchPartialProperties(Data data) {
return partialPropertyStacks.values().stream().map(partialPropertyStack ->
partialPropertyStack.fetchPartialProperties(data)).filter(Objects::nonNull).findFirst();
}
public void initPartialPropertyStack(Data instance, Object prefix, Data partial) {
partialPropertyStacks.computeIfAbsent(instance, _key -> fetchPartialProperties(instance)
.map(partialProperties -> partialProperties.partialPropertyStack)
.orElseGet(PartialPropertyStack::new)).setupPartialProperties(prefix, partial);
}
public Set collectPartialProperties(Data instance) {
PartialPropertyStack partialPropertyStack = partialPropertyStacks.get(instance);
if (partialPropertyStack != null)
return partialPropertyStack.collectPartialProperties(instance);
return fetchPartialProperties(instance).map(partialProperties ->
partialProperties.partialPropertyStack.collectPartialProperties(instance)).orElse(emptySet());
}
public NumberType getNumberType() {
return numberType;
}
public Optional getImplicitObject(Object obj) {
return objectImplicitMapper.tryGetData(obj).map(mapper -> mapper.apply(obj));
}
public Set methodToCurrying(Class> type, Object methodName) {
return RuntimeContextBuilder.this.methodToCurrying(type, methodName);
}
public Function fetchMetaFunction(DALNode property) {
return metaProperties.computeIfAbsent(property.getRootSymbolName(), k -> {
throw new RuntimeException(format("Meta property `%s` not found", property.getRootSymbolName()),
property.getPositionBegin());
});
}
public TextFormatter fetchFormatter(String name, int position) {
return textFormatterMap.computeIfAbsent(name, attribute -> {
throw new SyntaxException(format("Invalid text formatter `%s`, all supported formatters are:\n%s",
attribute, textFormatterMap.entrySet().stream().map(e -> format(" %s:\n %s",
e.getKey(), e.getValue().description())).collect(joining("\n"))), position);
});
}
public Checker fetchEqualsChecker(ExpectActual expectActual) {
return equalsCheckers.tryGetData(expectActual.getExpectInstance())
.orElse(ConditionalChecker.EQUALS_CHECKER);
}
public Checker fetchMatchesChecker(ExpectActual expectActual) {
return matchesCheckers.tryGetData(expectActual.getExpectInstance())
.orElseGet(expectActual::defaultMatchesChecker);
}
public Inspector fetchInspector(Data data) {
return inspectorBuilders.tryGetData(data.getInstance()).orElseGet(() -> Inspector::defaultInspector).apply(data);
}
}
}