Skip to content

Commit 40c9dce

Browse files
committed
Separate Fix expression interpretation from record data transformation.
Everything that is data-independent should be done only once at initialization time. IOW, perform as little work as possible per record.
1 parent f631e84 commit 40c9dce

File tree

3 files changed

+149
-78
lines changed

3 files changed

+149
-78
lines changed

metafix/src/main/java/org/metafacture/metafix/RecordTransformer.java

Lines changed: 90 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
import org.slf4j.LoggerFactory;
4040

4141
import java.util.LinkedHashMap;
42+
import java.util.LinkedList;
4243
import java.util.List;
4344
import java.util.Map;
44-
import java.util.concurrent.atomic.AtomicBoolean;
4545
import java.util.function.Consumer;
4646
import java.util.function.Function;
4747
import java.util.function.Supplier;
@@ -57,126 +57,138 @@ public class RecordTransformer { // checkstyle-disable-line ClassFanOutComplexit
5757

5858
private static final Logger LOG = LoggerFactory.getLogger(RecordTransformer.class);
5959

60-
private final List<Expression> expressions;
60+
private final List<Consumer<Record>> consumers = new LinkedList<>();
6161
private final Map<String, String> vars;
6262
private final Metafix metafix;
6363

64-
private Record record;
64+
private Supplier<String> currentMessageSupplier;
6565

6666
/*package-private*/ RecordTransformer(final Metafix metafix, final Fix fix) {
6767
this(metafix, fix.getElements());
6868
}
6969

7070
private RecordTransformer(final Metafix metafix, final List<Expression> expressions) {
71-
this.expressions = expressions;
7271
this.metafix = metafix;
7372
vars = metafix.getVars();
74-
}
75-
76-
public void transform(final Record currentRecord) {
77-
this.record = currentRecord;
78-
process(expressions);
79-
}
80-
81-
private void process(final List<Expression> currentExpressions) {
82-
currentExpressions.forEach(e -> {
83-
final List<String> params = resolveParams(e.getParams());
8473

74+
expressions.forEach(e -> {
8575
if (e instanceof Do) {
86-
processDo((Do) e, params);
76+
processDo((Do) e);
8777
}
8878
else if (e instanceof If) {
89-
processIf((If) e, params);
79+
processIf((If) e);
9080
}
9181
else if (e instanceof Unless) {
92-
processUnless((Unless) e, params);
82+
processUnless((Unless) e);
9383
}
9484
else if (e instanceof MethodCall) {
95-
processFunction((MethodCall) e, params);
85+
processFunction((MethodCall) e);
9686
}
9787
else {
9888
throw new FixProcessException(executionExceptionMessage(e));
9989
}
10090
});
10191
}
10292

103-
private void processDo(final Do expression, final List<String> params) {
104-
processExpression(expression, name -> {
105-
final FixContext context = getInstance(name, FixContext.class, FixBind::valueOf);
106-
context.execute(metafix, record, params, options(expression.getOptions()), new RecordTransformer(metafix, expression.getElements()));
107-
});
93+
public void transform(final Record record) {
94+
consumers.forEach(consumer -> {
95+
final FixExecutionException exception = tryRun(() -> consumer.accept(record));
10896

109-
// TODO, possibly: use morph collectors here
110-
// final CollectFactory collectFactory = new CollectFactory();
111-
// final Map<String, String> attributes = resolvedAttributeMap(params, expression.getOptions());
112-
// final Collect collect = collectFactory.newInstance(expression.getName(), attributes);
97+
if (exception != null) {
98+
metafix.getStrictness().handle(exception, record);
99+
}
100+
});
113101
}
114102

115-
private void processIf(final If expression, final List<String> params) {
116-
final ElsIf elseIfExpression = expression.getElseIf();
117-
final Else elseExpression = expression.getElse();
103+
private void processDo(final Do expression) {
104+
processFix(() -> executionExceptionMessage(expression), () -> {
105+
final FixContext context = getInstance(expression.getName(), FixContext.class, FixBind::valueOf);
106+
final RecordTransformer recordTransformer = new RecordTransformer(metafix, expression.getElements());
118107

119-
if (testConditional(expression, expression.eResource(), expression.getName(), params)) {
120-
process(expression.getElements());
121-
}
122-
else if (elseIfExpression != null && testConditional(elseIfExpression,
123-
elseIfExpression.eResource(), elseIfExpression.getName(), resolveParams(elseIfExpression.getParams()))) {
124-
process(elseIfExpression.getElements());
125-
}
126-
else if (elseExpression != null) {
127-
process(elseExpression.getElements());
128-
}
108+
return record -> context.execute(metafix, record, params(expression.getParams()), options(expression.getOptions()), recordTransformer);
109+
});
129110
}
130111

131-
private void processUnless(final Unless expression, final List<String> params) {
132-
if (!testConditional(expression, expression.eResource(), expression.getName(), params)) {
133-
process(expression.getElements());
134-
}
112+
private void processIf(final If ifExpression) {
113+
final ElsIf elseIfExpression = ifExpression.getElseIf();
114+
final Else elseExpression = ifExpression.getElse();
115+
116+
final Supplier<String> elseIfMessageSupplier = () -> executionExceptionMessage(elseIfExpression, elseIfExpression.eResource());
117+
final Supplier<String> elseMessageSupplier = () -> executionExceptionMessage(elseExpression, elseExpression.eResource());
118+
119+
processFix(() -> executionExceptionMessage(ifExpression, ifExpression.eResource()), () -> {
120+
final FixPredicate ifPredicate = getInstance(ifExpression.getName(), FixPredicate.class, FixConditional::valueOf);
121+
final FixPredicate elseIfPredicate = elseIfExpression != null ? getInstance(elseIfExpression.getName(), FixPredicate.class, FixConditional::valueOf) : null;
122+
123+
final RecordTransformer ifTransformer = new RecordTransformer(metafix, ifExpression.getElements());
124+
final RecordTransformer elseIfTransformer = elseIfExpression != null ? new RecordTransformer(metafix, elseIfExpression.getElements()) : null;
125+
final RecordTransformer elseTransformer = elseExpression != null ? new RecordTransformer(metafix, elseExpression.getElements()) : null;
126+
127+
return record -> {
128+
if (ifPredicate.test(metafix, record, params(ifExpression.getParams()), options(null))) { // TODO: options
129+
ifTransformer.transform(record);
130+
}
131+
else {
132+
if (elseIfExpression != null) {
133+
currentMessageSupplier = elseIfMessageSupplier;
134+
135+
if (elseIfPredicate.test(metafix, record, params(elseIfExpression.getParams()), options(null))) { // TODO: options
136+
elseIfTransformer.transform(record);
137+
return;
138+
}
139+
}
140+
141+
if (elseExpression != null) {
142+
currentMessageSupplier = elseMessageSupplier;
143+
elseTransformer.transform(record);
144+
}
145+
}
146+
};
147+
});
135148
}
136149

137-
private boolean testConditional(final EObject object, final Resource resource, final String conditional, final List<String> params) {
138-
LOG.debug("<IF>: {} parameters: {}", conditional, params);
139-
140-
final AtomicBoolean bool = new AtomicBoolean();
150+
private void processUnless(final Unless expression) {
151+
processFix(() -> executionExceptionMessage(expression, expression.eResource()), () -> {
152+
final FixPredicate predicate = getInstance(expression.getName(), FixPredicate.class, FixConditional::valueOf);
153+
final RecordTransformer recordTransformer = new RecordTransformer(metafix, expression.getElements());
141154

142-
processFix(() -> executionExceptionMessage(object, resource), () -> {
143-
final FixPredicate predicate = getInstance(conditional, FixPredicate.class, FixConditional::valueOf);
144-
bool.set(predicate.test(metafix, record, params, options(null))); // TODO: options
155+
return record -> {
156+
if (!predicate.test(metafix, record, params(expression.getParams()), options(null))) { // TODO: options
157+
recordTransformer.transform(record);
158+
}
159+
};
145160
});
146-
147-
return bool.get();
148-
149-
// TODO, possibly: use morph functions here (& in processFunction):
150-
// final FunctionFactory functionFactory = new FunctionFactory();
151-
// functionFactory.registerClass("not_equals", NotEquals.class);
152-
// functionFactory.registerClass("replace_all", Replace.class);
153-
// final Function function = functionFactory.newInstance(conditional,
154-
// resolvedAttributeMap(params, theIf.getOptions()));
155161
}
156162

157-
private void processFunction(final MethodCall expression, final List<String> params) {
158-
processExpression(expression, name -> {
159-
final FixFunction function = getInstance(name, FixFunction.class, FixMethod::valueOf);
160-
function.apply(metafix, record, params, options(expression.getOptions()));
163+
private void processFunction(final MethodCall expression) {
164+
processFix(() -> executionExceptionMessage(expression), () -> {
165+
final FixFunction function = getInstance(expression.getName(), FixFunction.class, FixMethod::valueOf);
166+
return record -> function.apply(metafix, record, params(expression.getParams()), options(expression.getOptions()));
161167
});
162168
}
163169

164170
private <T> T getInstance(final String name, final Class<T> baseType, final Function<String, ? extends T> enumFunction) {
165171
return name.contains(".") ? ReflectionUtil.loadClass(name, baseType).newInstance() : enumFunction.apply(name);
166172
}
167173

168-
private void processExpression(final Expression expression, final Consumer<String> consumer) {
169-
processFix(() -> executionExceptionMessage(expression), () -> consumer.accept(expression.getName()));
170-
}
174+
private void processFix(final Supplier<String> messageSupplier, final Supplier<Consumer<Record>> consumerSupplier) {
175+
currentMessageSupplier = messageSupplier;
176+
177+
final FixExecutionException exception = tryRun(() -> {
178+
final Consumer<Record> consumer = consumerSupplier.get();
179+
180+
consumers.add(record -> {
181+
currentMessageSupplier = messageSupplier;
182+
consumer.accept(record);
183+
});
184+
});
171185

172-
private void processFix(final Supplier<String> messageSupplier, final Runnable runnable) {
173-
final FixExecutionException exception = tryRun(messageSupplier, runnable);
174186
if (exception != null) {
175-
metafix.getStrictness().handle(exception, record);
187+
throw exception;
176188
}
177189
}
178190

179-
private FixExecutionException tryRun(final Supplier<String> messageSupplier, final Runnable runnable) { // checkstyle-disable-line ReturnCount
191+
private FixExecutionException tryRun(final Runnable runnable) { // checkstyle-disable-line ReturnCount
180192
try {
181193
runnable.run();
182194
}
@@ -187,11 +199,12 @@ private FixExecutionException tryRun(final Supplier<String> messageSupplier, fin
187199
return e; // TODO: Add nesting information?
188200
}
189201
catch (final IllegalStateException | NumberFormatException e) {
190-
return new FixExecutionException(messageSupplier.get(), e);
202+
return new FixExecutionException(currentMessageSupplier.get(), e);
191203
}
192204
catch (final RuntimeException e) { // checkstyle-disable-line IllegalCatch
193-
throw new FixProcessException(messageSupplier.get(), e);
205+
throw new FixProcessException(currentMessageSupplier.get(), e);
194206
}
207+
195208
return null;
196209
}
197210

@@ -206,14 +219,14 @@ private String executionExceptionMessage(final EObject object, final Resource re
206219
resource.getURI(), node.getStartLine(), NodeModelUtils.getTokenText(node));
207220
}
208221

209-
private List<String> resolveParams(final List<String> params) {
210-
return params.stream().map(this::resolveVars).collect(Collectors.toList());
211-
}
212-
213222
private String resolveVars(final String value) {
214223
return value == null ? null : StringUtil.format(value, Metafix.VAR_START, Metafix.VAR_END, false, vars);
215224
}
216225

226+
private List<String> params(final List<String> params) {
227+
return params.stream().map(this::resolveVars).collect(Collectors.toList());
228+
}
229+
217230
private Map<String, String> options(final Options options) {
218231
final Map<String, String> map = new LinkedHashMap<>();
219232

metafix/src/test/java/org/metafacture/metafix/MetafixIfTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,4 +1012,61 @@ public void shouldApplyCustomJavaPredicate() {
10121012
);
10131013
}
10141014

1015+
@Test
1016+
public void shouldIncludeLocationAndTextInProcessExceptionInConditional() {
1017+
final String text1 = "elsif exists()";
1018+
final String text2 = "nothing()";
1019+
final String message = "Error while executing Fix expression (at FILE, line 3): " + text1 + " " + text2;
1020+
1021+
MetafixTestHelpers.assertThrows(FixProcessException.class, s -> s.replaceAll("file:/.+?\\.fix", "FILE"), message, () ->
1022+
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
1023+
"if exists('animal')",
1024+
"nothing()",
1025+
text1,
1026+
text2,
1027+
"end"
1028+
),
1029+
i -> {
1030+
i.startRecord("1");
1031+
i.startEntity("animals");
1032+
i.literal("1", "dog");
1033+
i.literal("2", "cat");
1034+
i.literal("3", "zebra");
1035+
i.endEntity();
1036+
i.endRecord();
1037+
},
1038+
o -> {
1039+
}
1040+
)
1041+
);
1042+
}
1043+
1044+
@Test
1045+
public void shouldIncludeLocationAndTextInProcessExceptionInBody() {
1046+
final String text = "add_field()";
1047+
final String message = "Error while executing Fix expression (at FILE, line 4): " + text;
1048+
1049+
MetafixTestHelpers.assertThrows(FixProcessException.class, s -> s.replaceAll("file:/.+?\\.fix", "FILE"), message, () ->
1050+
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
1051+
"if exists('animal')",
1052+
"nothing()",
1053+
"elsif exists('animals')",
1054+
text,
1055+
"end"
1056+
),
1057+
i -> {
1058+
i.startRecord("1");
1059+
i.startEntity("animals");
1060+
i.literal("1", "dog");
1061+
i.literal("2", "cat");
1062+
i.literal("3", "zebra");
1063+
i.endEntity();
1064+
i.endRecord();
1065+
},
1066+
o -> {
1067+
}
1068+
)
1069+
);
1070+
}
1071+
10151072
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
^Exception in thread "main" org\.metafacture\.metafix\.FixProcessException: Error while executing Fix expression \(at file:.*/metafix/src/test/resources/org/metafacture/metafix/integration/script/fromJson/toJson/strictnessAbortProcessOnProcessException/test\.fix, line 2\): foo\(\)$
1+
^Exception in thread "main" org\.metafacture\.commons\.reflection\.ReflectionException: class could not be instantiated: class org\.metafacture\.metafix\.Metafix$
2+
^Caused by: org\.metafacture\.metafix\.FixProcessException: Error while executing Fix expression \(at file:.*/metafix/src/test/resources/org/metafacture/metafix/integration/script/fromJson/toJson/strictnessAbortProcessOnProcessException/test\.fix, line 2\): foo\(\)$
23
^Caused by: java\.lang\.IllegalArgumentException: No enum constant org\.metafacture\.metafix\.FixMethod.foo$

0 commit comments

Comments
 (0)