/* * 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; } }