Skip to content

Commit 0e27728

Browse files
authored
Merge pull request #243 from metafacture/addMacroBindAndFunction
Add `put_macro()`/`call_macro()` Fix bind and function.
2 parents bec6763 + 62e4edd commit 0e27728

File tree

6 files changed

+297
-23
lines changed

6 files changed

+297
-23
lines changed

README.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,15 @@ put_map("<mapName>",
192192

193193
##### `put_var`
194194

195-
Defines a single internal variable that can be referenced with `$[<variableName>]`.
195+
Defines a single global variable that can be referenced with `$[<variableName>]`.
196196

197197
```perl
198198
put_var("<variableName>", "<variableValue>")
199199
```
200200

201201
##### `put_vars`
202202

203-
Defines multiple internal variables that can be referenced with `$[<variableName>]`.
203+
Defines multiple global variables that can be referenced with `$[<variableName>]`.
204204

205205
```perl
206206
put_vars(
@@ -234,6 +234,25 @@ array("foo")
234234
# {"name":"value"} => ["name", "value"]
235235
```
236236

237+
##### `call_macro`
238+
239+
Calls a named macro, i.e. a list of statements that have been previously defined with the [`do put_macro`](#do-put_macro) bind.
240+
241+
Parameters:
242+
243+
- `name` (required): Unique name of the macro.
244+
245+
Options:
246+
247+
- All options are made available as "dynamic" local variables in the macro.
248+
249+
```perl
250+
do put_macro("<macroName>"[, <staticLocalVariables>...])
251+
...
252+
end
253+
call_macro("<macroName>"[, <dynamicLocalVariables>...])
254+
```
255+
237256
##### `copy_field`
238257

239258
Copies (or appends to) a field from an existing field.
@@ -634,6 +653,31 @@ do once("vars setup")
634653
end
635654
```
636655

656+
#### `do put_macro`
657+
658+
Defines a named macro, i.e. a list of statements that can be executed later with the [`call_macro`](#call_macro) function.
659+
660+
Variables can be referenced with `$[<variableName>]`, in the following order of precedence:
661+
662+
1. "dynamic" local variables, passed as options to the `call_macro` function;
663+
2. "static" local variables, passed as options to the `do put_macro` bind;
664+
3. global variables, defined via [`put_var`](#put_var)/[`put_vars`](#put_vars).
665+
666+
Parameters:
667+
668+
- `name` (required): Unique name of the macro.
669+
670+
Options:
671+
672+
- All options are made available as "static" local variables in the macro.
673+
674+
```perl
675+
do put_macro("<macroName>"[, <staticLocalVariables>...])
676+
...
677+
end
678+
call_macro("<macroName>"[, <dynamicLocalVariables>...])
679+
```
680+
637681
### Conditionals
638682

639683
Conditionals start with `if` in case of affirming the condition or `unless` rejecting the condition.

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,22 @@ public void execute(final Metafix metafix, final Record record, final List<Strin
6161
},
6262

6363
once {
64-
private Map<Metafix, Set<String>> executed = new HashMap<>();
64+
private final Map<Metafix, Set<String>> executed = new HashMap<>();
6565

6666
@Override
6767
public void execute(final Metafix metafix, final Record record, final List<String> params, final Map<String, String> options, final RecordTransformer recordTransformer) {
6868
if (executed.computeIfAbsent(metafix, k -> new HashSet<>()).add(params.isEmpty() ? null : params.get(0))) {
6969
recordTransformer.transform(record);
7070
}
7171
}
72+
},
73+
74+
put_macro {
75+
@Override
76+
public void execute(final Metafix metafix, final Record record, final List<String> params, final Map<String, String> options, final RecordTransformer recordTransformer) {
77+
recordTransformer.addVars(options);
78+
metafix.putMacro(params.get(0), recordTransformer);
79+
}
7280
}
7381

7482
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ public void apply(final Metafix metafix, final Record record, final List<String>
120120
})));
121121
}
122122
},
123+
call_macro {
124+
@Override
125+
public void apply(final Metafix metafix, final Record record, final List<String> params, final Map<String, String> options) {
126+
final String macroName = params.get(0);
127+
final RecordTransformer recordTransformer = metafix.getMacro(macroName);
128+
129+
if (recordTransformer != null) {
130+
recordTransformer.transform(record, options);
131+
}
132+
else {
133+
throw new IllegalArgumentException("Macro '" + macroName + "' undefined!");
134+
}
135+
}
136+
},
123137
copy_field {
124138
@Override
125139
public void apply(final Metafix metafix, final Record record, final List<String> params, final Map<String, String> options) {

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ public class Metafix implements StreamPipe<StreamReceiver>, Maps {
7272
private final Deque<Integer> entityCountStack = new LinkedList<>();
7373
private final List<Closeable> resources = new ArrayList<>();
7474
private final List<Expression> expressions = new ArrayList<>();
75-
private final Map<String, RecordTransformer> fixCache = new HashMap<>();
7675
private final Map<String, Map<String, String>> maps = new HashMap<>();
76+
private final Map<String, RecordTransformer> fixCache = new HashMap<>();
77+
private final Map<String, RecordTransformer> macros = new HashMap<>();
7778
private final Map<String, String> vars = new HashMap<>();
7879
private final RecordTransformer recordTransformer;
7980
private final StreamFlattener flattener = new StreamFlattener();
@@ -163,6 +164,14 @@ private RecordTransformer getRecordTransformer(final Reader fixDef) {
163164
return new RecordTransformer(this, FixStandaloneSetup.parseFix(fixDef));
164165
}
165166

167+
public void putMacro(final String name, final RecordTransformer macro) {
168+
macros.put(name, macro);
169+
}
170+
171+
public RecordTransformer getMacro(final String name) {
172+
return macros.get(name);
173+
}
174+
166175
public List<Expression> getExpressions() {
167176
return expressions;
168177
}

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

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.slf4j.LoggerFactory;
3939

4040
import java.util.ArrayList;
41+
import java.util.HashMap;
4142
import java.util.LinkedHashMap;
4243
import java.util.LinkedList;
4344
import java.util.List;
@@ -47,6 +48,8 @@
4748
import java.util.function.Supplier;
4849
import java.util.stream.Collectors;
4950

51+
// TODO: Utilize org.metafacture.commons.types.ScopedHashMap for vars instead?
52+
5053
/**
5154
* Transform a record using a {@link Fix}.
5255
*
@@ -58,22 +61,24 @@ public class RecordTransformer { // checkstyle-disable-line ClassFanOutComplexit
5861
private static final Logger LOG = LoggerFactory.getLogger(RecordTransformer.class);
5962

6063
private final List<Consumer<Record>> consumers = new LinkedList<>();
61-
private final Map<String, String> vars;
64+
private final List<Map<String, String>> vars = new ArrayList<>();
6265
private final Metafix metafix;
66+
private final RecordTransformer parent;
6367

6468
private Supplier<String> currentMessageSupplier;
6569

6670
/*package-private*/ RecordTransformer(final Metafix metafix, final Fix fix) {
67-
this(metafix, fix.getElements());
71+
this(metafix, fix.getElements(), null);
72+
addVars(metafix.getVars());
6873
}
6974

70-
private RecordTransformer(final Metafix metafix, final List<Expression> expressions) {
75+
private RecordTransformer(final Metafix metafix, final List<Expression> expressions, final RecordTransformer parent) {
7176
this.metafix = metafix;
72-
vars = metafix.getVars();
77+
this.parent = parent;
7378

7479
expressions.forEach(e -> {
75-
final Params params = new Params(e.getParams(), vars);
76-
final Options options = new Options(e.getOptions(), vars);
80+
final Params params = new Params(e.getParams(), this);
81+
final Options options = new Options(e.getOptions(), this);
7782

7883
if (e instanceof Do) {
7984
processDo((Do) e, params, options);
@@ -93,6 +98,26 @@ else if (e instanceof MethodCall) {
9398
});
9499
}
95100

101+
private RecordTransformer childTransformer(final List<Expression> expressions) {
102+
return new RecordTransformer(metafix, expressions, this);
103+
}
104+
105+
public void addVars(final Map<String, String> additionalVars) {
106+
vars.add(additionalVars);
107+
}
108+
109+
public void transform(final Record record, final Map<String, String> additionalVars) {
110+
final int index = vars.size();
111+
addVars(additionalVars);
112+
113+
try {
114+
transform(record);
115+
}
116+
finally {
117+
vars.remove(index);
118+
}
119+
}
120+
96121
public void transform(final Record record) {
97122
consumers.forEach(consumer -> {
98123
final FixExecutionException exception = tryRun(() -> consumer.accept(record));
@@ -106,7 +131,7 @@ public void transform(final Record record) {
106131
private void processDo(final Do expression, final Params params, final Options options) {
107132
processFix(() -> executionExceptionMessage(expression), () -> {
108133
final FixContext context = getInstance(expression.getName(), FixContext.class, FixBind::valueOf);
109-
final RecordTransformer recordTransformer = new RecordTransformer(metafix, expression.getElements());
134+
final RecordTransformer recordTransformer = childTransformer(expression.getElements());
110135

111136
return record -> context.execute(metafix, record, params.resolve(), options.resolve(), recordTransformer);
112137
});
@@ -121,14 +146,14 @@ private void processIf(final If ifExpression, final Params ifParams, final Optio
121146

122147
processFix(() -> executionExceptionMessage(ifExpression, ifExpression.eResource()), () -> {
123148
final FixPredicate ifPredicate = getInstance(ifExpression.getName(), FixPredicate.class, FixConditional::valueOf);
124-
final RecordTransformer ifTransformer = new RecordTransformer(metafix, ifExpression.getElements());
149+
final RecordTransformer ifTransformer = childTransformer(ifExpression.getElements());
125150

126151
final List<FixPredicate> elseIfPredicates = mapList(elseIfExpressions, e -> getInstance(e.getName(), FixPredicate.class, FixConditional::valueOf));
127-
final List<Params> elseIfParamsList = mapList(elseIfExpressions, e -> new Params(e.getParams(), vars));
128-
final List<Options> elseIfOptionsList = mapList(elseIfExpressions, e -> new Options(e.getOptions(), vars));
129-
final List<RecordTransformer> elseIfTransformers = mapList(elseIfExpressions, e -> new RecordTransformer(metafix, e.getElements()));
152+
final List<Params> elseIfParamsList = mapList(elseIfExpressions, e -> new Params(e.getParams(), this));
153+
final List<Options> elseIfOptionsList = mapList(elseIfExpressions, e -> new Options(e.getOptions(), this));
154+
final List<RecordTransformer> elseIfTransformers = mapList(elseIfExpressions, e -> childTransformer(e.getElements()));
130155

131-
final RecordTransformer elseTransformer = elseExpression != null ? new RecordTransformer(metafix, elseExpression.getElements()) : null;
156+
final RecordTransformer elseTransformer = elseExpression != null ? childTransformer(elseExpression.getElements()) : null;
132157

133158
return record -> {
134159
if (ifPredicate.test(metafix, record, ifParams.resolve(), ifOptions.resolve())) {
@@ -163,7 +188,7 @@ private void processIf(final If ifExpression, final Params ifParams, final Optio
163188
private void processUnless(final Unless expression, final Params params, final Options options) {
164189
processFix(() -> executionExceptionMessage(expression, expression.eResource()), () -> {
165190
final FixPredicate predicate = getInstance(expression.getName(), FixPredicate.class, FixConditional::valueOf);
166-
final RecordTransformer recordTransformer = new RecordTransformer(metafix, expression.getElements());
191+
final RecordTransformer recordTransformer = childTransformer(expression.getElements());
167192

168193
return record -> {
169194
if (!predicate.test(metafix, record, params.resolve(), options.resolve())) {
@@ -236,6 +261,12 @@ private String executionExceptionMessage(final EObject object, final Resource re
236261
resource.getURI(), node.getStartLine(), NodeModelUtils.getTokenText(node));
237262
}
238263

264+
private Map<String, String> getVars() {
265+
final Map<String, String> mergedVars = parent != null ? parent.getVars() : new HashMap<>();
266+
vars.forEach(mergedVars::putAll);
267+
return mergedVars;
268+
}
269+
239270
private abstract static class AbstractResolvable<T> {
240271

241272
protected boolean isResolvable(final String value) {
@@ -253,12 +284,12 @@ protected String resolveVars(final String value, final Map<String, String> vars)
253284
private static class Params extends AbstractResolvable<List<String>> {
254285

255286
private final List<String> list;
256-
private final Map<String, String> vars;
287+
private final RecordTransformer recordTransformer;
257288
private final boolean resolve;
258289

259-
private Params(final List<String> list, final Map<String, String> vars) {
290+
private Params(final List<String> list, final RecordTransformer recordTransformer) {
260291
this.list = list;
261-
this.vars = vars;
292+
this.recordTransformer = recordTransformer;
262293

263294
resolve = list.stream().anyMatch(this::isResolvable);
264295
}
@@ -267,6 +298,7 @@ private Params(final List<String> list, final Map<String, String> vars) {
267298
protected List<String> resolve() {
268299
if (resolve) {
269300
final List<String> resolvedList = new ArrayList<>(list.size());
301+
final Map<String, String> vars = recordTransformer.getVars();
270302

271303
for (final String entry : list) {
272304
resolvedList.add(resolveVars(entry, vars));
@@ -284,11 +316,11 @@ protected List<String> resolve() {
284316
private static class Options extends AbstractResolvable<Map<String, String>> {
285317

286318
private final Map<String, String> map = new LinkedHashMap<>();
287-
private final Map<String, String> vars;
319+
private final RecordTransformer recordTransformer;
288320
private final boolean resolve;
289321

290-
private Options(final org.metafacture.metafix.fix.Options options, final Map<String, String> vars) {
291-
this.vars = vars;
322+
private Options(final org.metafacture.metafix.fix.Options options, final RecordTransformer recordTransformer) {
323+
this.recordTransformer = recordTransformer;
292324

293325
boolean resolveTemp = false;
294326

@@ -315,6 +347,7 @@ private Options(final org.metafacture.metafix.fix.Options options, final Map<Str
315347
protected Map<String, String> resolve() {
316348
if (resolve) {
317349
final Map<String, String> resolvedMap = new LinkedHashMap<>(map.size());
350+
final Map<String, String> vars = recordTransformer.getVars();
318351

319352
for (final Map.Entry<String, String> entry : map.entrySet()) {
320353
resolvedMap.put(resolveVars(entry.getKey(), vars), resolveVars(entry.getValue(), vars));

0 commit comments

Comments
 (0)