Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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: 23 additions & 74 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,12 @@
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.NativeFunctionRegistry;
import fr.insee.vtl.engine.visitors.AssignmentVisitor;
import fr.insee.vtl.model.*;
import fr.insee.vtl.model.exceptions.VtlScriptException;
Expand All @@ -21,7 +20,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 +48,9 @@ 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 builtinRegistry = NativeFunctionRegistry.builtins();
private NativeFunctionRegistry extensionRegistry;
private NativeFunctionRegistry globalRegistry;

private volatile Map<String, ProcessingEngineFactory> processingEngineFactories;
private volatile String cachedProcessingEngineName;
Expand Down Expand Up @@ -124,7 +122,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 +429,42 @@ 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);
}
ensureExtensionRegistryLoaded();
try {
return extensionRegistry.resolve(name, types);
} catch (NoSuchMethodException ignored) {
return builtinRegistry.resolve(name, types);
}
throw new NoSuchMethodException(methodToString(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));
}

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();
return globalRegistry.resolveOrNull(name, types);
}

public Method registerMethod(String name, Method method) {
if (methodCache == null) {
loadMethods();
}
return methodCache.put(name, method);
ensureExtensionRegistryLoaded();
Comment thread
NicoLaval marked this conversation as resolved.
Outdated
return extensionRegistry.putAndReturnPrevious(name, method);
}

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

private void loadMethods() {
methodCache = new LinkedHashMap<>();
private void ensureExtensionRegistryLoaded() {
if (extensionRegistry != null) {
return;
}
extensionRegistry = NativeFunctionRegistry.empty();
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));
}
extensionRegistry.registerAll(provider.getFunctions(this));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fr.insee.vtl.engine.functions;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/** Built-in scalar function catalogue for the VTL engine. */
public interface BuiltinFunctionProvider {
Comment thread
NicoLaval marked this conversation as resolved.
Outdated

Map<String, List<Method>> getFunctions();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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 java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Aggregates all built-in scalar function providers. */
public final class NativeFunctionProviders {
Comment thread
NicoLaval marked this conversation as resolved.
Outdated

private static final List<BuiltinFunctionProvider> BUILTINS =
List.of(
new NumericFunctionsProvider(),
new StringFunctionsProvider(),
new DistanceFunctionsProvider(),
new ArithmeticFunctionsProvider(),
new ConditionalFunctionsProvider(),
new BooleanFunctionsProvider(),
new UnaryFunctionsProvider(),
new ComparisonOperatorFunctionsProvider(),
new ComparisonFunctionsProvider(),
new TemporalFunctionsProvider());

private NativeFunctionProviders() {}

public static Map<String, List<Method>> builtinFunctions() {
Map<String, List<Method>> functions = new LinkedHashMap<>();
for (BuiltinFunctionProvider provider : BUILTINS) {
provider
.getFunctions()
.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