/* * 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.io.SCXMLParser; import org.apache.commons.scxml.model.CustomAction; import org.apache.commons.scxml.model.ModelException; import org.apache.commons.scxml.model.SCXML; import org.apache.commons.scxml.model.TransitionTarget; import org.finra.datagenerator.engine.Frontier; import org.finra.datagenerator.engine.scxml.tags.CustomTagExtension; import org.finra.datagenerator.engine.scxml.tags.FileExtension; import org.finra.datagenerator.engine.scxml.tags.RangeExtension; import org.finra.datagenerator.engine.scxml.tags.SetAssignExtension; import org.finra.datagenerator.engine.scxml.tags.SingleValueAssignExtension; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Provides serialization tools for SCXMLFrontier. */ public class SCXMLGapper { private SCXML model; private void setModel(String model, List<CustomTagExtension> tagExtensionList) { List<CustomAction> customActions = new ArrayList<>(); for (CustomTagExtension tagExtension : tagExtensionList) { if (!tagExtension.getTagNameSpace().equals("http://www.w3.org/2005/07/scxml")) { CustomAction action = new CustomAction(tagExtension.getTagNameSpace(), tagExtension.getTagName(), tagExtension.getTagActionClass()); customActions.add(action); } } try { InputStream is = new ByteArrayInputStream(model.getBytes()); this.model = SCXMLParser.parse(new InputSource(is), null, customActions); } catch (IOException | SAXException | ModelException e) { e.printStackTrace(); } } /** * Takes a model and an SCXMLFrontier and decomposes the Frontier into a Map of Strings to Strings * These strings can be sent over a network to get a Frontier past a 'gap' * * @param frontier the Frontier * @param modelText the model * @return the map of strings representing a decomposition */ public Map<String, String> decompose(Frontier frontier, String modelText) { if (!(frontier instanceof SCXMLFrontier)) { return null; } TransitionTarget target = ((SCXMLFrontier) frontier).getRoot().nextState; Map<String, String> variables = ((SCXMLFrontier) frontier).getRoot().variables; Map<String, String> decomposition = new HashMap<String, String>(); decomposition.put("target", target.getId()); StringBuilder packedVariables = new StringBuilder(); for (Map.Entry<String, String> variable : variables.entrySet()) { packedVariables.append(variable.getKey()); packedVariables.append("::"); packedVariables.append(variable.getValue()); packedVariables.append(";"); } decomposition.put("variables", packedVariables.toString()); decomposition.put("model", modelText); return decomposition; } /** * Produces an SCXMLFrontier by reversing a decomposition; the model text is bundled into the decomposition. * * @param decomposition the decomposition, assembled back into a map * @return a rebuilt SCXMLFrontier */ public Frontier reproduce(Map<String, String> decomposition) { return reproduce(decomposition, new LinkedList<CustomTagExtension>()); } /** * Produces an SCXMLFrontier by reversing a decomposition; the model text is bundled into the decomposition. * * @param decomposition the decomposition, assembled back into a map * @param tagExtensionList custom tags to use in the model * @return a rebuilt SCXMLFrontier */ public Frontier reproduce(Map<String, String> decomposition, List<CustomTagExtension> tagExtensionList) { tagExtensionList = new LinkedList<>(tagExtensionList); tagExtensionList.add(new SetAssignExtension()); tagExtensionList.add(new SingleValueAssignExtension()); tagExtensionList.add(new FileExtension()); tagExtensionList.add(new RangeExtension()); setModel(decomposition.get("model"), tagExtensionList); TransitionTarget target = (TransitionTarget) model.getTargets().get(decomposition.get("target")); Map<String, String> variables = new HashMap<>(); String[] assignments = decomposition.get("variables").split(";"); for (int i = 0; i < assignments.length; i++) { String[] a = assignments[i].split("::"); if (a.length == 2) { variables.put(a[0], a[1]); } else { variables.put(a[0], ""); } } return new SCXMLFrontier(new PossibleState(target, variables), model, tagExtensionList); } }