Skip to content

Commit 7a18cea

Browse files
committed
Add extension loader modules
1 parent 2e3b02d commit 7a18cea

20 files changed

+950
-306
lines changed

src/main/java/com/relogiclabs/jschema/internal/function/FunctionMap.java

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.relogiclabs.jschema.internal.loader;
2+
3+
import com.relogiclabs.jschema.exception.ArgumentTypeException;
4+
import com.relogiclabs.jschema.exception.InvocationRuntimeException;
5+
import com.relogiclabs.jschema.internal.util.ReflectionHelper;
6+
import com.relogiclabs.jschema.type.EValue;
7+
8+
import java.lang.reflect.Array;
9+
import java.lang.reflect.Method;
10+
import java.lang.reflect.Parameter;
11+
import java.util.List;
12+
13+
import static com.relogiclabs.jschema.internal.loader.SchemaFunction.VARIADIC_ARITY;
14+
import static com.relogiclabs.jschema.internal.util.CollectionHelper.subList;
15+
import static com.relogiclabs.jschema.internal.util.ReflectionHelper.getDerived;
16+
import static com.relogiclabs.jschema.internal.util.ReflectionHelper.getMethodArity;
17+
import static com.relogiclabs.jschema.internal.util.ReflectionHelper.isMatched;
18+
19+
public abstract class AbstractScriptWrapper {
20+
protected final Method method;
21+
protected final Parameter[] parameters;
22+
protected final int arity;
23+
24+
public AbstractScriptWrapper(Method method) {
25+
this.method = method;
26+
this.parameters = method.getParameters();
27+
this.arity = getMethodArity(parameters);
28+
}
29+
30+
public abstract String[] getKeys();
31+
32+
public String getName() {
33+
return method.getName();
34+
}
35+
36+
private boolean isVariadic() {
37+
return arity == VARIADIC_ARITY;
38+
}
39+
40+
protected Object[] processArguments(List<? extends EValue> arguments) {
41+
if(isVariadic() && arguments.size() < parameters.length - 1)
42+
throw failOnVariadicArity();
43+
var result = new Object[parameters.length];
44+
if(parameters.length == 0) return result;
45+
var endIndex = parameters.length - (isVariadic() ? 1 : 0);
46+
for(int i = 0; i < endIndex; i++) {
47+
if(!isMatched(parameters[i], arguments.get(i)))
48+
throw failOnInvalidArgumentType(parameters[i], arguments.get(i));
49+
result[i] = arguments.get(i);
50+
}
51+
if(isVariadic()) {
52+
var remainingArgs = subList(arguments, endIndex);
53+
result[endIndex] = processVarArgs(parameters[endIndex], remainingArgs);
54+
}
55+
return result;
56+
}
57+
58+
private Object processVarArgs(Parameter parameter, List<? extends EValue> arguments) {
59+
var componentType = parameter.getType().getComponentType();
60+
if(componentType == null) throw new IllegalStateException("Invalid function parameter");
61+
var result = Array.newInstance(componentType, arguments.size());
62+
for(var i = 0; i < arguments.size(); i++) {
63+
var arg = arguments.get(i);
64+
if(!componentType.isInstance(getDerived(arg)))
65+
throw failOnInvalidArgumentType(parameter, arg);
66+
Array.set(result, i, arg);
67+
}
68+
return result;
69+
}
70+
71+
public String getSignature() {
72+
return ReflectionHelper.getSignature(method);
73+
}
74+
75+
protected abstract InvocationRuntimeException failOnVariadicArity();
76+
protected abstract ArgumentTypeException failOnInvalidArgumentType(Parameter parameter,
77+
EValue argument);
78+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.relogiclabs.jschema.internal.loader;
2+
3+
import lombok.Getter;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
@Getter
11+
public class ConstraintStorage {
12+
private final Map<String, List<SchemaFunction>> functions;
13+
14+
public ConstraintStorage(int capacity) {
15+
this.functions = new HashMap<>(capacity);
16+
}
17+
18+
public void addFunction(SchemaFunction function) {
19+
for(var key : function.getKeys()) {
20+
var list = functions.get(key);
21+
if(list == null) list = new ArrayList<>(10);
22+
list.add(function);
23+
functions.put(key, list);
24+
}
25+
}
26+
27+
public List<SchemaFunction> getFunction(String key) {
28+
return functions.get(key);
29+
}
30+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.relogiclabs.jschema.internal.loader;
2+
3+
import com.relogiclabs.jschema.exception.ClassInstantiationException;
4+
import com.relogiclabs.jschema.exception.InvalidImportException;
5+
import com.relogiclabs.jschema.extension.ConstraintFunction;
6+
import com.relogiclabs.jschema.extension.ConstraintFunctions;
7+
import com.relogiclabs.jschema.extension.InvokableNative;
8+
import com.relogiclabs.jschema.extension.ScriptFunction;
9+
import com.relogiclabs.jschema.extension.ScriptFunctions;
10+
import com.relogiclabs.jschema.extension.ScriptMethod;
11+
import com.relogiclabs.jschema.extension.ScriptMethods;
12+
import lombok.Getter;
13+
14+
import java.lang.reflect.InvocationTargetException;
15+
16+
import static com.relogiclabs.jschema.message.ErrorCode.IMPLOD01;
17+
import static com.relogiclabs.jschema.message.ErrorCode.IMPLOD02;
18+
import static com.relogiclabs.jschema.message.ErrorCode.IMPLOD03;
19+
import static com.relogiclabs.jschema.message.ErrorCode.IMPLOD04;
20+
import static com.relogiclabs.jschema.message.ErrorCode.INSTCR01;
21+
import static com.relogiclabs.jschema.message.ErrorCode.INSTCR02;
22+
import static com.relogiclabs.jschema.message.ErrorCode.INSTCR03;
23+
import static com.relogiclabs.jschema.message.ErrorCode.INSTCR04;
24+
25+
@Getter
26+
public class ImportLoader {
27+
private final ConstraintStorage constraints;
28+
private final ScriptStorage scripts;
29+
30+
public ImportLoader(ConstraintStorage constraints, ScriptStorage scripts) {
31+
this.constraints = constraints;
32+
this.scripts = scripts;
33+
}
34+
35+
public InvokableNative load(Class<?> module) {
36+
var instance = createInstance(module);
37+
for(var method : module.getMethods()) {
38+
var constraintFunction = method.getAnnotation(ConstraintFunction.class);
39+
if(constraintFunction != null) {
40+
if(!(instance instanceof ConstraintFunctions ins))
41+
throw failOnNotImplement(IMPLOD02, ConstraintFunctions.class, module);
42+
constraints.addFunction(new NativeSchemaFunction(method, constraintFunction, ins));
43+
}
44+
var scriptFunction = method.getAnnotation(ScriptFunction.class);
45+
if(scriptFunction != null) {
46+
if(!(instance instanceof ScriptFunctions ins))
47+
throw failOnNotImplement(IMPLOD03, ScriptFunctions.class, module);
48+
scripts.addFunction(new ScriptFunctionWrapper(method, scriptFunction, ins));
49+
}
50+
var scriptMethod = method.getAnnotation(ScriptMethod.class);
51+
if(scriptMethod != null) {
52+
if(!(instance instanceof ScriptMethods ins))
53+
throw failOnNotImplement(IMPLOD04, ScriptMethods.class, module);
54+
scripts.addMethod(new ScriptMethodWrapper(method, scriptMethod, ins));
55+
}
56+
}
57+
return instance;
58+
}
59+
60+
private static InvokableNative createInstance(Class<?> module) {
61+
Object instance;
62+
try {
63+
instance = module.getDeclaredConstructor().newInstance();
64+
} catch (NoSuchMethodException e) {
65+
throw failOnCreateInstance(INSTCR01, e, module);
66+
} catch (InstantiationException e) {
67+
throw failOnCreateInstance(INSTCR02, e, module);
68+
} catch (InvocationTargetException e) {
69+
throw failOnCreateInstance(INSTCR03, e, module);
70+
} catch (IllegalAccessException e) {
71+
throw failOnCreateInstance(INSTCR04, e, module);
72+
}
73+
if(!(instance instanceof InvokableNative result))
74+
throw failOnNotImplement(IMPLOD01, InvokableNative.class, module);
75+
return result;
76+
}
77+
78+
private static RuntimeException failOnNotImplement(String code, Class<?> type, Class<?> module) {
79+
return new InvalidImportException(code, module + " not implements " + type);
80+
}
81+
82+
private static RuntimeException failOnCreateInstance(String code, Exception ex, Class<?> module) {
83+
return new ClassInstantiationException(code, "Fail to create an instance of " + module, ex);
84+
}
85+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.relogiclabs.jschema.internal.loader;
2+
3+
import com.relogiclabs.jschema.extension.InvokableNative;
4+
import com.relogiclabs.jschema.function.SchemaFunctions;
5+
import com.relogiclabs.jschema.internal.tree.ConstraintRegistry;
6+
import com.relogiclabs.jschema.internal.tree.ScriptRegistry;
7+
import com.relogiclabs.jschema.library.ArrayMethods;
8+
import com.relogiclabs.jschema.library.CommonMethods;
9+
import com.relogiclabs.jschema.library.GlobalFunctions;
10+
import com.relogiclabs.jschema.library.NumberMethods;
11+
import com.relogiclabs.jschema.library.ObjectMethods;
12+
import com.relogiclabs.jschema.library.StringMethods;
13+
import lombok.Getter;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
@Getter
19+
public class InternalModuleLoader {
20+
private static final int INTERNAL_IMPORT_CAPACITY = 10;
21+
private final Map<String, InvokableNative> imports = new HashMap<>(INTERNAL_IMPORT_CAPACITY);
22+
private final ImportLoader loader = new ImportLoader(ConstraintRegistry.getInternals(),
23+
ScriptRegistry.getInternals());
24+
25+
private void loadModule(Class<?> module) {
26+
var instance = loader.load(module);
27+
imports.put(module.getName(), instance);
28+
}
29+
30+
private void loadBatch() {
31+
loadModule(SchemaFunctions.class);
32+
loadModule(GlobalFunctions.class);
33+
loadModule(CommonMethods.class);
34+
loadModule(NumberMethods.class);
35+
loadModule(StringMethods.class);
36+
loadModule(ArrayMethods.class);
37+
loadModule(ObjectMethods.class);
38+
}
39+
40+
public static Map<String, InvokableNative> loadModules() {
41+
var loader = new InternalModuleLoader();
42+
loader.loadBatch();
43+
return loader.imports;
44+
}
45+
}

0 commit comments

Comments
 (0)