Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions vtl-engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ flowchart TB
DR["DatasetResults.withStructure"]
end

subgraph scalar["expressions — scalar natives"]
TF["TemporalFunctions, …"]
subgraph scalar["functions/providers — scalar natives"]
TF["NumericFunctionsProvider, …"]
end

subgraph processors["processors — default runtime"]
Expand Down Expand Up @@ -95,5 +95,5 @@ flowchart TB
| **vtl-engine** | `semantics.<domain>` | VTL operator logic (`join`, `aggregation`, `validation`, `time`, …) |
| **vtl-engine** | `semantics.attribute` | Cross-cutting viral attribute propagation |
| **vtl-engine** | `processors.InMemoryProcessingEngine` | Default in-memory `ProcessingEngine` |
| **vtl-engine** | `expressions` | Scalar native helpers (e.g. `TemporalFunctions`) |
| **vtl-engine** | `functions.providers` | Built-in scalar native functions (`Map<String, List<Method>>`) |
| **vtl-spark** / **vtl-spark4** | `SparkProcessingEngine` | Spark-backed `ProcessingEngine` |
135 changes: 0 additions & 135 deletions vtl-engine/src/main/java/fr/insee/vtl/engine/VtlNativeMethods.java

This file was deleted.

97 changes: 19 additions & 78 deletions vtl-engine/src/main/java/fr/insee/vtl/engine/VtlScriptEngine.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package fr.insee.vtl.engine;

import static fr.insee.vtl.engine.VtlNativeMethods.NATIVE_METHODS;

import fr.insee.vtl.antlr.runtime.*;
import fr.insee.vtl.antlr.runtime.misc.Interval;
import fr.insee.vtl.antlr.runtime.tree.ParseTree;
import fr.insee.vtl.antlr.runtime.tree.TerminalNode;
import fr.insee.vtl.engine.exceptions.VtlRuntimeException;
import fr.insee.vtl.engine.exceptions.VtlSyntaxException;
import fr.insee.vtl.engine.functions.NativeFunctionProviders;
import fr.insee.vtl.engine.functions.NativeFunctionRegistry;
import fr.insee.vtl.engine.visitors.AssignmentVisitor;
import fr.insee.vtl.model.*;
import fr.insee.vtl.model.exceptions.VtlScriptException;
Expand All @@ -21,7 +21,6 @@
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.script.*;

/**
Expand Down Expand Up @@ -50,9 +49,8 @@ public class VtlScriptEngine extends AbstractScriptEngine {

private final ScriptEngineFactory factory;
private final VtlParseCache parseCache = new VtlParseCache();
private Map<String, Method> methodCache;

private Map<String, Method> globalMethodCache;
private final NativeFunctionRegistry functionRegistry = NativeFunctionRegistry.empty();
private NativeFunctionRegistry globalRegistry;

private volatile Map<String, ProcessingEngineFactory> processingEngineFactories;
private volatile String cachedProcessingEngineName;
Expand All @@ -65,6 +63,10 @@ public class VtlScriptEngine extends AbstractScriptEngine {
*/
public VtlScriptEngine(ScriptEngineFactory factory) {
this.factory = factory;
registerProvider(NativeFunctionProviders.INSTANCE);
for (FunctionProvider provider : ServiceLoader.load(FunctionProvider.class)) {
registerProvider(provider);
}
}

public static Positioned toPositioned(ParseTree tree) {
Expand Down Expand Up @@ -124,7 +126,7 @@ public static Positioned fromTokens(Token from, Token to) {
return () -> position;
}

static boolean matchParameters(Method method, Class<?>... classes) {
public static boolean matchParameters(Method method, Class<?>... classes) {
Type[] genericParameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterTypes = method.getParameterTypes();

Expand Down Expand Up @@ -431,91 +433,30 @@ public ScriptEngineFactory getFactory() {
}

public VtlMethod findMethod(String name, Collection<Class> types) throws NoSuchMethodException {
Set<Method> customMethods =
methodCache == null ? Set.of() : new HashSet<>(methodCache.values());
Set<Method> methods =
Stream.concat(NATIVE_METHODS.stream(), customMethods.stream()).collect(Collectors.toSet());

List<Method> candidates =
methods.stream()
.filter(method -> method.getName().equals(name))
.filter(method -> matchParameters(method, types.toArray(Class[]::new)))
.collect(Collectors.toList());
if (candidates.size() == 1) {
return new VtlMethod(candidates.get(0));
}
// TODO: Handle parameter resolution.
for (Method method : methods) {
if (method.getName().equals(name)
&& types.equals(Arrays.asList(method.getParameterTypes()))) {
return new VtlMethod(method);
}
}
throw new NoSuchMethodException(methodToString(name, types));
return functionRegistry.resolve(name, types);
}

public VtlMethod findGlobalMethod(String name, Collection<Class> types)
throws NoSuchMethodException {
if (globalMethodCache == null) return null;
Set<Method> methods = new HashSet<>(globalMethodCache.values());

List<Method> candidates =
methods.stream()
.filter(method -> method.getName().equals(name))
.filter(method -> matchParameters(method, types.toArray(Class[]::new)))
.collect(Collectors.toList());

if (candidates.size() == 0) {
// It's not a global method
if (globalRegistry == null) {
return null;
}

if (candidates.size() == 1) {
return new VtlMethod(candidates.get(0));
}
// TODO: Handle parameter resolution.
for (Method method : methods) {
if (method.getName().equals(name)
&& types.equals(Arrays.asList(method.getParameterTypes()))) {
return new VtlMethod(method);
}
}
throw new NoSuchMethodException(methodToString(name, types));
return globalRegistry.resolveOrNull(name, types);
}

private String methodToString(String name, Collection<Class> argTypes) {
StringJoiner sj = new StringJoiner(", ", name + "(", ")");
if (argTypes != null) {
for (Class<?> c : argTypes) {
sj.add(c == null ? "null" : c.getSimpleName());
}
}
return sj.toString();
public void registerProvider(FunctionProvider provider) {
Objects.requireNonNull(provider);
functionRegistry.registerAll(provider.getFunctions(this));
}

public Method registerMethod(String name, Method method) {
if (methodCache == null) {
loadMethods();
}
return methodCache.put(name, method);
return functionRegistry.putAndReturnPrevious(name, method);
}

public Method registerGlobalMethod(String name, Method method) {
if (globalMethodCache == null) {
globalMethodCache = new LinkedHashMap<>();
}
return globalMethodCache.put(name, method);
}

private void loadMethods() {
methodCache = new LinkedHashMap<>();
ServiceLoader<FunctionProvider> providers = ServiceLoader.load(FunctionProvider.class);
for (FunctionProvider provider : providers) {
Map<String, Method> functions = provider.getFunctions(this);
// TODO: rename function name with 'name' instead of java name
for (String name : functions.keySet()) {
methodCache.put(name, functions.get(name));
}
if (globalRegistry == null) {
globalRegistry = NativeFunctionRegistry.empty();
}
return globalRegistry.putAndReturnPrevious(name, method);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package fr.insee.vtl.engine.functions;

import fr.insee.vtl.engine.functions.providers.ArithmeticFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.BooleanFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.ComparisonFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.ComparisonOperatorFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.ConditionalFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.DistanceFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.NumericFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.StringFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.TemporalFunctionsProvider;
import fr.insee.vtl.engine.functions.providers.UnaryFunctionsProvider;
import fr.insee.vtl.model.FunctionProvider;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.script.ScriptEngine;

/** Built-in scalar function catalogue exposed as a {@link FunctionProvider}. */
public enum NativeFunctionProviders implements FunctionProvider {
INSTANCE;

private static final List<Supplier<Map<String, List<Method>>>> PARTS =
List.of(
() -> new NumericFunctionsProvider().getFunctions(),
() -> new StringFunctionsProvider().getFunctions(),
() -> new DistanceFunctionsProvider().getFunctions(),
() -> new ArithmeticFunctionsProvider().getFunctions(),
() -> new ConditionalFunctionsProvider().getFunctions(),
() -> new BooleanFunctionsProvider().getFunctions(),
() -> new UnaryFunctionsProvider().getFunctions(),
() -> new ComparisonOperatorFunctionsProvider().getFunctions(),
() -> new ComparisonFunctionsProvider().getFunctions(),
() -> new TemporalFunctionsProvider().getFunctions());

@Override
public Map<String, List<Method>> getFunctions(ScriptEngine vtlEngine) {
return builtinFunctions();
}

public static Map<String, List<Method>> builtinFunctions() {
Map<String, List<Method>> functions = new LinkedHashMap<>();
for (Supplier<Map<String, List<Method>>> part : PARTS) {
part.get()
.forEach(
(vtlName, methods) ->
functions.merge(
vtlName,
methods,
(left, right) -> {
var merged = new ArrayList<>(left);
merged.addAll(right);
return List.copyOf(merged);
}));
}
return functions;
}
}
Loading
Loading