diff --git a/build.gradle b/build.gradle index 5c48c7c..92269d1 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,8 @@ dependencies { compile "org.parboiled:parboiled-java:$parboiledJavaVersion" compile "org.yaml:snakeyaml:$snakeyamlVersion" compile "org.apache.commons:commons-csv:$commonsCsvVersion" + compile "com.googlecode.json-simple:json-simple:$gsonVersion" + compile "com.jayway.jsonpath:json-path:$jsonPathVersion" testCompile "junit:junit:$junitVersion" testCompile "org.spockframework:spock-core:$spockCoreVersion" diff --git a/gradle.properties b/gradle.properties index 01351b5..c22cc98 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,3 +12,5 @@ junitVersion=4.12 spockCoreVersion=1.0-groovy-2.4 cglibNodepVersion=3.2.0 groovyAllVersion=2.4.3 +gsonVersion=1.1 +jsonPathVersion=2.3.0 diff --git a/src/main/java/io/smartcat/ranger/core/UserDefinedValue.java b/src/main/java/io/smartcat/ranger/core/UserDefinedValue.java new file mode 100644 index 0000000..22389a6 --- /dev/null +++ b/src/main/java/io/smartcat/ranger/core/UserDefinedValue.java @@ -0,0 +1,159 @@ +package io.smartcat.ranger.core; + +import com.jayway.jsonpath.JsonPath; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author hkilari + * + * UserDefined Value Implementation to allow Berserker User define a Custom Value in + * berserker ranger configuration. The Custom Value will be retrieved from the data source + * configured by User based on his query config + * + */ +public class UserDefinedValue extends Value { + + private Value jsonName; + private Value filter; + private Value regExpression; + private List finalContent; + private long contentlastUpdationTime; + private Value refreshInterval; + private Value defaultRefreshInterval = new Value() { + }; + + /** + * Constructor for UserDefinedValue. + * + * @param regExpression + * To support the case where the requirement is not complete field and only specific + * part. + * @param refreshInterval + * To refresh the data at configured intervals if the source support dynamic data + * retrieval. + * @param filter + * JSON path query to extract a Filed from specified Data Source. For semantics, please + * refer to https://github.com/json-path/JsonPath. + * @param jsonName + * Can be a name of JSON file available in classpath/ A Rest endpoint that return a JSON + * response. + */ + public UserDefinedValue(Value regExpression, Value refreshInterval, + Value filter, Value jsonName) { + defaultRefreshInterval.val = "600"; + if (jsonName == null) { + throw new IllegalArgumentException("datasource cannot be null."); + } + this.jsonName = jsonName; + this.filter = filter; + this.regExpression = regExpression; + this.refreshInterval = refreshInterval == null ? defaultRefreshInterval : refreshInterval; + this.finalContent = new ArrayList<>(); + } + + @Override + public void reset() { + jsonName.reset(); + filter.reset(); + regExpression.reset(); + refreshInterval.reset(); + super.reset(); + + } + + @Override + protected void eval() { + if (isContentExpired()) { + getDataFromJson(); + } + val = getRandomValue(); + } + + private boolean isContentExpired() { + // Checks if the finalContent to be refreshed based on used defined refreshInterval. + if (System.currentTimeMillis() - contentlastUpdationTime > Long.parseLong(refreshInterval.val) + * 1000) { + return true; + } + return false; + } + + private String getRandomValue() { + if (finalContent != null && !finalContent.isEmpty()) + return extractField(finalContent.get(new Random().nextInt(finalContent.size())).toString(), + regExpression.get()); + else + return ""; + } + + private void getDataFromJson() { + try { + // check If Source is a http service + if (jsonName.get().contains("http")) { + getDataFromHttpRequest(); + } else { + // Source treated as a local json file + InputStream in = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(jsonName.get() + ".json"); + if (in == null) { + in = new FileInputStream(jsonName.get() + ".json"); + } + if (in != null) { + finalContent = JsonPath.read(in, filter.get()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Retrieves data from a http service endpoint. + */ + private void getDataFromHttpRequest() { + try { + InputStream in; + URL obj = new URL(jsonName.val); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + con.setRequestMethod("GET"); + con.setRequestProperty("accept", "application/json"); + int responseCode = con.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { // success + in = con.getInputStream(); + finalContent = JsonPath.read(in, filter.get()); + in.close(); + contentlastUpdationTime = System.currentTimeMillis(); + } else { + System.out.println("GET request not worked"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * @param latestCommandOutput + * @param regExpression + * @return + */ + private String extractField(String inputString, String regExpression) { + if (regExpression != null && !"".equals(regExpression.trim())) { + Pattern p = Pattern.compile(regExpression); + Matcher m = p.matcher(inputString); + if (m.find()) { + if (m.groupCount() > 0) + return m.group(1); + } + return null; + } + return inputString; + } +} diff --git a/src/main/java/io/smartcat/ranger/parser/ValueExpressionParser.java b/src/main/java/io/smartcat/ranger/parser/ValueExpressionParser.java index 373a8d0..bcbe486 100644 --- a/src/main/java/io/smartcat/ranger/parser/ValueExpressionParser.java +++ b/src/main/java/io/smartcat/ranger/parser/ValueExpressionParser.java @@ -33,6 +33,7 @@ import io.smartcat.ranger.core.StringTransformer; import io.smartcat.ranger.core.TimeFormatTransformer; import io.smartcat.ranger.core.UUIDValue; +import io.smartcat.ranger.core.UserDefinedValue; import io.smartcat.ranger.core.Value; import io.smartcat.ranger.core.ValueProxy; import io.smartcat.ranger.core.WeightedValue; @@ -72,1182 +73,1255 @@ */ public class ValueExpressionParser extends BaseParser { - private static final String STRING_VALUE_DELIMITER = "stringValueDelimiter"; - - private final Map> proxyValues; - - /** - * Range value factory. - */ - protected final RangeValueFactory rangeValueFactory; - - /** - * Circular range value factory. - */ - protected final CircularRangeValueFactory circularRangeValueFactory; - - private String parentName; - - /** - * Constructs parser with initial proxyValues. - * - * @param proxyValues Map containing proxy values by name. - */ - public ValueExpressionParser(Map> proxyValues) { - this.proxyValues = proxyValues; - this.rangeValueFactory = new RangeValueFactory(); - this.circularRangeValueFactory = new CircularRangeValueFactory(); - } - - /** - * Sets parent name. - * - * @param parentName Parent name. - */ - public void setParentName(String parentName) { - this.parentName = parentName; - } - - /** - * Whitespace definition. - * - * @return Whitespace definition rule. - */ - public Rule whitespace() { - return AnyOf(" \t"); - } - - /** - * Newline definition. - * - * @return Newline definition rule. - */ - public Rule newline() { - return AnyOf("\r\n"); - } - - /** - * Comma definition. - * - * @return Comma definition rule. - */ - public Rule comma() { - return Sequence(ZeroOrMore(whitespace()), ",", ZeroOrMore(whitespace())); - } - - /** - * Open parenthesis definition. - * - * @return Open parenthesis definition rule. - */ - public Rule openParenthesis() { - return Sequence(ZeroOrMore(whitespace()), "(", ZeroOrMore(whitespace())); - } - - /** - * Closed parenthesis definition. - * - * @return Closed parenthesis definition rule. - */ - public Rule closedParenthesis() { - return Sequence(ZeroOrMore(whitespace()), ")", ZeroOrMore(whitespace())); - } - - /** - * Open bracket definition. - * - * @return Open bracket definition rule. - */ - public Rule openBracket() { - return Sequence(ZeroOrMore(whitespace()), "[", ZeroOrMore(whitespace())); - } - - /** - * Closed bracket definition. - * - * @return Closed bracket definition rule. - */ - public Rule closedBracket() { - return Sequence(ZeroOrMore(whitespace()), "]", ZeroOrMore(whitespace())); - } - - /** - * Sign definition. - * - * @return Sign definition rule. - */ - public Rule sign() { - return AnyOf("+-"); - } - - /** - * Letter definition. - * - * @return Letter definition rule. - */ - public Rule letter() { - return FirstOf('_', CharRange('a', 'z'), CharRange('A', 'Z')); - } - - /** - * Digit definition. - * - * @return Digit definition rule. - */ - public Rule digit() { - return CharRange('0', '9'); - } - - /** - * Letter or digit definition. - * - * @return Letter or digit definition rule. - */ - public Rule letterOrDigit() { - return FirstOf(letter(), digit()); - } - - /** - * Escape sequence definition. - * - * @return Escape sequence definition rule. - */ - public Rule escape() { - return Sequence('\\', AnyOf("btnfr\"\'\\")); - } - - /** - * Unsigned integer definition. - * - * @return Unsigned integer definition rule. - */ - public Rule unsignedIntegerLiteral() { - return OneOrMore(digit()); - } - - /** - * Exponent definition. - * - * @return Exponent definition rule. - */ - public Rule exponent() { - return Sequence(AnyOf("eE"), Optional(sign()), unsignedIntegerLiteral()); - } - - /** - * Null value definition. - * - * @return Null value definition rule. - */ - public Rule nullValue() { - return Sequence("null", openParenthesis(), closedParenthesis(), push(new NullValue())); - } - - /** - * Byte definition. - * - * @return Byte definition rule. - */ - public Rule explicitByteLiteral() { - return Sequence(function("byte", Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), - push(Byte.parseByte((String) pop()))); - } - - /** - * Short definition. - * - * @return Short definition rule. - */ - public Rule explicitShortLiteral() { - return Sequence( - function("short", Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), - push(Short.parseShort((String) pop()))); - } - - /** - * Implicit integer definition. - * - * @return Implicit integer definition rule. - */ - public Rule implicitIntegerLiteral() { - return Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), ACTION(tryParseInt())); - } - - /** - * Explicit integer definition. - * - * @return Explicit integer definition rule. - */ - public Rule explicitIntegerLiteral() { - return Sequence(function("int", Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), - push(Integer.parseInt((String) pop()))); - } - - /** - * Integer definition. - * - * @return Integer definition rule. - */ - public Rule integerLiteral() { - return FirstOf(explicitIntegerLiteral(), implicitIntegerLiteral()); - } - - /** - * Implicit integer definition. - * - * @return Implicit integer definition rule. - */ - public Rule implicitLongLiteral() { - return Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(Long.parseLong(match()))); - } - - /** - * Explicit integer definition. - * - * @return Explicit integer definition rule. - */ - public Rule explicitLongLiteral() { - return Sequence(function("long", Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), - push(Long.parseLong((String) pop()))); - } - - /** - * Long definition. - * - * @return Long definition rule. - */ - public Rule longLiteral() { - return FirstOf(explicitLongLiteral(), implicitLongLiteral()); - } - - /** - * Float definition. - * - * @return Float definition rule. - */ - public Rule explicitFloatLiteral() { - return Sequence( - function("float", Sequence(Sequence(Optional(sign()), - FirstOf(Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), Optional(exponent())), - Sequence('.', unsignedIntegerLiteral(), Optional(exponent())), - Sequence(unsignedIntegerLiteral(), Optional(exponent())))), - push(match()))), - push(Float.parseFloat((String) pop()))); - } - - /** - * Implicit double definition. - * - * @return Implicit double definition rule. - */ - public Rule implicitDoubleLiteral() { - return Sequence( + private static final String STRING_VALUE_DELIMITER = "stringValueDelimiter"; + + private final Map> proxyValues; + + /** + * Range value factory. + */ + protected final RangeValueFactory rangeValueFactory; + + /** + * Circular range value factory. + */ + protected final CircularRangeValueFactory circularRangeValueFactory; + + private String parentName; + + /** + * Constructs parser with initial proxyValues. + * + * @param proxyValues + * Map containing proxy values by name. + */ + public ValueExpressionParser(Map> proxyValues) { + this.proxyValues = proxyValues; + this.rangeValueFactory = new RangeValueFactory(); + this.circularRangeValueFactory = new CircularRangeValueFactory(); + } + + /** + * Sets parent name. + * + * @param parentName + * Parent name. + */ + public void setParentName(String parentName) { + this.parentName = parentName; + } + + /** + * Whitespace definition. + * + * @return Whitespace definition rule. + */ + public Rule whitespace() { + return AnyOf(" \t"); + } + + /** + * Newline definition. + * + * @return Newline definition rule. + */ + public Rule newline() { + return AnyOf("\r\n"); + } + + /** + * Comma definition. + * + * @return Comma definition rule. + */ + public Rule comma() { + return Sequence(ZeroOrMore(whitespace()), ",", ZeroOrMore(whitespace())); + } + + /** + * Open parenthesis definition. + * + * @return Open parenthesis definition rule. + */ + public Rule openParenthesis() { + return Sequence(ZeroOrMore(whitespace()), "(", ZeroOrMore(whitespace())); + } + + /** + * Closed parenthesis definition. + * + * @return Closed parenthesis definition rule. + */ + public Rule closedParenthesis() { + return Sequence(ZeroOrMore(whitespace()), ")", ZeroOrMore(whitespace())); + } + + /** + * Open bracket definition. + * + * @return Open bracket definition rule. + */ + public Rule openBracket() { + return Sequence(ZeroOrMore(whitespace()), "[", ZeroOrMore(whitespace())); + } + + /** + * Closed bracket definition. + * + * @return Closed bracket definition rule. + */ + public Rule closedBracket() { + return Sequence(ZeroOrMore(whitespace()), "]", ZeroOrMore(whitespace())); + } + + /** + * Sign definition. + * + * @return Sign definition rule. + */ + public Rule sign() { + return AnyOf("+-"); + } + + /** + * Letter definition. + * + * @return Letter definition rule. + */ + public Rule letter() { + return FirstOf('_', CharRange('a', 'z'), CharRange('A', 'Z')); + } + + /** + * Digit definition. + * + * @return Digit definition rule. + */ + public Rule digit() { + return CharRange('0', '9'); + } + + /** + * Letter or digit definition. + * + * @return Letter or digit definition rule. + */ + public Rule letterOrDigit() { + return FirstOf(letter(), digit()); + } + + /** + * Escape sequence definition. + * + * @return Escape sequence definition rule. + */ + public Rule escape() { + return Sequence('\\', AnyOf("btnfr\"\'\\")); + } + + /** + * Unsigned integer definition. + * + * @return Unsigned integer definition rule. + */ + public Rule unsignedIntegerLiteral() { + return OneOrMore(digit()); + } + + /** + * Exponent definition. + * + * @return Exponent definition rule. + */ + public Rule exponent() { + return Sequence(AnyOf("eE"), Optional(sign()), unsignedIntegerLiteral()); + } + + /** + * Null value definition. + * + * @return Null value definition rule. + */ + public Rule nullValue() { + return Sequence("null", openParenthesis(), closedParenthesis(), push(new NullValue())); + } + + /** + * Byte definition. + * + * @return Byte definition rule. + */ + public Rule explicitByteLiteral() { + return Sequence( + function("byte", + Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), + push(Byte.parseByte((String) pop()))); + } + + /** + * Short definition. + * + * @return Short definition rule. + */ + public Rule explicitShortLiteral() { + return Sequence( + function("short", + Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), + push(Short.parseShort((String) pop()))); + } + + /** + * Implicit integer definition. + * + * @return Implicit integer definition rule. + */ + public Rule implicitIntegerLiteral() { + return Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), ACTION(tryParseInt())); + } + + /** + * Explicit integer definition. + * + * @return Explicit integer definition rule. + */ + public Rule explicitIntegerLiteral() { + return Sequence( + function("int", + Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), + push(Integer.parseInt((String) pop()))); + } + + /** + * Integer definition. + * + * @return Integer definition rule. + */ + public Rule integerLiteral() { + return FirstOf(explicitIntegerLiteral(), implicitIntegerLiteral()); + } + + /** + * Implicit integer definition. + * + * @return Implicit integer definition rule. + */ + public Rule implicitLongLiteral() { + return Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), + push(Long.parseLong(match()))); + } + + /** + * Explicit integer definition. + * + * @return Explicit integer definition rule. + */ + public Rule explicitLongLiteral() { + return Sequence( + function("long", + Sequence(Sequence(Optional(sign()), unsignedIntegerLiteral()), push(match()))), + push(Long.parseLong((String) pop()))); + } + + /** + * Long definition. + * + * @return Long definition rule. + */ + public Rule longLiteral() { + return FirstOf(explicitLongLiteral(), implicitLongLiteral()); + } + + /** + * Float definition. + * + * @return Float definition rule. + */ + public Rule explicitFloatLiteral() { + return Sequence( + function("float", + Sequence( Sequence(Optional(sign()), - FirstOf(Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), Optional(exponent())), - Sequence('.', unsignedIntegerLiteral(), Optional(exponent())))), - push(Double.parseDouble(match()))); - } - - /** - * Explicit double definition. - * - * @return Explicit double definition rule. - */ - public Rule explicitDoubleLiteral() { - return Sequence( - function("double", Sequence(Sequence(Optional(sign()), - FirstOf(Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), Optional(exponent())), - Sequence('.', unsignedIntegerLiteral(), Optional(exponent())), - Sequence(unsignedIntegerLiteral(), Optional(exponent())))), - push(match()))), - push(Double.parseDouble((String) pop()))); - } - - /** - * Double definition. - * - * @return Double definition rule. - */ - public Rule doubleLiteral() { - return FirstOf(explicitDoubleLiteral(), implicitDoubleLiteral()); - } - - /** - * Number definition. - * - * @return Number definition rule. - */ - public Rule numberLiteral() { - return FirstOf(explicitByteLiteral(), explicitShortLiteral(), explicitIntegerLiteral(), explicitLongLiteral(), - explicitFloatLiteral(), explicitDoubleLiteral(), implicitDoubleLiteral(), implicitIntegerLiteral(), - implicitLongLiteral()); - } - - /** - * Number value definition. - * - * @return Number value definition rule. - */ - public Rule numberLiteralValue() { - return Sequence(numberLiteral(), push(ConstantValue.of(pop()))); - } - - /** - * Boolean literal definition. - * - * @return Boolean literal definition rule. - */ - public Rule booleanLiteral() { - return Sequence(FirstOf(FirstOf("True", "true"), FirstOf("False", "false")), - push(Boolean.parseBoolean(match()))); - } - - /** - * Boolean value definition. - * - * @return Boolean value definition rule. - */ - public Rule booleanLiteralValue() { - return Sequence(booleanLiteral(), push(ConstantValue.of(pop()))); - } - - /** - * String definition. - * - * @return String definition rule. - */ - public Rule stringLiteral() { - return FirstOf(singleQuoteStringLiteral(), doubleQuoteStringLiteral()); - } - - /** - * Character literal definition. - * - * @return Character literal definition rule. - */ - public Rule charLiteral() { - return Sequence(Sequence('\'', FirstOf(escape(), Sequence(TestNot(AnyOf("'\\")), ANY)), '\''), - push(new Character(match().charAt(1) == '\\' ? match().charAt(2) : match().charAt(1)))); - } - - /** - * Naked string definition. - * - * @return Naked string definition rule. - */ - public Rule nakedStringLiteral() { - return Sequence(TestNot(AnyOf("\r\n\"'\\")), ZeroOrMore(ANY), push(match())); - } - - /** - * Single quote string definition. - * - * @return Single quote string definition rule. - */ - public Rule singleQuoteStringLiteral() { - return Sequence(Sequence("'", ZeroOrMore(FirstOf(escape(), Sequence(TestNot(AnyOf("\r\n'\\")), ANY))), "'"), - push(trimOffEnds(match()))); - } - - /** - * Double quote string definition. - * - * @return Double quote string definition rule. - */ - public Rule doubleQuoteStringLiteral() { - return Sequence(Sequence('"', ZeroOrMore(FirstOf(escape(), Sequence(TestNot(AnyOf("\r\n\"\\")), ANY))), '"'), - push(trimOffEnds(match()))); - } - - /** - * String value definition. - * - * @return String value definition rule. - */ - public Rule stringLiteralValue() { - return Sequence(FirstOf(stringLiteral(), nakedStringLiteral()), push(ConstantValue.of(pop()))); - } - - /** - * Literal definition. - * - * @return Literal definition rule. - */ - public Rule literalValue() { - return FirstOf(nullValue(), numberLiteralValue(), booleanLiteralValue(), stringLiteralValue()); - } - - /** - * Identifier definition. - * - * @return Identifier definition rule. - */ - public Rule identifier() { - return Sequence(Sequence(letter(), ZeroOrMore(letterOrDigit())), push(match())); - } - - /** - * Identifier definition which does not push match to value stack. - * - * @return Identifier definition rule. - */ - public Rule identifierWithNoPush() { - return Sequence(letter(), ZeroOrMore(letterOrDigit())); - } - - /** - * Number range definition. - * - * @return Number range definition rule. - */ - public Rule numberRange() { - return Sequence(Sequence(numberLiteral(), "..", numberLiteral()), - push(createNumberRange((Number) pop(1), (Number) pop()))); - } - - /** - * Character range definition. - * - * @return Character range definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule charRange() { - return Sequence(Sequence(charLiteral(), "..", charLiteral()), - push(new Range((Character) pop(1), (Character) pop()))); - } - - /** - * Function definition. - * - * @param functionName Name of a function. - * @return Function definition rule. - */ - protected Rule function(String functionName) { - return function(functionName, fromStringLiteral("")); - } - - /** - * Function definition. - * - * @param functionArgument Function argument rule. - * @return Function definition rule. - */ - protected Rule function(Rule functionArgument) { - return function("", functionArgument); - } - - /** - * Function definition. - * - * @param functionName Name of a function. - * @param functionArgument Function argument rule. - * @return Function definition rule. - */ - protected Rule function(String functionName, Rule functionArgument) { - return Sequence(functionName, openParenthesis(), functionArgument, closedParenthesis()); - } - - /** - * List of items enclosed in brackets. - * - * @param rule Rule of a list item. - * @return Bracket list definition rule. - */ - protected Rule bracketList(Rule rule) { - return Sequence(openBracket(), list(rule), closedBracket()); - } - - /** - * List of items. - * - * @param rule Rule of a list item. - * @return List definition rule. - */ - protected Rule list(Rule rule) { - return Sequence(Sequence(push("args"), Optional(rule, ZeroOrMore(comma(), rule))), - push(getItemsUpToDelimiter("args"))); - } - - /** - * Value reference definition. - * - * @return Value reference definition rule. - */ - public Rule valueReference() { - return Sequence('$', Sequence(Sequence(identifierWithNoPush(), ZeroOrMore('.', identifierWithNoPush())), - push(getValueProxy(match())))); - } - - /** - * Uniform distribution definition. - * - * @return Uniform distribution definition rule. - */ - public Rule uniformDistribution() { - return Sequence(function("uniform"), push(new UniformDistribution())); - } - - /** - * Normal distribution definition. - * - * @return Normal distribution definition rule. - */ - public Rule normalDistribution() { - return Sequence(function("normal", list(numberLiteral())), push(createNormalDistribution())); - } - - /** - * Distribution definition. - * - * @return Distribution definition rule. - */ - public Rule distribution() { - return FirstOf(uniformDistribution(), normalDistribution()); - } - - /** - * Discrete value definition. - * - * @return Discrete value definition rule. - */ - public Rule discreteValue() { - return Sequence(function("random", Sequence(bracketList(value()), Optional(comma(), distribution()))), - push(createDiscreteValue())); - } - - /** - * Long range value definition. - * - * @return Long range value definition rule. - */ - public Rule rangeValue() { - return Sequence( - function("random", - Sequence(numberRange(), - Optional(comma(), booleanLiteral(), Optional(comma(), distribution())))), - push(createRangeValue())); - } - - /** - * UUID value definition. - * - * @return UUID value definition rule. - */ - public Rule uuidValue() { - return Sequence(function("uuid"), push(new UUIDValue())); - } - - /** - * Circular value definition. - * - * @return Circular value definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule circularValue() { - return Sequence(function("circular", bracketList(value())), push(new CircularValue((List) pop()))); - } - - /** - * Circular range value definition. - * - * @return Circular range value definition rule. - */ - @SuppressWarnings({ "rawtypes" }) - public Rule circularRangeValue() { - return Sequence(function("circular", Sequence(numberRange(), comma(), numberLiteral())), - push(circularRangeValueFactory.create((Range) pop(1), (Number) pop()))); - } - - /** - * List value definition. - * - * @return List value definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule listValue() { - return Sequence(function("list", bracketList(value())), push(new ListValue((List) pop()))); - } - - /** - * Empty list value definition. - * - * @return Empty list value definition rule. - */ - @SuppressWarnings({ "rawtypes" }) - public Rule emptyListValue() { - return Sequence(function("emptyList"), push(new EmptyListValue())); - } - - /** - * Empty map value definition. - * - * @return Empty map value definition rule. - */ - @SuppressWarnings({ "rawtypes" }) - public Rule emptyMapValue() { - return Sequence(function("emptyMap"), push(new EmptyMapValue())); - } - - /** - * Random length list value definition. - * - * @return Random length list value definition rule. - */ - public Rule randomLengthListValue() { - return Sequence(function("list", Sequence(numberLiteral(), comma(), numberLiteral(), comma(), value(), - Optional(comma(), distribution()))), push(createRandomLengthListValue())); - } - - /** - * Weighted value pair definition. - * - * @return Weighted value pair definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule weightedValuePair() { - return Sequence(function(Sequence(value(), comma(), numberLiteral())), - push(new WeightedValuePair((Value) pop(1), ((Number) pop()).doubleValue()))); - } - - /** - * Weighted value definition. - * - * @return Weighted value definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule weightedValue() { - return Sequence(function("weighted", bracketList(weightedValuePair())), push(new WeightedValue((List) pop()))); - } - - /** - * Count value pair definition. - * - * @return Count value pair definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule countValuePair() { - return Sequence(function(Sequence(value(), comma(), longLiteral())), - push(new CountValuePair((Value) pop(1), (Long) pop()))); - } - - /** - * Exact weighted value definition. - * - * @return Weighted value definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule exactWeightedValue() { - return Sequence(function("exactly", bracketList(countValuePair())), - push(new ExactWeightedValue<>((List) pop()))); - } - - /** - * Random content value definition. - * - * @return Random content value definition rule. - */ - public Rule randomContentStringValue() { - return Sequence(function("randomContentString", Sequence(value(), Optional(comma(), bracketList(charRange())))), - push(createRandomContentStringValue())); - } - - /** - * Now definition. - * - * @return Now definition rule. - */ - public Rule now() { - return Sequence(function("now"), push(new NowValue())); - } - - /** - * Now date definition. - * - * @return Now date definition rule. - */ - public Rule nowDate() { - return Sequence(function("nowDate"), push(new NowDateValue())); - } - - /** - * Now local date definition. - * - * @return Now local date definition rule. - */ - public Rule nowLocalDate() { - return Sequence(function("nowLocalDate"), push(new NowLocalDateValue())); - } - - /** - * Now local date time definition. - * - * @return Now local date time definition rule. - */ - public Rule nowLocalDateTime() { - return Sequence(function("nowLocalDateTime"), push(new NowLocalDateTimeValue())); - } - - /** - * Addition value definition. - * - * @return Addition value definition rule. - */ - public Rule additionValue() { - return Sequence(function("add", Sequence(stringLiteral(), comma(), value(), comma(), value())), - push(createAdditionValue())); - } - - /** - * Subtraction value definition. - * - * @return Subtraction value definition rule. - */ - public Rule subtractionValue() { - return Sequence(function("subtract", Sequence(stringLiteral(), comma(), value(), comma(), value())), - push(createSubtractionValue())); - } - - /** - * Multiplication value definition. - * - * @return Multiplication value definition rule. - */ - public Rule multiplicationValue() { - return Sequence(function("multiply", Sequence(stringLiteral(), comma(), value(), comma(), value())), - push(createMultiplicationValue())); - } - - /** - * Division value definition. - * - * @return Division value definition rule. - */ - public Rule divisionValue() { - return Sequence(function("divide", Sequence(stringLiteral(), comma(), value(), comma(), value())), - push(createDivisionValue())); - } - - /** - * CSV value definition. - * - * @return CSV value definition rule. - */ - public Rule csvReaderValue() { - return Sequence( - function("csv", - Sequence(stringLiteral(), - Optional(comma(), charLiteral(), - Optional(comma(), stringLiteral(), comma(), booleanLiteral(), comma(), - FirstOf(nullValue(), charLiteral()), comma(), charLiteral(), comma(), - booleanLiteral(), comma(), FirstOf(nullValue(), stringLiteral()))))), - push(createCsvReaderValue())); - } - - /** - * Generator definition. - * - * @return Generator definition rule. - */ - public Rule generator() { - return FirstOf(discreteValue(), rangeValue(), uuidValue(), circularValue(), circularRangeValue(), listValue(), - emptyListValue(), emptyMapValue(), randomLengthListValue(), weightedValue(), exactWeightedValue(), - randomContentStringValue(), now(), nowDate(), nowLocalDate(), nowLocalDateTime(), additionValue(), - subtractionValue(), multiplicationValue(), divisionValue(), csvReaderValue()); - } - - /** - * String transformer definition. - * - * @return String transformer definition rule. - */ - public Rule stringTransformer() { - return Sequence(Sequence("string", openParenthesis(), stringLiteral(), push(STRING_VALUE_DELIMITER), - ZeroOrMore(comma(), value()), closedParenthesis()), push(getStringValue())); - } - - /** - * JSON transformer definition. - * - * @return JSON transformer definition rule. - */ - public Rule jsonTransformer() { - return Sequence(function("json", valueReference()), push(new JsonTransformer((Value) pop()))); - } - - /** - * Time format transformer definition. - * - * @return Time format transformer definition rule. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Rule timeFormatTransformer() { - return Sequence(function("time", Sequence(stringLiteral(), comma(), value())), - push(new TimeFormatTransformer((String) pop(1), (Value) pop()))); - } - - /** - * Getter transformer definition. - * - * @return Getter transformer definition rule. - */ - @SuppressWarnings({ "rawtypes" }) - public Rule getterTransformer() { - return Sequence(function("get", Sequence(stringLiteral(), comma(), value())), - push(new GetterTransformer<>((String) pop(1), Object.class, (Value) pop()))); - } - - /** - * Transformer definition. - * - * @return Transformer definition rule. - */ - public Rule transformer() { - return FirstOf(stringTransformer(), jsonTransformer(), timeFormatTransformer(), getterTransformer()); - } - - /** - * Value definition. - * - * @return Value definition rule. - */ - public Rule value() { - return FirstOf(valueReference(), generator(), transformer(), literalValue()); - } - - /** - * Tries to parse matched string to Integer. - * - * @return True if matched string is parsed to Integer, otherwise false. - */ - protected boolean tryParseInt() { - String match = match(); - Integer i = null; - try { - i = Integer.parseInt(match); - push(i); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - /** - * Creates normal distribution. - * - * @return Instance of {@link NormalDistribution}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected NormalDistribution createNormalDistribution() { - List args = (List) pop(); - if (args.isEmpty()) { - return new NormalDistribution(); - } - if (args.size() != 4) { - throw new RuntimeException("Normal distribution must have following parameters:" - + " mean, standard deviation, lower bound and upper bound."); - } - return new NormalDistribution(args.get(0).doubleValue(), args.get(1).doubleValue(), args.get(2).doubleValue(), - args.get(3).doubleValue()); - } - - /** - * Creates discrete value. - * - * @return Instance of {@link DiscreteValue}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected DiscreteValue createDiscreteValue() { - return peek() instanceof Distribution ? new DiscreteValue((List) pop(1), (Distribution) pop()) - : new DiscreteValue((List) pop()); - } - - /** - * Creates appropriate number range depending on number types. - * - * @param beginning Beginning of the range. - * @param end End of the range. - * @return An instance of {@link Range}. - */ - protected Range createNumberRange(Number beginning, Number end) { - if (beginning instanceof Double || end instanceof Double) { - return new Range(beginning.doubleValue(), end.doubleValue()); - } - if (beginning instanceof Float || end instanceof Float) { - return new Range(beginning.floatValue(), end.floatValue()); - } - if (beginning instanceof Long || end instanceof Long) { - return new Range(beginning.longValue(), end.longValue()); - } - if (beginning instanceof Integer || end instanceof Integer) { - return new Range(beginning.intValue(), end.intValue()); - } - if (beginning instanceof Short || end instanceof Short) { - return new Range(beginning.shortValue(), end.shortValue()); - } - if (beginning instanceof Byte || end instanceof Byte) { - return new Range(beginning.byteValue(), end.byteValue()); - } - throw new RuntimeException("Unsupported number type: " + beginning.getClass().getName()); - } - - /** - * Creates long range value. - * - * @return Instance of {@link RangeValueLong}. - */ - @SuppressWarnings({ "rawtypes" }) - protected RangeValue createRangeValue() { - Distribution dist = peek() instanceof Distribution ? (Distribution) pop() : null; - Boolean useEdgeCases = peek() instanceof Boolean ? (Boolean) pop() : null; - Range range = (Range) pop(); - return rangeValueFactory.create(range, useEdgeCases, dist); - } - - /** - * Creates random length list value. - * - * @return Instance of {@link RandomLengthListValue}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected RandomLengthListValue createRandomLengthListValue() { - Distribution dist = peek() instanceof Distribution ? (Distribution) pop() : null; - Value elementGenerator = (Value) pop(); - int maxLength = (int) pop(); - int minLength = (int) pop(); - if (dist == null) { - return new RandomLengthListValue(minLength, maxLength, elementGenerator); - } else { - return new RandomLengthListValue(minLength, maxLength, elementGenerator, dist); - } - } - - /** - * Creates random content value. - * - * @return Instance of {@link RandomContentStringValue}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected RandomContentStringValue createRandomContentStringValue() { - return peek() instanceof List ? new RandomContentStringValue((Value) pop(1), (List) pop()) - : new RandomContentStringValue((Value) pop()); - } - - /** - * Creates addition value. - * - * @return An addition value. - */ - @SuppressWarnings("rawtypes") - protected Object createAdditionValue() { - String type = (String) pop(2); - Value summand1 = (Value) pop(1); - Value summand2 = (Value) pop(); - switch (type) { - case "byte": - return new AdditionValueByte(summand1, summand2); - case "short": - return new AdditionValueShort(summand1, summand2); - case "int": - return new AdditionValueInteger(summand1, summand2); - case "long": - return new AdditionValueLong(summand1, summand2); - case "float": - return new AdditionValueFloat(summand1, summand2); - case "double": - return new AdditionValueDouble(summand1, summand2); - default: - throw new RuntimeException("Unsupported type for addition value. Type: " + type); - } - } - - /** - * Creates subtraction value. - * - * @return An subtraction value. - */ - @SuppressWarnings("rawtypes") - protected Object createSubtractionValue() { - String type = (String) pop(2); - Value minuend = (Value) pop(1); - Value subtrahend = (Value) pop(); - switch (type) { - case "byte": - return new SubtractionValueByte(minuend, subtrahend); - case "short": - return new SubtractionValueShort(minuend, subtrahend); - case "int": - return new SubtractionValueInteger(minuend, subtrahend); - case "long": - return new SubtractionValueLong(minuend, subtrahend); - case "float": - return new SubtractionValueFloat(minuend, subtrahend); - case "double": - return new SubtractionValueDouble(minuend, subtrahend); - default: - throw new RuntimeException("Unsupported type for subtraction value. Type: " + type); - } - } - - /** - * Creates multiplication value. - * - * @return An multiplication value. - */ - @SuppressWarnings("rawtypes") - protected Object createMultiplicationValue() { - String type = (String) pop(2); - Value factor1 = (Value) pop(1); - Value factor2 = (Value) pop(); - switch (type) { - case "byte": - return new MultiplicationValueByte(factor1, factor2); - case "short": - return new MultiplicationValueShort(factor1, factor2); - case "int": - return new MultiplicationValueInteger(factor1, factor2); - case "long": - return new MultiplicationValueLong(factor1, factor2); - case "float": - return new MultiplicationValueFloat(factor1, factor2); - case "double": - return new MultiplicationValueDouble(factor1, factor2); - default: - throw new RuntimeException("Unsupported type for multplication value. Type: " + type); - } - } - - /** - * Creates division value. - * - * @return An division value. - */ - @SuppressWarnings("rawtypes") - protected Object createDivisionValue() { - String type = (String) pop(2); - Value dividend = (Value) pop(1); - Value divisor = (Value) pop(); - switch (type) { - case "byte": - return new DivisionValueByte(dividend, divisor); - case "short": - return new DivisionValueShort(dividend, divisor); - case "int": - return new DivisionValueInteger(dividend, divisor); - case "long": - return new DivisionValueLong(dividend, divisor); - case "float": - return new DivisionValueFloat(dividend, divisor); - case "double": - return new DivisionValueDouble(dividend, divisor); - default: - throw new RuntimeException("Unsupported type for division value. Type: " + type); - } - } - - /** - * Creates CSV value. - * - * @return An CSV value. - */ - protected CsvReaderValue createCsvReaderValue() { - CSVParserSettings parserSettings = null; - switch (getContext().getValueStack().size()) { - case 1: - parserSettings = new CSVParserSettings((String) pop()); - break; - case 2: - parserSettings = new CSVParserSettings((String) pop(1), (char) pop()); - break; - case 8: - parserSettings = new CSVParserSettings((String) pop(7), (char) pop(6), (String) pop(5), (boolean) pop(4), - peek(3) instanceof NullValue ? null : (Character) pop(3), (char) pop(2), (boolean) pop(1), - peek() instanceof NullValue ? null : (String) pop()); - break; - default: - throw new RuntimeException("Unsupported number of parameters, should not happen ever."); - } - return new CsvReaderValue(parserSettings); - } - - /** - * Trims off ' and " characters from beginning and end of the string. - * - * @param s String to be trimmed off. - * @return Trimmed off string. - */ - protected String trimOffEnds(String s) { - return s.substring(1, s.length() - 1); - } - - /** - * Returns or creates new value proxy for given name. - * - * @param name Name of the value proxy. - * @return Proxy value. - */ - protected Value getValueProxy(String name) { - String parent = parentName; - while (parent != null) { - String testName = null; - if (parent.isEmpty()) { - testName = name; - parent = null; - } else { - testName = parent + "." + name; - parent = stripOffLastReference(parent); - } - if (proxyValues.containsKey(testName)) { - return proxyValues.get(testName); - } - } - throw new InvalidReferenceNameException(name); - } - - /** - * Strips off the last reference from name. - * - * @param name Name from which to strip off the last reference. - * @return Name with stripped off last reference. - */ - protected String stripOffLastReference(String name) { - if (!name.contains(".")) { - return ""; - } else { - return name.substring(0, name.lastIndexOf('.')); - } - } - - /** - * Constructs {@link StringTransformer}. - * - * @return Instance of {@link StringTransformer}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected Value getStringValue() { - List values = getItemsUpToDelimiter(STRING_VALUE_DELIMITER); - String formatString = (String) pop(); - return new StringTransformer(formatString, values); - } - - /** - * Collects all items up to specified delimiter. - * - * @param delimiter Delimiter up to which to collect all the items. - * @param Type of item. - * @return List of items up to specified delimiter. - */ - @SuppressWarnings({ "unchecked" }) - protected List getItemsUpToDelimiter(String delimiter) { - List result = new ArrayList<>(); - while (true) { - Object val = pop(); - if (val instanceof String && ((String) val).equals(delimiter)) { - break; - } else { - result.add((T) val); - } - } - Collections.reverse(result); - return result; - } + FirstOf( + Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), + Optional(exponent())), + Sequence('.', unsignedIntegerLiteral(), Optional(exponent())), + Sequence(unsignedIntegerLiteral(), Optional(exponent())))), + push(match()))), + push(Float.parseFloat((String) pop()))); + } + + /** + * Implicit double definition. + * + * @return Implicit double definition rule. + */ + public Rule implicitDoubleLiteral() { + return Sequence( + Sequence(Optional(sign()), + FirstOf( + Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), + Optional(exponent())), + Sequence('.', unsignedIntegerLiteral(), Optional(exponent())))), + push(Double.parseDouble(match()))); + } + + /** + * Explicit double definition. + * + * @return Explicit double definition rule. + */ + public Rule explicitDoubleLiteral() { + return Sequence( + function("double", + Sequence( + Sequence(Optional(sign()), + FirstOf( + Sequence(unsignedIntegerLiteral(), '.', unsignedIntegerLiteral(), + Optional(exponent())), + Sequence('.', unsignedIntegerLiteral(), Optional(exponent())), + Sequence(unsignedIntegerLiteral(), Optional(exponent())))), + push(match()))), + push(Double.parseDouble((String) pop()))); + } + + /** + * Double definition. + * + * @return Double definition rule. + */ + public Rule doubleLiteral() { + return FirstOf(explicitDoubleLiteral(), implicitDoubleLiteral()); + } + + /** + * Number definition. + * + * @return Number definition rule. + */ + public Rule numberLiteral() { + return FirstOf(explicitByteLiteral(), explicitShortLiteral(), explicitIntegerLiteral(), + explicitLongLiteral(), explicitFloatLiteral(), explicitDoubleLiteral(), + implicitDoubleLiteral(), implicitIntegerLiteral(), implicitLongLiteral()); + } + + /** + * Number value definition. + * + * @return Number value definition rule. + */ + public Rule numberLiteralValue() { + return Sequence(numberLiteral(), push(ConstantValue.of(pop()))); + } + + /** + * Boolean literal definition. + * + * @return Boolean literal definition rule. + */ + public Rule booleanLiteral() { + return Sequence(FirstOf(FirstOf("True", "true"), FirstOf("False", "false")), + push(Boolean.parseBoolean(match()))); + } + + /** + * Boolean value definition. + * + * @return Boolean value definition rule. + */ + public Rule booleanLiteralValue() { + return Sequence(booleanLiteral(), push(ConstantValue.of(pop()))); + } + + /** + * String definition. + * + * @return String definition rule. + */ + public Rule stringLiteral() { + return FirstOf(singleQuoteStringLiteral(), doubleQuoteStringLiteral()); + } + + /** + * Character literal definition. + * + * @return Character literal definition rule. + */ + public Rule charLiteral() { + return Sequence(Sequence('\'', FirstOf(escape(), Sequence(TestNot(AnyOf("'\\")), ANY)), '\''), + push(new Character(match().charAt(1) == '\\' ? match().charAt(2) : match().charAt(1)))); + } + + /** + * Naked string definition. + * + * @return Naked string definition rule. + */ + public Rule nakedStringLiteral() { + return Sequence(TestNot(AnyOf("\r\n\"'\\")), ZeroOrMore(ANY), push(match())); + } + + /** + * Single quote string definition. + * + * @return Single quote string definition rule. + */ + public Rule singleQuoteStringLiteral() { + return Sequence( + Sequence("'", ZeroOrMore(FirstOf(escape(), Sequence(TestNot(AnyOf("\r\n'\\")), ANY))), "'"), + push(trimOffEnds(match()))); + } + + /** + * Double quote string definition. + * + * @return Double quote string definition rule. + */ + public Rule doubleQuoteStringLiteral() { + return Sequence(Sequence('"', + ZeroOrMore(FirstOf(escape(), Sequence(TestNot(AnyOf("\r\n\"\\")), ANY))), '"'), + push(trimOffEnds(match()))); + } + + /** + * String value definition. + * + * @return String value definition rule. + */ + public Rule stringLiteralValue() { + return Sequence(FirstOf(stringLiteral(), nakedStringLiteral()), push(ConstantValue.of(pop()))); + } + + /** + * Literal definition. + * + * @return Literal definition rule. + */ + public Rule literalValue() { + return FirstOf(nullValue(), numberLiteralValue(), booleanLiteralValue(), stringLiteralValue()); + } + + /** + * Identifier definition. + * + * @return Identifier definition rule. + */ + public Rule identifier() { + return Sequence(Sequence(letter(), ZeroOrMore(letterOrDigit())), push(match())); + } + + /** + * Identifier definition which does not push match to value stack. + * + * @return Identifier definition rule. + */ + public Rule identifierWithNoPush() { + return Sequence(letter(), ZeroOrMore(letterOrDigit())); + } + + /** + * Number range definition. + * + * @return Number range definition rule. + */ + public Rule numberRange() { + return Sequence(Sequence(numberLiteral(), "..", numberLiteral()), + push(createNumberRange((Number) pop(1), (Number) pop()))); + } + + /** + * Character range definition. + * + * @return Character range definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule charRange() { + return Sequence(Sequence(charLiteral(), "..", charLiteral()), + push(new Range((Character) pop(1), (Character) pop()))); + } + + /** + * Function definition. + * + * @param functionName + * Name of a function. + * @return Function definition rule. + */ + protected Rule function(String functionName) { + return function(functionName, fromStringLiteral("")); + } + + /** + * Function definition. + * + * @param functionArgument + * Function argument rule. + * @return Function definition rule. + */ + protected Rule function(Rule functionArgument) { + return function("", functionArgument); + } + + /** + * Function definition. + * + * @param functionName + * Name of a function. + * @param functionArgument + * Function argument rule. + * @return Function definition rule. + */ + protected Rule function(String functionName, Rule functionArgument) { + return Sequence(functionName, openParenthesis(), functionArgument, closedParenthesis()); + } + + /** + * List of items enclosed in brackets. + * + * @param rule + * Rule of a list item. + * @return Bracket list definition rule. + */ + protected Rule bracketList(Rule rule) { + return Sequence(openBracket(), list(rule), closedBracket()); + } + + /** + * List of items. + * + * @param rule + * Rule of a list item. + * @return List definition rule. + */ + protected Rule list(Rule rule) { + return Sequence(Sequence(push("args"), Optional(rule, ZeroOrMore(comma(), rule))), + push(getItemsUpToDelimiter("args"))); + } + + /** + * Value reference definition. + * + * @return Value reference definition rule. + */ + public Rule valueReference() { + return Sequence('$', + Sequence(Sequence(identifierWithNoPush(), ZeroOrMore('.', identifierWithNoPush())), + push(getValueProxy(match())))); + } + + /** + * Uniform distribution definition. + * + * @return Uniform distribution definition rule. + */ + public Rule uniformDistribution() { + return Sequence(function("uniform"), push(new UniformDistribution())); + } + + /** + * Normal distribution definition. + * + * @return Normal distribution definition rule. + */ + public Rule normalDistribution() { + return Sequence(function("normal", list(numberLiteral())), push(createNormalDistribution())); + } + + /** + * Distribution definition. + * + * @return Distribution definition rule. + */ + public Rule distribution() { + return FirstOf(uniformDistribution(), normalDistribution()); + } + + /** + * Discrete value definition. + * + * @return Discrete value definition rule. + */ + public Rule discreteValue() { + return Sequence( + function("random", Sequence(bracketList(value()), Optional(comma(), distribution()))), + push(createDiscreteValue())); + } + + /** + * Long range value definition. + * + * @return Long range value definition rule. + */ + public Rule rangeValue() { + return Sequence( + function("random", + Sequence(numberRange(), + Optional(comma(), booleanLiteral(), Optional(comma(), distribution())))), + push(createRangeValue())); + } + + /** + * UUID value definition. + * + * @return UUID value definition rule. + */ + public Rule uuidValue() { + return Sequence(function("uuid"), push(new UUIDValue())); + } + + /** + * Circular value definition. + * + * @return Circular value definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule circularValue() { + return Sequence(function("circular", bracketList(value())), + push(new CircularValue((List) pop()))); + } + + /** + * Circular range value definition. + * + * @return Circular range value definition rule. + */ + @SuppressWarnings({ "rawtypes" }) + public Rule circularRangeValue() { + return Sequence(function("circular", Sequence(numberRange(), comma(), numberLiteral())), + push(circularRangeValueFactory.create((Range) pop(1), (Number) pop()))); + } + + /** + * List value definition. + * + * @return List value definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule listValue() { + return Sequence(function("list", bracketList(value())), push(new ListValue((List) pop()))); + } + + /** + * Empty list value definition. + * + * @return Empty list value definition rule. + */ + @SuppressWarnings({ "rawtypes" }) + public Rule emptyListValue() { + return Sequence(function("emptyList"), push(new EmptyListValue())); + } + + /** + * Empty map value definition. + * + * @return Empty map value definition rule. + */ + @SuppressWarnings({ "rawtypes" }) + public Rule emptyMapValue() { + return Sequence(function("emptyMap"), push(new EmptyMapValue())); + } + + /** + * Random length list value definition. + * + * @return Random length list value definition rule. + */ + public Rule randomLengthListValue() { + return Sequence(function("list", Sequence(numberLiteral(), comma(), numberLiteral(), comma(), + value(), Optional(comma(), distribution()))), push(createRandomLengthListValue())); + } + + /** + * Weighted value pair definition. + * + * @return Weighted value pair definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule weightedValuePair() { + return Sequence(function(Sequence(value(), comma(), numberLiteral())), + push(new WeightedValuePair((Value) pop(1), ((Number) pop()).doubleValue()))); + } + + /** + * Weighted value definition. + * + * @return Weighted value definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule weightedValue() { + return Sequence(function("weighted", bracketList(weightedValuePair())), + push(new WeightedValue((List) pop()))); + } + + /** + * Count value pair definition. + * + * @return Count value pair definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule countValuePair() { + return Sequence(function(Sequence(value(), comma(), longLiteral())), + push(new CountValuePair((Value) pop(1), (Long) pop()))); + } + + /** + * Exact weighted value definition. + * + * @return Weighted value definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule exactWeightedValue() { + return Sequence(function("exactly", bracketList(countValuePair())), + push(new ExactWeightedValue<>((List) pop()))); + } + + /** + * Random content value definition. + * + * @return Random content value definition rule. + */ + public Rule randomContentStringValue() { + return Sequence( + function("randomContentString", + Sequence(value(), Optional(comma(), bracketList(charRange())))), + push(createRandomContentStringValue())); + } + + /** + * UserDefined Content Value definition. + * + * @return UserDefined Content Value definition Rule + */ + public Rule userDefinedValue() { + return Sequence( + function("userDefinedValue", + Sequence(value(), comma(), value(), comma(), value(), comma(), value())), + push(createUserDefinedValue())); + } + + /** + * Now definition. + * + * @return Now definition rule. + */ + public Rule now() { + return Sequence(function("now"), push(new NowValue())); + } + + /** + * Now date definition. + * + * @return Now date definition rule. + */ + public Rule nowDate() { + return Sequence(function("nowDate"), push(new NowDateValue())); + } + + /** + * Now local date definition. + * + * @return Now local date definition rule. + */ + public Rule nowLocalDate() { + return Sequence(function("nowLocalDate"), push(new NowLocalDateValue())); + } + + /** + * Now local date time definition. + * + * @return Now local date time definition rule. + */ + public Rule nowLocalDateTime() { + return Sequence(function("nowLocalDateTime"), push(new NowLocalDateTimeValue())); + } + + /** + * Addition value definition. + * + * @return Addition value definition rule. + */ + public Rule additionValue() { + return Sequence(function("add", Sequence(stringLiteral(), comma(), value(), comma(), value())), + push(createAdditionValue())); + } + + /** + * Subtraction value definition. + * + * @return Subtraction value definition rule. + */ + public Rule subtractionValue() { + return Sequence( + function("subtract", Sequence(stringLiteral(), comma(), value(), comma(), value())), + push(createSubtractionValue())); + } + + /** + * Multiplication value definition. + * + * @return Multiplication value definition rule. + */ + public Rule multiplicationValue() { + return Sequence( + function("multiply", Sequence(stringLiteral(), comma(), value(), comma(), value())), + push(createMultiplicationValue())); + } + + /** + * Division value definition. + * + * @return Division value definition rule. + */ + public Rule divisionValue() { + return Sequence( + function("divide", Sequence(stringLiteral(), comma(), value(), comma(), value())), + push(createDivisionValue())); + } + + /** + * CSV value definition. + * + * @return CSV value definition rule. + */ + public Rule csvReaderValue() { + return Sequence( + function("csv", + Sequence(stringLiteral(), + Optional(comma(), charLiteral(), + Optional(comma(), stringLiteral(), comma(), booleanLiteral(), comma(), + FirstOf(nullValue(), charLiteral()), comma(), charLiteral(), comma(), + booleanLiteral(), comma(), FirstOf(nullValue(), stringLiteral()))))), + push(createCsvReaderValue())); + } + + /** + * Generator definition. + * + * @return Generator definition rule. + */ + public Rule generator() { + return FirstOf(discreteValue(), rangeValue(), uuidValue(), circularValue(), + circularRangeValue(), listValue(), emptyListValue(), emptyMapValue(), + randomLengthListValue(), weightedValue(), exactWeightedValue(), randomContentStringValue(), + now(), nowDate(), nowLocalDate(), nowLocalDateTime(), additionValue(), subtractionValue(), + multiplicationValue(), divisionValue(), csvReaderValue(), userDefinedValue()); + } + + /** + * String transformer definition. + * + * @return String transformer definition rule. + */ + public Rule stringTransformer() { + return Sequence(Sequence("string", openParenthesis(), stringLiteral(), + push(STRING_VALUE_DELIMITER), ZeroOrMore(comma(), value()), closedParenthesis()), + push(getStringValue())); + } + + /** + * JSON transformer definition. + * + * @return JSON transformer definition rule. + */ + public Rule jsonTransformer() { + return Sequence(function("json", valueReference()), + push(new JsonTransformer((Value) pop()))); + } + + /** + * Time format transformer definition. + * + * @return Time format transformer definition rule. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Rule timeFormatTransformer() { + return Sequence(function("time", Sequence(stringLiteral(), comma(), value())), + push(new TimeFormatTransformer((String) pop(1), (Value) pop()))); + } + + /** + * Getter transformer definition. + * + * @return Getter transformer definition rule. + */ + @SuppressWarnings({ "rawtypes" }) + public Rule getterTransformer() { + return Sequence(function("get", Sequence(stringLiteral(), comma(), value())), + push(new GetterTransformer<>((String) pop(1), Object.class, (Value) pop()))); + } + + /** + * Transformer definition. + * + * @return Transformer definition rule. + */ + public Rule transformer() { + return FirstOf(stringTransformer(), jsonTransformer(), timeFormatTransformer(), + getterTransformer()); + } + + /** + * Value definition. + * + * @return Value definition rule. + */ + public Rule value() { + return FirstOf(valueReference(), generator(), transformer(), literalValue()); + } + + /** + * Tries to parse matched string to Integer. + * + * @return True if matched string is parsed to Integer, otherwise false. + */ + protected boolean tryParseInt() { + String match = match(); + Integer i = null; + try { + i = Integer.parseInt(match); + push(i); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Creates normal distribution. + * + * @return Instance of {@link NormalDistribution}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected NormalDistribution createNormalDistribution() { + List args = (List) pop(); + if (args.isEmpty()) { + return new NormalDistribution(); + } + if (args.size() != 4) { + throw new RuntimeException("Normal distribution must have following parameters:" + + " mean, standard deviation, lower bound and upper bound."); + } + return new NormalDistribution(args.get(0).doubleValue(), args.get(1).doubleValue(), + args.get(2).doubleValue(), args.get(3).doubleValue()); + } + + /** + * Creates discrete value. + * + * @return Instance of {@link DiscreteValue}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected DiscreteValue createDiscreteValue() { + return peek() instanceof Distribution + ? new DiscreteValue((List) pop(1), (Distribution) pop()) + : new DiscreteValue((List) pop()); + } + + /** + * Creates appropriate number range depending on number types. + * + * @param beginning + * Beginning of the range. + * @param end + * End of the range. + * @return An instance of {@link Range}. + */ + protected Range createNumberRange(Number beginning, Number end) { + if (beginning instanceof Double || end instanceof Double) { + return new Range(beginning.doubleValue(), end.doubleValue()); + } + if (beginning instanceof Float || end instanceof Float) { + return new Range(beginning.floatValue(), end.floatValue()); + } + if (beginning instanceof Long || end instanceof Long) { + return new Range(beginning.longValue(), end.longValue()); + } + if (beginning instanceof Integer || end instanceof Integer) { + return new Range(beginning.intValue(), end.intValue()); + } + if (beginning instanceof Short || end instanceof Short) { + return new Range(beginning.shortValue(), end.shortValue()); + } + if (beginning instanceof Byte || end instanceof Byte) { + return new Range(beginning.byteValue(), end.byteValue()); + } + throw new RuntimeException("Unsupported number type: " + beginning.getClass().getName()); + } + + /** + * Creates long range value. + * + * @return Instance of {@link RangeValueLong}. + */ + @SuppressWarnings({ "rawtypes" }) + protected RangeValue createRangeValue() { + Distribution dist = peek() instanceof Distribution ? (Distribution) pop() : null; + Boolean useEdgeCases = peek() instanceof Boolean ? (Boolean) pop() : null; + Range range = (Range) pop(); + return rangeValueFactory.create(range, useEdgeCases, dist); + } + + /** + * Creates random length list value. + * + * @return Instance of {@link RandomLengthListValue}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected RandomLengthListValue createRandomLengthListValue() { + Distribution dist = peek() instanceof Distribution ? (Distribution) pop() : null; + Value elementGenerator = (Value) pop(); + int maxLength = (int) pop(); + int minLength = (int) pop(); + if (dist == null) { + return new RandomLengthListValue(minLength, maxLength, elementGenerator); + } else { + return new RandomLengthListValue(minLength, maxLength, elementGenerator, dist); + } + } + + /** + * Creates random content value. + * + * @return Instance of {@link RandomContentStringValue}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected RandomContentStringValue createRandomContentStringValue() { + return peek() instanceof List + ? new RandomContentStringValue((Value) pop(1), (List) pop()) + : new RandomContentStringValue((Value) pop()); + } + + /** + * Creates addition value. + * + * @return An addition value. + */ + @SuppressWarnings("rawtypes") + protected Object createAdditionValue() { + String type = (String) pop(2); + Value summand1 = (Value) pop(1); + Value summand2 = (Value) pop(); + switch (type) { + case "byte" : + return new AdditionValueByte(summand1, summand2); + case "short" : + return new AdditionValueShort(summand1, summand2); + case "int" : + return new AdditionValueInteger(summand1, summand2); + case "long" : + return new AdditionValueLong(summand1, summand2); + case "float" : + return new AdditionValueFloat(summand1, summand2); + case "double" : + return new AdditionValueDouble(summand1, summand2); + default : + throw new RuntimeException("Unsupported type for addition value. Type: " + type); + } + } + + /** + * Creates subtraction value. + * + * @return An subtraction value. + */ + @SuppressWarnings("rawtypes") + protected Object createSubtractionValue() { + String type = (String) pop(2); + Value minuend = (Value) pop(1); + Value subtrahend = (Value) pop(); + switch (type) { + case "byte" : + return new SubtractionValueByte(minuend, subtrahend); + case "short" : + return new SubtractionValueShort(minuend, subtrahend); + case "int" : + return new SubtractionValueInteger(minuend, subtrahend); + case "long" : + return new SubtractionValueLong(minuend, subtrahend); + case "float" : + return new SubtractionValueFloat(minuend, subtrahend); + case "double" : + return new SubtractionValueDouble(minuend, subtrahend); + default : + throw new RuntimeException("Unsupported type for subtraction value. Type: " + type); + } + } + + /** + * Creates multiplication value. + * + * @return An multiplication value. + */ + @SuppressWarnings("rawtypes") + protected Object createMultiplicationValue() { + String type = (String) pop(2); + Value factor1 = (Value) pop(1); + Value factor2 = (Value) pop(); + switch (type) { + case "byte" : + return new MultiplicationValueByte(factor1, factor2); + case "short" : + return new MultiplicationValueShort(factor1, factor2); + case "int" : + return new MultiplicationValueInteger(factor1, factor2); + case "long" : + return new MultiplicationValueLong(factor1, factor2); + case "float" : + return new MultiplicationValueFloat(factor1, factor2); + case "double" : + return new MultiplicationValueDouble(factor1, factor2); + default : + throw new RuntimeException("Unsupported type for multplication value. Type: " + type); + } + } + + /** + * Creates division value. + * + * @return An division value. + */ + @SuppressWarnings("rawtypes") + protected Object createDivisionValue() { + String type = (String) pop(2); + Value dividend = (Value) pop(1); + Value divisor = (Value) pop(); + switch (type) { + case "byte" : + return new DivisionValueByte(dividend, divisor); + case "short" : + return new DivisionValueShort(dividend, divisor); + case "int" : + return new DivisionValueInteger(dividend, divisor); + case "long" : + return new DivisionValueLong(dividend, divisor); + case "float" : + return new DivisionValueFloat(dividend, divisor); + case "double" : + return new DivisionValueDouble(dividend, divisor); + default : + throw new RuntimeException("Unsupported type for division value. Type: " + type); + } + } + + /** + * Creates CSV value. + * + * @return An CSV value. + */ + protected CsvReaderValue createCsvReaderValue() { + CSVParserSettings parserSettings = null; + switch (getContext().getValueStack().size()) { + case 1 : + parserSettings = new CSVParserSettings((String) pop()); + break; + case 2 : + parserSettings = new CSVParserSettings((String) pop(1), (char) pop()); + break; + case 8 : + parserSettings = new CSVParserSettings((String) pop(7), (char) pop(6), (String) pop(5), + (boolean) pop(4), peek(3) instanceof NullValue ? null : (Character) pop(3), + (char) pop(2), (boolean) pop(1), peek() instanceof NullValue ? null : (String) pop()); + break; + default : + throw new RuntimeException("Unsupported number of parameters, should not happen ever."); + } + return new CsvReaderValue(parserSettings); + } + + /** + * Creates a UserDefinedValue fetched from the configured Data Source. + * + * @return UserDefinedValue + */ + @SuppressWarnings("unchecked") + protected UserDefinedValue createUserDefinedValue() { + return new UserDefinedValue((Value) pop(), (Value) pop(), (Value) pop(), + (Value) pop()); + } + + /** + * Trims off ' and " characters from beginning and end of the string. + * + * @param s + * String to be trimmed off. + * @return Trimmed off string. + */ + protected String trimOffEnds(String s) { + return s.substring(1, s.length() - 1); + } + + /** + * Returns or creates new value proxy for given name. + * + * @param name + * Name of the value proxy. + * @return Proxy value. + */ + protected Value getValueProxy(String name) { + String parent = parentName; + while (parent != null) { + String testName = null; + if (parent.isEmpty()) { + testName = name; + parent = null; + } else { + testName = parent + "." + name; + parent = stripOffLastReference(parent); + } + if (proxyValues.containsKey(testName)) { + return proxyValues.get(testName); + } + } + throw new InvalidReferenceNameException(name); + } + + /** + * Strips off the last reference from name. + * + * @param name + * Name from which to strip off the last reference. + * @return Name with stripped off last reference. + */ + protected String stripOffLastReference(String name) { + if (!name.contains(".")) { + return ""; + } else { + return name.substring(0, name.lastIndexOf('.')); + } + } + + /** + * Constructs {@link StringTransformer}. + * + * @return Instance of {@link StringTransformer}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected Value getStringValue() { + List values = getItemsUpToDelimiter(STRING_VALUE_DELIMITER); + String formatString = (String) pop(); + return new StringTransformer(formatString, values); + } + + /** + * Collects all items up to specified delimiter. + * + * @param delimiter + * Delimiter up to which to collect all the items. + * @param + * Type of item. + * @return List of items up to specified delimiter. + */ + @SuppressWarnings({ "unchecked" }) + protected List getItemsUpToDelimiter(String delimiter) { + List result = new ArrayList<>(); + while (true) { + Object val = pop(); + if (val instanceof String && ((String) val).equals(delimiter)) { + break; + } else { + result.add((T) val); + } + } + Collections.reverse(result); + return result; + } }