diff --git a/src/main/java/com/lmax/simpledsl/DslParam.java b/src/main/java/com/lmax/simpledsl/DslParam.java deleted file mode 100644 index e0b945c..0000000 --- a/src/main/java/com/lmax/simpledsl/DslParam.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -/** - * The base class for all param types. - */ -public abstract class DslParam -{ - /** - * @return this param as a {@code RequiredParam}. - * @deprecated With the addition of covariant return types, this should no longer be necessary. - */ - @Deprecated - public RequiredParam getAsRequiredParam() - { - return null; - } - - /** - * Return this param as a {@link SimpleDslParam} or {@code null} if the param cannot be viewed as a {@code SimpleDslParam} (e.g. because it's a {@link RepeatingParamGroup}). - * - * @return this param as a {@code SimpleDslParam}. - */ - public abstract SimpleDslParam getAsSimpleDslParam(); - - /** - * Return this param as a {@link RepeatingParamGroup} or {@code null} if the param cannot be viewed as a {@code SimpleDslParam} (e.g. because it's a {@code SimpleDslParam}). - * - * @return this param as a {@code RepeatingParamGroup}. - */ - public abstract RepeatingParamGroup asRepeatingParamGroup(); - - /** - * Used as part of parsing the arguments. - * - * @param currentPosition the current argument position in the parse. - * @param args the arguments to be consumed. - * @return the new argument position. - * @deprecated This is not intended to be part of the public API and will be removed in a future release. - */ - public abstract int consume(int currentPosition, NameValuePair... args); - - /** - * Determine if a value is required for this parameter. - * - * @return {@code true} if and only if this param is required. - */ - public boolean isRequired() - { - return false; - } - - /** - * Determine if a value is optional for this parameter. - * - * @return {@code true} if and only if this param is optional. - */ - public boolean isOptional() - { - return false; - } - - /** - * Determine a value was supplied for this parameter. - * - * @return {@code true} if and only if a value (including a blank value) was supplied for this parameter. - */ - public abstract boolean hasValue(); - - /** - * Get the name of this parameter. - * - * @return the parameter name. - */ - public abstract String getName(); - - /** - * Used as part of parsing the arguments. - * - * @return {@code true} if and only if the value is valid. - * @deprecated This is not intended to be part of the public API and will be removed in a future release. - */ - public boolean isValid() - { - return true; - } -} diff --git a/src/main/java/com/lmax/simpledsl/DslParams.java b/src/main/java/com/lmax/simpledsl/DslParams.java deleted file mode 100644 index da21cff..0000000 --- a/src/main/java/com/lmax/simpledsl/DslParams.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * The main entry point for defining the DSL language. Create a DslParams instance with the supplied arguments and the supported params. - * The supplied values can then be retrieved using {@link DslParams#value(String)} and related methods. - * - *
{@code
- *     public void createUser(String... args) {
- *         DslParams params = new DslParams(args,
- *                                          new RequiredParam("user"),
- *                                          new OptionalParam("password"));
- *         getDriver().createUser(params.value("user"), params.valueAsOptional("password"));
- *     }
- * }
- */ -public class DslParams extends DslValues -{ - private static final String USAGE_TOKEN = "-usage"; - - private final DslParam[] params; - private final Map paramsByName = new HashMap<>(); - - /** - * Create a new DslParams to define the supported dsl language. - * - * @param args the arguments supplied by the test. - * @param params the supported parameters. - * @throws IllegalArgumentException if an invalid parameter is specified - */ - public DslParams(final String[] args, final DslParam... params) - { - this.params = params; - checkUsage(args, params); - - final NameValuePair[] arguments = parseArguments(args); - - for (final DslParam param : params) - { - paramsByName.put(param.getName().toLowerCase(), param); - } - - int currentPosition = 0; - - final boolean allParamsOptional = Arrays.stream(params).allMatch(DslParam::isOptional); - final boolean allArgsPositional = Arrays.stream(arguments).filter(Objects::nonNull) - .map(NameValuePair::getName) - .allMatch(Objects::isNull); - if (allParamsOptional && allArgsPositional && params.length == args.length) - { - for (final DslParam param : params) - { - currentPosition = param.consume(currentPosition, arguments); - } - } - - for (final DslParam param : params) - { - if (!param.isRequired() || currentPosition >= arguments.length || !matches(arguments[currentPosition], param)) - { - break; - } - currentPosition = param.consume(currentPosition, arguments); - } - - while (currentPosition < args.length) - { - if (arguments[currentPosition] == null) - { - currentPosition++; - continue; - } - final DslParam param = getDslParam(arguments[currentPosition].getName()); - if (param != null) - { - currentPosition = param.consume(currentPosition, arguments); - } - else - { - throw new IllegalArgumentException("Unexpected argument " + arguments[currentPosition]); - } - } - - checkAllRequiredParamsSupplied(params); - completedParsingArguments(); - } - - private boolean matches(final NameValuePair argument, final DslParam param) - { - return argument.getName() == null || param.getName().equalsIgnoreCase(argument.getName()); - } - - @Override - public String value(final String name) - { - final DslParam param = getDslParam(name); - if (param != null) - { - return param.getAsSimpleDslParam().getValue(); - } - throw new IllegalArgumentException(name + " was not a parameter"); - } - - @Override - public String[] values(final String name) - { - final DslParam param = getDslParam(name); - if (param != null) - { - return param.getAsSimpleDslParam().getValues(); - } - throw new IllegalArgumentException(name + " was not a parameter"); - } - - - /** - * Retrieve the sets of values supplied for a {@link RepeatingParamGroup} or an empty array if no values were supplied. {@code RepeatingParamGroup} requires that it's first parameter - * be a {@link RequiredParam} and the group as a whole is referenced using the name of that parameter. e.g. - * - *
{@code
-     *   DslParams params = new DslParams(args, new RepeatingParamGroup(new RequiredParam("user"),
-     *                                                                  new OptionalParam("password")));
-     *   RepeatingGroup[] usersToCreate = params.valuesAsGroup("user");
-     * }
- * - * @param groupName the name of the first required parameter. - * @return an array of RepeatingGroup instances, one for each set of values supplied for the parameter. - * @throws IllegalArgumentException if {@code name} does not match the name of a RepeatingParamGroup parameter. - */ - public RepeatingGroup[] valuesAsGroup(final String groupName) - { - final DslParam param = getDslParam(groupName); - if (param == null) - { - throw new IllegalArgumentException(groupName + " was not a parameter"); - } - final RepeatingParamGroup repeatingParamGroup = param.asRepeatingParamGroup(); - if (repeatingParamGroup == null) - { - throw new IllegalArgumentException(groupName + " was not a repeating group"); - } - return repeatingParamGroup.values(); - } - - /** - * Get the supported parameters. Supplied values will have been parsed into the parameters. - * - * @return the array of {@link DslParam} instances supplied to the constructor. - */ - public DslParam[] getParams() - { - return params; - } - - /** - * A shorthand way to create a {@code DslParams} instance that accepts a single required parameter and return the value that was supplied for that parameter. - * - * @param args the arguments supplied by the test. - * @param requiredParamName the name of the required parameter. - * @return the value supplied for the parameter. - */ - public static String getSingleRequiredParamValue(final String[] args, final String requiredParamName) - { - return new DslParams(args, new RequiredParam(requiredParamName)).value(requiredParamName); - } - - /** - * Utility method for defining a DSL method that doesn't accept any arguments. This is an alternative to removing the {@code String... args} parameter entirely - * when a consistent public API is desired. - * - * @param args the parameters provided. - */ - public static void checkEmpty(final String[] args) - { - new DslParams(args); - } - - @Override - public boolean hasValue(final String name) - { - final DslParam param = getDslParam(name); - return param != null && param.hasValue(); - } - - private void checkUsage(final String[] args, final DslParam... params) - { - if (args != null && args.length == 1 && USAGE_TOKEN.equals(args[0])) - { - throw new DslParamsUsageException(params); - } - } - - private void checkAllRequiredParamsSupplied(final DslParam[] params) - { - for (final DslParam param : params) - { - if (!param.isValid()) - { - throw new IllegalArgumentException("Missing value for parameter: " + param.getName()); - } - } - } - - private NameValuePair[] parseArguments(final String[] args) - { - final NameValuePair[] arguments = new NameValuePair[args.length]; - for (int i = 0; i < args.length; i++) - { - arguments[i] = args[i] != null ? new NameValuePair(args[i]) : null; - } - return arguments; - } - - private void completedParsingArguments() - { - for (final DslParam param : params) - { - final SimpleDslParam simpleDslParam = param.getAsSimpleDslParam(); - if (simpleDslParam != null) - { - simpleDslParam.completedParsing(); - } - } - } - - private DslParam getDslParam(final String name) - { - return name != null ? paramsByName.get(name.toLowerCase()) : null; - } -} diff --git a/src/main/java/com/lmax/simpledsl/NameValuePair.java b/src/main/java/com/lmax/simpledsl/NameValuePair.java deleted file mode 100644 index c146728..0000000 --- a/src/main/java/com/lmax/simpledsl/NameValuePair.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -class NameValuePair -{ - private static final String SPLIT_NAME_VALUE_REGEX = "=|:"; - - private final String originalValue; - private final String name; - private final String value; - - NameValuePair(final String argString) - { - originalValue = argString; - final String[] splitArg = (argString + " ").split(SPLIT_NAME_VALUE_REGEX); - if (splitArg.length == 1) - { - name = null; - value = argString.trim(); - } - else - { - name = splitArg[0].trim(); - // result is the remainder of argString, in case it contains the split regex chars too - value = argString.substring(splitArg[0].length() + 1).trim(); - } - } - - public String getName() - { - return name; - } - - public String getValue() - { - return value; - } - - @Override - public String toString() - { - return originalValue; - } -} diff --git a/src/main/java/com/lmax/simpledsl/OptionalParam.java b/src/main/java/com/lmax/simpledsl/OptionalParam.java deleted file mode 100755 index b5ab857..0000000 --- a/src/main/java/com/lmax/simpledsl/OptionalParam.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -/** - * An optional parameter. Callers may omit this parameter and the call is still considered valid. When provided, the parameter name must be specified, e.g. {@code "firstName: Joan"}. - * Optional parameters can have a default value which is used when no other value is provided. - * - *

By default, only a single value is allowed. Multiple values can be allowed by calling {@link #setAllowMultipleValues()}

- * - *

Optional parameters are useful as a way of reducing boilerplate in tests by providing default settings and as a way to optionally assert behaviours or values. For example providing - * a method that checks the first name and/or the last name:

- * - *
{@code
- *   DslParams params = new DslParams(args,
- *                                    new OptionalParam("firstName"),
- *                                    new OptionalParam("lastName"));
- *   params.valueAsOptional("firstName").ifPresent(driver::checkFirstName);
- *   params.valueAsOptional("lastName").ifPresent(driver::checkLastName);
- * }
- * - *

Selective assertions like this help tests be flexible to changes outside the area they intend to test by only asserting on the values that are actually relevant.

- * - * @see DslValues#valueAsOptional(String) - */ -public class OptionalParam extends SimpleDslParam -{ - private String defaultValue; - - /** - * Create a new optional param. - * - * @param name the name of the parameter. - */ - public OptionalParam(final String name) - { - super(name); - } - - /** - * Set a default value for this parameter. - * If a default is provided, the parameter will always be considered to have a value and will return the default if no other value is provided by the caller. - * - * @param defaultValue the default value for the parameter. - * @return this parameter - */ - public OptionalParam setDefault(final String defaultValue) - { - this.defaultValue = defaultValue; - return this; - } - - @Override - public String getDefaultValue() - { - return defaultValue; - } - - @Override - public String[] getValues() - { - final String[] values = super.getValues(); - return values.length > 0 ? values : defaultValue != null ? new String[]{defaultValue} : new String[0]; - } - - @Override - public boolean isOptional() - { - return true; - } -} diff --git a/src/main/java/com/lmax/simpledsl/RepeatingGroup.java b/src/main/java/com/lmax/simpledsl/RepeatingGroup.java deleted file mode 100644 index b932ed0..0000000 --- a/src/main/java/com/lmax/simpledsl/RepeatingGroup.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Provides access to the values for a single instance of a {@link RepeatingParamGroup}. Apart from not supporting nested {@code RepeatingParamGroup}s, all the value accessor methods from - * {@link DslParams} are available. - * - * @see RepeatingParamGroup - * @see DslParams#valuesAsGroup(String) - */ -public class RepeatingGroup extends DslValues -{ - private final Map> valuesByName = new HashMap<>(); - - /** - * Used as part of argument parsing. Not intended to be part of the public API and will be removed in a future release. - * - * @param name the name of the parameter - * @param value the value to add. - * @deprecated Not intended to be part of the public API and will be removed in a future release. - */ - public void addValue(final String name, final String value) - { - List values = getValues(name); - if (values == null) - { - values = new ArrayList<>(); - valuesByName.put(name.toLowerCase(), values); - } - values.add(value); - } - - @Override - public boolean hasValue(final String name) - { - return valuesByName.containsKey(name.toLowerCase()); - } - - @Override - public String value(final String name) - { - final String[] strings = values(name); - return strings.length > 0 ? strings[0] : null; - } - - @Override - public String[] values(final String name) - { - final List values = getValues(name); - return values != null ? values.toArray(new String[values.size()]) : new String[0]; - } - - private List getValues(final String name) - { - return name != null ? valuesByName.get(name.toLowerCase()) : null; - } -} diff --git a/src/main/java/com/lmax/simpledsl/RepeatingParamGroup.java b/src/main/java/com/lmax/simpledsl/RepeatingParamGroup.java deleted file mode 100644 index bc3a690..0000000 --- a/src/main/java/com/lmax/simpledsl/RepeatingParamGroup.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Define a group of parameters that can be repeated 0 or more times. The first parameter in the group must be a {@link RequiredParam} and is used to identify the start - * of the group in arguments and when retrieving the groups from {@link DslParams#valuesAsGroup(String)}. Other parameters may follow the first parameter in any order. - * - *

Multiple {@code RepeatingParamGroup}s can be used within the same {@code DslParams} but they cannot be nested.

- * - *
{@code
- * public void createUsers(String... args) {
- *     DslParams params = new DslParams(
- *         args,
- *         new RepeatingParamGroup(
- *             new RequiredParam("user"),
- *             new OptionalParam("password").setDefault("aPassword")
- *         ));
- *     for (RepeatingGroup user : params.valueAsGroup("user")) {
- *         driver.createUser(user.value("user"), user.value("password"));
- *     }
- * }
- * }
- * - * @see RepeatingGroup - * @see DslParams#valuesAsGroup(String) - */ -public class RepeatingParamGroup extends DslParam -{ - private final SimpleDslParam identity; - private final Map paramsByName = new HashMap<>(); - private final List values = new ArrayList<>(); - - /** - * Create a new {@code RepeatingParamGroup}. - * - * @param firstParam the parameter that marks the start of the group. - * @param params the other parameters in the group. - */ - public RepeatingParamGroup(final RequiredParam firstParam, final SimpleDslParam... params) - { - this.identity = firstParam; - paramsByName.put(identity.getName(), identity); - for (final SimpleDslParam param : params) - { - paramsByName.put(param.getName(), param); - } - } - - @Override - public SimpleDslParam getAsSimpleDslParam() - { - return null; - } - - @Override - public RepeatingParamGroup asRepeatingParamGroup() - { - return this; - } - - /** - * Return the value groups supplied for this repeating group. - * - * @return an array of {@link RepeatingGroup} instances, one for each set of values provided for this repeating group. - */ - public RepeatingGroup[] values() - { - return values.toArray(new RepeatingGroup[values.size()]); - } - - /** - * {@inheritDoc} - * - * @deprecated This is not intended to be part of the public API and will be removed in a future release. - */ - @Override - public int consume(final int startingPosition, final NameValuePair... arguments) - { - final RepeatingGroup group = new RepeatingGroup(); - int currentPosition = startingPosition; - while (currentPosition < arguments.length) - { - final NameValuePair argument = arguments[currentPosition]; - if (argument != null) - { - final SimpleDslParam param = paramsByName.get(argument.getName()); - if (param != null) - { - if (group.hasValue(argument.getName()) && !param.allowMultipleValues) - { - break; - } - param.checkValidValue(argument.getValue()); - group.addValue(argument.getName(), argument.getValue()); - currentPosition++; - } - else - { - break; - } - } - else - { - currentPosition++; - } - } - for (final SimpleDslParam param : paramsByName.values()) - { - if (!group.hasValue(param.getName())) - { - if (param.isRequired()) - { - throw new IllegalArgumentException("Did not supply a value for " + param.getName() + " in group " + identity.getName()); - } - else if (param.getDefaultValue() != null) - { - group.addValue(param.getName(), param.getDefaultValue()); - } - } - } - values.add(group); - return currentPosition; - } - - @Override - public boolean hasValue() - { - return !values.isEmpty(); - } - - @Override - public String getName() - { - return identity.getName(); - } -} diff --git a/src/main/java/com/lmax/simpledsl/RequiredParam.java b/src/main/java/com/lmax/simpledsl/RequiredParam.java deleted file mode 100755 index e52bc7d..0000000 --- a/src/main/java/com/lmax/simpledsl/RequiredParam.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -/** - * A required parameter. An exception will be thrown when parsing the arguments if a value is not supplied for this parameter. - * The name of the parameter may be omitted if required parameters are listed first. For example with the params: - * - *
{@code
- * public void login(String... args) {
- *     DslParams params = new DslParams(args,
- *                                      new RequiredParam("user"),
- *                                      new RequiredParam("password"));
- * }
- * }
- *

- * The following two calls are exactly equivalent: - *

{@code
- * login("joan", "myPassword");
- * login("user: joan", "password: myPassword");
- * }
- *

- * Required parameters can be supplied out of order if the name is specified, so the following is also equivalent: - * - *

{@code
- * login("password: myPassword", "user: joan");
- * }
- *

- * It is highly recommended to include the parameter name if there is any ambiguity about the meaning of the parameter. - * - *

By default, only a single value is allowed. Multiple values can be allowed by calling {@link #setAllowMultipleValues()}

- */ -public class RequiredParam extends SimpleDslParam -{ - /** - * Create a required parameter. - * - * @param name the name of the parameter. - */ - public RequiredParam(final String name) - { - super(name); - } - - @Override - public RequiredParam getAsRequiredParam() - { - return this; - } - - @Override - public boolean isValid() - { - return getValues().length != 0; - } - - @Override - public boolean isRequired() - { - return true; - } - - @Override - public int consume(final int currentPosition, final NameValuePair... args) - { - int position = currentPosition; - while (position < args.length && consumeSingleParam(args, position)) - { - position++; - if (!allowMultipleValues) - { - break; - } - } - return position; - } - - private boolean consumeSingleParam(final NameValuePair[] args, final int position) - { - final NameValuePair nameValue = getArg(args, position); - if (!matches(nameValue.getName())) - { - return false; - } - addValue(nameValue.getValue()); - return true; - } - - private boolean matches(final String argName) - { - return argName == null || name.equalsIgnoreCase(argName); - } - - private NameValuePair getArg(final NameValuePair[] args, final int position) - { - if (position >= args.length) - { - throw new IllegalArgumentException("Missing parameter: " + name); - } - return args[position]; - } -} diff --git a/src/main/java/com/lmax/simpledsl/SimpleDslParam.java b/src/main/java/com/lmax/simpledsl/SimpleDslParam.java deleted file mode 100644 index 8b9d636..0000000 --- a/src/main/java/com/lmax/simpledsl/SimpleDslParam.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -/** - * The root type for all simple params. - * - * @param

the actual type of simple parameter. - */ -public abstract class SimpleDslParam

> extends DslParam -{ - private final List values = new LinkedList<>(); - private static final String DEFAULT_DELIMITER = ","; - private String[] allowedValues; - protected final String name; - protected boolean allowMultipleValues; - protected String multipleValueSeparator; - private BiConsumer consumer; - private boolean calledConsumer; - - /** - * Create the parameter. - * - * @param name the parameter name. - */ - public SimpleDslParam(final String name) - { - this.name = name; - } - - @Override - public SimpleDslParam getAsSimpleDslParam() - { - return this; - } - - @Override - public RepeatingParamGroup asRepeatingParamGroup() - { - return null; - } - - /** - * Restrict the allowed values for this parameter to the specified set. Specifying a value outside of this set will result in an exception being thrown when parsing the arguments. - * - * @param allowedValues the allowable values for this parameter. - * @return this parameter - */ - public P setAllowedValues(final String... allowedValues) - { - this.allowedValues = allowedValues; - return (P) this; - } - - /** - * Allow multiple values to be specified for this parameter, either as separate arguments or using comma (,) as a delimiter. - *

- * The following calls are equivalent: - *

{@code
-     * verifyUsersPresent("user: joan", "user: jenny", "user: joanne");
-     * verifyUsersPresent("user: joan, jenny, joanne");
-     * }
- * - * @return this parameter - * @see #setAllowMultipleValues(String) - */ - public P setAllowMultipleValues() - { - return setAllowMultipleValues(DEFAULT_DELIMITER); - } - - /** - * Allow multiple values to be specified for this parameter, either as separate arguments or using the specified string as a delimiter. - * - * @param delimiter the delimiter to use to separate values - * @return this parameter - * @see #setAllowMultipleValues() - */ - public P setAllowMultipleValues(final String delimiter) - { - allowMultipleValues = true; - multipleValueSeparator = delimiter; - return (P) this; - } - - /** - * Set a consumer which is called for every value supplied to this parameter. If multiple values are allowed, the consumer will be called once for each supplied value. - * For an {@link OptionalParam} with a default value set, the consumer will be called with the default value if no other value is supplied. - * - * @param consumer the consumer provide values to. - * @return this parameter. - * @see #setConsumer(BiConsumer) - */ - public P setConsumer(final Consumer consumer) - { - this.consumer = (name, value) -> consumer.accept(value); - return (P) this; - } - - - /** - * Set a consumer which is called with the name and value for every value supplied to this parameter. If multiple values are allowed, the consumer will be called once for each supplied value. - * For an {@link OptionalParam} with a default value set, the consumer will be called with the default value if no other value is supplied. - *

- * This differs from {@link #setConsumer(Consumer)} by supplying the name and value to the consumer. The name is supplied as the first argument and the value as the second. - * - * @param consumer the consumer provide values to. - * @return this parameter. - * @see #setConsumer(Consumer) - */ - public P setConsumer(final BiConsumer consumer) - { - this.consumer = consumer; - return (P) this; - } - - @Override - public int consume(final int currentPosition, final NameValuePair... args) - { - final NameValuePair arg = args[currentPosition]; - addValue(null == arg ? null : arg.getValue()); - return currentPosition + 1; - } - - @Override - public boolean hasValue() - { - return allowMultipleValues ? getValues().length > 0 : getValue() != null; - } - - /** - * Get the default value for this parameter. - * - * @return the default value. - */ - public String getDefaultValue() - { - return null; - } - - /** - * Used when parsing values. Not intended to be part of the public API and will be removed in a future release. - * - * @param value the value to add to this parameter. - */ - public void addValue(final String value) - { - if (allowMultipleValues) - { - final String[] values = value.split(multipleValueSeparator); - for (final String singleValue : values) - { - addSingleValue(singleValue.trim()); - } - } - else - { - addSingleValue(value); - } - } - - private void addSingleValue(final String value) - { - checkCanAddValue(); - values.add(checkValidValue(value)); - callConsumer(value); - } - - String checkValidValue(final String value) - { - if (allowedValues != null) - { - for (final String allowedValue : allowedValues) - { - if (allowedValue.equalsIgnoreCase(value)) - { - return allowedValue; - } - } - throw new IllegalArgumentException(name + " parameter value '" + value + "' must be one of: " + Arrays.toString(allowedValues)); - } - return value; - } - - private void checkCanAddValue() - { - if (!allowMultipleValues && values.size() == 1) - { - throw new IllegalArgumentException("Multiple " + name + " parameters are not allowed"); - } - } - - /** - * Get the value for this parameter. If multiple values are allowed, use {@link #getValues()} instead. - * - * @return the value for this parameter or {@code null} if the parameter has no value. - * @throws IllegalArgumentException if multiple values are allowed. - */ - public String getValue() - { - if (allowMultipleValues) - { - throw new IllegalArgumentException("getValues() should be used when multiple values are allowed"); - } - final String[] strings = getValues(); - return strings.length > 0 ? strings[0] : null; - } - - /** - * Retrieve the values supplied for this parameter as an array. Returns an empty array if the parameter is optional and a value has not been supplied. - * - * @return an array of values supplied for the parameter. - */ - public String[] getValues() - { - return values.toArray(new String[values.size()]); - } - - void completedParsing() - { - if (!calledConsumer && getDefaultValue() != null) - { - callConsumer(getDefaultValue()); - } - } - - private void callConsumer(final String value) - { - if (consumer != null) - { - consumer.accept(getName(), value); - calledConsumer = true; - } - } - - @Override - public String getName() - { - return name; - } -} diff --git a/src/main/java/com/lmax/simpledsl/api/DslArg.java b/src/main/java/com/lmax/simpledsl/api/DslArg.java new file mode 100644 index 0000000..a3e7d25 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/DslArg.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +/** + * The base class for all arg types. + */ +public interface DslArg +{ + /** + * Get the name of this argument. + * + * @return the argument name. + */ + String getName(); + + /** + * Determine if a value is required for this argument. + * + * @return {@literal true} if and only if this argument is required. + */ + boolean isRequired(); + + /** + * Get a default value for this argument. + * + * If the argument is required, this method will throw an {@link IllegalArgumentException}. + * + * @return the default value for the argument + */ + String getDefaultValue(); + + /** + * Check whether this argument can take multiple values. + * + * @return {@literal true} if and only if the argument takes multiple values. + */ + boolean isAllowMultipleValues(); + + /** + * Get the separator that can be used to separate multiple values. + * + * @return the separator for splitting multiple values. + */ + String getMultipleValueSeparator(); + + /** + * Get the specific values that this argument will accept. + * + * @return the values allowed by this argument, or {@literal null} if all values are allowed + */ + String[] getAllowedValues(); +} diff --git a/src/main/java/com/lmax/simpledsl/api/DslParams.java b/src/main/java/com/lmax/simpledsl/api/DslParams.java new file mode 100644 index 0000000..f1f64c6 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/DslParams.java @@ -0,0 +1,83 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +import com.lmax.simpledsl.internal.DslParamsParser; + +/** + * The main entry point for defining the DSL language. Create a DslParams instance with the supplied arguments and the supported params. + * The supplied values can then be retrieved using {@link DslParams#value(String)} and related methods. + * + *

{@code
+ *     public void createUser(String... args) {
+ *         DslParams params = new DslParams(args,
+ *                                          new RequiredParam("user"),
+ *                                          new OptionalParam("password"));
+ *         getDriver().createUser(params.value("user"), params.valueAsOptional("password"));
+ *     }
+ * }
+ */ +public interface DslParams extends DslValues +{ + /** + * Retrieve the set of values supplied for a {@link RepeatingArgGroup} or an empty array if no values were supplied. + * {@link RepeatingArgGroup} requires that it's first argument be a {@link RequiredArg} and the group as a whole is + * referenced using the name of that argument. e.g. + * + *
{@code
+     *   DslParams params = new DslParams(args,
+     *                              new RepeatingArgGroup(
+     *                                  new RequiredArg("user"),
+     *                                  new OptionalArg("password")));
+     *   RepeatingGroup[] usersToCreate = params.valuesAsGroup("user");
+     * }
+ * + * @param groupName the name of the first required parameter. + * @return an array of {@link RepeatingGroup} instances, one for each set of values supplied for the argument. + * @throws IllegalArgumentException if {@code name} does not match the name of a {@link RepeatingArgGroup}. + */ + RepeatingGroup[] valuesAsGroup(String groupName); + + /** + * Create new {@link DslParams}. + * + * @param args the values + * @param arguments the {@link DslArg args} + * @return the new {@link DslParams} + */ + static DslParams create(String[] args, DslArg... arguments) + { + return new DslParamsParser().parse(args, arguments); + } + + + /** + * A shorthand way to create a {@link DslParams} instance that accepts a single required parameter and return the + * value that was supplied for that parameter. + * + * @param args the arguments supplied by the test. + * @param requiredParamName the name of the required parameter. + * @return the value supplied for the parameter. + */ + static String getSingleRequiredParamValue(final String[] args, final String requiredParamName) + { + return new DslParamsParser() + .parse(args, new RequiredArg(requiredParamName)) + .value(requiredParamName); + } + +} diff --git a/src/main/java/com/lmax/simpledsl/DslValues.java b/src/main/java/com/lmax/simpledsl/api/DslValues.java similarity index 73% rename from src/main/java/com/lmax/simpledsl/DslValues.java rename to src/main/java/com/lmax/simpledsl/api/DslValues.java index c4047a2..79a5470 100644 --- a/src/main/java/com/lmax/simpledsl/DslValues.java +++ b/src/main/java/com/lmax/simpledsl/api/DslValues.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.lmax.simpledsl; +package com.lmax.simpledsl.api; import java.math.BigDecimal; @@ -23,77 +23,57 @@ import java.util.Optional; /** - * Base class for value containers such as {@link DslParams} and {@link RepeatingGroup}. Provides a range of methods to access the supplied values in a convenient form such as parsed into an int. + * Base class for value containers such as {@link DslParams} and {@link RepeatingGroup}. + *

+ * Provides a range of methods to access the supplied values in a convenient form such as parsed into an int. */ -public abstract class DslValues +public interface DslValues { /** - * Retrieve the value supplied for a parameter. May return {@code null} if the parameter is optional and a value has not been supplied. + * Determine if a value was supplied for a parameter. + *

+ * Returns true when the parameter is supplied with an empty value. * * @param name the name of the parameter. - * @return the value supplied for that parameter. - * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. + * @return true if the parameter was supplied, otherwise false. + * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. */ - public abstract String value(String name); + boolean hasValue(String name); /** - * Retrieve the value supplied for a parameter as an {@link Optional}. The optional will be empty if the parameter was not supplied. + * Retrieve the value supplied for a parameter. + *

+ * May return {@code null} if the parameter is optional and a value has not been supplied. * - * @param name the name of the parameter - * @return an Optional containing the value supplied for the parameter, if any. + * @param name the name of the parameter. + * @return the value supplied for that parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. */ - public Optional valueAsOptional(final String name) - { - return Optional.ofNullable(value(name)); - } + String value(String name); /** - * Retrieve the values supplied for a parameter as an array. Returns an empty array if the parameter is optional and a value has not been supplied. + * Retrieve the values supplied for a parameter as an array. + *

+ * Returns an empty array if the parameter is optional and a value has not been supplied. * * @param name the name of the parameter. * @return an array of values supplied for the parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. */ - public abstract String[] values(String name); + String[] values(String name); /** - * Retrieve the values supplied for a parameter as a {@link List}. Returns an empty list if the parameter is optional and a value has not been supplied. - * - * @param name the name of the parameter. - * @return a List of values supplied for the parameter. - * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. - */ - public List valuesAsList(final String name) - { - return Arrays.asList(values(name)); - } - - /** - * Retrieve the values supplied for a parameter as an {@link Optional} {@link List}. Returns an empty Optional if the parameter is optional and a value has not been supplied. - * In most cases @{link #valuesAsList} is the more suitable method, but this variant is useful if there is an important difference between a parameter being set to an empty list - * or not being supplied. - * - *

For example, a test that checks the properties of a user may have an optional check for the roles assigned to that user:

- * - *
{@code
-     *     DslParams params = new DslParams(args,
-     *                                      new RequiredParam("user"),
-     *                                      new OptionalParam("suspended"),
-     *                                      new OptionalParam("roles").setAllowMultipleValues());
-     *     params.valuesAsOptional("roles").ifPresent(roles -> getDriver().verifyRoles(params.value("user"), roles));
-     * }
- * - *

It's also possible to achieve this distinction by checking the parameter was supplied using @{link #hasValue}.

+ * Retrieve the value supplied for a parameter as an {@link Optional}. + *

+ * The optional will be empty if the parameter was not supplied. * - * @param name the name of the parameter. - * @return a List of values supplied for the parameter. - * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. + * @param name the name of the parameter + * @return an Optional containing the value supplied for the parameter, if any. + * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. */ - public Optional> valuesAsOptional(final String name) + default Optional valueAsOptional(final String name) { - final List values = valuesAsList(name); - return values.isEmpty() ? Optional.>empty() : Optional.of(values); + return Optional.ofNullable(value(name)); } /** @@ -104,7 +84,7 @@ public Optional> valuesAsOptional(final String name) * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. * @throws NumberFormatException if the supplied value can't be parsed as an {@code int} (including because it wasn't supplied). */ - public int valueAsInt(final String name) + default int valueAsInt(final String name) { return Integer.parseInt(value(name)); } @@ -117,32 +97,36 @@ public int valueAsInt(final String name) * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. * @throws NumberFormatException if the supplied value can't be parsed as a {@code long} (including because it wasn't supplied). */ - public long valueAsLong(final String name) + default long valueAsLong(final String name) { - return Long.valueOf(value(name)); + return Long.parseLong(value(name)); } /** - * Retrieve the value supplied for a parameter as a boolean. The value is parsed using {@link Boolean#parseBoolean(String)}. + * Retrieve the value supplied for a parameter as a {@literal boolean}. + *

+ * The value is parsed using {@link Boolean#parseBoolean(String)}. * * @param name the name of the parameter. * @return the value supplied for that parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. */ - public boolean valueAsBoolean(final String name) + default boolean valueAsBoolean(final String name) { - return Boolean.valueOf(value(name)); + return Boolean.parseBoolean(value(name)); } /** - * Retrieve the value supplied for a parameter as a {@link BigDecimal}, parsed using {@link BigDecimal#BigDecimal(String)}. + * Retrieve the value supplied for a parameter as a {@link BigDecimal}. + *

+ * The value is parsed using {@link BigDecimal#BigDecimal(String)}. * * @param name the name of the parameter. * @return the value supplied for that parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. * @throws NumberFormatException if the supplied value can't be parsed as a {@code BigDecimal} */ - public BigDecimal valueAsBigDecimal(final String name) + default BigDecimal valueAsBigDecimal(final String name) { final String value = value(name); return value != null ? new BigDecimal(value) : null; @@ -150,16 +134,18 @@ public BigDecimal valueAsBigDecimal(final String name) /** - * Retrieve the value supplied for a parameter as a {@code double}, parsed using {@link Double#valueOf(String)}. + * Retrieve the value supplied for a parameter as a {@code double}. + *

+ * The value is parsed using {@link Double#parseDouble(String)}. * * @param name the name of the parameter. * @return the value supplied for that parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. * @throws NumberFormatException if the supplied value can't be parsed as a {@code double} */ - public double valueAsDouble(final String name) + default double valueAsDouble(final String name) { - return Double.valueOf(value(name)); + return Double.parseDouble(value(name)); } /** @@ -182,21 +168,64 @@ public double valueAsDouble(final String name) * @return the value supplied for that parameter, formatted as a parameter ready to pass on to another method that uses Simple-DSL. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter or if the parameter supports multiple values. */ - public String valueAsParam(final String name) + default String valueAsParam(final String name) { final String value = value(name); return value != null ? name + ": " + value : null; } /** - * Retrieve the values supplied for a parameter as an {@code int} array. Returns an empty array if the parameter is optional and a value has not been supplied. + * Retrieve the values supplied for a parameter as a {@link List}. Returns an empty list if the parameter is optional and a value has not been supplied. + * + * @param name the name of the parameter. + * @return a List of values supplied for the parameter. + * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. + */ + default List valuesAsList(final String name) + { + return Arrays.asList(values(name)); + } + + /** + * Retrieve the values supplied for a parameter as an {@link Optional} {@link List}. + *

+ * Returns an empty Optional if the parameter is optional and a value has not been supplied. + *

+ * In most cases {@link #valuesAsList} is the more suitable method. + * This variant is useful if there is an important difference between a parameter being set to an empty list vs not being supplied. + *

+ * For example, a test that checks the properties of a user may have an optional check for the roles assigned to that user: + *

{@code
+     *     DslParams params = new DslParams(args,
+     *                                      new RequiredParam("user"),
+     *                                      new OptionalParam("suspended"),
+     *                                      new OptionalParam("roles").setAllowMultipleValues());
+     *     params.valuesAsOptional("roles").ifPresent(roles -> getDriver().verifyRoles(params.value("user"), roles));
+     * }
+ *

+ * It's also possible to achieve this distinction by checking the parameter was supplied using {@link #hasValue}. + * + * @param name the name of the parameter. + * @return a List of values supplied for the parameter. + * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. + */ + default Optional> valuesAsOptional(final String name) + { + final List values = valuesAsList(name); + return values.isEmpty() ? Optional.empty() : Optional.of(values); + } + + /** + * Retrieve the values supplied for a parameter as an {@code int} array. + *

+ * Returns an empty array if the parameter is optional and a value has not been supplied. * * @param name the name of the parameter. * @return an array of values supplied for the parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. * @throws NumberFormatException if any of the supplied values can not be parsed as an {@code int}. */ - public int[] valuesAsInts(final String name) + default int[] valuesAsInts(final String name) { final String[] values = values(name); final int[] parsedValues = new int[values.length]; @@ -208,14 +237,16 @@ public int[] valuesAsInts(final String name) } /** - * Retrieve the values supplied for a parameter as a {@code long} array. Returns an empty array if the parameter is optional and a value has not been supplied. + * Retrieve the values supplied for a parameter as a {@code long} array. + *

+ * Returns an empty array if the parameter is optional and a value has not been supplied. * * @param name the name of the parameter. * @return an array of values supplied for the parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. * @throws NumberFormatException if any of the supplied values can not be parsed as an {@code long}. */ - public long[] valuesAsLongs(final String name) + default long[] valuesAsLongs(final String name) { final String[] values = values(name); final long[] parsedValues = new long[values.length]; @@ -227,14 +258,16 @@ public long[] valuesAsLongs(final String name) } /** - * Retrieve the values supplied for a parameter as a {@link BigDecimal} array. Returns an empty array if the parameter is optional and a value has not been supplied. + * Retrieve the values supplied for a parameter as a {@link BigDecimal} array. + *

+ * Returns an empty array if the parameter is optional and a value has not been supplied. * * @param name the name of the parameter. * @return an array of values supplied for the parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. * @throws NumberFormatException if any of the supplied values can not be parsed as an {@code BigDecimal}. */ - public BigDecimal[] valuesAsBigDecimals(final String name) + default BigDecimal[] valuesAsBigDecimals(final String name) { final String[] values = values(name); final BigDecimal[] parsedValues = new BigDecimal[values.length]; @@ -246,14 +279,16 @@ public BigDecimal[] valuesAsBigDecimals(final String name) } /** - * Retrieve the values supplied for a parameter as a {@code double} array. Returns an empty array if the parameter is optional and a value has not been supplied. + * Retrieve the values supplied for a parameter as a {@code double} array. + *

+ * Returns an empty array if the parameter is optional and a value has not been supplied. * * @param name the name of the parameter. * @return an array of values supplied for the parameter. * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. * @throws NumberFormatException if any of the supplied values can not be parsed as an {@code double}. */ - public double[] valuesAsDoubles(final String name) + default double[] valuesAsDoubles(final String name) { final String[] values = values(name); final double[] parsedValues = new double[values.length]; @@ -265,11 +300,9 @@ public double[] valuesAsDoubles(final String name) } /** - * Determine if a value was supplied for a parameter. Returns true when the parameter is supplied with an empty value. + * Get the supported parameters. Supplied values will have been parsed into the parameters. * - * @param name the name of the parameter. - * @return true if the parameter was supplied, otherwise false. - * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. + * @return the array of {@link DslArg DslArgs} supplied to the constructor. */ - public abstract boolean hasValue(String name); + DslArg[] getParams(); } diff --git a/src/main/java/com/lmax/simpledsl/api/OptionalArg.java b/src/main/java/com/lmax/simpledsl/api/OptionalArg.java new file mode 100644 index 0000000..2020570 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/OptionalArg.java @@ -0,0 +1,52 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +/** + * An optional argument. + *

+ * Callers may omit this argument and the call is still considered valid. + * When provided, the argument name must be specified, e.g. {@code "firstName: Joan"}. + * Optional argument can have a default value that is used when no other value is provided. + *

+ * By default, only a single value is allowed. + * Multiple values can be allowed by calling {@link #setAllowMultipleValues()}. + *

+ * Optional arguments are useful as a way of reducing boilerplate in tests by providing default settings and as a way to + * optionally assert behaviours or values. + * For example providing a method that checks the first name and/or the last name: + * + *

{@code
+ *   DslParams params = new DslParams(args,
+ *                                    new OptionalParam("firstName"),
+ *                                    new OptionalParam("lastName"));
+ *   params.valueAsOptional("firstName").ifPresent(driver::checkFirstName);
+ *   params.valueAsOptional("lastName").ifPresent(driver::checkLastName);
+ * }
+ *

+ * Selective assertions like this help tests be flexible to changes outside the area they intend to test by only + * asserting on the values that are actually relevant. + * + * @see DslValues#valueAsOptional(String) + */ +public class OptionalArg extends SimpleDslArg +{ + public OptionalArg(final String name) + { + super(name, false); + } +} diff --git a/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java b/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java new file mode 100644 index 0000000..f99d5ec --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java @@ -0,0 +1,110 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +/** + * Define a group of arguments that can be repeated 0 or more times. + *

+ * The first argument in the group must be a {@link RequiredArg} and is used to identify the start of the group in + * arguments and when retrieving the groups from {@link DslParams#valuesAsGroup(String)}. Other argument may follow + * the first argument in any order. + *

+ * Multiple {@link RepeatingArgGroup RepeatingArgGroups} can be used within the same {@link DslParams} but they + * cannot be nested. + * + *

{@code
+ * public void createUsers(String... args) {
+ *     DslParams params = new DslParams(
+ *         args,
+ *         new RepeatingParamGroup(
+ *             new RequiredParam("user"),
+ *             new OptionalParam("password").setDefault("aPassword")
+ *         ));
+ *     for (RepeatingGroup user : params.valueAsGroup("user")) {
+ *         driver.createUser(user.value("user"), user.value("password"));
+ *     }
+ * }
+ * }
+ */ +public class RepeatingArgGroup implements DslArg +{ + private final RequiredArg identity; + private final SimpleDslArg[] otherArgs; + + public RepeatingArgGroup(final RequiredArg firstArg, final SimpleDslArg... otherArgs) + { + this.identity = firstArg; + this.otherArgs = otherArgs; + } + + @Override + public String getName() + { + return identity.getName(); + } + + @Override + public boolean isRequired() + { + return false; + } + + @Override + public String getDefaultValue() + { + throw new IllegalArgumentException("A repeating group can not have a default value"); + } + + @Override + public boolean isAllowMultipleValues() + { + return false; + } + + @Override + public String getMultipleValueSeparator() + { + return null; + } + + @Override + public String[] getAllowedValues() + { + return null; + } + + /** + * Get the {@link RequiredArg} that identifies this {@link RepeatingArgGroup}. + * + * @return the {@link RequiredArg}. + */ + public RequiredArg getIdentity() + { + return identity; + } + + /** + * Get all the {@link DslArg DslArgs}, except for the {@link #getIdentity() identity argument} that comprise this + * {@link RepeatingArgGroup}. + * + * @return the {@link DslArg DslArgs}. + */ + public SimpleDslArg[] getOtherArgs() + { + return otherArgs; + } +} diff --git a/src/main/java/com/lmax/simpledsl/DslParamsUsageException.java b/src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java old mode 100755 new mode 100644 similarity index 52% rename from src/main/java/com/lmax/simpledsl/DslParamsUsageException.java rename to src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java index 95cfb6e..fb22374 --- a/src/main/java/com/lmax/simpledsl/DslParamsUsageException.java +++ b/src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java @@ -13,29 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.lmax.simpledsl; + +package com.lmax.simpledsl.api; /** - * Thrown when a -usage parameter is supplied. + * Provides access to the values for a single instance of a {@link RepeatingArgGroup}. + *

+ * Apart from not supporting nested {@link RepeatingArgGroup RepeatingArgGroups}, all the value accessor methods + * from {@link DslParams} are available. * - * @deprecated To be removed in a future release. Use {@link DslParams#getParams()} instead. + * @see RepeatingArgGroup + * @see DslParams#valuesAsGroup(String) */ -public class DslParamsUsageException extends RuntimeException +public interface RepeatingGroup extends DslValues { - private final DslParam[] params; - - public DslParamsUsageException(final DslParam[] params) - { - this.params = params; - } - - /** - * Get the supported parameters. - * - * @return the supported parameters. - */ - public DslParam[] getParams() - { - return params; - } } diff --git a/src/main/java/com/lmax/simpledsl/api/RequiredArg.java b/src/main/java/com/lmax/simpledsl/api/RequiredArg.java new file mode 100644 index 0000000..997906e --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/RequiredArg.java @@ -0,0 +1,84 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +/** + * A required argument. + *

+ * An exception will be thrown when parsing the arguments if a value is not supplied for this argument. + * The name of the argument may be omitted if required arguments are listed first. + * For example with the params: + * + *

{@code
+ * public void login(String... args) {
+ *     DslParams params = new DslParams(args,
+ *                                      new RequiredParam("user"),
+ *                                      new RequiredParam("password"));
+ * }
+ * }
+ *

+ * The following two calls are exactly equivalent: + *

{@code
+ * login("joan", "myPassword");
+ * login("user: joan", "password: myPassword");
+ * }
+ *

+ * Required arguments can be supplied out of order if the name is specified, so the following is also equivalent: + * + *

{@code
+ * login("password: myPassword", "user: joan");
+ * }
+ *

+ * It is highly recommended including the argument name if there is any ambiguity about the meaning of the argument. + *

+ * By default, only a single value is allowed. + * Multiple values can be allowed by calling {@link #setAllowMultipleValues()}. + */ +public class RequiredArg extends SimpleDslArg +{ + public RequiredArg(final String name) + { + super(name, true); + } + + @Override + public RequiredArg setDefault(final String defaultValue) + { + throw new IllegalArgumentException("A required argument can not have a default value"); + } + + @Override + public RequiredArg setAllowedValues(final String... allowedValues) + { + super.setAllowedValues(allowedValues); + return this; + } + + @Override + public RequiredArg setAllowMultipleValues() + { + super.setAllowMultipleValues(); + return this; + } + + @Override + public RequiredArg setAllowMultipleValues(final String delimiter) + { + super.setAllowMultipleValues(delimiter); + return this; + } +} diff --git a/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java b/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java new file mode 100644 index 0000000..d953cea --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java @@ -0,0 +1,140 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.api; + +/** + * The root type for all simple args. + */ +public abstract class SimpleDslArg implements DslArg +{ + private static final String DEFAULT_DELIMITER = ","; + + private final String name; + private final boolean required; + + protected String defaultValue; + protected boolean allowMultipleValues; + protected String multipleValueSeparator; + protected String[] allowedValues; + + public SimpleDslArg(final String name, final boolean required) + { + this.name = name; + this.required = required; + } + + @Override + public String getName() + { + return name; + } + + @Override + public boolean isRequired() + { + return required; + } + + @Override + public String getDefaultValue() + { + if (required) + { + throw new IllegalArgumentException("A required argument can not have a default value"); + } + + return defaultValue; + } + + @Override + public boolean isAllowMultipleValues() + { + return allowMultipleValues; + } + + @Override + public String getMultipleValueSeparator() + { + return multipleValueSeparator; + } + + @Override + public String[] getAllowedValues() + { + return allowedValues; + } + + /** + * Set a default value for this argument. + *

+ * If a default is provided, the argument will be considered to always have a value, and will return the default if + * no other value is provided by the caller. + * + * @param defaultValue the default value for the argument. + * @return this argument + * @throws IllegalArgumentException if the default value cannot be set + */ + public SimpleDslArg setDefault(final String defaultValue) + { + this.defaultValue = defaultValue; + return this; + } + + /** + * Restrict the allowed values for this argument to the specified set. + * Specifying a value outside of this set will result in an exception being thrown when parsing the arguments. + * + * @param allowedValues the allowable values for this argument. + * @return this argument + */ + public SimpleDslArg setAllowedValues(final String... allowedValues) + { + this.allowedValues = allowedValues; + return this; + } + + /** + * Allow multiple values to be specified for this argument, either as separate arguments or using comma (,) as a delimiter. + *

+ * The following calls are equivalent: + *

{@code
+     * verifyUsersPresent("user: joan", "user: jenny", "user: joanne");
+     * verifyUsersPresent("user: joan, jenny, joanne");
+     * }
+ * + * @return this argument + * @see #setAllowMultipleValues(String) + */ + public SimpleDslArg setAllowMultipleValues() + { + return setAllowMultipleValues(DEFAULT_DELIMITER); + } + + /** + * Allow multiple values to be specified for this argument, either as separate arguments or using the specified string as a delimiter. + * + * @param delimiter the delimiter to use to separate values + * @return this argument + * @see #setAllowMultipleValues() + */ + public SimpleDslArg setAllowMultipleValues(final String delimiter) + { + allowMultipleValues = true; + multipleValueSeparator = delimiter; + return this; + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/DslParam.java b/src/main/java/com/lmax/simpledsl/internal/DslParam.java new file mode 100644 index 0000000..b029559 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/DslParam.java @@ -0,0 +1,28 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +abstract class DslParam +{ + abstract String getName(); + + abstract SimpleDslParam getAsSimpleDslParam(); + + abstract RepeatingParamGroup asRepeatingParamGroup(); + + abstract boolean hasValue(); +} diff --git a/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java new file mode 100644 index 0000000..2599eed --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java @@ -0,0 +1,86 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.DslArg; +import com.lmax.simpledsl.api.DslParams; +import com.lmax.simpledsl.api.RepeatingGroup; + +import java.util.Map; +import java.util.Optional; + +/** + * The internal implementation of {@link DslParams}. + */ +final class DslParamsImpl implements DslParams +{ + private final DslArg[] args; + private final Map paramsByName; + + DslParamsImpl(final DslArg[] args, final Map paramsByName) + { + this.args = args; + this.paramsByName = paramsByName; + } + + @Override + public String value(final String name) + { + final DslParam param = getDslParam(name); + return param.getAsSimpleDslParam().getValue(); + } + + @Override + public String[] values(final String name) + { + final DslParam param = getDslParam(name); + return param.getAsSimpleDslParam().getValues(); + } + + @Override + public RepeatingGroup[] valuesAsGroup(final String groupName) + { + final DslParam param = getDslParam(groupName); + final RepeatingParamGroup repeatingParamGroup = param.asRepeatingParamGroup(); + return repeatingParamGroup.values(); + } + + @Override + public boolean hasValue(final String name) + { + return findDslParam(name) + .map(DslParam::hasValue) + .orElse(false); + } + + @Override + public DslArg[] getParams() + { + return args; + } + + private DslParam getDslParam(final String name) + { + return findDslParam(name).orElseThrow(() -> new IllegalArgumentException(name + " is not a parameter")); + } + + private Optional findDslParam(final String name) + { + return Optional.ofNullable(name) + .map(String::toLowerCase) + .map(paramsByName::get); + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/DslParamsParser.java b/src/main/java/com/lmax/simpledsl/internal/DslParamsParser.java new file mode 100644 index 0000000..f8a2631 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/DslParamsParser.java @@ -0,0 +1,358 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.DslArg; +import com.lmax.simpledsl.api.DslParams; +import com.lmax.simpledsl.api.RepeatingArgGroup; +import com.lmax.simpledsl.api.SimpleDslArg; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parser for transforming a specification of {@link DslArg DslArgs} and a set of provided {@link String} values into + * usable {@link DslParams}. + */ +public class DslParamsParser +{ + /** + * Construct new {@link DslParams} from the given {@link DslArg DslArgs} and values. + * + * @param args the values + * @param dslArgs the {@link DslArg} specifications + * @return the parsed {@link DslParams} + * @throws IllegalArgumentException if any of the provided {@link String} arguments are invalid + */ + public DslParams parse(final String[] args, final DslArg... dslArgs) + { + final Deque arguments = parseArgumentValues(args); + + final ArgumentProcessor argumentProcessor = new ArgumentProcessor(); + argumentProcessor.drain(dslArgs, arguments); + + final Map paramsByName = argumentProcessor.collect(dslArgs); + return new DslParamsImpl(dslArgs, paramsByName); + } + + private static Deque parseArgumentValues(final String[] args) + { + final ArrayDeque nameValuePairs = new ArrayDeque<>(); + for (String arg : args) + { + final NameValuePair nameValuePair = NameValuePair.fromArgumentString(arg); + nameValuePairs.add(nameValuePair); + } + return nameValuePairs; + } + + private static final class ArgumentProcessor + { + private final SimpleArgumentProcessor simpleProcessor = new SimpleArgumentProcessor(new HashMap<>(), "Missing value for parameter: %s"); + private final RepeatingGroupArgumentProcessor groupProcessor = new RepeatingGroupArgumentProcessor(new HashMap<>()); + + void drain(final DslArg[] args, final Deque arguments) + { + for (final DslArg arg : args) + { + final NameValuePair argument = arguments.peekFirst(); + if (argument != null) + { + if (argument == NameValuePair.NULL) + { + arguments.pollFirst(); + continue; + } + + if (invalidNamedParameter(arg, argument)) + { + break; + } + + consume(arg, arguments); + } + } + + final Map argsByName = new HashMap<>(); + for (DslArg dslArg : args) + { + if (argsByName.put(dslArg.getName().toLowerCase(), dslArg) != null) + { + throw new IllegalArgumentException("Duplicate parameter '" + dslArg.getName() + "'"); + } + } + + while (!arguments.isEmpty()) + { + final NameValuePair argument = arguments.peekFirst(); + if (argument == NameValuePair.NULL) + { + arguments.pollFirst(); + continue; + } + + if (argument.name == null) + { + throw new IllegalArgumentException("Unexpected ambiguous argument " + argument.originalValue); + } + + final DslArg arg = argsByName.get(argument.name.toLowerCase()); + if (arg == null) + { + throw new IllegalArgumentException("Unexpected argument " + argument.originalValue); + } + + consume(arg, arguments); + } + } + + Map collect(final DslArg[] args) + { + final Map map = new HashMap<>(); + for (final DslArg dslArg : args) + { + // TODO: work out how we can avoid this cast :/ + final DslParam val = dslArg instanceof RepeatingArgGroup + ? groupProcessor.collect((RepeatingArgGroup) dslArg) + : simpleProcessor.collect((SimpleDslArg) dslArg); + map.put(dslArg.getName().toLowerCase(), val); + } + return map; + } + + private void consume(final DslArg arg, final Deque arguments) + { + if (arg instanceof RepeatingArgGroup) + { + // TODO: work out how we can avoid this cast :/ + groupProcessor.consume((RepeatingArgGroup) arg, arguments); + } + else + { + simpleProcessor.consume(arg, arguments); + } + } + + private boolean invalidNamedParameter(final DslArg arg, final NameValuePair argument) + { + return argument.name != null && + ((arg.isRequired() && !arg.getName().equals(argument.name) || !arg.isRequired())); + } + } + + private static final class SimpleArgumentProcessor + { + private final Map> valuesByArg; + private final String requiredParamMissingError; + + SimpleArgumentProcessor(final Map> valuesByArg, final String requiredParamMissingError) + { + this.valuesByArg = valuesByArg; + this.requiredParamMissingError = requiredParamMissingError; + } + + void consume(final DslArg arg, final Deque args) + { + final List values = valuesByArg.computeIfAbsent(arg, k -> new ArrayList<>()); + while (consumeSingleParam(arg, args, values)) + { + if (!arg.isAllowMultipleValues()) + { + break; + } + } + } + + SimpleDslParam collect(final SimpleDslArg arg) + { + final List values = valuesByArg.getOrDefault(arg, Collections.emptyList()); + final List validatedValues = validateSimpleArg(arg, values); + return new SimpleDslParam(arg.getName(), validatedValues); + } + + private static boolean consumeSingleParam(final DslArg arg, final Deque args, final List values) + { + if (!args.isEmpty()) + { + final NameValuePair nameValue = args.peekFirst(); + if (matches(arg, nameValue)) + { + addValue(arg, nameValue.value, values); + args.pollFirst(); + return true; + } + } + return false; + } + + private static boolean matches(final DslArg arg, final NameValuePair value) + { + return (value != NameValuePair.NULL) && + (value.name == null || arg.getName().equalsIgnoreCase(value.name)); + } + + private static void addValue(final DslArg arg, final String value, final List values) + { + if (arg.isAllowMultipleValues()) + { + final String[] vals = value.split(arg.getMultipleValueSeparator()); + for (final String singleValue : vals) + { + addSingleValue(arg, singleValue.trim(), values); + } + } + else + { + addSingleValue(arg, value, values); + } + } + + private static void addSingleValue(final DslArg arg, final String value, final List values) + { + checkCanAddValue(arg, values); + values.add(checkValidValue(arg, value)); + } + + private static void checkCanAddValue(final DslArg arg, final List values) + { + if (!arg.isAllowMultipleValues() && values.size() == 1) + { + throw new IllegalArgumentException("Multiple " + arg.getName() + " parameters are not allowed"); + } + } + + private List validateSimpleArg(final SimpleDslArg arg, final List values) + { + if (values.isEmpty()) + { + if (arg.isRequired()) + { + throw new IllegalArgumentException(String.format(requiredParamMissingError, arg.getName())); + } + else + { + return arg.getDefaultValue() == null + ? Collections.emptyList() + : Collections.singletonList(arg.getDefaultValue()); + } + } + return values; + } + } + + private static final class RepeatingGroupArgumentProcessor + { + final Map> groupsByArg; + + RepeatingGroupArgumentProcessor(final Map> groupsByArg) + { + this.groupsByArg = groupsByArg; + } + + void consume(final RepeatingArgGroup groupArg, final Deque arguments) + { + final Map> valuesByArg = new HashMap<>(); + final SimpleArgumentProcessor processor = new SimpleArgumentProcessor(valuesByArg, "Did not supply a value for %s in group " + groupArg.getName()); + + processor.consume(groupArg.getIdentity(), arguments); + + final Map argsByName = new HashMap<>(); + argsByName.put(groupArg.getIdentity().getName(), groupArg.getIdentity()); + for (SimpleDslArg dslArg : groupArg.getOtherArgs()) + { + if (argsByName.put(dslArg.getName().toLowerCase(), dslArg) != null) + { + throw new IllegalArgumentException("Duplicate parameter '" + dslArg.getName() + "' in group " + groupArg.getName()); + } + } + + while (!arguments.isEmpty()) + { + final NameValuePair argument = arguments.peekFirst(); + if (argument == NameValuePair.NULL) + { + arguments.pollFirst(); + continue; + } + + if (argument.name == null) + { + throw new IllegalArgumentException("Unexpected ambiguous argument " + argument.originalValue); + } + + final DslArg arg = argsByName.get(argument.name.toLowerCase()); + if (arg == null) + { + break; + } + + final List argValues = valuesByArg.computeIfAbsent(arg, k -> new ArrayList<>()); + if (!argValues.isEmpty() && !arg.isAllowMultipleValues()) + { + break; + } + + checkValidValue(arg, argument.value); + argValues.add(argument.value); + arguments.pollFirst(); + } + + // TODO: this whole thing here is a bit hacky! + final Map> valuesByName = new HashMap<>(); + for (final SimpleDslArg simpleDslArg : argsByName.values()) + { + final SimpleDslParam param = processor.collect(simpleDslArg); + if (param.hasValue()) + { + valuesByName.put(param.getName().toLowerCase(), param.getValuesAsList()); + } + } + + final DslArg[] dslArgs = new DslArg[groupArg.getOtherArgs().length + 1]; + dslArgs[0] = groupArg.getIdentity(); + System.arraycopy(groupArg.getOtherArgs(), 0, dslArgs, 1, groupArg.getOtherArgs().length); + groupsByArg.computeIfAbsent(groupArg, k -> new ArrayList<>()).add(new RepeatingParamValues(dslArgs, valuesByName)); + } + + RepeatingParamGroup collect(final RepeatingArgGroup arg) + { + return new RepeatingParamGroup(arg.getName(), groupsByArg.getOrDefault(arg, Collections.emptyList())); + } + } + + private static String checkValidValue(final DslArg arg, final String value) + { + if (arg.getAllowedValues() != null) + { + for (final String allowedValue : arg.getAllowedValues()) + { + if (allowedValue.equalsIgnoreCase(value)) + { + return allowedValue; + } + } + throw new IllegalArgumentException(arg.getName() + " parameter value '" + value + "' must be one of: " + Arrays.toString(arg.getAllowedValues())); + } + return value; + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/NameValuePair.java b/src/main/java/com/lmax/simpledsl/internal/NameValuePair.java new file mode 100644 index 0000000..d30713c --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/NameValuePair.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +class NameValuePair +{ + static final NameValuePair NULL = new NameValuePair(null, null, null); + + private static final String SPLIT_NAME_VALUE_REGEX = "[=:]"; + + public final String originalValue; + public final String name; + public final String value; + + NameValuePair(final String originalValue, final String name, final String value) + { + this.originalValue = originalValue; + this.name = name; + this.value = value; + } + + static NameValuePair fromArgumentString(final String argString) + { + if (argString == null) + { + return NULL; + } + else + { + final String[] splitArg = (argString + " ").split(SPLIT_NAME_VALUE_REGEX, 2); + return splitArg.length == 2 + ? new NameValuePair(argString, splitArg[0].trim(), splitArg[1].trim()) + : new NameValuePair(argString, null, argString.trim()); + } + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java new file mode 100644 index 0000000..f7a50f0 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.RepeatingGroup; + +import java.util.List; + +class RepeatingParamGroup extends DslParam +{ + private final String name; + private final List values; + + RepeatingParamGroup(final String name, final List values) + { + this.name = name; + this.values = values; + } + + @Override + public String getName() + { + return name; + } + + @Override + public SimpleDslParam getAsSimpleDslParam() + { + throw new IllegalArgumentException(name + " is a repeating group"); + } + + @Override + public RepeatingParamGroup asRepeatingParamGroup() + { + return this; + } + + /** + * Return the value groups supplied for this repeating group. + * + * @return an array of {@link RepeatingParamValues} instances, one for each set of values provided for this repeating group. + */ + public RepeatingGroup[] values() + { + return values.toArray(new RepeatingParamValues[0]); + } + + @Override + public boolean hasValue() + { + return !values.isEmpty(); + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java new file mode 100644 index 0000000..eb52357 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java @@ -0,0 +1,50 @@ +package com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.DslArg; +import com.lmax.simpledsl.api.RepeatingGroup; + +import java.util.List; +import java.util.Map; + +class RepeatingParamValues implements RepeatingGroup +{ + private final DslArg[] dslArgs; + private final Map> valuesByName; + + RepeatingParamValues(final DslArg[] dslArgs, final Map> valuesByName) + { + this.dslArgs = dslArgs; + this.valuesByName = valuesByName; + } + + @Override + public boolean hasValue(final String name) + { + return valuesByName.containsKey(name.toLowerCase()); + } + + @Override + public String value(final String name) + { + final String[] strings = values(name); + return strings.length > 0 ? strings[0] : null; + } + + @Override + public String[] values(final String name) + { + final List values = getValues(name); + return values != null ? values.toArray(new String[0]) : new String[0]; + } + + @Override + public DslArg[] getParams() + { + return dslArgs; + } + + private List getValues(final String name) + { + return name != null ? valuesByName.get(name.toLowerCase()) : null; + } +} diff --git a/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java b/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java new file mode 100644 index 0000000..50ab351 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java @@ -0,0 +1,81 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import java.util.Collections; +import java.util.List; + +class SimpleDslParam extends DslParam +{ + private final String name; + private final List values; + + SimpleDslParam(final String name, final List values) + { + this.name = name; + this.values = values; + } + + @Override + public String getName() + { + return name; + } + + @Override + SimpleDslParam getAsSimpleDslParam() + { + return this; + } + + @Override + RepeatingParamGroup asRepeatingParamGroup() + { + throw new IllegalArgumentException(name + " is not a repeating group"); + } + + @Override + boolean hasValue() + { + return !values.isEmpty(); + } + + /** + * Get the value for this parameter. If multiple values are allowed, use {@link #getValues()} instead. + * + * @return the value for this parameter or {@code null} if the parameter has no value. + * @throws IllegalArgumentException if multiple values are allowed. + */ + public String getValue() + { + if (values.size() > 1) + { + throw new IllegalArgumentException("getValues() should be used when multiple values are allowed"); + } + final String[] strings = getValues(); + return strings.length > 0 ? strings[0] : null; + } + + List getValuesAsList() + { + return Collections.unmodifiableList(values); + } + + String[] getValues() + { + return getValuesAsList().toArray(new String[0]); + } +} diff --git a/src/test/java/com/lmax/simpledsl/DslParamsTest.java b/src/test/java/com/lmax/simpledsl/DslParamsTest.java deleted file mode 100644 index 8d9e9f2..0000000 --- a/src/test/java/com/lmax/simpledsl/DslParamsTest.java +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.util.LinkedList; -import java.util.Optional; -import java.util.function.Consumer; - -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DslParamsTest -{ - @Test - public void shouldReturnValueAsInt() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(1, params.valueAsInt("a")); - } - - @Test - public void shouldThrowNumberFormatExceptionWhenValueAsIntCalledForParameterThatIsNotSupplied() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertThrows( - IllegalArgumentException.class, - () -> params.valueAsInt("a"), - "Multiple foo parameters are not allowed"); - } - - @Test - public void shouldReturnValueAsLong() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(1L, params.valueAsLong("a")); - } - - @Test - public void shouldReturnValueAsBoolean() - { - final String[] args = {"a=true"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertTrue(params.valueAsBoolean("a")); - } - - @Test - public void shouldReturnValueAsBigDecimal() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - final DslParams params = new DslParams(args, parameters); - assertEquals(0, BigDecimal.ONE.compareTo(params.valueAsBigDecimal("a"))); - } - - @Test - public void shouldReturnValueAsDouble() - { - final String[] args = {"a=1.23"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - final DslParams params = new DslParams(args, parameters); - assertEquals(1.23d, params.valueAsDouble("a"), 0d); - } - - @Test - public void shouldReturnNullValueWhenBigDecimalIsNotFound() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - assertNull(params.valueAsBigDecimal("a")); - } - - @Test - public void shouldReturnValueAsParamForOptionalValueThatWasSpecified() - { - final String[] args = {"a: value"}; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - assertEquals("a: value", params.valueAsParam("a")); - } - - @Test - public void shouldReturnNullValueAsParamWhenOptionalParamNotSpecified() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - assertNull(params.valueAsParam("a")); - } - - @Test - public void shouldReturnValuesAsIntArray() - { - final String[] args = {"a: 1, 2, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; - - final DslParams params = new DslParams(args, parameters); - - assertArrayEquals(new int[]{1, 2, 3}, params.valuesAsInts("a")); - } - - @Test - public void shouldReturnValuesAsLongArray() - { - final String[] args = {"a: 1, 2, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; - - final DslParams params = new DslParams(args, parameters); - - assertArrayEquals(new long[]{1, 2, 3}, params.valuesAsLongs("a")); - } - - @Test - public void shouldReturnValuesAsBigDecimalArray() - { - - final String[] args = {"a: 1, 2.23, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; - - final DslParams params = new DslParams(args, parameters); - - assertArrayEquals(new BigDecimal[]{new BigDecimal("1"), new BigDecimal("2.23"), new BigDecimal("3")}, params.valuesAsBigDecimals("a")); - } - - @Test - public void shouldReturnValuesAsDoubleArray() - { - final String[] args = {"a: 1, 2.23, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; - - final DslParams params = new DslParams(args, parameters); - - assertArrayEquals(new double[]{1, 2.23, 3}, params.valuesAsDoubles("a")); - } - - @Test - public void shouldExtractRequiredParametersWhenNamed() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RequiredParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldExtractOptionalParametersWhenNamed() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new OptionalParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldExtractMixedParameterTypesWhenNamed() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldReportValueAsPresentWhenProvided() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertTrue(params.hasValue("b")); - } - - @Test - public void shouldNotReportValueAsPresentWhenNotProvided() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertFalse(params.hasValue("b")); - } - - @Test - public void shouldReturnTrueFromHasValueIfAnEmptyValueIsSupplied() - { - final String[] args = {"a="}; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertTrue(params.hasValue("a")); - } - - @Test - public void shouldReturnTrueFromHasValueIfADefaultValueIsUsed() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; - - final DslParams params = new DslParams(args, parameters); - - assertTrue(params.hasValue("a")); - } - - @Test - public void shouldReturnEmptyOptionalWhenValueIsNotSupplied() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(params.valueAsOptional("a"), Optional.empty()); - } - - @Test - public void shouldReturnOptionalWithValueWhenValueIsSupplied() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(params.valueAsOptional("a"), Optional.of("value")); - } - - @Test - public void shouldReturnEmptyOptionalWhenMultipleParameterValueIsNotSupplied() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(params.valuesAsOptional("a"), Optional.empty()); - } - - @Test - public void shouldReturnOptionalListWhenMultipleParameterValueIsSupplied() - { - final String[] args = {"a=value1", "a=value2"}; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - - final DslParams params = new DslParams(args, parameters); - - assertEquals(params.valuesAsOptional("a"), Optional.of(asList("value1", "value2"))); - } - - @Test - public void shouldBeAbleToExtractMultipleRequiredParamsWhenAllParamsAreNamed() - { - final String[] args = {"a=1", "b=2", "b=3", "c=4"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RequiredParam("b").setAllowMultipleValues(), - new RequiredParam("c") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertArrayEquals(new String[]{"2", "3"}, params.values("b")); - assertEquals("4", params.value("c")); - } - - @Test - public void shouldBeAbleToExtractMultipleRequiredParamsWhenSubsequentRequiredParamsAreNotNamed() - { - final String[] args = {"a=1", "b=2", "3", "c=4"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RequiredParam("b").setAllowMultipleValues(), - new RequiredParam("c") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertArrayEquals(new String[]{"2", "3"}, params.values("b")); - assertEquals("4", params.value("c")); - } - - @Test - public void shouldBeAbleToExtractMultipleRequiredParamsAndMultipleOptionalParamsWithOptionalParamsInRandomOrder() - { - final String[] args = {"a=1", "b=2", "b=3", "c=4", "d=5", "c=6"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RequiredParam("b").setAllowMultipleValues(), - new OptionalParam("c").setAllowMultipleValues(), - new OptionalParam("d") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertArrayEquals(new String[]{"2", "3"}, params.values("b")); - assertArrayEquals(new String[]{"4", "6"}, params.values("c")); - assertEquals("5", params.value("d")); - } - - @Test - public void shouldBeAbleToDetectPresenceOfParamsWithMultipleValuesAllowed() - { - final String[] args = {"a=1", "a=2"}; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - - final DslParams params = new DslParams(args, parameters); - - assertTrue(params.hasValue("a")); - } - - @Test - public void shouldBeAbleToDetectAbsenceOfParamsWithMultipleValuesAllowed() - { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - - final DslParams params = new DslParams(args, parameters); - - assertFalse(params.hasValue("a")); - } - - @Test - public void shouldBeAbleToSpecifiedNamedRequiredParamsInAnyOrder() - { - final String[] args = {"b=2", "a=1"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldExtractOptionalParamsFromPositionalArguments() - { - final String[] args = {"1", "2"}; - final DslParam[] parameters = { - new OptionalParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldExtractOptionalParamsFromPositionalArgumentsWhenNullArgumentsArePresent() - { - final String[] args = {null, "1", null}; - final DslParam[] parameters = { - new OptionalParam("a"), - new OptionalParam("b"), - new OptionalParam("c") - }; - - final DslParams params = new DslParams(args, parameters); - - assertNull(params.value("a")); - assertEquals("1", params.value("b")); - assertNull(params.value("c")); - } - - @Test - public void shouldMatchParamsIgnoringCase() - { - final String[] args = {"a=1", "B=2"}; - final DslParam[] parameters = { - new RequiredParam("A"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("B")); - assertTrue(params.hasValue("a")); - assertTrue(params.hasValue("B")); - } - - @Test - public void shouldIgnoreNullArgumentsAmongstOptionalParameters() - { - final String[] args = {null, "a=1", null, "b=2", null}; - final DslParam[] parameters = { - new OptionalParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldIgnoreNullArgumentsInRepeatingGroups() - { - final String[] args = {"a=1", "b=2", null}; - final DslParam[] parameters = { - new RepeatingParamGroup( - new RequiredParam("a"), - new RequiredParam("b")) - }; - - final DslParams params = new DslParams(args, parameters); - - final RepeatingGroup[] group = params.valuesAsGroup("a"); - assertEquals("1", group[0].value("a")); - assertEquals("2", group[0].value("b")); - } - - @Test - public void shouldBeAbleToRetrieveGroupsOfParams() - { - final String[] args = {"a: value", "group: Joe", "value: 1", "group: Jenny", "value: 2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("group"), - new RequiredParam("value")) - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("value", params.value("a")); - final RepeatingGroup[] groups = params.valuesAsGroup("group"); - assertEquals(2, groups.length); - assertEquals("Joe", groups[0].value("group")); - assertEquals("1", groups[0].value("value")); - assertEquals("Jenny", groups[1].value("group")); - assertEquals("2", groups[1].value("value")); - } - - @Test - public void shouldRaiseErrorIfRequiredParameterMissingFromGroup() - { - final String[] args = {"a: value", "myGroup: Joe", "myGroup: Jenny", "myValue: 2"}; - final DslParam[] params = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("myGroup"), - new RequiredParam("myValue")) - }; - - final IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParams(args, params)); - - assertEquals("Did not supply a value for myValue in group myGroup", exception.getMessage()); - } - - @Test - public void shouldBeAbleToSpecifyMultipleValuesForParamInGroup() - { - final String[] args = {"a: value", "group: Joe", "group: Jenny", "value: 1", "value: 2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("group"), - new OptionalParam("value").setAllowMultipleValues()) - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("value", params.value("a")); - final RepeatingGroup[] groups = params.valuesAsGroup("group"); - assertEquals(2, groups.length); - assertEquals("Joe", groups[0].value("group")); - assertEquals(0, groups[0].values("value").length); - assertEquals("Jenny", groups[1].value("group")); - assertEquals(2, groups[1].values("value").length); - assertEquals("1", groups[1].values("value")[0]); - assertEquals("2", groups[1].values("value")[1]); - } - - @Test - public void shouldBeAbleToRetrieveGroupsOfParamsWhenSomeOptionalValuesAreOmitted() - { - final String[] args = { - "a: value", - "group: Joe", "value: 1", - "group: Jenny", "optional: X", "value: 2" - }; - final DslParam[] parameters = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("group"), - new OptionalParam("optional"), - new OptionalParam("value")) - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("value", params.value("a")); - final RepeatingGroup[] groups = params.valuesAsGroup("group"); - assertEquals(2, groups.length); - assertEquals("Joe", groups[0].value("group")); - assertEquals("1", groups[0].value("value")); - assertNull(groups[0].value("optional")); - - assertEquals("Jenny", groups[1].value("group")); - assertEquals("2", groups[1].value("value")); - assertEquals("X", groups[1].value("optional")); - } - - @Test - public void shouldEnforceAllowedValuesInRepeatingGroups() - { - final String[] args = {"a: value", "myGroup: Joe", "myValue: 1"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("myGroup"), - new RequiredParam("myValue").setAllowedValues("A", "B")) - }; - - final IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParams(args, parameters)); - - assertEquals("myValue parameter value '1' must be one of: [A, B]", exception.getMessage()); - } - - @Test - public void shouldUseDefaultValuesForOptionalParametersInRepeatingGroups() - { - final String[] args = {"a: value", - "group: Joe", "value: 1", - "group: Jenny", "optional: X", "value: 2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RepeatingParamGroup( - new RequiredParam("group"), - new OptionalParam("optional").setDefault("default"), - new OptionalParam("value")) - }; - - final DslParams params = new DslParams(args, parameters); - - assertEquals("value", params.value("a")); - final RepeatingGroup[] groups = params.valuesAsGroup("group"); - assertEquals(2, groups.length); - assertEquals("Joe", groups[0].value("group")); - assertEquals("1", groups[0].value("value")); - assertEquals("default", groups[0].value("optional")); - - assertEquals("Jenny", groups[1].value("group")); - assertEquals("2", groups[1].value("value")); - assertEquals("X", groups[1].value("optional")); - } - - @Test - public void shouldCallConsumerForDefaultValues() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - - final String[] args = {}; - final DslParam[] parameters = {new OptionalParam("a").setDefault("b").setConsumer(consumer)}; - - new DslParams(args, parameters); - - assertEquals(1, list.size()); - assertEquals("b", list.get(0)); - } - - @Test - public void shouldThrowAnExceptionWhenMissingAValueForARequiredParam() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a"), new RequiredParam("b")}; - - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParams(args, parameters)); - - assertEquals("Missing value for parameter: b", exception.getMessage()); - } - - @Test - public void shouldThrowAnExceptionIfAnUnexpectedParameterValueIsPassedIn() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = {new RequiredParam("a")}; - - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParams(args, parameters)); - - assertEquals("Unexpected argument b=2", exception.getMessage()); - } - - @Test - public void shouldThrowAnExceptionContainingTheDslParamsForIntrospectionWhenPassedTheUsageArg() - { - final String[] args = {"-usage"}; - final SimpleDslParam[] params = {new RequiredParam("a"), new OptionalParam("b")}; - - final DslParamsUsageException exception = assertThrows( - DslParamsUsageException.class, - () -> new DslParams(args, params)); - - assertArrayEquals(params, exception.getParams()); - } -} diff --git a/src/test/java/com/lmax/simpledsl/OptionalParamTest.java b/src/test/java/com/lmax/simpledsl/OptionalParamTest.java deleted file mode 100755 index 3a55433..0000000 --- a/src/test/java/com/lmax/simpledsl/OptionalParamTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import org.junit.jupiter.api.Test; - -import java.util.LinkedList; -import java.util.function.Consumer; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class OptionalParamTest -{ - @Test - public void shouldNotReportAsRequired() - { - final DslParam param = new OptionalParam("foo"); - assertFalse(param.isRequired()); - } - - @Test - public void shouldReturnNullIfNoValueIsProvidedAndNoDefaultValueIsSet() - { - final SimpleDslParam param = new OptionalParam("foo"); - assertNull(param.getValue()); - assertArrayEquals(new String[0], param.getValues()); - } - - @Test - public void shouldUseTheDefaultValueIfNoValueProvided() - { - final SimpleDslParam param = new OptionalParam("foo").setDefault("def"); - assertEquals("def", param.getValue()); - assertArrayEquals(new String[]{"def"}, param.getValues()); - } - - @Test - public void shouldNotOverrideTheProvidedValueWithTheDefaultValue() - { - final SimpleDslParam param = new OptionalParam("foo").setDefault("def"); - param.addValue("1"); - assertEquals("1", param.getValue()); - assertArrayEquals(new String[]{"1"}, param.getValues()); - } - - @Test - public void shouldCallConsumeWhenAParameterValueIsProvided() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new OptionalParam("foo").setConsumer(consumer); - param.addValue("abc"); - - assertEquals(1, list.size()); - assertEquals("abc", list.get(0)); - } - - @Test - public void shouldCallConsumeWithTheDefaultValueIfNoParameterValueIsProvided() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new OptionalParam("foo").setDefault("abc").setConsumer(consumer); - param.completedParsing(); - - assertEquals(1, list.size()); - assertEquals("abc", list.get(0)); - } - - @Test - public void shouldNotCallConsumeIfNoParameterValueIsProvidedAndNoDefaultValueIsSet() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new OptionalParam("foo").setConsumer(consumer); - param.completedParsing(); - - assertEquals(0, list.size()); - } - -} diff --git a/src/test/java/com/lmax/simpledsl/RequiredParamTest.java b/src/test/java/com/lmax/simpledsl/RequiredParamTest.java deleted file mode 100644 index f85cf2b..0000000 --- a/src/test/java/com/lmax/simpledsl/RequiredParamTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import org.junit.jupiter.api.Test; - -import java.util.LinkedList; -import java.util.function.Consumer; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class RequiredParamTest -{ - @Test - public void shouldReportAsRequired() - { - final DslParam param = new RequiredParam("foo"); - assertTrue(param.isRequired()); - } - - @Test - public void shouldAssignProvidedValue() - { - final RequiredParam param = new RequiredParam("foo"); - final int position = param.consume(1, new NameValuePair("goo = 0"), new NameValuePair("foo = 1"), new NameValuePair("bar = 2")); - assertEquals(2, position); - assertEquals("1", param.getValue()); - } - - @Test - public void shouldAssignProvidedValueIgnoringCase() - { - final RequiredParam param = new RequiredParam("foo"); - final int position = param.consume(1, new NameValuePair("goo = 0"), new NameValuePair("FOO = 1"), new NameValuePair("bar = 2")); - assertEquals(2, position); - assertEquals("1", param.getValue()); - } - - @Test - public void shouldAssignProvidedValueWhenUnnamed() - { - final RequiredParam param = new RequiredParam("foo"); - final int position = param.consume(1, new NameValuePair("0"), new NameValuePair("1"), new NameValuePair("2")); - assertEquals(2, position); - assertEquals("1", param.getValue()); - } - - @Test - public void shouldAssignProvidedValueAtTheHeadOfTheList() - { - final RequiredParam param = new RequiredParam("goo"); - final int position = param.consume(0, new NameValuePair("goo = 0"), new NameValuePair("bar = 1"), new NameValuePair("foo = 2")); - assertEquals(1, position); - assertEquals("0", param.getValue()); - } - - @Test - public void shouldAssignProvidedValueAtTheTailOfTheList() - { - final RequiredParam param = new RequiredParam("foo"); - final int position = param.consume(2, new NameValuePair("goo = 0"), new NameValuePair("bar = 1"), new NameValuePair("foo = 2")); - assertEquals(3, position); - assertEquals("2", param.getValue()); - } - - @Test - public void shouldConsumeMultipleParamsUpToTheFirstParamNamedDifferently() - { - final RequiredParam param = new RequiredParam("foo").setAllowMultipleValues(); - final int position = param.consume(1, new NameValuePair("first param"), new NameValuePair("foo = 1"), new NameValuePair("2"), new NameValuePair("foo = 3"), new NameValuePair("4"), - new NameValuePair("something else = 5")); - assertEquals(5, position); - assertArrayEquals(new String[]{"1", "2", "3", "4"}, param.getValues()); - } - - @Test - public void shouldConsumeMultipleParamsBySplittingValuesByCommaDelimiter() - { - final RequiredParam param = new RequiredParam("foo").setAllowMultipleValues(); - final int position = param.consume(1, new NameValuePair("first param"), new NameValuePair("foo = 1, 2"), new NameValuePair("foo = 3"), new NameValuePair("4"), - new NameValuePair("something else = 5")); - assertEquals(4, position); - assertArrayEquals(new String[]{"1", "2", "3", "4"}, param.getValues()); - } - - @Test - public void shouldConsumeMultipleParamsBySplittingValuesByUserSuppliedDelimiter() - { - final RequiredParam param = new RequiredParam("foo").setAllowMultipleValues("\\|"); - final int position = param.consume(1, new NameValuePair("first param"), new NameValuePair("foo = 1| 2"), new NameValuePair("foo = 3"), new NameValuePair("4"), - new NameValuePair("something else = 5")); - assertEquals(4, position); - assertArrayEquals(new String[]{"1", "2", "3", "4"}, param.getValues()); - } - - @Test - public void shouldCallConsumerWhenASingleValueIsProvided() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new RequiredParam("foo").setConsumer(consumer); - param.addValue("abc"); - - assertEquals(1, list.size()); - assertEquals("abc", list.get(0)); - } - -} diff --git a/src/test/java/com/lmax/simpledsl/SimpleDslParamTest.java b/src/test/java/com/lmax/simpledsl/SimpleDslParamTest.java deleted file mode 100755 index 0943dcf..0000000 --- a/src/test/java/com/lmax/simpledsl/SimpleDslParamTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2011 LMAX Ltd. - * - * 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 com.lmax.simpledsl; - -import org.junit.jupiter.api.Test; - -import java.util.LinkedList; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class SimpleDslParamTest -{ - @Test - public void shouldReturnTheValueAdded() - { - final SimpleDslParam param = new TestParam("foo"); - param.addValue("12"); - assertEquals("12", param.getValue()); - } - - @Test - public void shouldAddMultipleValuesWhenMultipleValuesAreAllowed() - { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); - param.addValue("12"); - param.addValue("34"); - } - - @Test - public void shouldReturnAllTheValuesAddedWhenMultipleValuesAreAllowed() - { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); - param.addValue("12"); - param.addValue("34"); - assertArrayEquals(new String[]{"12", "34"}, param.getValues()); - } - - @Test - public void shouldRestrictWhichValuesAreAllowed() - { - final SimpleDslParam param = new TestParam("foo").setAllowedValues("12", "34"); - - final IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> param.addValue("56")); - - assertEquals("foo parameter value '56' must be one of: [12, 34]", exception.getMessage()); - } - - @Test - public void shouldMatchAllowedValuesCaseInsensitivelyButStillReturnTheValuesWithTheProvidedCase() - { - final SimpleDslParam param = new TestParam("foo").setAllowedValues("abc", "def").setAllowMultipleValues(); - param.addValue("abc"); - param.addValue("DeF"); - assertArrayEquals(new String[]{"abc", "def"}, param.getValues()); - } - - @Test - public void shouldCallConsumerWhenSingleValueProvided() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new TestParam("foo").setConsumer(consumer); - param.addValue("abc"); - - assertEquals(1, list.size()); - assertEquals("abc", list.get(0)); - } - - @Test - public void shouldCallConsumerInCorrectOrderWhenMultipleValuesProvided() - { - final LinkedList list = new LinkedList<>(); - final Consumer consumer = list::add; - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues().setConsumer(consumer); - param.addValue("abc"); - param.addValue("def"); - param.addValue("ghi"); - - assertEquals(3, list.size()); - assertEquals("abc", list.get(0)); - assertEquals("def", list.get(1)); - assertEquals("ghi", list.get(2)); - } - - @Test - public void shouldCallBiConsumerWithTheCorrectParameterName() - { - final LinkedList list = new LinkedList<>(); - final BiConsumer consumer = (name, value) -> list.add(name); - final SimpleDslParam param1 = new TestParam("foo").setAllowMultipleValues().setConsumer(consumer); - final SimpleDslParam param2 = new TestParam("bar").setAllowMultipleValues().setConsumer(consumer); - param1.addValue("abc"); - param1.addValue("def"); - param2.addValue("ghi"); - - assertEquals(3, list.size()); - assertEquals("foo", list.get(0)); - assertEquals("foo", list.get(1)); - assertEquals("bar", list.get(2)); - } - - @Test - public void shouldThrowExceptionOnSecondCallToAddValueWhenMultipleValuesNotAllowed() - { - final SimpleDslParam param = new TestParam("foo"); - param.addValue("12"); - - assertThrows( - IllegalArgumentException.class, - () -> param.addValue("12"), - "Multiple foo parameters are not allowed"); - } - - @Test - public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleValues() - { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); - - final IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - param::getValue); - - assertEquals("getValues() should be used when multiple values are allowed", exception.getMessage()); - } - - private static class TestParam extends SimpleDslParam - { - TestParam(final String name) - { - super(name); - } - } -} diff --git a/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java new file mode 100644 index 0000000..d1899ec --- /dev/null +++ b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java @@ -0,0 +1,266 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.DslArg; +import com.lmax.simpledsl.api.DslParams; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DslParamsImplTest +{ + @Test + public void shouldReturnValueAsInt() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(1, params.valueAsInt("a")); + } + + @Test + public void shouldThrowNumberFormatExceptionWhenValueAsIntCalledForParameterThatIsNotSupplied() + { + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + final NumberFormatException exception = assertThrows( + NumberFormatException.class, + () -> params.valueAsInt("a")); + + assertEquals("null", exception.getMessage()); + } + + @Test + public void shouldReturnValueAsLong() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(1L, params.valueAsLong("a")); + } + + @Test + public void shouldReturnValueAsBoolean() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("true")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertTrue(params.valueAsBoolean("a")); + } + + @Test + public void shouldReturnValueAsBigDecimal() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(0, BigDecimal.ONE.compareTo(params.valueAsBigDecimal("a"))); + } + + @Test + public void shouldReturnValueAsDouble() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1.23")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(1.23d, params.valueAsDouble("a"), 0d); + } + + @Test + public void shouldReturnNullValueWhenBigDecimalIsNotFound() + { + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertNull(params.valueAsBigDecimal("a")); + } + + @Test + public void shouldReturnValueAsParamForOptionalValueThatWasSpecified() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("value")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals("a: value", params.valueAsParam("a")); + } + + @Test + public void shouldReturnNullValueAsParamWhenSimpleDslParamNotSpecified() + { + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertNull(params.valueAsParam("a")); + } + + @Test + public void shouldReturnValuesAsIntArray() + { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2", "3")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertArrayEquals(new int[]{1, 2, 3}, params.valuesAsInts("a")); + } + + @Test + public void shouldReturnValuesAsLongArray() + { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2", "3")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertArrayEquals(new long[]{1, 2, 3}, params.valuesAsLongs("a")); + } + + @Test + public void shouldReturnValuesAsBigDecimalArray() + { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2.23", "3")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertArrayEquals( + new BigDecimal[]{new BigDecimal("1"), new BigDecimal("2.23"), new BigDecimal("3")}, + params.valuesAsBigDecimals("a") + ); + } + + @Test + public void shouldReturnValuesAsDoubleArray() + { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2.23", "3")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertArrayEquals(new double[]{1, 2.23, 3}, params.valuesAsDoubles("a")); + } + + @Test + public void shouldReportOptionalValueAsPresentWhenValueProvided() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + final SimpleDslParam bParam = new SimpleDslParam("b", singletonList("2")); + + final Map paramsByName = new HashMap<>(); + paramsByName.put("a", aParam); + paramsByName.put("b", bParam); + + final DslParams params = new DslParamsImpl(new DslArg[0], paramsByName); + + assertTrue(params.hasValue("b")); + } + + @Test + public void shouldNotReportOptionalValueAsPresentWhenNoValueProvided() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + final SimpleDslParam bParam = new SimpleDslParam("b", emptyList()); + + final Map paramsByName = new HashMap<>(); + paramsByName.put("a", aParam); + paramsByName.put("b", bParam); + + final DslParams params = new DslParamsImpl(new DslArg[0], paramsByName); + + assertFalse(params.hasValue("b")); + } + + @Test + public void shouldReportRequiredValueAsPresentWhenEmptyValueProvided() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertTrue(params.hasValue("a")); + } + + @Test + public void shouldReportOptionalValueAsPresentWhenEmptyValueProvided() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertTrue(params.hasValue("a")); + } + + @Test + public void shouldReturnEmptyOptionalWhenValueIsNotSupplied() + { + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(Optional.empty(), params.valueAsOptional("a")); + } + + @Test + public void shouldReturnOptionalWithValueWhenValueIsSupplied() + { + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("value")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(Optional.of("value"), params.valueAsOptional("a")); + } + + @Test + public void shouldReturnEmptyOptionalWhenMultipleParameterValueIsNotSupplied() + { + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(Optional.empty(), params.valuesAsOptional("a")); + } + + @Test + public void shouldReturnOptionalListWhenMultipleParameterValueIsSupplied() + { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("value1", "value2")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); + + assertEquals(Optional.of(asList("value1", "value2")), params.valuesAsOptional("a")); + } +} diff --git a/src/test/java/com/lmax/simpledsl/internal/DslParamsParserTest.java b/src/test/java/com/lmax/simpledsl/internal/DslParamsParserTest.java new file mode 100644 index 0000000..72fbac7 --- /dev/null +++ b/src/test/java/com/lmax/simpledsl/internal/DslParamsParserTest.java @@ -0,0 +1,808 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.DslArg; +import com.lmax.simpledsl.api.DslParams; +import com.lmax.simpledsl.api.OptionalArg; +import com.lmax.simpledsl.api.RepeatingArgGroup; +import com.lmax.simpledsl.api.RepeatingGroup; +import com.lmax.simpledsl.api.RequiredArg; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DslParamsParserTest +{ + @Test + public void shouldExtractRequiredArgsWhenNamed() + { + final String[] args = {"a=1", "b=2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldExtractRequiredArgsWhenUnnamed() + { + final String[] args = {"1", "2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + @Test + public void shouldExtractOptionalArgsWhenNamed() + { + final String[] args = {"a=1", "b=2"}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldExtractOptionalArgsWhenUnnamed() + { + final String[] args = {"1", "2"}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldParseAMixtureOfNamedAndUnnamedParametersWhenParametersAreRequired() + { + final String[] args = {"a=1", "2", "c: 3"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b"), + new OptionalArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals(1, params.valueAsInt("a")); + assertEquals(2, params.valueAsInt("b")); + assertEquals(3, params.valueAsInt("c")); + } + + @Test + public void shouldUseTheDefaultValueForAnOptionalArgsWhenNoValueIsProvided() + { + final String[] args = new String[0]; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b").setDefault("value") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("b")); + } + + @Test + public void shouldUseNullForAnOptionalArgsWhenNoValueOrDefaultValueIsProvided() + { + final String[] args = new String[0]; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b").setDefault("value") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertNull(params.value("a")); + } + + @Test + public void shouldExtractMixedParameterTypesWhenNamed() + { + final String[] args = {"a=1", "b=2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldBeAbleToExtractMultipleRequiredArgsWhenAllParamsAreNamed() + { + final String[] args = {"a=1", "b=2", "b=3", "c=4"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b").setAllowMultipleValues(), + new RequiredArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertArrayEquals(new String[]{"2", "3"}, params.values("b")); + assertEquals("4", params.value("c")); + } + + @Test + public void shouldBeAbleToExtractMultipleValuesForOneParameterUsingTheDefaultSeparator() + { + final String[] args = {"a=1", "b=2, 3", "c=4"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b").setAllowMultipleValues(), + new RequiredArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertArrayEquals(new String[]{"2", "3"}, params.values("b")); + assertEquals("4", params.value("c")); + } + + @Test + public void shouldBeAbleToExtractMultipleValuesForOneParameterUsingTheACustomSeparator() + { + final String[] args = {"a=1", "b=2,00;3,00", "c=4"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b").setAllowMultipleValues(";"), + new RequiredArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertArrayEquals(new String[]{"2,00", "3,00"}, params.values("b")); + assertEquals("4", params.value("c")); + } + + @Test + public void shouldBeAbleToExtractMultipleRequiredArgsWhenSubsequentRequiredArgsAreNotNamed() + { + final String[] args = {"a=1", "b=2", "3", "c=4"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b").setAllowMultipleValues(), + new RequiredArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertArrayEquals(new String[]{"2", "3"}, params.values("b")); + assertEquals("4", params.value("c")); + } + + @Test + public void shouldBeAbleToExtractMultipleRequiredArgsWhenWithClearBoundariesBetweenArguments() + { + final String[] args = {"a=1", "2", "3", "b=2", "3", "c=4"}; + final DslArg[] parameters = { + new RequiredArg("a").setAllowMultipleValues(), + new RequiredArg("b").setAllowMultipleValues(), + new RequiredArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertArrayEquals(new String[]{"1", "2", "3"}, params.values("a")); + assertArrayEquals(new String[]{"2", "3"}, params.values("b")); + assertEquals("4", params.value("c")); + } + + @Test + public void shouldBeAbleToExtractMultipleRequiredArgsAndMultipleOptionalArgsWithOptionalArgsInRandomOrder() + { + final String[] args = {"a=1", "b=2", "b=3", "c=4", "d=5", "c=6"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b").setAllowMultipleValues(), + new OptionalArg("c").setAllowMultipleValues(), + new OptionalArg("d") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertArrayEquals(new String[]{"2", "3"}, params.values("b")); + assertArrayEquals(new String[]{"4", "6"}, params.values("c")); + assertEquals("5", params.value("d")); + } + + @Test + public void shouldBeAbleToDetectPresenceOfParamsWithMultipleValuesAllowed() + { + final String[] args = {"a=1", "a=2"}; + final DslArg[] parameters = {new OptionalArg("a").setAllowMultipleValues()}; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertTrue(params.hasValue("a")); + } + + @Test + public void shouldBeAbleToDetectAbsenceOfParamsWithMultipleValuesAllowed() + { + final String[] args = new String[0]; + final DslArg[] parameters = {new OptionalArg("a").setAllowMultipleValues()}; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertFalse(params.hasValue("a")); + } + + @Test + public void shouldBeAbleToSpecifiedNamedRequiredArgsInAnyOrder() + { + final String[] args = {"b=2", "a=1"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldExtractOptionalArgsFromPositionalArguments() + { + final String[] args = {"1", "2"}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldExtractOptionalArgsFromPositionalArgumentsWhenNullArgumentsArePresent() + { + final String[] args = {null, "1", null}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b"), + new OptionalArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertNull(params.value("a")); + assertEquals("1", params.value("b")); + assertNull(params.value("c")); + } + + @Test + public void shouldMatchParamsIgnoringCase() + { + final String[] args = {"a=1", "B=2"}; + final DslArg[] parameters = { + new RequiredArg("A"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("B")); + assertTrue(params.hasValue("a")); + assertTrue(params.hasValue("B")); + } + + @Test + public void shouldIgnoreNullArgumentsAmongstOptionalArgs() + { + final String[] args = {null, "a=1", null, "b=2", null}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("1", params.value("a")); + assertEquals("2", params.value("b")); + } + + @Test + public void shouldIgnoreNullArgumentsInRepeatingGroups() + { + final String[] args = {"a=1", "b=2", null}; + final DslArg[] parameters = { + new RepeatingArgGroup( + new RequiredArg("a"), + new RequiredArg("b")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + final RepeatingGroup[] group = params.valuesAsGroup("a"); + assertEquals("1", group[0].value("a")); + assertEquals("2", group[0].value("b")); + } + + @Test + public void shouldBeAbleToRetrieveGroupsOfParams() + { + final String[] args = {"a: value", "group: Joe", "value: 1", "group: Jenny", "value: 2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new RequiredArg("value")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals("1", groups[0].value("value")); + assertEquals("Jenny", groups[1].value("group")); + assertEquals("2", groups[1].value("value")); + } + + @Test + public void shouldBeAbleToRetrieveArgumentsFollowingTheEndOfARepeatingGroup() + { + final String[] args = {"a: value", "group: Joe", "value: 1", "group: Jenny", "value: 2", "b: 12", "c: hello"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new OptionalArg("b"), + new RepeatingArgGroup( + new RequiredArg("group"), + new RequiredArg("value")), + new OptionalArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("12", params.value("b")); + assertEquals("hello", params.value("c")); + } + + @Test + public void shouldBeAbleToSpecifyMultipleValuesForParamInGroup() + { + final String[] args = {"a: value", "group: Joe", "group: Jenny", "value: 1", "value: 2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new OptionalArg("value").setAllowMultipleValues()) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals(0, groups[0].values("value").length); + assertEquals("Jenny", groups[1].value("group")); + assertEquals(2, groups[1].values("value").length); + assertEquals("1", groups[1].values("value")[0]); + assertEquals("2", groups[1].values("value")[1]); + } + + @Test + @Disabled("Doesn't work?") + public void shouldBeAbleToSpecifyMultipleValuesForParamInGroupUsingTheDefaultSeparator() + { + final String[] args = {"a: value", "group: Joe", "group: Jenny", "value: 1, 2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new OptionalArg("value").setAllowMultipleValues()) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals(0, groups[0].values("value").length); + assertEquals("Jenny", groups[1].value("group")); + assertEquals(2, groups[1].values("value").length); + assertEquals("1", groups[1].values("value")[0]); + assertEquals("2", groups[1].values("value")[1]); + } + + @Test + @Disabled("Doesn't work?") + public void shouldBeAbleToSpecifyMultipleValuesForParamInGroupUsingACustomSeparator() + { + final String[] args = {"a: value", "group: Joe", "group: Jenny", "value: 1;2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new OptionalArg("value").setAllowMultipleValues(";")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals(0, groups[0].values("value").length); + assertEquals("Jenny", groups[1].value("group")); + assertEquals(2, groups[1].values("value").length); + assertEquals("1", groups[1].values("value")[0]); + assertEquals("2", groups[1].values("value")[1]); + } + + @Test + public void shouldBeAbleToRetrieveGroupsOfParamsWhenSomeOptionalValuesAreOmitted() + { + final String[] args = { + "a: value", + "group: Joe", "value: 1", + "group: Jenny", "optional: X", "value: 2" + }; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new OptionalArg("optional"), + new OptionalArg("value")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals("1", groups[0].value("value")); + assertNull(groups[0].value("optional")); + + assertEquals("Jenny", groups[1].value("group")); + assertEquals("2", groups[1].value("value")); + assertEquals("X", groups[1].value("optional")); + } + + @Test + public void shouldUseDefaultValuesForOptionalArgsInRepeatingGroups() + { + final String[] args = { + "a: value", + "group: Joe", "value: 1", + "group: Jenny", "optional: X", "value: 2" + }; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("group"), + new OptionalArg("optional").setDefault("default"), + new OptionalArg("value")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertEquals("value", params.value("a")); + final RepeatingGroup[] groups = params.valuesAsGroup("group"); + assertEquals(2, groups.length); + assertEquals("Joe", groups[0].value("group")); + assertEquals("1", groups[0].value("value")); + assertEquals("default", groups[0].value("optional")); + + assertEquals("Jenny", groups[1].value("group")); + assertEquals("2", groups[1].value("value")); + assertEquals("X", groups[1].value("optional")); + } + + @Test + public void shouldMatchAllowedValuesCaseInsensitivelyAndReturnValuesUsingTheCaseProvidedInTheDSL() + { + final String[] args = { + "myValue: abc", "myValue: DeF", + }; + final DslArg[] parameters = { + new RequiredArg("myValue").setAllowedValues("abc", "def").setAllowMultipleValues(), + }; + + final DslParamsParser parser = new DslParamsParser(); + + final DslParams params = parser.parse(args, parameters); + + assertArrayEquals(new String[]{"abc", "def"}, params.values("myValue")); + } + + @Test + public void shouldThrowAnExceptionIfRequiredArgeterMissingFromGroup() + { + final String[] args = {"a: value", "myGroup: Joe", "myGroup: Jenny", "myValue: 2"}; + final DslArg[] params = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("myGroup"), + new RequiredArg("myValue")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, params)); + + assertEquals("Did not supply a value for myValue in group myGroup", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfTwoValuesProvidedWhenMultipleValuesAreNotAllowed() + { + final String[] args = {"foo: value1", "foo: value2"}; + final DslArg[] params = {new RequiredArg("foo")}; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, params)); + + assertEquals("Multiple foo parameters are not allowed", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfInvalidParameterValueIsSpecified() + { + final String[] args = {"a: value", "myValue: 1"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("myValue").setAllowedValues("A", "B") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters)); + + assertEquals("myValue parameter value '1' must be one of: [A, B]", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfInvalidParameterValueIsSpecifiedInRepeatingGroup() + { + final String[] args = {"a: value", "myGroup: Joe", "myValue: 1"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RepeatingArgGroup( + new RequiredArg("myGroup"), + new RequiredArg("myValue").setAllowedValues("A", "B")) + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters)); + + assertEquals("myValue parameter value '1' must be one of: [A, B]", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionWhenMissingAValueForARequiredArg() + { + final String[] args = {"a=1"}; + final DslArg[] parameters = {new RequiredArg("a"), new RequiredArg("b")}; + + final DslParamsParser parser = new DslParamsParser(); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters)); + + assertEquals("Missing value for parameter: b", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfAnUnexpectedParameterValueIsPassedIn() + { + final String[] args = {"a=1", "b=2"}; + final DslArg[] parameters = {new RequiredArg("a")}; + + final DslParamsParser parser = new DslParamsParser(); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters)); + + assertEquals("Unexpected argument b=2", exception.getMessage()); + } + + + @Test + public void shouldThrowAnExceptionIfUnnamedRequiredArgFollowsNamedOptionalArg() + { + final String[] args = {"a=1", "2"}; + final DslArg[] parameters = { + new OptionalArg("a"), + new RequiredArg("b"), + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters) + ); + + assertEquals("Unexpected ambiguous argument 2", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfUnnamedOptionalArgFollowsNamedOptionalArg() + { + final String[] args = {"a=1", "2"}; + final DslArg[] parameters = { + new OptionalArg("a"), + new OptionalArg("b"), + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters) + ); + + assertEquals("Unexpected ambiguous argument 2", exception.getMessage()); + } + + @Test + public void shouldThrowAnExceptionIfUnnamedRequiredArgIsProvidedAfterNamedOptionalArgOutOfDefinitionOrder() + { + final String[] args = {"b=1", "2"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new OptionalArg("b"), + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters) + ); + + assertEquals("Unexpected ambiguous argument 2", exception.getMessage()); + } + + @Test + public void maybeShouldThrowExceptionIfRequiredParametersArePassedOutOfOrder() + { + final String[] args = {"b=2", "1", "c: 3"}; + final DslArg[] parameters = { + new RequiredArg("a"), + new RequiredArg("b"), + new OptionalArg("c") + }; + + final DslParamsParser parser = new DslParamsParser(); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> parser.parse(args, parameters) + ); + + assertEquals("Unexpected ambiguous argument 1", exception.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/com/lmax/simpledsl/NameValuePairTest.java b/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java similarity index 54% rename from src/test/java/com/lmax/simpledsl/NameValuePairTest.java rename to src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java index 6f40914..23b97de 100755 --- a/src/test/java/com/lmax/simpledsl/NameValuePairTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.lmax.simpledsl; +package com.lmax.simpledsl.internal; import org.junit.jupiter.api.Test; @@ -25,48 +25,48 @@ public class NameValuePairTest @Test public void shouldSplitNameAndValueWithEquals() { - final NameValuePair pair = new NameValuePair("a=1"); - assertEquals("a", pair.getName()); - assertEquals("1", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString("a=1"); + assertEquals("a", pair.name); + assertEquals("1", pair.value); } @Test public void shouldSplitNameAndValueWithColon() { - final NameValuePair pair = new NameValuePair("a:1"); - assertEquals("a", pair.getName()); - assertEquals("1", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString("a:1"); + assertEquals("a", pair.name); + assertEquals("1", pair.value); } @Test public void shouldTrimWhitespace() { - final NameValuePair pair = new NameValuePair(" a = 1 "); - assertEquals("a", pair.getName()); - assertEquals("1", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString(" a = 1 "); + assertEquals("a", pair.name); + assertEquals("1", pair.value); } @Test public void shouldPreserveValueIfMultipleSplitTokensArePresent() { - final NameValuePair pair = new NameValuePair("message: ERROR: Something went wrong!"); - assertEquals("message", pair.getName()); - assertEquals("ERROR: Something went wrong!", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString("message: ERROR: Something went wrong!"); + assertEquals("message", pair.name); + assertEquals("ERROR: Something went wrong!", pair.value); } @Test public void shouldSetTheNameToNullAndAssignsEverythingToTheValueInAnUnnamedPair() { - final NameValuePair pair = new NameValuePair("value without a name"); - assertNull(pair.getName()); - assertEquals("value without a name", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString("value without a name"); + assertNull(pair.name); + assertEquals("value without a name", pair.value); } @Test public void shouldUseAnEmptyStringWhenNoValueIsProvided() { - final NameValuePair pair = new NameValuePair("name:"); - assertEquals("name", pair.getName()); - assertEquals("", pair.getValue()); + final NameValuePair pair = NameValuePair.fromArgumentString("name:"); + assertEquals("name", pair.name); + assertEquals("", pair.value); } } diff --git a/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java b/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java new file mode 100755 index 0000000..590156d --- /dev/null +++ b/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2011 LMAX Ltd. + * + * 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 com.lmax.simpledsl.internal; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class SimpleDslParamTest +{ + @Test + public void shouldReturnTheValueAdded() + { + final SimpleDslParam param = new SimpleDslParam("foo", Collections.singletonList("12")); + + assertEquals("12", param.getValue()); + } + + @Test + public void shouldReturnAllTheValuesAddedWhenMultipleValuesAreAllowed() + { + final SimpleDslParam param = new SimpleDslParam("foo", asList("12", "34")); + + assertArrayEquals(new String[]{"12", "34"}, param.getValues()); + } + + @Test + public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleValues() + { + final SimpleDslParam param = new SimpleDslParam("foo", asList("value1", "value2")); + + final IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + param::getValue); + + assertEquals("getValues() should be used when multiple values are allowed", exception.getMessage()); + } +} \ No newline at end of file