Skip to content

Commit 7e83d03

Browse files
committed
TEDEFO-4319 Implement linked field properties and computed privacy properties in translator
1 parent eb470cf commit 7e83d03

5 files changed

Lines changed: 443 additions & 112 deletions

File tree

src/main/java/eu/europa/ted/efx/exceptions/ConsistencyCheckException.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public enum ErrorCode {
2727
MISSING_TYPE_ANNOTATION,
2828
UNKNOWN_EXPRESSION_TYPE,
2929
INVALID_VARIABLE_CONTEXT,
30-
UNHANDLED_PRIVACY_SETTING
30+
UNHANDLED_PRIVACY_SETTING,
31+
UNHANDLED_LINKED_FIELD_PROPERTY
3132
}
3233

3334
private static final String TYPE_NOT_REGISTERED =
@@ -67,6 +68,11 @@ public enum ErrorCode {
6768
"This indicates a bug in the translator. " +
6869
"Add the missing case to the switch in getPrivacySettingOfField().";
6970

71+
private static final String UNHANDLED_LINKED_FIELD_PROPERTY =
72+
"Linked field property '%s' is not handled. " +
73+
"This indicates a bug in the translator. " +
74+
"Add the missing case to getLinkedFieldId().";
75+
7076
private final ErrorCode errorCode;
7177

7278
private ConsistencyCheckException(ErrorCode errorCode, String message) {
@@ -111,4 +117,9 @@ public static ConsistencyCheckException unhandledPrivacySetting(Object setting)
111117
return new ConsistencyCheckException(ErrorCode.UNHANDLED_PRIVACY_SETTING,
112118
String.format(UNHANDLED_PRIVACY_SETTING, setting));
113119
}
120+
121+
public static ConsistencyCheckException unhandledLinkedFieldProperty(String property) {
122+
return new ConsistencyCheckException(ErrorCode.UNHANDLED_LINKED_FIELD_PROPERTY,
123+
String.format(UNHANDLED_LINKED_FIELD_PROPERTY, property));
124+
}
114125
}

src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java

Lines changed: 140 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -215,58 +215,91 @@ private String getTranslatedScript() {
215215
return sb.toString().trim();
216216
}
217217

218-
protected static String getFieldId(FieldReferenceContext ctx) {
218+
private String getLinkedFieldId(String baseFieldId, LinkedFieldPropertyContext ctx) {
219+
if (ctx.PublicationDate() != null)
220+
return this.symbols.getPrivacySettingOfField(baseFieldId, PrivacySetting.PUBLICATION_DATE_FIELD);
221+
else if (ctx.JustificationCode() != null)
222+
return this.symbols.getPrivacySettingOfField(baseFieldId, PrivacySetting.JUSTIFICATION_CODE_FIELD);
223+
else if (ctx.JustificationDescription() != null)
224+
return this.symbols.getPrivacySettingOfField(baseFieldId, PrivacySetting.JUSTIFICATION_DESCRIPTION_FIELD);
225+
else
226+
throw ConsistencyCheckException.unhandledLinkedFieldProperty(ctx.getText());
227+
}
228+
229+
protected String getFieldId(LinkedFieldReferenceContext ctx) {
230+
if (ctx == null) {
231+
return null;
232+
}
233+
String baseFieldId = ctx.simpleFieldReference().fieldId.getText();
234+
if (ctx.linkedFieldProperty() == null) {
235+
return baseFieldId;
236+
}
237+
return this.getLinkedFieldId(baseFieldId, ctx.linkedFieldProperty());
238+
}
239+
240+
protected String getFieldId(EfxParser.FieldMentionContext ctx) {
241+
if (ctx == null) {
242+
return null;
243+
}
244+
String baseFieldId = ctx.fieldId.getText();
245+
if (ctx.linkedFieldProperty() == null) {
246+
return baseFieldId;
247+
}
248+
return this.getLinkedFieldId(baseFieldId, ctx.linkedFieldProperty());
249+
}
250+
251+
protected String getFieldId(FieldReferenceContext ctx) {
219252
if (ctx == null) {
220253
return null;
221254
}
222255

223256
if (ctx.absoluteFieldReference() != null) {
224-
return getFieldId(ctx.absoluteFieldReference());
257+
return this.getFieldId(ctx.absoluteFieldReference());
225258
}
226259

227260
if (ctx.fieldReferenceInOtherNotice() != null) {
228-
return getFieldId(ctx.fieldReferenceInOtherNotice());
261+
return this.getFieldId(ctx.fieldReferenceInOtherNotice());
229262
}
230263
assert false : "Unexpected context type for field reference: " + ctx.getClass().getSimpleName();
231264
return null;
232265
}
233266

234-
protected static String getFieldId(AbsoluteFieldReferenceContext ctx) {
267+
protected String getFieldId(AbsoluteFieldReferenceContext ctx) {
235268
if (ctx == null) {
236269
return null;
237270
}
238-
return ctx.reference.reference.simpleFieldReference().fieldId.getText();
271+
return this.getFieldId(ctx.reference.reference.linkedFieldReference());
239272
}
240273

241-
protected static String getFieldId(FieldReferenceInOtherNoticeContext ctx) {
274+
protected String getFieldId(FieldReferenceInOtherNoticeContext ctx) {
242275
if (ctx == null) {
243276
return null;
244277
}
245-
return ctx.reference.reference.reference.reference.reference.simpleFieldReference().fieldId.getText();
278+
return this.getFieldId(ctx.reference.reference.reference.reference.reference.linkedFieldReference());
246279
}
247280

248-
protected static String getFieldId(FieldContextContext ctx) {
281+
protected String getFieldId(FieldContextContext ctx) {
249282
if (ctx == null) {
250283
return null;
251284
}
252285

253286
if (ctx.absoluteFieldReference() != null) {
254-
return getFieldId(ctx.absoluteFieldReference());
287+
return this.getFieldId(ctx.absoluteFieldReference());
255288
}
256289

257290
if (ctx.fieldReferenceWithPredicate() != null) {
258-
return getFieldId(ctx.fieldReferenceWithPredicate());
291+
return this.getFieldId(ctx.fieldReferenceWithPredicate());
259292
}
260293

261294
assert false : "Unexpected context type for field reference: " + ctx.getClass().getSimpleName();
262295
return null;
263296
}
264297

265-
protected static String getFieldId(FieldReferenceWithPredicateContext ctx) {
298+
protected String getFieldId(FieldReferenceWithPredicateContext ctx) {
266299
if (ctx == null) {
267300
return null;
268301
}
269-
return ctx.fieldReferenceWithAxis().simpleFieldReference().fieldId.getText();
302+
return this.getFieldId(ctx.fieldReferenceWithAxis().linkedFieldReference());
270303
}
271304

272305
protected static String getNodeId(NodeReferenceContext ctx) {
@@ -1102,6 +1135,16 @@ public void exitSimpleFieldReference(EfxParser.SimpleFieldReferenceContext ctx)
11021135
symbols.getRelativePathOfField(ctx.fieldId.getText(), this.efxContext.symbol()));
11031136
}
11041137

1138+
@Override
1139+
public void exitLinkedFieldReference(LinkedFieldReferenceContext ctx) {
1140+
if (ctx.linkedFieldProperty() != null) {
1141+
this.stack.pop(PathExpression.class); // discard base field path
1142+
String companionFieldId = getFieldId(ctx);
1143+
this.stack.push(
1144+
symbols.getRelativePathOfField(companionFieldId, this.efxContext.symbol()));
1145+
}
1146+
}
1147+
11051148
@Override
11061149
public void enterAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
11071150
if (ctx.Slash() != null) {
@@ -1630,69 +1673,98 @@ public void exitEndsWithFunction(EndsWithFunctionContext ctx) {
16301673
// #region Privacy settings ------------------------------------------------
16311674

16321675
@Override
1633-
public void exitFieldIsWithholdableCondition(FieldIsWithholdableConditionContext ctx) {
1634-
final String fieldId = ctx.fieldMention().getText();
1676+
public void exitFieldWasWithheldProperty(FieldWasWithheldPropertyContext ctx) {
1677+
final String fieldId = getFieldId(ctx.fieldMention());
1678+
if (this.isFieldRepeatableFromContext(fieldId, this.efxContext.peek())) {
1679+
throw TypeMismatchException.fieldMayRepeat(fieldId, this.efxContext.symbol());
1680+
}
16351681

16361682
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
1637-
final boolean negated = ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER);
1638-
final boolean isWithholdable = privacyCode != null && !privacyCode.isEmpty();
1639-
this.stack.push(this.script.getBooleanEquivalent(negated != isWithholdable));
1683+
if (privacyCode == null || privacyCode.isEmpty()) {
1684+
throw InvalidUsageException.fieldNotWithholdable(fieldId);
1685+
}
1686+
1687+
this.stack.push(this.composeWasWithheldCondition(fieldId, privacyCode));
16401688
}
16411689

16421690
@Override
1643-
public void exitFieldWasWithheldCondition(FieldWasWithheldConditionContext ctx) {
1644-
final String fieldId = ctx.fieldMention().getText();
1691+
public void exitFieldIsWithheldProperty(FieldIsWithheldPropertyContext ctx) {
1692+
final String fieldId = getFieldId(ctx.fieldMention());
1693+
if (this.isFieldRepeatableFromContext(fieldId, this.efxContext.peek())) {
1694+
throw TypeMismatchException.fieldMayRepeat(fieldId, this.efxContext.symbol());
1695+
}
16451696

16461697
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
16471698
if (privacyCode == null || privacyCode.isEmpty()) {
16481699
throw InvalidUsageException.fieldNotWithholdable(fieldId);
16491700
}
16501701

1651-
BooleanExpression result = this.composeWasWithheldCondition(fieldId, privacyCode);
1652-
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
1653-
result = this.script.composeLogicalNot(result);
1654-
}
1655-
this.stack.push(result);
1702+
this.stack.push(this.script.composeLogicalAnd(
1703+
this.composeWasWithheldCondition(fieldId, privacyCode),
1704+
this.composeStillWithheldCondition(fieldId)));
1705+
}
1706+
1707+
@Override
1708+
public void exitFieldIsWithholdableProperty(FieldIsWithholdablePropertyContext ctx) {
1709+
final String fieldId = getFieldId(ctx.fieldMention());
1710+
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
1711+
final boolean isWithholdable = privacyCode != null && !privacyCode.isEmpty();
1712+
this.stack.push(this.script.getBooleanEquivalent(isWithholdable));
16561713
}
16571714

16581715
@Override
1659-
public void exitFieldIsWithheldCondition(FieldIsWithheldConditionContext ctx) {
1660-
final String fieldId = ctx.fieldMention().getText();
1716+
public void exitFieldIsDisclosedProperty(FieldIsDisclosedPropertyContext ctx) {
1717+
final String fieldId = getFieldId(ctx.fieldMention());
1718+
if (this.isFieldRepeatableFromContext(fieldId, this.efxContext.peek())) {
1719+
throw TypeMismatchException.fieldMayRepeat(fieldId, this.efxContext.symbol());
1720+
}
16611721

16621722
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
16631723
if (privacyCode == null || privacyCode.isEmpty()) {
16641724
throw InvalidUsageException.fieldNotWithholdable(fieldId);
16651725
}
16661726

1667-
// "is withheld" = "was withheld" AND "still withheld"
1668-
BooleanExpression result = this.script.composeLogicalAnd(
1669-
this.composeWasWithheldCondition(fieldId, privacyCode),
1670-
this.composeStillWithheldCondition(fieldId));
1671-
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
1672-
result = this.script.composeLogicalNot(result);
1673-
}
1674-
this.stack.push(result);
1727+
// "isDisclosed" = "was withheld" AND NOT "still withheld" AND NOT "masked"
1728+
this.stack.push(this.script.composeLogicalAnd(
1729+
this.script.composeLogicalAnd(
1730+
this.composeWasWithheldCondition(fieldId, privacyCode),
1731+
this.script.composeLogicalNot(this.composeStillWithheldCondition(fieldId))),
1732+
this.script.composeLogicalNot(this.composeIsMaskedCondition(fieldId))));
16751733
}
16761734

16771735
@Override
1678-
public void exitFieldIsDisclosedCondition(FieldIsDisclosedConditionContext ctx) {
1679-
final String fieldId = ctx.fieldMention().getText();
1736+
public void exitFieldIsMaskedProperty(FieldIsMaskedPropertyContext ctx) {
1737+
final String fieldId = getFieldId(ctx.fieldMention());
1738+
if (this.isFieldRepeatableFromContext(fieldId, this.efxContext.peek())) {
1739+
throw TypeMismatchException.fieldMayRepeat(fieldId, this.efxContext.symbol());
1740+
}
16801741

16811742
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
16821743
if (privacyCode == null || privacyCode.isEmpty()) {
16831744
throw InvalidUsageException.fieldNotWithholdable(fieldId);
16841745
}
16851746

1686-
// "is disclosed" = "was withheld" AND NOT "still withheld" AND NOT "masked"
1687-
BooleanExpression result = this.script.composeLogicalAnd(
1688-
this.script.composeLogicalAnd(
1689-
this.composeWasWithheldCondition(fieldId, privacyCode),
1690-
this.script.composeLogicalNot(this.composeStillWithheldCondition(fieldId))),
1691-
this.composeNotMaskedCondition(fieldId));
1692-
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
1693-
result = this.script.composeLogicalNot(result);
1747+
// "isMasked" = was withheld AND field value equals the privacy mask
1748+
this.stack.push(this.script.composeLogicalAnd(
1749+
this.composeWasWithheldCondition(fieldId, privacyCode),
1750+
this.composeIsMaskedCondition(fieldId)));
1751+
}
1752+
1753+
@Override
1754+
public void exitFieldPrivacyCodeProperty(FieldPrivacyCodePropertyContext ctx) {
1755+
final String fieldId = getFieldId(ctx.fieldMention());
1756+
final String privacyCode = this.symbols.getPrivacyCodeOfField(fieldId);
1757+
if (privacyCode == null || privacyCode.isEmpty()) {
1758+
throw InvalidUsageException.fieldNotWithholdable(fieldId);
16941759
}
1695-
this.stack.push(result);
1760+
this.stack.push(this.script.getStringLiteralFromUnquotedString(privacyCode));
1761+
}
1762+
1763+
private boolean isFieldRepeatableFromContext(String fieldId, Context context) {
1764+
String contextNodeId = context.isFieldContext()
1765+
? this.symbols.getParentNodeOfField(context.symbol())
1766+
: context.symbol();
1767+
return this.symbols.isFieldRepeatableFromContext(fieldId, contextNodeId);
16961768
}
16971769

16981770
private BooleanExpression composeWasWithheldCondition(String fieldId, String privacyCode) {
@@ -1726,13 +1798,31 @@ private BooleanExpression composeStillWithheldCondition(String fieldId) {
17261798
BooleanExpression.class);
17271799
}
17281800

1729-
private BooleanExpression composeNotMaskedCondition(String fieldId) {
1801+
private BooleanExpression composeIsMaskedCondition(String fieldId) {
17301802
final String maskingValue = this.symbols.getPrivacyMask(fieldId);
1731-
final PathExpression fieldPath = this.symbols.getRelativePathOfField(fieldId, this.efxContext.symbol());
1803+
final PathExpression fieldValue = this.script.composeFieldValueReference(this.symbols.getRelativePathOfField(fieldId, this.efxContext.symbol()));
1804+
1805+
if (!(fieldValue instanceof ScalarExpression)) {
1806+
throw TypeMismatchException.fieldMayRepeat(fieldId, this.efxContext.symbol());
1807+
}
1808+
17321809
return this.script.composeComparisonOperation(
1733-
new StringExpression(this.script.composeFieldValueReference(fieldPath).getScript()),
1734-
"!=",
1735-
this.script.getStringLiteralFromUnquotedString(maskingValue));
1810+
TypedExpression.from(fieldValue, ScalarExpression.class),
1811+
"==",
1812+
this.getTypedLiteralFromUnquotedString(maskingValue, fieldValue.getDataType()));
1813+
}
1814+
1815+
private ScalarExpression getTypedLiteralFromUnquotedString(String value, Class<? extends EfxDataType> type) {
1816+
if (EfxDataType.Number.class.isAssignableFrom(type)) {
1817+
return this.script.getNumericLiteralEquivalent(value);
1818+
}
1819+
if (EfxDataType.Date.class.isAssignableFrom(type)) {
1820+
return this.script.getDateLiteralEquivalent(value);
1821+
}
1822+
if (EfxDataType.Time.class.isAssignableFrom(type)) {
1823+
return this.script.getTimeLiteralEquivalent(value);
1824+
}
1825+
return this.script.getStringLiteralFromUnquotedString(value);
17361826
}
17371827

17381828
// #endregion Privacy settings ---------------------------------------------

0 commit comments

Comments
 (0)