Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1045687
TEDEFO-5231 Read the SDK 2 data types from fields/fwd/data-types.json
rouschr Oct 8, 2026
96d7e60
TEDEFO-5228 Read the SDK 2 nodes from fields/fwd/nodes.json
rouschr Oct 8, 2026
3f4f5dc
TEDEFO-5229 Read the SDK 2 fields from fields/fwd/fields.json
rouschr Oct 8, 2026
ca123c5
TEDEFO-5230 Load the SDK 2 special purpose maps and link the disclosu…
rouschr Oct 8, 2026
bf1f0b0
TEDEFO-5231: Read the data types without a version
rousso Oct 11, 2026
8ac7eeb
TEDEFO-5228: Read the nodes of each SDK version with its own repository
rousso Oct 11, 2026
2cd7073
TEDEFO-5229: Read the fields of each SDK version with its own repository
rousso Oct 11, 2026
e90f1f1
TEDEFO-5230: Attach the disclosure fields to each field that can be w…
rousso Oct 11, 2026
42242c5
TEDEFO-5230: Read the special purpose maps as stable identifiers
rousso Oct 12, 2026
d953d54
TEDEFO-5229: Use the SDK 2 names for the methods that read the disclo…
rousso Oct 12, 2026
7cad432
TEDEFO-5230: Use the SDK 2 name for the setter of the disclosure control
rousso Oct 12, 2026
c56a0c1
TEDEFO-5229: Give the undisclosed field selector in its selector form
rousso Oct 12, 2026
cf2158a
TEDEFO-5228: Declare a node before its parent in the SDK 2 test nodes
rousso Oct 12, 2026
23a8850
TEDEFO-5229: Name the reader of the disclosure fields extractDisclosu…
rousso Oct 26, 2026
abcd84c
TEDEFO-5230: Name the setter of the disclosure fields setDisclosureCo…
rousso Oct 26, 2026
145d036
TEDEFO-5228: Deprecate the constructor of the node repository that ta…
rousso Nov 4, 2026
3ba298b
TEDEFO-5229: Deprecate the constructors of the field repository that …
rousso Nov 4, 2026
521178c
TEDEFO-5231: Give the data types through the repository factory too
rousso Nov 4, 2026
6a12c0d
TEDEFO-5231: Deprecate the constructor of the data type repository th…
rousso Nov 4, 2026
58c156f
TEDEFO-5230: Default the disclosure fields when the SDK gives no stab…
rousso Nov 4, 2026
e73749a
TEDEFO-5229: Read the fwd codelist property renamed by TEDEFO-5240
rouschr Nov 4, 2026
bab1601
TEDEFO-5229: Test the code list of SDK 2 fields under both of its names
rousso Nov 4, 2026
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
47 changes: 47 additions & 0 deletions src/main/java/eu/europa/ted/eforms/sdk/SdkConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public class SdkConstants {
public static final String FIELDS_DIR_NAME = "fields";

public static final String NODES_JSON_FILE_NAME = "nodes.json";

/**
* Key for the array of nodes of fields/fwd/nodes.json in SDK 2, where fields/fields.json uses
* {@link #FIELDS_JSON_XML_STRUCTURE_KEY} (TEDEFO-5228).
*/
public static final String NODES_JSON_NODES_KEY = "nodes";

public static final String FIELDS_JSON_FILE_NAME = "fields.json";
public static final String DATA_TYPES_JSON_FILE_NAME = "data-types.json";
public static final String DATA_TYPES_JSON_DATA_TYPES_KEY = "dataTypes";
Expand Down Expand Up @@ -58,6 +65,46 @@ public class SdkConstants {
*/
public static final String FWD_DIR_NAME = "fwd";

/**
* Key of the map of stable identifiers, in fields/fwd/fields.json and fields/fwd/nodes.json of
* SDK 2 (TEDEFO-1877, TEDEFO-5210). A stable identifier names the field or the node that plays a
* particular role, such as "disclosureDate", and stays the same throughout a major version of the
* SDK, whereas the identifier of the field or the node may change from one minor version to the
* next. The stable identifiers are plain strings, as the map is data and will grow
* (TEDEFO-5230).
*
* <p>
* The SDK 2 files call the map "specialPurpose".
* </p>
*/
public static final String STABLE_IDS_KEY = "specialPurpose";

/**
* Stable identifier of the field holding the disclosure group of a withheld field, which SDK 1
* named per field as privacy.unpublishedFieldId.
*/
public static final String STABLE_ID_DISCLOSURE_GROUP = "disclosureGroup";

/**
* Stable identifier of the field holding the justification code of a withheld field, which
* SDK 1 named per field as privacy.reasonCodeFieldId.
*/
public static final String STABLE_ID_DISCLOSURE_JUSTIFICATION_CODE =
"disclosureJustificationCode";

/**
* Stable identifier of the field holding the justification description of a withheld field,
* which SDK 1 named per field as privacy.reasonDescriptionFieldId.
*/
public static final String STABLE_ID_DISCLOSURE_JUSTIFICATION_DESCRIPTION =
"disclosureJustificationDescription";

/**
* Stable identifier of the field holding the disclosure date of a withheld field, which SDK 1
* named per field as privacy.publicationDateFieldId.
*/
public static final String STABLE_ID_DISCLOSURE_DATE = "disclosureDate";

private SdkConstants() {}

public enum SdkResource implements PathResource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* factory.
*/
public enum SdkComponentType {
FIELD, NODE, CODELIST, NOTICE_TYPE, EFX_EXPRESSION_TRANSLATOR, EFX_TEMPLATE_TRANSLATOR, EFX_RULES_TRANSLATOR,
FIELD, NODE, CODELIST, NOTICE_TYPE, NODE_REPOSITORY, FIELD_REPOSITORY, EFX_EXPRESSION_TRANSLATOR, EFX_TEMPLATE_TRANSLATOR, EFX_RULES_TRANSLATOR,
EFX_COMPUTE_DEPENDENCY_EXTRACTOR, EFX_VALIDATION_DEPENDENCY_EXTRACTOR,
SYMBOL_RESOLVER, SCRIPT_GENERATOR, MARKUP_GENERATOR, VALIDATOR_GENERATOR;
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
* Each field in the SDK has a type (e.g., "text", "date", "amount"). This entity captures
* type-level metadata such as the privacy masking value, the attribute that the fields of the type
* carry (e.g., the currency of an amount) with its data type, and the code list of the data types
* whose values come from one. Currently hardcoded; will be loaded from data-types.json when it is
* added to the SDK.
* whose values come from one. SDK 1 publishes no data types, so they come from the data-types.json
* resource of this library; SDK 2 publishes them in fields/fwd/data-types.json. Both have the same
* format, so a data type does not depend on the SDK version.
*/
public class SdkDataType {
private final String id;
Expand Down
108 changes: 104 additions & 4 deletions src/main/java/eu/europa/ted/eforms/sdk/entity/SdkField.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ public abstract class SdkField implements Comparable<SdkField> {
private final String codelistId;
private final boolean repeatable;
private final String privacyCode;
private final PrivacySettings privacySettings;
// Not final: SDK 2 no longer names the disclosure fields per field, so they are filled in after
// this field is read, from the stable identifiers of the SDK (TEDEFO-5230).
private PrivacySettings privacySettings;
private final String withholdingCondition;
private final String undisclosedFieldSelector;
private final List<String> attributes;
private final String attributeOf;
private final String attributeName;
Expand Down Expand Up @@ -119,6 +123,8 @@ protected SdkField(final String id, final String type, final String parentNodeId
this.repeatable = repeatable;
this.privacyCode = null;
this.privacySettings = null;
this.withholdingCondition = null;
this.undisclosedFieldSelector = null;
this.attributes = Collections.emptyList();
this.attributeOf = null;
this.attributeName = null;
Expand All @@ -132,9 +138,10 @@ protected SdkField(final JsonNode fieldNode) {
this.type = fieldNode.get("type").asText(null);
this.codelistId = extractCodelistId(fieldNode);
this.repeatable = extractRepeatable(fieldNode);
final JsonNode privacyNode = fieldNode.get("privacy");
this.privacyCode = privacyNode != null ? privacyNode.get("code").asText(null) : null;
this.privacySettings = extractPrivacy(privacyNode);
this.privacyCode = this.extractDisclosureGroupId(fieldNode);
this.privacySettings = this.extractDisclosureControlFields(fieldNode);
this.withholdingCondition = this.extractWithholdingCondition(fieldNode);
this.undisclosedFieldSelector = this.extractUndisclosedFieldSelector(fieldNode);
this.attributes = extractAttributes(fieldNode);
this.attributeOf = fieldNode.has("attributeOf") ? fieldNode.get("attributeOf").asText(null) : null;
this.attributeName = fieldNode.has("attributeName") ? fieldNode.get("attributeName").asText(null) : null;
Expand Down Expand Up @@ -180,6 +187,63 @@ protected boolean extractRepeatable(final JsonNode fieldNode) {
return valueNode.asBoolean(false);
}

/**
* Returns the identifier of the disclosure group of the field, from the code of the privacy block
* of fields/fields.json. SDK 2 gives it as the groupId of the disclosureControl block instead
* (TEDEFO-5229).
*
* <p>
* Called while this field is being constructed, so an override must read the given node only,
* and no state of its own.
* </p>
*/
protected String extractDisclosureGroupId(final JsonNode fieldNode) {
final JsonNode privacyNode = fieldNode.get("privacy");
return privacyNode != null ? privacyNode.get("code").asText(null) : null;
}

/**
* Returns the fields that hold the disclosure data of this field, from the privacy block of
* fields/fields.json. SDK 2 no longer gives them per field: they are the same for every field,
* and are linked to each field in TEDEFO-5230.
*
* <p>
* Called while this field is being constructed, so an override must read the given node only,
* and no state of its own.
* </p>
*/
protected PrivacySettings extractDisclosureControlFields(final JsonNode fieldNode) {
return this.extractPrivacy(fieldNode.get("privacy"));
}

/**
* Returns the EFX condition under which this field is withheld, for the fields that are withheld
* only for some of their instances, or null for every other field. Only SDK 2 gives it, in the
* disclosureControl block (TEDEFO-5229). It is not translated.
*
* <p>
* Called while this field is being constructed, so an override must read the given node only,
* and no state of its own.
* </p>
*/
protected String extractWithholdingCondition(final JsonNode fieldNode) {
return null;
}

/**
* Returns the EFX selector locating this field when it is withheld, for the fields that are
* withheld only for some of their instances, or null for every other field. Only SDK 2 gives it,
* in the disclosureControl block (TEDEFO-5229). It is not translated.
*
* <p>
* Called while this field is being constructed, so an override must read the given node only,
* and no state of its own.
* </p>
*/
protected String extractUndisclosedFieldSelector(final JsonNode fieldNode) {
return null;
}

protected PrivacySettings extractPrivacy(final JsonNode privacyNode) {
if (privacyNode == null) {
return null;
Expand Down Expand Up @@ -215,6 +279,15 @@ public String getType() {
return this.type;
}

/**
* Returns the type as the SDK declares it, without any version-specific mapping. Lets a version
* that must not inherit such a mapping bypass it (see SdkFieldV1's measure/duration mapping,
* which applies to SDK 1 only).
*/
protected final String getDeclaredType() {
return this.type;
}

public String getCodelistId() {
return this.codelistId;
}
Expand Down Expand Up @@ -275,6 +348,33 @@ public PrivacySettings getPrivacySettings() {
return this.privacySettings;
}

/**
* Sets the fields that hold the disclosure data of this field. SDK 1 names them in the privacy
* block of each withheld field, so they are read with the field; SDK 2 names them once, by their
* stable identifiers in fields/fwd/fields.json, so they are filled in afterwards (TEDEFO-5230).
*/
public void setDisclosureControlFields(final PrivacySettings disclosureControlFields) {
this.privacySettings = disclosureControlFields;
}

/**
* @return the EFX condition ({@code {fieldId} ${conditionEfx}}) under which this field is
* withheld, as the SDK gives it and untranslated, or null when this field is not withheld
* or is withheld for all of its instances. Only SDK 2 gives it (TEDEFO-5229).
*/
public String getWithholdingCondition() {
return this.withholdingCondition;
}

/**
* @return the EFX selector ({@code {ND-Root} &{/fieldId[conditionEfx]}}) locating this field
* when it is withheld, as the SDK gives it and untranslated, or null when this field is not
* withheld or is withheld for all of its instances. Only SDK 2 gives it (TEDEFO-5229).
*/
public String getUndisclosedFieldSelector() {
return this.undisclosedFieldSelector;
}

public SdkNode getParentNode() {
return this.parentNode;
}
Expand Down
105 changes: 105 additions & 0 deletions src/main/java/eu/europa/ted/eforms/sdk/entity/v2/SdkFieldV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,26 @@
import eu.europa.ted.eforms.sdk.component.SdkComponentType;
import eu.europa.ted.eforms.sdk.entity.v1.SdkFieldV1;

/**
* A field of SDK 2, read from fields/fwd/fields.json (TEDEFO-5229).
*
* <p>
* Compared with fields/fields.json, the properties this entity reads differ as follows: repeatable
* is a plain boolean instead of an object with a value; the privacy block is replaced by
* disclosureControl, whose groupId is the privacy code and which also carries the two EFX
* expressions of the fields that are withheld only for some of their instances; and the four
* fields that held the disclosure data are no longer given per field, so they are not read here
* (TEDEFO-5230 links them to each field instead).
* </p>
*/
@SdkComponent(versions = {"2"}, componentType = SdkComponentType.FIELD)
public class SdkFieldV2 extends SdkFieldV1 {

/**
* Replaces the privacy block of fields/fields.json (TEDEFO-5128).
*/
private static final String DISCLOSURE_CONTROL = "disclosureControl";

private final String alias;

public SdkFieldV2(String id, String type, String parentNodeId, String xpathAbsolute,
Expand Down Expand Up @@ -40,4 +58,91 @@ public SdkFieldV2(
public String getAlias() {
return alias;
}

/**
* fields/fwd/fields.json gives repeatable as a plain boolean, where fields/fields.json gives an
* object with a value. Both are read, because an SDK 2 field is read from either file.
*/
@Override
protected boolean extractRepeatable(final JsonNode fieldNode) {
final JsonNode repeatableNode = fieldNode.get("repeatable");
if (repeatableNode != null && repeatableNode.isBoolean()) {
return repeatableNode.booleanValue();
}
return super.extractRepeatable(fieldNode);
}

/**
* fields/fwd/fields.json renames the field property from codeList to codelist (TEDEFO-5240),
* where fields/fields.json still gives codeList. An SDK 2 field is read from either file, so both
* are tried, the fwd name first.
*/
@Override
protected String extractCodelistId(final JsonNode fieldNode) {
final JsonNode codelistNode = fieldNode.get("codelist");
if (codelistNode == null) {
return super.extractCodelistId(fieldNode);
}
final JsonNode valueNode = codelistNode.get("value");
if (valueNode == null) {
return null;
}
return valueNode.get("id").asText(null);
}

/**
* fields/fwd/fields.json gives the identifier of the disclosure group as the groupId of the
* disclosureControl block, where fields/fields.json gives it as the code of the privacy block.
*/
@Override
protected String extractDisclosureGroupId(final JsonNode fieldNode) {
if (disclosureControlOf(fieldNode) == null) {
return super.extractDisclosureGroupId(fieldNode);
}
return textOfDisclosureControl(fieldNode, "groupId");
}

/**
* fields/fwd/fields.json no longer gives the fields that hold the disclosure data per field: they
* are the same for every field, and TEDEFO-5230 links them to each field instead. They are still
* read from the privacy block of fields/fields.json.
*/
@Override
protected PrivacySettings extractDisclosureControlFields(final JsonNode fieldNode) {
if (disclosureControlOf(fieldNode) == null) {
return super.extractDisclosureControlFields(fieldNode);
}
return null;
}

@Override
protected String extractWithholdingCondition(final JsonNode fieldNode) {
return textOfDisclosureControl(fieldNode, "withholdingCondition");
}

@Override
protected String extractUndisclosedFieldSelector(final JsonNode fieldNode) {
return textOfDisclosureControl(fieldNode, "undisclosedFieldSelector");
}

/**
* SDK 2 separates duration from measure, so the mapping of measure onto duration that
* {@link SdkFieldV1} applies for SDK 1 must not apply here.
*/
@Override
public String getType() {
return this.getDeclaredType();
}

private static JsonNode disclosureControlOf(final JsonNode fieldNode) {
return fieldNode.get(DISCLOSURE_CONTROL);
}

private static String textOfDisclosureControl(final JsonNode fieldNode, final String property) {
final JsonNode disclosureControl = disclosureControlOf(fieldNode);
if (disclosureControl == null || !disclosureControl.hasNonNull(property)) {
return null;
}
return disclosureControl.get(property).asText(null);
}
}
Loading
Loading