org.finra.datagenerator.engine.scxml.SCXMLFrontier.java Source code

Java tutorial

Introduction

Here is the source code for org.finra.datagenerator.engine.scxml.SCXMLFrontier.java

Source

/*
 * Copyright 2014 DataGenerator Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.finra.datagenerator.engine.scxml;

import org.apache.commons.scxml.Context;
import org.apache.commons.scxml.SCXMLExecutor;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.apache.commons.scxml.env.jsp.ELContext;
import org.apache.commons.scxml.env.jsp.ELEvaluator;
import org.apache.commons.scxml.model.Action;
import org.apache.commons.scxml.model.OnEntry;
import org.apache.commons.scxml.model.SCXML;
import org.apache.commons.scxml.model.Transition;
import org.apache.commons.scxml.model.TransitionTarget;
import org.apache.log4j.Logger;
import org.finra.datagenerator.distributor.ProcessingStrategy;
import org.finra.datagenerator.engine.Frontier;
import org.finra.datagenerator.engine.scxml.tags.CustomTagExtension;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Frontier implementation for generating data with SCXML state machine models.
 */
public class SCXMLFrontier extends SCXMLExecutor implements Frontier {

    private final PossibleState root;
    private static final Logger log = Logger.getLogger(SCXMLFrontier.class);
    private List<CustomTagExtension> tagExtensionList;

    /**
     * Constructor
     *
     * @param possibleState the root node of the model and partial variable assignment to start a dfs from
     * @param model the model text
     * @param tagExtensionList custom tags used in this model
     */
    public SCXMLFrontier(final PossibleState possibleState, final SCXML model,
            final List<CustomTagExtension> tagExtensionList) {
        root = possibleState;
        this.tagExtensionList = tagExtensionList;
        this.setStateMachine(model);

        ELEvaluator elEvaluator = new ELEvaluator();
        ELContext context = new ELContext();

        this.setEvaluator(elEvaluator);
        this.setRootContext(context);
    }

    /**
     * Constructor
     *
     * @param possibleState the root node of the model and partial variable assignment to start a dfs from
     * @param model the model text
     */
    public SCXMLFrontier(final PossibleState possibleState, final SCXML model) {
        this(possibleState, model, new LinkedList<CustomTagExtension>());
    }

    /**
     * Performs a DFS on the model, starting from root, giving results to the processingStrategy
     * Just a public wrapper for private dfs function
     *
     * @param processingStrategy the results handler
     * @param flag used to stop the search before completion
     */
    public void searchForScenarios(ProcessingStrategy processingStrategy, AtomicBoolean flag) {
        dfs(processingStrategy, flag, root);
    }

    private void dfs(ProcessingStrategy processingStrategy, AtomicBoolean flag, PossibleState state) {
        if (flag.get()) {
            return;
        }

        TransitionTarget nextState = state.nextState;

        //reached end of chart, valid assignment found
        if (nextState.getId().equalsIgnoreCase("end")) {
            processingStrategy.processOutput(state.variables);

            return;
        }

        //run every action in series
        List<Map<String, String>> product = new LinkedList<>();
        product.add(new HashMap<>(state.variables));

        OnEntry entry = nextState.getOnEntry();
        List<Action> actions = entry.getActions();

        for (Action action : actions) {
            for (CustomTagExtension tagExtension : tagExtensionList) {
                if (tagExtension.getTagActionClass().isInstance(action)) {
                    product = tagExtension.pipelinePossibleStates(action, product);
                }
            }
        }

        //go through every transition and see which of the products are valid, recursive searching on them
        List<Transition> transitions = nextState.getTransitionsList();

        for (Transition transition : transitions) {
            String condition = transition.getCond();
            TransitionTarget target = ((List<TransitionTarget>) transition.getTargets()).get(0);

            for (Map<String, String> p : product) {
                Boolean pass;

                if (condition == null) {
                    pass = true;
                } else {
                    //scrub the context clean so we may use it to evaluate transition conditional
                    Context context = this.getRootContext();
                    context.reset();

                    //set up new context
                    for (Map.Entry<String, String> e : p.entrySet()) {
                        context.set(e.getKey(), e.getValue());
                    }

                    //evaluate condition
                    try {
                        pass = (Boolean) this.getEvaluator().eval(context, condition);
                    } catch (SCXMLExpressionException ex) {
                        pass = false;
                    }
                }

                //transition condition satisfied, continue search recursively
                if (pass) {
                    PossibleState result = new PossibleState(target, p);
                    dfs(processingStrategy, flag, result);
                }
            }
        }
    }

    public PossibleState getRoot() {
        return root;
    }
}