Skip to content
Merged
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
474 changes: 404 additions & 70 deletions src/main/java/eu/europa/ted/eforms/sdk/SdkSymbolResolver.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2026 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
* compliance with the Licence. You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence
* is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence for the specific language governing permissions and limitations under
* the Licence.
*/
package eu.europa.ted.efx.exceptions;

/**
* Exception thrown when the toolkit encounters an internal consistency check failure.
* This indicates a bug in the toolkit implementation, not a user error.
* These are "should never happen" errors that suggest developer mistakes in the toolkit code.
*/
public class ConsistencyCheckException extends IllegalStateException {

public enum ErrorCode {
TYPE_NOT_REGISTERED,
UNSUPPORTED_TYPE_IN_CONDITIONAL,
MISSING_TYPE_MAPPING,
MISSING_TYPE_ANNOTATION,
UNKNOWN_EXPRESSION_TYPE,
INVALID_VARIABLE_CONTEXT
}

private static final String TYPE_NOT_REGISTERED =
"EfxDataType %s is not registered in TYPE_VARIANTS. " +
"This indicates a bug in the type system. " +
"Add the missing type to TYPE_VARIANTS with its scalar and sequence variants, " +
"ensuring subtypes appear before supertypes. " +
"Run EfxTypeLatticeTest to verify the registration.";

private static final String UNSUPPORTED_TYPE_IN_CONDITIONAL =
"Type %s is not supported in conditional expressions. " +
"This indicates the translator is missing a handler for this type. " +
"Add an else-if branch for this type in exitConditionalExpression().";

private static final String MISSING_TYPE_MAPPING =
"Type %s is not mapped in %s. " +
"This indicates the translator is missing a type mapping. " +
"Add the missing type to the map.";

private static final String MISSING_TYPE_ANNOTATION =
"TypedExpression class %s is missing @EfxDataTypeAssociation annotation. " +
"This indicates a bug in the type system. " +
"Add the annotation to specify which EfxDataType this expression represents.";

private static final String UNKNOWN_EXPRESSION_TYPE =
"Expression type %s is not handled by the type conversion logic. " +
"This indicates a bug in the type system. " +
"The target type must be PathExpression, SequenceExpression, or ScalarExpression.";

private static final String INVALID_VARIABLE_CONTEXT =
"Variable context is neither a field nor a node context. " +
"This indicates a bug in the translator. " +
"Ensure all variable contexts are properly classified as FieldContext or NodeContext.";

private final ErrorCode errorCode;

private ConsistencyCheckException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public static ConsistencyCheckException typeNotRegistered(Class<?> type) {
return new ConsistencyCheckException(ErrorCode.TYPE_NOT_REGISTERED,
String.format(TYPE_NOT_REGISTERED, type.getName()));
}

public static ConsistencyCheckException unsupportedTypeInConditional(Class<?> type) {
return new ConsistencyCheckException(ErrorCode.UNSUPPORTED_TYPE_IN_CONDITIONAL,
String.format(UNSUPPORTED_TYPE_IN_CONDITIONAL, type.getName()));
}

public static ConsistencyCheckException missingTypeMapping(Class<?> type, String mapName) {
return new ConsistencyCheckException(ErrorCode.MISSING_TYPE_MAPPING,
String.format(MISSING_TYPE_MAPPING, type.getName(), mapName));
}

public static ConsistencyCheckException missingTypeAnnotation(Class<?> type) {
return new ConsistencyCheckException(ErrorCode.MISSING_TYPE_ANNOTATION,
String.format(MISSING_TYPE_ANNOTATION, type.getName()));
}

public static ConsistencyCheckException unknownExpressionType(Class<?> type) {
return new ConsistencyCheckException(ErrorCode.UNKNOWN_EXPRESSION_TYPE,
String.format(UNKNOWN_EXPRESSION_TYPE, type.getName()));
}

public static ConsistencyCheckException invalidVariableContext() {
return new ConsistencyCheckException(ErrorCode.INVALID_VARIABLE_CONTEXT, INVALID_VARIABLE_CONTEXT);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/*
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
* compliance with the Licence. You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence
* is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence for the specific language governing permissions and limitations under
* the Licence.
*/
package eu.europa.ted.efx.exceptions;

import org.antlr.v4.runtime.misc.ParseCancellationException;
Expand All @@ -12,38 +25,53 @@
*/
@SuppressWarnings("squid:MaximumInheritanceDepth") // Necessary to integrate with ANTLR4 parser cancellation
public class InvalidArgumentException extends ParseCancellationException {

public enum ErrorCode {
ARGUMENT_NUMBER_MISMATCH,
ARGUMENT_TYPE_MISMATCH,
UNSUPPORTED_SEQUENCE_TYPE,
MISSING_ARGUMENT
}

private static final String ARGUMENT_NUMBER_MISMATCH = "Argument number mismatch in call to %s '%s'. Expected %d but got %d.";
private static final String ARGUMENT_TYPE_MISMATCH = "Argument type mismatch for argument %d in call to %s '%s'. Expected %s but got %s.";
private static final String UNSUPPORTED_SEQUENCE_TYPE = "Unsupported sequence type '%s' in call to %s.";
private static final String MISSING_ARGUMENT = "No argument passed for parameter '%s'.";

private InvalidArgumentException(String message) {
private final ErrorCode errorCode;

private InvalidArgumentException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public static InvalidArgumentException argumentNumberMismatch(Parametrised identifier, int expectedNumber, int actualNumber) {
return new InvalidArgumentException(
return new InvalidArgumentException(ErrorCode.ARGUMENT_NUMBER_MISMATCH,
String.format(ARGUMENT_NUMBER_MISMATCH, identifier.getClass().getSimpleName().toLowerCase(),
identifier.name, expectedNumber, actualNumber));
}

public static InvalidArgumentException argumentNumberMismatch(Parametrised identifier, int expectedNumber) {
return argumentNumberMismatch(identifier, expectedNumber, expectedNumber + 1);
}

public static InvalidArgumentException argumentTypeMismatch(int position, Parametrised identifier,
Class<? extends TypedExpression> expectedType, Class<? extends TypedExpression> actualType) {
return new InvalidArgumentException(
return new InvalidArgumentException(ErrorCode.ARGUMENT_TYPE_MISMATCH,
String.format(ARGUMENT_TYPE_MISMATCH, position + 1, identifier.getClass().getSimpleName().toLowerCase(),
identifier.name, TypedExpression.getEfxDataType(expectedType).getSimpleName(),
TypedExpression.getEfxDataType(actualType).getSimpleName()));
}

public static InvalidArgumentException unsupportedSequenceType(String type, String functionName) {
return new InvalidArgumentException(String.format(UNSUPPORTED_SEQUENCE_TYPE, type, functionName));
return new InvalidArgumentException(ErrorCode.UNSUPPORTED_SEQUENCE_TYPE, String.format(UNSUPPORTED_SEQUENCE_TYPE, type, functionName));
}

public static InvalidArgumentException missingArgument(String parameterName) {
return new InvalidArgumentException(String.format(MISSING_ARGUMENT, parameterName));
return new InvalidArgumentException(ErrorCode.MISSING_ARGUMENT, String.format(MISSING_ARGUMENT, parameterName));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/*
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
* compliance with the Licence. You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence
* is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence for the specific language governing permissions and limitations under
* the Licence.
*/
package eu.europa.ted.efx.exceptions;

import org.antlr.v4.runtime.misc.ParseCancellationException;
Expand All @@ -10,18 +23,37 @@
*/
@SuppressWarnings("squid:MaximumInheritanceDepth") // Necessary to integrate with ANTLR4 parser cancellation
public class InvalidIdentifierException extends ParseCancellationException {

public enum ErrorCode {
UNDECLARED_IDENTIFIER,
IDENTIFIER_ALREADY_DECLARED,
NOT_A_CONTEXT_VARIABLE
}

private static final String UNDECLARED_IDENTIFIER = "Identifier '%s' is not declared.";
private static final String IDENTIFIER_ALREADY_DECLARED = "Identifier '%s' is already declared in this scope.";
private static final String NOT_A_CONTEXT_VARIABLE = "Variable '%s' is not a context variable.";

private final ErrorCode errorCode;

private InvalidIdentifierException(String message) {
private InvalidIdentifierException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public static InvalidIdentifierException undeclaredIdentifier(String identifierName) {
return new InvalidIdentifierException(String.format(UNDECLARED_IDENTIFIER, identifierName));
return new InvalidIdentifierException(ErrorCode.UNDECLARED_IDENTIFIER, String.format(UNDECLARED_IDENTIFIER, identifierName));
}

public static InvalidIdentifierException alreadyDeclared(String identifierName) {
return new InvalidIdentifierException(String.format(IDENTIFIER_ALREADY_DECLARED, identifierName));
return new InvalidIdentifierException(ErrorCode.IDENTIFIER_ALREADY_DECLARED, String.format(IDENTIFIER_ALREADY_DECLARED, identifierName));
}

public static InvalidIdentifierException notAContextVariable(String variableName) {
return new InvalidIdentifierException(ErrorCode.NOT_A_CONTEXT_VARIABLE, String.format(NOT_A_CONTEXT_VARIABLE, variableName));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/*
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
* compliance with the Licence. You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence
* is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence for the specific language governing permissions and limitations under
* the Licence.
*/
package eu.europa.ted.efx.exceptions;

import org.antlr.v4.runtime.misc.ParseCancellationException;
Expand All @@ -9,41 +22,57 @@
*/
@SuppressWarnings("squid:MaximumInheritanceDepth") // Necessary to integrate with ANTLR4 parser cancellation
public class InvalidIndentationException extends ParseCancellationException {
private static final String INCONSISTENT_INDENTATION_SPACES =
"Inconsistent indentation. Expected a multiple of %d spaces.";
private static final String INDENTATION_LEVEL_SKIPPED = "Indentation level skipped.";
private static final String START_INDENT_AT_ZERO =
"Incorrect indentation. Please do not indent the first level in your template.";
private static final String MIXED_INDENTATION =
"Do not mix indentation methods. Stick with either tabs or spaces.";
private static final String NO_NESTING_ON_INVOCATIONS =
"Nesting content under a template invocation is not allowed.";
private static final String NO_INDENT_ON_TEMPLATE_DECLARATIONS = "Indentation is not allowed on template declaration lines.";

private InvalidIndentationException(String message) {

public enum ErrorCode {
INCONSISTENT_INDENTATION_SPACES,
INDENTATION_LEVEL_SKIPPED,
START_INDENT_AT_ZERO,
MIXED_INDENTATION,
NO_NESTING_ON_INVOCATIONS,
NO_INDENT_ON_TEMPLATE_DECLARATIONS
}

private static final String INCONSISTENT_INDENTATION_SPACES = "Inconsistent indentation. Expected a multiple of %d spaces.";
private static final String INDENTATION_LEVEL_SKIPPED = "Indentation level skipped.";
private static final String START_INDENT_AT_ZERO = "Incorrect indentation. Please do not indent the first level in your template.";
private static final String MIXED_INDENTATION = "Do not mix indentation methods. Stick with either tabs or spaces.";
private static final String NO_NESTING_ON_INVOCATIONS = "Nesting content under a template invocation is not allowed.";
private static final String NO_INDENT_ON_TEMPLATE_DECLARATIONS = "Indentation is not allowed on template declaration lines.";

private final ErrorCode errorCode;

private InvalidIndentationException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public static InvalidIndentationException inconsistentSpaces(int spaces) {
return new InvalidIndentationException(String.format(INCONSISTENT_INDENTATION_SPACES, spaces));
return new InvalidIndentationException(ErrorCode.INCONSISTENT_INDENTATION_SPACES,
String.format(INCONSISTENT_INDENTATION_SPACES, spaces));
}

public static InvalidIndentationException indentationLevelSkipped() {
return new InvalidIndentationException(INDENTATION_LEVEL_SKIPPED);
return new InvalidIndentationException(ErrorCode.INDENTATION_LEVEL_SKIPPED, INDENTATION_LEVEL_SKIPPED);
}

public static InvalidIndentationException startIndentAtZero() {
return new InvalidIndentationException(START_INDENT_AT_ZERO);
return new InvalidIndentationException(ErrorCode.START_INDENT_AT_ZERO, START_INDENT_AT_ZERO);
}

public static InvalidIndentationException mixedIndentation() {
return new InvalidIndentationException(MIXED_INDENTATION);
return new InvalidIndentationException(ErrorCode.MIXED_INDENTATION, MIXED_INDENTATION);
}

public static InvalidIndentationException noNestingOnInvocations() {
return new InvalidIndentationException(NO_NESTING_ON_INVOCATIONS);
return new InvalidIndentationException(ErrorCode.NO_NESTING_ON_INVOCATIONS, NO_NESTING_ON_INVOCATIONS);
}

public static InvalidIndentationException noIndentOnTemplateDeclarations() {
return new InvalidIndentationException(NO_INDENT_ON_TEMPLATE_DECLARATIONS);
return new InvalidIndentationException(ErrorCode.NO_INDENT_ON_TEMPLATE_DECLARATIONS,
NO_INDENT_ON_TEMPLATE_DECLARATIONS);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/*
* Copyright 2025 European Union
*
* Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European
* Commission – subsequent versions of the EUPL (the "Licence"); You may not use this work except in
* compliance with the Licence. You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence
* is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence for the specific language governing permissions and limitations under
* the Licence.
*/
package eu.europa.ted.efx.exceptions;

import org.antlr.v4.runtime.misc.ParseCancellationException;
Expand All @@ -7,18 +20,43 @@
*/
@SuppressWarnings("squid:MaximumInheritanceDepth") // Necessary to integrate with ANTLR4 parser cancellation
public class InvalidUsageException extends ParseCancellationException {

public enum ErrorCode {
SHORTHAND_REQUIRES_CODE_OR_INDICATOR,
SHORTHAND_REQUIRES_FIELD_CONTEXT,
INVALID_NOTICE_SUBTYPE_RANGE_ORDER,
INVALID_NOTICE_SUBTYPE_TOKEN
}

private static final String SHORTHAND_REQUIRES_CODE_OR_INDICATOR = "Indirect label reference shorthand #{%1$s}, requires a field of type 'code' or 'indicator'. Field %1$s is of type %2$s.";
private static final String SHORTHAND_REQUIRES_FIELD_CONTEXT = "The %s shorthand syntax can only be used when a field is declared as context.";
private static final String INVALID_NOTICE_SUBTYPE_RANGE_ORDER = "Notice subtype range '%s-%s' is not in ascending order.";
private static final String INVALID_NOTICE_SUBTYPE_TOKEN = "Invalid notice subtype token '%s'. Expected format: 'X' or 'X-Y'.";

private final ErrorCode errorCode;

private InvalidUsageException(String message) {
private InvalidUsageException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public static InvalidUsageException shorthandRequiresCodeOrIndicator(String fieldName, String fieldType) {
return new InvalidUsageException(String.format(SHORTHAND_REQUIRES_CODE_OR_INDICATOR, fieldName, fieldType));
return new InvalidUsageException(ErrorCode.SHORTHAND_REQUIRES_CODE_OR_INDICATOR, String.format(SHORTHAND_REQUIRES_CODE_OR_INDICATOR, fieldName, fieldType));
}

public static InvalidUsageException shorthandRequiresFieldContext(String shorthandType) {
return new InvalidUsageException(String.format(SHORTHAND_REQUIRES_FIELD_CONTEXT, shorthandType));
return new InvalidUsageException(ErrorCode.SHORTHAND_REQUIRES_FIELD_CONTEXT, String.format(SHORTHAND_REQUIRES_FIELD_CONTEXT, shorthandType));
}

public static InvalidUsageException invalidNoticeSubtypeRangeOrder(String start, String end) {
return new InvalidUsageException(ErrorCode.INVALID_NOTICE_SUBTYPE_RANGE_ORDER, String.format(INVALID_NOTICE_SUBTYPE_RANGE_ORDER, start, end));
}

public static InvalidUsageException invalidNoticeSubtypeToken(String token) {
return new InvalidUsageException(ErrorCode.INVALID_NOTICE_SUBTYPE_TOKEN, String.format(INVALID_NOTICE_SUBTYPE_TOKEN, token));
}
}
Loading