2020import org .metafacture .fix .fix .Do ;
2121import org .metafacture .fix .fix .Expression ;
2222import org .metafacture .fix .fix .Fix ;
23+ import org .metafacture .fix .fix .If ;
2324import org .metafacture .fix .fix .MethodCall ;
2425import org .metafacture .fix .fix .Options ;
2526import org .metafacture .metamorph .api .Collect ;
27+ import org .metafacture .metamorph .api .ConditionAware ;
2628import org .metafacture .metamorph .api .FlushListener ;
2729import org .metafacture .metamorph .api .Function ;
2830import org .metafacture .metamorph .api .InterceptorFactory ;
3133import org .metafacture .metamorph .functions .NotEquals ;
3234import org .metafacture .metamorph .functions .Replace ;
3335
36+ import com .google .common .collect .Multimap ;
3437import org .eclipse .emf .common .util .EList ;
3538import org .eclipse .xtext .xbase .lib .Pair ;
3639
@@ -54,6 +57,7 @@ public class FixBuilder { // checkstyle-disable-line ClassDataAbstractionCouplin
5457 static final String ARRAY_MARKER = "[]" ;
5558 private static final String FLUSH_WITH = "flushWith" ;
5659 private static final String RECORD = "record" ;
60+
5761 private final Deque <StackFrame > stack = new LinkedList <>();
5862 private final InterceptorFactory interceptorFactory ;
5963 private final Metafix metafix ;
@@ -177,10 +181,12 @@ protected void exitCollectorAndFlushWith(final String flushWith) {
177181 if (parent .isInEntity ()) {
178182 ((Entity ) parent .getPipe ()).setNameSource (delegate );
179183 }
180- // TODO: condition handling, see MorphBuilder
181-
182- parent .getPipe ().addNamedValueSource (delegate );
183-
184+ else if (parent .isInCondition ()) {
185+ ((ConditionAware ) parent .getPipe ()).setConditionSource (delegate );
186+ }
187+ else {
188+ parent .getPipe ().addNamedValueSource (delegate );
189+ }
184190 final Collect collector = (Collect ) tailPipe ;
185191 if (null != flushWith ) {
186192 collector .setWaitForFlush (true );
@@ -218,19 +224,72 @@ private void processSubexpressions(final List<Expression> expressions, final Str
218224 if (sub instanceof Do ) {
219225 processBind (sub , p );
220226 }
227+ else if (sub instanceof If ) {
228+ processConditional (sub , p );
229+ }
221230 else {
222231 processFunction (sub , p , source );
223232 }
224233 }
225234 }
226235
236+ private void processConditional (final Expression expression , final EList <String > p ) {
237+ final boolean isTopLevelIf = stack .peek ().pipe instanceof Metafix ;
238+ // If we're at the top level, we wrap the If into a collector:
239+ if (isTopLevelIf ) {
240+ final Collect collect = collectFactory .newInstance ("choose" );
241+ stack .push (new StackFrame (collect ));
242+ }
243+ enterIf ();
244+ final If theIf = (If ) expression ;
245+ enterDataMap (p , false );
246+ final Map <String , String > attributes = resolvedAttributeMap (p , null );
247+ attributes .put ("string" , resolvedAttribute (p , 2 )); // for contains & equals morph functions
248+ // TODO: support morph functions: regexp -> match, morph quantors as prefixes: none_, all_, any_
249+ runMetamorphFunction (expression .getName (), attributes );
250+ exitData ();
251+ // The Metamorph IF acts like a guard, executing not nested statements, but following statements:
252+ exitIf ();
253+ processSubexpressions (theIf .getElements (), resolvedAttribute (p , 1 ));
254+ // As a draft for a record mode, we might do something like this instead:
255+ if (metafix .isRecordMode () && testConditional (theIf .getName (), p )) {
256+ processSubexpressions (theIf .getElements (), resolvedAttribute (p , 1 ));
257+ }
258+ // If we're at the top level, we close the wrapping collector:
259+ if (isTopLevelIf ) {
260+ exitCollectorAndFlushWith (RECORD );
261+ }
262+ }
263+
264+ private boolean testConditional (final String conditional , final EList <String > p ) {
265+ System .out .printf ("<IF>: %s p: %s\n " , conditional , p );
266+ boolean result = false ;
267+ final String field = resolvedAttribute (p , 1 );
268+ final String value = resolvedAttribute (p , 2 );
269+ final Multimap <String , String > map = metafix .getCurrentRecord ();
270+ System .out .printf ("<%s>: field: %s value: %s in: %s\n " , conditional , field , value , map );
271+ switch (conditional ) {
272+ case "any_match" :
273+ result = map .containsKey (field ) && map .get (field ).stream ().anyMatch (v -> v .matches (value ));
274+ break ;
275+ case "all_match" :
276+ result = map .containsKey (field ) && map .get (field ).stream ().allMatch (v -> v .matches (value ));
277+ break ;
278+ default :
279+ }
280+ return result ;
281+ }
282+
227283 private void processFunction (final Expression expression , final List <String > params , final String source ) {
228284 final FixFunction functionToRun = findFixFunction (expression );
229285 if (functionToRun != null ) {
286+ System .out .printf ("Running Fix function %s, params %s, source %s\n " , expression .getName (), params , source );
230287 functionToRun .apply (this , expression , params , source );
231288 }
232289 else {
233- runMetamorphFunction (expression , params );
290+ final Options options = ((MethodCall ) expression ).getOptions ();
291+ final Map <String , String > attributes = resolvedAttributeMap (params , options );
292+ runMetamorphFunction (expression .getName (), attributes );
234293 }
235294 }
236295
@@ -243,15 +302,15 @@ private FixFunction findFixFunction(final Expression expression) {
243302 return null ;
244303 }
245304
246- private void runMetamorphFunction (final Expression expression , final List <String > params ) {
247- final Map <String , String > attributes = resolvedAttributeMap (params , ((MethodCall ) expression ).getOptions ());
248- if (functionFactory .containsKey (expression .getName ())) {
305+ private void runMetamorphFunction (final String name , final Map <String , String > attributes ) {
306+ if (functionFactory .containsKey (name )) {
249307 final String flushWith = attributes .remove (FLUSH_WITH );
250- final Function function = functionFactory .newInstance (expression . getName () , attributes );
308+ final Function function = functionFactory .newInstance (name , attributes );
251309 if (null != flushWith ) {
252310 registerFlush (flushWith , function );
253311 }
254312 function .setMaps (metafix );
313+
255314 final StackFrame head = stack .peek ();
256315 final NamedValuePipe interceptor = interceptorFactory .createNamedValueInterceptor ();
257316 final NamedValuePipe delegate ;
@@ -266,7 +325,7 @@ private void runMetamorphFunction(final Expression expression, final List<String
266325 head .setPipe (function );
267326 }
268327 else {
269- throw new IllegalArgumentException (expression . getName () + " not found" );
328+ throw new IllegalArgumentException (name + " not found" );
270329 }
271330 }
272331
@@ -288,10 +347,12 @@ void exitData() {
288347 if (parent .isInEntity ()) {
289348 ((Entity ) parent .getPipe ()).setNameSource (delegate );
290349 }
291-
292- // TODO: condition handling, see MorphBuilder
293-
294- parent .getPipe ().addNamedValueSource (delegate );
350+ else if (parent .isInCondition ()) {
351+ ((ConditionAware ) parent .getPipe ()).setConditionSource (delegate );
352+ }
353+ else {
354+ parent .getPipe ().addNamedValueSource (delegate );
355+ }
295356 }
296357
297358 NamedValuePipe enterDataMap (final List <String > params , final boolean standalone ) {
@@ -356,6 +417,17 @@ void enterDataFunction(final String fieldName, final NamedValuePipe function, fi
356417 head .setPipe (function );
357418 }
358419
420+ void enterIf () {
421+ assert stack .peek ().getPipe () instanceof ConditionAware :
422+ "statement `if` is not allowed in the current element" ;
423+
424+ stack .peek ().setInCondition (true );
425+ }
426+
427+ void exitIf () {
428+ stack .peek ().setInCondition (false );
429+ }
430+
359431 private void addNestedField (final List <String > params , final String resolvedAttribute ) {
360432 final String [] keyElements = resolvedAttribute .split ("\\ ." );
361433 final Pair <Entity , Entity > firstAndLast = createEntities (keyElements );
@@ -418,10 +490,20 @@ private static class StackFrame {
418490
419491 private boolean inEntity ;
420492
493+ private boolean inCondition ;
494+
421495 private StackFrame (final NamedValuePipe pipe ) {
422496 this .pipe = pipe ;
423497 }
424498
499+ public void setInCondition (final boolean inCondition ) {
500+ this .inCondition = inCondition ;
501+ }
502+
503+ public boolean isInCondition () {
504+ return inCondition ;
505+ }
506+
425507 public NamedValuePipe getPipe () {
426508 return pipe ;
427509 }
0 commit comments