/*
* 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.tags;
import org.apache.commons.logging.Log;
import org.apache.commons.scxml.ErrorReporter;
import org.apache.commons.scxml.EventDispatcher;
import org.apache.commons.scxml.SCInstance;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.apache.commons.scxml.model.Action;
import org.apache.commons.scxml.model.ModelException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Implementation of dg:nwise tag
*/
public class NWiseExtension implements CustomTagExtension<NWiseExtension.NWiseAction> {
public Class<NWiseAction> getTagActionClass() {
return NWiseAction.class;
}
public String getTagName() {
return "nwise";
}
public String getTagNameSpace() {
return "org.finra.datagenerator";
}
private void makeNWiseTuplesHelper(List<Set<String>> resultTuples, String[] variables,
int startVariable, Set<String> preGivenVariables, int nWise) {
if (nWise <= 0) {
Set<String> result = new HashSet<>(preGivenVariables);
resultTuples.add(result);
return;
}
for (int varIndex = startVariable; varIndex <= variables.length - nWise; varIndex++) {
preGivenVariables.add(variables[varIndex]);
makeNWiseTuplesHelper(resultTuples, variables, varIndex + 1, preGivenVariables, nWise - 1);
preGivenVariables.remove(variables[varIndex]);
}
}
/**
* Produces all tuples of size n chosen from a list of variable names
*
* @param variables the list of variable names to make tuples of
* @param nWise the size of the desired tuples
* @return all tuples of size nWise
*/
public List<Set<String>> makeNWiseTuples(String[] variables, int nWise) {
List<Set<String>> completeTuples = new ArrayList<>();
makeNWiseTuplesHelper(completeTuples, variables, 0, new HashSet<String>(), nWise);
return completeTuples;
}
private void expandTupleHelper(List<Map<String, String>> resultExpansions, String[] variables,
Map<String, String[]> variableDomains, int nextVariable,
Map<String, String> partialAssignment) {
if (nextVariable >= variables.length) {
Map<String, String> resultAssignment = new HashMap<>(partialAssignment);
resultExpansions.add(resultAssignment);
return;
}
String variable = variables[nextVariable];
for (String domainValue : variableDomains.get(variable)) {
partialAssignment.put(variable, domainValue);
expandTupleHelper(resultExpansions, variables, variableDomains, nextVariable + 1, partialAssignment);
}
}
/**
* Expands a tuple of variable names into every combination of assignments to those variables
*
* @param tuple a list of variables
* @param variableDomains a map defining the domain for each variable
* @return the cartesian product of the domains of each variable in the tuple
*/
public List<Map<String, String>> expandTupleIntoTestCases(Set<String> tuple, Map<String, String[]> variableDomains) {
List<Map<String, String>> expandedTestCases = new ArrayList<>();
String[] variables = tuple.toArray(new String[tuple.size()]);
expandTupleHelper(expandedTestCases, variables, variableDomains, 0, new HashMap<String, String>());
return expandedTestCases;
}
/**
* Finds all nWise combinations of a set of variables, each with a given domain of values
*
* @param nWise the number of variables in each combination
* @param coVariables the varisbles
* @param variableDomains the domains
* @return all nWise combinations of the set of variables
*/
public List<Map<String, String>> produceNWise(int nWise, String[] coVariables, Map<String, String[]> variableDomains) {
List<Set<String>> tuples = makeNWiseTuples(coVariables, nWise);
List<Map<String, String>> testCases = new ArrayList<>();
for (Set<String> tuple : tuples) {
testCases.addAll(expandTupleIntoTestCases(tuple, variableDomains));
}
return testCases;
}
/**
* Uses current variable assignments and info in an NWiseActionTag to expand on an n wise combinatorial set
*
* @param action an NWiseAction Action
* @param possibleStateList a current list of possible states produced so far from expanding a model state
* @return every input possible state expanded on an n wise combinatorial set defined by that input possible state
*/
public List<Map<String, String>> pipelinePossibleStates(NWiseAction action, List<Map<String, String>> possibleStateList) {
String[] coVariables = action.getCoVariables().split(",");
int n = Integer.valueOf(action.getN());
List<Map<String, String>> newPossibleStateList = new ArrayList<>();
for (Map<String, String> possibleState : possibleStateList) {
Map<String, String[]> variableDomains = new HashMap<>();
Map<String, String> defaultVariableValues = new HashMap<>();
for (String variable : coVariables) {
String variableMetaInfo = possibleState.get(variable);
String[] variableDomain = variableMetaInfo.split(",");
variableDomains.put(variable, variableDomain);
defaultVariableValues.put(variable, variableDomain[0]);
}
List<Map<String, String>> nWiseCombinations = produceNWise(n, coVariables, variableDomains);
for (Map<String, String> nWiseCombination : nWiseCombinations) {
Map<String, String> newPossibleState = new HashMap<>(possibleState);
newPossibleState.putAll(defaultVariableValues);
newPossibleState.putAll(nWiseCombination);
newPossibleStateList.add(newPossibleState);
}
}
return newPossibleStateList;
}
/**
* A custom Action for the 'dg:nwise' tag inside models
*/
public static class NWiseAction extends Action {
private String coVariables;
private String n;
public String getCoVariables() {
return coVariables;
}
public void setCoVariables(String coVariables) {
this.coVariables = coVariables;
}
public String getN() {
return n;
}
public void setN(String n) {
this.n = n;
}
/**
* Required implementation of an abstract method in Action
*
* @param eventDispatcher unused
* @param errorReporter unused
* @param scInstance unused
* @param log unused
* @param collection unused
* @throws ModelException never
* @throws SCXMLExpressionException never
*/
public void execute(EventDispatcher eventDispatcher, ErrorReporter errorReporter, SCInstance scInstance,
Log log, Collection collection) throws ModelException, SCXMLExpressionException {
}
}
}