From 4301e24787568af37262e646a503c96a93e4d811 Mon Sep 17 00:00:00 2001 From: swarren12 <33803336+swarren12@users.noreply.github.com> Date: Thu, 28 Oct 2021 20:39:16 +0100 Subject: [PATCH 1/4] Remove "usage" option --- .../java/com/lmax/simpledsl/DslParams.java | 11 ----- .../simpledsl/DslParamsUsageException.java | 41 ------------------- .../com/lmax/simpledsl/DslParamsTest.java | 13 ------ 3 files changed, 65 deletions(-) delete mode 100755 src/main/java/com/lmax/simpledsl/DslParamsUsageException.java diff --git a/src/main/java/com/lmax/simpledsl/DslParams.java b/src/main/java/com/lmax/simpledsl/DslParams.java index da21cff..7ca21b1 100644 --- a/src/main/java/com/lmax/simpledsl/DslParams.java +++ b/src/main/java/com/lmax/simpledsl/DslParams.java @@ -35,8 +35,6 @@ */ public class DslParams extends DslValues { - private static final String USAGE_TOKEN = "-usage"; - private final DslParam[] params; private final Map paramsByName = new HashMap<>(); @@ -50,7 +48,6 @@ public class DslParams extends DslValues public DslParams(final String[] args, final DslParam... params) { this.params = params; - checkUsage(args, params); final NameValuePair[] arguments = parseArguments(args); @@ -201,14 +198,6 @@ public boolean hasValue(final String 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) diff --git a/src/main/java/com/lmax/simpledsl/DslParamsUsageException.java b/src/main/java/com/lmax/simpledsl/DslParamsUsageException.java deleted file mode 100755 index 95cfb6e..0000000 --- a/src/main/java/com/lmax/simpledsl/DslParamsUsageException.java +++ /dev/null @@ -1,41 +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; - -/** - * Thrown when a -usage parameter is supplied. - * - * @deprecated To be removed in a future release. Use {@link DslParams#getParams()} instead. - */ -public class DslParamsUsageException extends RuntimeException -{ - 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/test/java/com/lmax/simpledsl/DslParamsTest.java b/src/test/java/com/lmax/simpledsl/DslParamsTest.java index 8d9e9f2..751cd67 100644 --- a/src/test/java/com/lmax/simpledsl/DslParamsTest.java +++ b/src/test/java/com/lmax/simpledsl/DslParamsTest.java @@ -672,17 +672,4 @@ public void shouldThrowAnExceptionIfAnUnexpectedParameterValueIsPassedIn() 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()); - } } From d788f0f89b437a6580d354330823e453c40df2f8 Mon Sep 17 00:00:00 2001 From: swarren12 <33803336+swarren12@users.noreply.github.com> Date: Thu, 28 Oct 2021 23:32:51 +0100 Subject: [PATCH 2/4] Remove deprecated method --- src/main/java/com/lmax/simpledsl/DslParam.java | 10 ---------- src/main/java/com/lmax/simpledsl/RequiredParam.java | 6 ------ 2 files changed, 16 deletions(-) diff --git a/src/main/java/com/lmax/simpledsl/DslParam.java b/src/main/java/com/lmax/simpledsl/DslParam.java index e0b945c..94670d5 100644 --- a/src/main/java/com/lmax/simpledsl/DslParam.java +++ b/src/main/java/com/lmax/simpledsl/DslParam.java @@ -21,16 +21,6 @@ */ 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}). * diff --git a/src/main/java/com/lmax/simpledsl/RequiredParam.java b/src/main/java/com/lmax/simpledsl/RequiredParam.java index e52bc7d..f1be7b9 100755 --- a/src/main/java/com/lmax/simpledsl/RequiredParam.java +++ b/src/main/java/com/lmax/simpledsl/RequiredParam.java @@ -55,12 +55,6 @@ public RequiredParam(final String name) super(name); } - @Override - public RequiredParam getAsRequiredParam() - { - return this; - } - @Override public boolean isValid() { From 6b8197d44a9a64685e6f4891029f4bfd283d11af Mon Sep 17 00:00:00 2001 From: swarren12 <33803336+swarren12@users.noreply.github.com> Date: Thu, 28 Oct 2021 20:47:14 +0100 Subject: [PATCH 3/4] Distinguish public API from implementation Attempt to differentiate the classes that are intended to be used by the client from those needed internally by separating the two into distinct packages. Everything under the new `internal` package should, eventually, become package-private except (probably) for a single entry-point. --- .../java/com/lmax/simpledsl/DslParam.java | 92 ------- .../com/lmax/simpledsl/OptionalParam.java | 84 ------ .../com/lmax/simpledsl/RepeatingGroup.java | 77 ------ .../lmax/simpledsl/RepeatingParamGroup.java | 156 ----------- .../com/lmax/simpledsl/RequiredParam.java | 109 -------- .../com/lmax/simpledsl/SimpleDslParam.java | 260 ------------------ .../java/com/lmax/simpledsl/api/DslArg.java | 81 ++++++ .../com/lmax/simpledsl/api/DslParams.java | 92 +++++++ .../lmax/simpledsl/{ => api}/DslValues.java | 182 ++++++------ .../com/lmax/simpledsl/api/OptionalArg.java | 87 ++++++ .../lmax/simpledsl/api/RepeatingArgGroup.java | 112 ++++++++ .../lmax/simpledsl/api/RepeatingGroup.java | 30 ++ .../com/lmax/simpledsl/api/RequiredArg.java | 65 +++++ .../com/lmax/simpledsl/api/SimpleDslArg.java | 120 ++++++++ .../com/lmax/simpledsl/internal/DslParam.java | 40 +++ .../DslParamsImpl.java} | 96 ++----- .../{ => internal}/NameValuePair.java | 2 +- .../simpledsl/internal/OptionalParam.java | 52 ++++ .../internal/RepeatingParamGroup.java | 153 +++++++++++ .../internal/RepeatingParamValues.java | 49 ++++ .../simpledsl/internal/RequiredParam.java | 77 ++++++ .../simpledsl/internal/SimpleDslParam.java | 155 +++++++++++ .../DslParamsImplTest.java} | 118 ++++---- .../{ => internal}/NameValuePairTest.java | 2 +- .../{ => internal}/OptionalParamTest.java | 49 +--- .../{ => internal}/RequiredParamTest.java | 20 +- .../{ => internal}/SimpleDslParamTest.java | 89 ++---- 27 files changed, 1332 insertions(+), 1117 deletions(-) delete mode 100644 src/main/java/com/lmax/simpledsl/DslParam.java delete mode 100755 src/main/java/com/lmax/simpledsl/OptionalParam.java delete mode 100644 src/main/java/com/lmax/simpledsl/RepeatingGroup.java delete mode 100644 src/main/java/com/lmax/simpledsl/RepeatingParamGroup.java delete mode 100755 src/main/java/com/lmax/simpledsl/RequiredParam.java delete mode 100644 src/main/java/com/lmax/simpledsl/SimpleDslParam.java create mode 100644 src/main/java/com/lmax/simpledsl/api/DslArg.java create mode 100644 src/main/java/com/lmax/simpledsl/api/DslParams.java rename src/main/java/com/lmax/simpledsl/{ => api}/DslValues.java (75%) create mode 100644 src/main/java/com/lmax/simpledsl/api/OptionalArg.java create mode 100644 src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java create mode 100644 src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java create mode 100644 src/main/java/com/lmax/simpledsl/api/RequiredArg.java create mode 100644 src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java create mode 100644 src/main/java/com/lmax/simpledsl/internal/DslParam.java rename src/main/java/com/lmax/simpledsl/{DslParams.java => internal/DslParamsImpl.java} (57%) rename src/main/java/com/lmax/simpledsl/{ => internal}/NameValuePair.java (97%) create mode 100755 src/main/java/com/lmax/simpledsl/internal/OptionalParam.java create mode 100644 src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java create mode 100644 src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java create mode 100755 src/main/java/com/lmax/simpledsl/internal/RequiredParam.java create mode 100644 src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java rename src/test/java/com/lmax/simpledsl/{DslParamsTest.java => internal/DslParamsImplTest.java} (84%) rename src/test/java/com/lmax/simpledsl/{ => internal}/NameValuePairTest.java (98%) rename src/test/java/com/lmax/simpledsl/{ => internal}/OptionalParamTest.java (50%) rename src/test/java/com/lmax/simpledsl/{ => internal}/RequiredParamTest.java (89%) rename src/test/java/com/lmax/simpledsl/{ => internal}/SimpleDslParamTest.java (52%) 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 94670d5..0000000 --- a/src/main/java/com/lmax/simpledsl/DslParam.java +++ /dev/null @@ -1,92 +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 {@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/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 f1be7b9..0000000 --- a/src/main/java/com/lmax/simpledsl/RequiredParam.java +++ /dev/null @@ -1,109 +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 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..972cb54 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/DslArg.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.api; + +import java.util.function.Function; + +/** + * 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(); + + /** + * Determine if a value is optional for this argument. + * + * @return {@literal true} if and only if this argument is optional. + */ + default boolean isOptional() + { + return !isRequired(); + } + + /** + * 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(); + + /** + * Transform this {@link DslArg} by applying an appropriate {@link Function}. + * + * @param ifRequired the {@link Function} to apply if this {@link DslArg} is compatible with a {@link RequiredArg}. + * @param ifOptional the {@link Function} to apply if this {@link DslArg} is compatible with an {@link OptionalArg}. + * @param ifGroup the {@link Function} to apply if this {@link DslArg} is compatible with a {@link RepeatingArgGroup}. + * @param the return type. + * @return the transformed object. + */ + T fold(Function ifRequired, Function ifOptional, Function ifGroup); +} 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..f223a3a --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/DslParams.java @@ -0,0 +1,92 @@ +/* + * 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.DslParamsImpl; + +/** + * 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); + + /** + * 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 create(args, new RequiredArg(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. + */ + static void checkEmpty(final String[] args) + { + new DslParamsImpl(args); + } + + /** + * Create a new DslParams to define the supported dsl language. + * + * @param args the arguments supplied by the test. + * @param params the supported parameters. + * @return the parsed {@link DslParams} + * @throws IllegalArgumentException if an invalid parameter is specified + */ + static DslParams create(final String[] args, final DslArg... params) + { + return new DslParamsImpl(args, params); + } +} diff --git a/src/main/java/com/lmax/simpledsl/DslValues.java b/src/main/java/com/lmax/simpledsl/api/DslValues.java similarity index 75% rename from src/main/java/com/lmax/simpledsl/DslValues.java rename to src/main/java/com/lmax/simpledsl/api/DslValues.java index c4047a2..7f889f2 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]; @@ -263,13 +298,4 @@ public double[] valuesAsDoubles(final String name) } return parsedValues; } - - /** - * 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 true if the parameter was supplied, otherwise false. - * @throws IllegalArgumentException if {@code name} does not match the name of a supported parameter. - */ - public abstract boolean hasValue(String name); } 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..330d2d4 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/OptionalArg.java @@ -0,0 +1,87 @@ +/* + * 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 java.util.function.Function; + +/** + * 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 +{ + private String defaultValue; + + public OptionalArg(final String name) + { + super(name, false); + } + + /** + * Get a default value for this argument. + * + * @return the default value for the argument + */ + public String getDefaultValue() + { + return defaultValue; + } + + /** + * 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 + */ + public OptionalArg setDefault(final String defaultValue) + { + this.defaultValue = defaultValue; + return this; + } + + @Override + public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) + { + return ifOptional.apply(this); + } +} 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..d4721b3 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java @@ -0,0 +1,112 @@ +/* + * 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 java.util.function.Function; + +/** + * 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 DslArg[] 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 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 DslArg[] getOtherArgs() + { + return otherArgs; + } + + @Override + public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) + { + return ifGroup.apply(this); + } +} diff --git a/src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java b/src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java new file mode 100644 index 0000000..fb22374 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/RepeatingGroup.java @@ -0,0 +1,30 @@ +/* + * 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; + +/** + * 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. + * + * @see RepeatingArgGroup + * @see DslParams#valuesAsGroup(String) + */ +public interface RepeatingGroup extends DslValues +{ +} 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..564d953 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/RequiredArg.java @@ -0,0 +1,65 @@ +/* + * 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 java.util.function.Function; + +/** + * 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 T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) + { + return ifRequired.apply(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..d39de04 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java @@ -0,0 +1,120 @@ +/* + * 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. + * + * @param

the actual type of simple argument. + */ +public abstract class SimpleDslArg

> implements DslArg +{ + private static final String DEFAULT_DELIMITER = ","; + + private final String name; + private final boolean required; + + 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 boolean isAllowMultipleValues() + { + return allowMultipleValues; + } + + @Override + public String getMultipleValueSeparator() + { + return multipleValueSeparator; + } + + @Override + public String[] getAllowedValues() + { + return allowedValues; + } + + /** + * 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 P setAllowedValues(final String... allowedValues) + { + this.allowedValues = allowedValues; + return me(); + } + + /** + * 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 P 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 P setAllowMultipleValues(final String delimiter) + { + allowMultipleValues = true; + multipleValueSeparator = delimiter; + return me(); + } + + @SuppressWarnings("unchecked") + protected P me() + { + return (P) 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..7ea50e3 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/DslParam.java @@ -0,0 +1,40 @@ +/* + * 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; + +/** + * The base class for all param types. + */ +abstract class DslParam +{ + abstract DslArg getArg(); + + abstract SimpleDslParam getAsSimpleDslParam(); + + abstract RepeatingParamGroup asRepeatingParamGroup(); + + abstract int consume(int currentPosition, NameValuePair... args); + + abstract boolean hasValue(); + + boolean isValid() + { + return true; + } +} diff --git a/src/main/java/com/lmax/simpledsl/DslParams.java b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java similarity index 57% rename from src/main/java/com/lmax/simpledsl/DslParams.java rename to src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java index 7ca21b1..e2dc2e8 100644 --- a/src/main/java/com/lmax/simpledsl/DslParams.java +++ b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java @@ -13,27 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.lmax.simpledsl; +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.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import static java.util.Arrays.stream; + /** - * 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"));
- *     }
- * }
+ * The internal implementation of {@link DslParams}. */ -public class DslParams extends DslValues +public final class DslParamsImpl implements DslParams { private final DslParam[] params; private final Map paramsByName = new HashMap<>(); @@ -45,7 +40,14 @@ public class DslParams extends DslValues * @param params the supported parameters. * @throws IllegalArgumentException if an invalid parameter is specified */ - public DslParams(final String[] args, final DslParam... params) + public DslParamsImpl(final String[] args, final DslArg... params) + { + this(args, stream(params) + .map(arg -> arg.fold(RequiredParam::new, OptionalParam::new, RepeatingParamGroup::new)) + .toArray(DslParam[]::new)); + } + + DslParamsImpl(final String[] args, final DslParam... params) { this.params = params; @@ -53,13 +55,13 @@ public DslParams(final String[] args, final DslParam... params) for (final DslParam param : params) { - paramsByName.put(param.getName().toLowerCase(), param); + paramsByName.put(param.getArg().getName().toLowerCase(), param); } int currentPosition = 0; - final boolean allParamsOptional = Arrays.stream(params).allMatch(DslParam::isOptional); - final boolean allArgsPositional = Arrays.stream(arguments).filter(Objects::nonNull) + final boolean allParamsOptional = stream(params).map(DslParam::getArg).allMatch(DslArg::isOptional); + final boolean allArgsPositional = stream(arguments).filter(Objects::nonNull) .map(NameValuePair::getName) .allMatch(Objects::isNull); if (allParamsOptional && allArgsPositional && params.length == args.length) @@ -72,7 +74,7 @@ public DslParams(final String[] args, final DslParam... params) for (final DslParam param : params) { - if (!param.isRequired() || currentPosition >= arguments.length || !matches(arguments[currentPosition], param)) + if (!param.getArg().isRequired() || currentPosition >= arguments.length || !matches(arguments[currentPosition], param)) { break; } @@ -98,12 +100,11 @@ public DslParams(final String[] args, final DslParam... params) } checkAllRequiredParamsSupplied(params); - completedParsingArguments(); } private boolean matches(final NameValuePair argument, final DslParam param) { - return argument.getName() == null || param.getName().equalsIgnoreCase(argument.getName()); + return argument.getName() == null || param.getArg().getName().equalsIgnoreCase(argument.getName()); } @Override @@ -128,21 +129,7 @@ public String[] values(final String name) 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. - */ + @Override public RepeatingGroup[] valuesAsGroup(final String groupName) { final DslParam param = getDslParam(groupName); @@ -168,29 +155,6 @@ 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) { @@ -204,7 +168,7 @@ private void checkAllRequiredParamsSupplied(final DslParam[] params) { if (!param.isValid()) { - throw new IllegalArgumentException("Missing value for parameter: " + param.getName()); + throw new IllegalArgumentException("Missing value for parameter: " + param.getArg().getName()); } } } @@ -219,18 +183,6 @@ private NameValuePair[] parseArguments(final String[] args) 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/internal/NameValuePair.java similarity index 97% rename from src/main/java/com/lmax/simpledsl/NameValuePair.java rename to src/main/java/com/lmax/simpledsl/internal/NameValuePair.java index c146728..855422a 100644 --- a/src/main/java/com/lmax/simpledsl/NameValuePair.java +++ b/src/main/java/com/lmax/simpledsl/internal/NameValuePair.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; class NameValuePair { diff --git a/src/main/java/com/lmax/simpledsl/internal/OptionalParam.java b/src/main/java/com/lmax/simpledsl/internal/OptionalParam.java new file mode 100755 index 0000000..7563e49 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/OptionalParam.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.internal; + +import com.lmax.simpledsl.api.OptionalArg; + +class OptionalParam extends SimpleDslParam +{ + private String defaultValue; + + OptionalParam(final String name) + { + super(new OptionalArg(name)); + } + + OptionalParam(final OptionalArg arg) + { + super(arg); + } + + @Override + public String getDefaultValue() + { + return defaultValue; + } + + OptionalParam setDefault(final String defaultValue) + { + this.defaultValue = defaultValue; + return this; + } + + @Override + public String[] getValues() + { + final String[] values = super.getValues(); + return values.length > 0 ? values : defaultValue != null ? new String[]{defaultValue} : new String[0]; + } +} 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..35da907 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java @@ -0,0 +1,153 @@ +/* + * 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.RepeatingArgGroup; +import com.lmax.simpledsl.api.RepeatingGroup; +import com.lmax.simpledsl.api.SimpleDslArg; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.stream; + +class RepeatingParamGroup extends DslParam +{ + protected final RepeatingArgGroup identity; + protected final Map> paramsByName = new HashMap<>(); + private final List values = new ArrayList<>(); + + RepeatingParamGroup(final RequiredParam firstParam, final SimpleDslParam... params) + { + this.identity = new RepeatingArgGroup(firstParam.getArg(), stream(params) + .map(SimpleDslParam::getArg) + .toArray(SimpleDslArg[]::new)); + + paramsByName.put(firstParam.getArg().getName(), firstParam); + Arrays.stream(params) + .forEach(param -> paramsByName.put(param.getArg().getName(), param)); + } + + RepeatingParamGroup(final RepeatingArgGroup group) + { + this( + new RequiredParam(group.getIdentity()), + stream(group.getOtherArgs()) + .map(arg -> (SimpleDslParam) arg.fold( + RequiredParam::new, + OptionalParam::new, + invalid -> + { + throw new IllegalArgumentException("Cannot nest RepeatingArgGroups"); + }) + ) + .toArray(SimpleDslParam[]::new) + ); + } + + @Override + public RepeatingArgGroup getArg() + { + return identity; + } + + @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 RepeatingParamValues} instances, one for each set of values provided for this repeating group. + */ + public RepeatingGroup[] values() + { + return values.toArray(new RepeatingParamValues[0]); + } + + /** + * {@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 RepeatingParamValues group = new RepeatingParamValues(); + 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.getArg().isAllowMultipleValues()) + { + 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.getArg().getName())) + { + if (param.getArg().isRequired()) + { + throw new IllegalArgumentException("Did not supply a value for " + param.getArg().getName() + " in group " + identity.getName()); + } + else if (param.getDefaultValue() != null) + { + group.addValue(param.getArg().getName(), param.getDefaultValue()); + } + } + } + values.add(group); + return currentPosition; + } + + @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..13b91c6 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java @@ -0,0 +1,49 @@ +package com.lmax.simpledsl.internal; + +import com.lmax.simpledsl.api.RepeatingGroup; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class RepeatingParamValues implements RepeatingGroup +{ + private final Map> valuesByName = new HashMap<>(); + + 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/internal/RequiredParam.java b/src/main/java/com/lmax/simpledsl/internal/RequiredParam.java new file mode 100755 index 0000000..bb174ca --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/RequiredParam.java @@ -0,0 +1,77 @@ +/* + * 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.RequiredArg; + +class RequiredParam extends SimpleDslParam +{ + RequiredParam(final String name) + { + this(new RequiredArg(name)); + } + + RequiredParam(final RequiredArg arg) + { + super(arg); + } + + @Override + boolean isValid() + { + return getValues().length != 0; + } + + @Override + int consume(final int currentPosition, final NameValuePair... args) + { + int position = currentPosition; + while (position < args.length && consumeSingleParam(args, position)) + { + position++; + if (!getArg().isAllowMultipleValues()) + { + 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 || getArg().getName().equalsIgnoreCase(argName); + } + + private NameValuePair getArg(final NameValuePair[] args, final int position) + { + if (position >= args.length) + { + throw new IllegalArgumentException("Missing parameter: " + getArg().getName()); + } + return args[position]; + } +} 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..30e55c0 --- /dev/null +++ b/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java @@ -0,0 +1,155 @@ +/* + * 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.SimpleDslArg; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +abstract class SimpleDslParam, P extends SimpleDslParam> extends DslParam +{ + private final A arg; + private final List values = new LinkedList<>(); + + SimpleDslParam(final A arg) + { + this.arg = arg; + } + + @Override + public A getArg() + { + return arg; + } + + P setAllowedValues(final String... allowedValues) + { + arg.setAllowedValues(allowedValues); + return me(); + } + + P setAllowMultipleValues() + { + arg.setAllowMultipleValues(); + return me(); + } + + P setAllowMultipleValues(final String delimiter) + { + arg.setAllowMultipleValues(delimiter); + return me(); + } + + @Override + SimpleDslParam getAsSimpleDslParam() + { + return this; + } + + @Override + RepeatingParamGroup asRepeatingParamGroup() + { + return null; + } + + @Override + int consume(final int currentPosition, final NameValuePair... args) + { + final NameValuePair arg = args[currentPosition]; + addValue(null == arg ? null : arg.getValue()); + return currentPosition + 1; + } + + @Override + boolean hasValue() + { + return arg.isAllowMultipleValues() ? getValues().length > 0 : getValue() != null; + } + + String getDefaultValue() + { + return null; + } + + void addValue(final String value) + { + if (arg.isAllowMultipleValues()) + { + final String[] values = value.split(arg.getMultipleValueSeparator()); + for (final String singleValue : values) + { + addSingleValue(singleValue.trim()); + } + } + else + { + addSingleValue(value); + } + } + + private void addSingleValue(final String value) + { + checkCanAddValue(); + values.add(checkValidValue(value)); + } + + String checkValidValue(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; + } + + private void checkCanAddValue() + { + if (!arg.isAllowMultipleValues() && values.size() == 1) + { + throw new IllegalArgumentException("Multiple " + arg.getName() + " parameters are not allowed"); + } + } + + String getValue() + { + if (arg.isAllowMultipleValues()) + { + throw new IllegalArgumentException("getValues() should be used when multiple values are allowed"); + } + final String[] strings = getValues(); + return strings.length > 0 ? strings[0] : null; + } + + String[] getValues() + { + return values.toArray(new String[0]); + } + + @SuppressWarnings("unchecked") + protected P me() + { + return (P) this; + } +} diff --git a/src/test/java/com/lmax/simpledsl/DslParamsTest.java b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java similarity index 84% rename from src/test/java/com/lmax/simpledsl/DslParamsTest.java rename to src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java index 751cd67..1317b74 100644 --- a/src/test/java/com/lmax/simpledsl/DslParamsTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.lmax.simpledsl; +package com.lmax.simpledsl.internal; +import com.lmax.simpledsl.api.DslParams; +import com.lmax.simpledsl.api.RepeatingGroup; 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; @@ -30,7 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -public class DslParamsTest +public class DslParamsImplTest { @Test public void shouldReturnValueAsInt() @@ -38,7 +38,7 @@ public void shouldReturnValueAsInt() final String[] args = {"a=1"}; final DslParam[] parameters = {new RequiredParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(1, params.valueAsInt("a")); } @@ -49,12 +49,13 @@ public void shouldThrowNumberFormatExceptionWhenValueAsIntCalledForParameterThat final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); - assertThrows( - IllegalArgumentException.class, - () -> params.valueAsInt("a"), - "Multiple foo parameters are not allowed"); + final NumberFormatException exception = assertThrows( + NumberFormatException.class, + () -> params.valueAsInt("a")); + + assertEquals("null", exception.getMessage()); } @Test @@ -63,7 +64,7 @@ public void shouldReturnValueAsLong() final String[] args = {"a=1"}; final DslParam[] parameters = {new RequiredParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(1L, params.valueAsLong("a")); } @@ -74,7 +75,7 @@ public void shouldReturnValueAsBoolean() final String[] args = {"a=true"}; final DslParam[] parameters = {new RequiredParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertTrue(params.valueAsBoolean("a")); } @@ -85,7 +86,7 @@ public void shouldReturnValueAsBigDecimal() final String[] args = {"a=1"}; final DslParam[] parameters = {new RequiredParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(0, BigDecimal.ONE.compareTo(params.valueAsBigDecimal("a"))); } @@ -95,7 +96,7 @@ public void shouldReturnValueAsDouble() final String[] args = {"a=1.23"}; final DslParam[] parameters = {new RequiredParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(1.23d, params.valueAsDouble("a"), 0d); } @@ -105,7 +106,7 @@ public void shouldReturnNullValueWhenBigDecimalIsNotFound() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertNull(params.valueAsBigDecimal("a")); } @@ -115,7 +116,7 @@ public void shouldReturnValueAsParamForOptionalValueThatWasSpecified() final String[] args = {"a: value"}; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("a: value", params.valueAsParam("a")); } @@ -125,7 +126,7 @@ public void shouldReturnNullValueAsParamWhenOptionalParamNotSpecified() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertNull(params.valueAsParam("a")); } @@ -137,7 +138,7 @@ public void shouldReturnValuesAsIntArray() new OptionalParam("a").setAllowMultipleValues() }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertArrayEquals(new int[]{1, 2, 3}, params.valuesAsInts("a")); } @@ -150,7 +151,7 @@ public void shouldReturnValuesAsLongArray() new OptionalParam("a").setAllowMultipleValues() }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertArrayEquals(new long[]{1, 2, 3}, params.valuesAsLongs("a")); } @@ -164,7 +165,7 @@ public void shouldReturnValuesAsBigDecimalArray() new OptionalParam("a").setAllowMultipleValues() }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertArrayEquals(new BigDecimal[]{new BigDecimal("1"), new BigDecimal("2.23"), new BigDecimal("3")}, params.valuesAsBigDecimals("a")); } @@ -177,7 +178,7 @@ public void shouldReturnValuesAsDoubleArray() new OptionalParam("a").setAllowMultipleValues() }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertArrayEquals(new double[]{1, 2.23, 3}, params.valuesAsDoubles("a")); } @@ -191,7 +192,7 @@ public void shouldExtractRequiredParametersWhenNamed() new RequiredParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -206,7 +207,7 @@ public void shouldExtractOptionalParametersWhenNamed() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -221,7 +222,7 @@ public void shouldExtractMixedParameterTypesWhenNamed() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -236,7 +237,7 @@ public void shouldReportValueAsPresentWhenProvided() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertTrue(params.hasValue("b")); } @@ -250,7 +251,7 @@ public void shouldNotReportValueAsPresentWhenNotProvided() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertFalse(params.hasValue("b")); } @@ -261,7 +262,7 @@ public void shouldReturnTrueFromHasValueIfAnEmptyValueIsSupplied() final String[] args = {"a="}; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertTrue(params.hasValue("a")); } @@ -272,7 +273,7 @@ public void shouldReturnTrueFromHasValueIfADefaultValueIsUsed() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertTrue(params.hasValue("a")); } @@ -283,7 +284,7 @@ public void shouldReturnEmptyOptionalWhenValueIsNotSupplied() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(params.valueAsOptional("a"), Optional.empty()); } @@ -294,7 +295,7 @@ public void shouldReturnOptionalWithValueWhenValueIsSupplied() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(params.valueAsOptional("a"), Optional.of("value")); } @@ -305,7 +306,7 @@ public void shouldReturnEmptyOptionalWhenMultipleParameterValueIsNotSupplied() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(params.valuesAsOptional("a"), Optional.empty()); } @@ -316,7 +317,7 @@ public void shouldReturnOptionalListWhenMultipleParameterValueIsSupplied() final String[] args = {"a=value1", "a=value2"}; final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals(params.valuesAsOptional("a"), Optional.of(asList("value1", "value2"))); } @@ -331,7 +332,7 @@ public void shouldBeAbleToExtractMultipleRequiredParamsWhenAllParamsAreNamed() new RequiredParam("c") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertArrayEquals(new String[]{"2", "3"}, params.values("b")); @@ -348,7 +349,7 @@ public void shouldBeAbleToExtractMultipleRequiredParamsWhenSubsequentRequiredPar new RequiredParam("c") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertArrayEquals(new String[]{"2", "3"}, params.values("b")); @@ -366,7 +367,7 @@ public void shouldBeAbleToExtractMultipleRequiredParamsAndMultipleOptionalParams new OptionalParam("d") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertArrayEquals(new String[]{"2", "3"}, params.values("b")); @@ -380,7 +381,7 @@ public void shouldBeAbleToDetectPresenceOfParamsWithMultipleValuesAllowed() final String[] args = {"a=1", "a=2"}; final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertTrue(params.hasValue("a")); } @@ -391,7 +392,7 @@ public void shouldBeAbleToDetectAbsenceOfParamsWithMultipleValuesAllowed() final String[] args = new String[0]; final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertFalse(params.hasValue("a")); } @@ -405,7 +406,7 @@ public void shouldBeAbleToSpecifiedNamedRequiredParamsInAnyOrder() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -420,7 +421,7 @@ public void shouldExtractOptionalParamsFromPositionalArguments() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -436,7 +437,7 @@ public void shouldExtractOptionalParamsFromPositionalArgumentsWhenNullArgumentsA new OptionalParam("c") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertNull(params.value("a")); assertEquals("1", params.value("b")); @@ -452,7 +453,7 @@ public void shouldMatchParamsIgnoringCase() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("B")); @@ -469,7 +470,7 @@ public void shouldIgnoreNullArgumentsAmongstOptionalParameters() new OptionalParam("b") }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("1", params.value("a")); assertEquals("2", params.value("b")); @@ -485,7 +486,7 @@ public void shouldIgnoreNullArgumentsInRepeatingGroups() new RequiredParam("b")) }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); final RepeatingGroup[] group = params.valuesAsGroup("a"); assertEquals("1", group[0].value("a")); @@ -503,7 +504,7 @@ public void shouldBeAbleToRetrieveGroupsOfParams() new RequiredParam("value")) }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("value", params.value("a")); final RepeatingGroup[] groups = params.valuesAsGroup("group"); @@ -527,7 +528,7 @@ public void shouldRaiseErrorIfRequiredParameterMissingFromGroup() final IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> new DslParams(args, params)); + () -> new DslParamsImpl(args, params)); assertEquals("Did not supply a value for myValue in group myGroup", exception.getMessage()); } @@ -543,7 +544,7 @@ public void shouldBeAbleToSpecifyMultipleValuesForParamInGroup() new OptionalParam("value").setAllowMultipleValues()) }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("value", params.value("a")); final RepeatingGroup[] groups = params.valuesAsGroup("group"); @@ -572,7 +573,7 @@ public void shouldBeAbleToRetrieveGroupsOfParamsWhenSomeOptionalValuesAreOmitted new OptionalParam("value")) }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("value", params.value("a")); final RepeatingGroup[] groups = params.valuesAsGroup("group"); @@ -599,7 +600,7 @@ public void shouldEnforceAllowedValuesInRepeatingGroups() final IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> new DslParams(args, parameters)); + () -> new DslParamsImpl(args, parameters)); assertEquals("myValue parameter value '1' must be one of: [A, B]", exception.getMessage()); } @@ -618,7 +619,7 @@ public void shouldUseDefaultValuesForOptionalParametersInRepeatingGroups() new OptionalParam("value")) }; - final DslParams params = new DslParams(args, parameters); + final DslParams params = new DslParamsImpl(args, parameters); assertEquals("value", params.value("a")); final RepeatingGroup[] groups = params.valuesAsGroup("group"); @@ -632,21 +633,6 @@ public void shouldUseDefaultValuesForOptionalParametersInRepeatingGroups() 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() { @@ -655,7 +641,7 @@ public void shouldThrowAnExceptionWhenMissingAValueForARequiredParam() IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> new DslParams(args, parameters)); + () -> new DslParamsImpl(args, parameters)); assertEquals("Missing value for parameter: b", exception.getMessage()); } @@ -668,7 +654,7 @@ public void shouldThrowAnExceptionIfAnUnexpectedParameterValueIsPassedIn() IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> new DslParams(args, parameters)); + () -> new DslParamsImpl(args, parameters)); assertEquals("Unexpected argument b=2", exception.getMessage()); } diff --git a/src/test/java/com/lmax/simpledsl/NameValuePairTest.java b/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java similarity index 98% rename from src/test/java/com/lmax/simpledsl/NameValuePairTest.java rename to src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java index 6f40914..6b602a2 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; diff --git a/src/test/java/com/lmax/simpledsl/OptionalParamTest.java b/src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.java similarity index 50% rename from src/test/java/com/lmax/simpledsl/OptionalParamTest.java rename to src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.java index 3a55433..2421185 100755 --- a/src/test/java/com/lmax/simpledsl/OptionalParamTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.java @@ -13,13 +13,10 @@ * 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; -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; @@ -31,13 +28,13 @@ public class OptionalParamTest public void shouldNotReportAsRequired() { final DslParam param = new OptionalParam("foo"); - assertFalse(param.isRequired()); + assertFalse(param.getArg().isRequired()); } @Test public void shouldReturnNullIfNoValueIsProvidedAndNoDefaultValueIsSet() { - final SimpleDslParam param = new OptionalParam("foo"); + final SimpleDslParam param = new OptionalParam("foo"); assertNull(param.getValue()); assertArrayEquals(new String[0], param.getValues()); } @@ -45,7 +42,7 @@ public void shouldReturnNullIfNoValueIsProvidedAndNoDefaultValueIsSet() @Test public void shouldUseTheDefaultValueIfNoValueProvided() { - final SimpleDslParam param = new OptionalParam("foo").setDefault("def"); + final SimpleDslParam param = new OptionalParam("foo").setDefault("def"); assertEquals("def", param.getValue()); assertArrayEquals(new String[]{"def"}, param.getValues()); } @@ -53,45 +50,9 @@ public void shouldUseTheDefaultValueIfNoValueProvided() @Test public void shouldNotOverrideTheProvidedValueWithTheDefaultValue() { - final SimpleDslParam param = new OptionalParam("foo").setDefault("def"); + 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/internal/RequiredParamTest.java similarity index 89% rename from src/test/java/com/lmax/simpledsl/RequiredParamTest.java rename to src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java index f85cf2b..02e5af6 100644 --- a/src/test/java/com/lmax/simpledsl/RequiredParamTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java @@ -13,13 +13,10 @@ * 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; -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; @@ -30,7 +27,7 @@ public class RequiredParamTest public void shouldReportAsRequired() { final DslParam param = new RequiredParam("foo"); - assertTrue(param.isRequired()); + assertTrue(param.getArg().isRequired()); } @Test @@ -107,17 +104,4 @@ public void shouldConsumeMultipleParamsBySplittingValuesByUserSuppliedDelimiter( 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/internal/SimpleDslParamTest.java similarity index 52% rename from src/test/java/com/lmax/simpledsl/SimpleDslParamTest.java rename to src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java index 0943dcf..ff761ab 100755 --- a/src/test/java/com/lmax/simpledsl/SimpleDslParamTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.lmax.simpledsl; +package com.lmax.simpledsl.internal; +import com.lmax.simpledsl.api.OptionalArg; +import com.lmax.simpledsl.api.RepeatingArgGroup; +import com.lmax.simpledsl.api.RequiredArg; +import com.lmax.simpledsl.api.SimpleDslArg; import org.junit.jupiter.api.Test; -import java.util.LinkedList; -import java.util.function.BiConsumer; -import java.util.function.Consumer; +import java.util.function.Function; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +32,7 @@ public class SimpleDslParamTest @Test public void shouldReturnTheValueAdded() { - final SimpleDslParam param = new TestParam("foo"); + final SimpleDslParam param = new TestParam("foo"); param.addValue("12"); assertEquals("12", param.getValue()); } @@ -38,7 +40,7 @@ public void shouldReturnTheValueAdded() @Test public void shouldAddMultipleValuesWhenMultipleValuesAreAllowed() { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); + final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); param.addValue("12"); param.addValue("34"); } @@ -46,7 +48,7 @@ public void shouldAddMultipleValuesWhenMultipleValuesAreAllowed() @Test public void shouldReturnAllTheValuesAddedWhenMultipleValuesAreAllowed() { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); + final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); param.addValue("12"); param.addValue("34"); assertArrayEquals(new String[]{"12", "34"}, param.getValues()); @@ -55,7 +57,7 @@ public void shouldReturnAllTheValuesAddedWhenMultipleValuesAreAllowed() @Test public void shouldRestrictWhichValuesAreAllowed() { - final SimpleDslParam param = new TestParam("foo").setAllowedValues("12", "34"); + final SimpleDslParam param = new TestParam("foo").setAllowedValues("12", "34"); final IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, @@ -67,61 +69,16 @@ public void shouldRestrictWhichValuesAreAllowed() @Test public void shouldMatchAllowedValuesCaseInsensitivelyButStillReturnTheValuesWithTheProvidedCase() { - final SimpleDslParam param = new TestParam("foo").setAllowedValues("abc", "def").setAllowMultipleValues(); + 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"); + final SimpleDslParam param = new TestParam("foo"); param.addValue("12"); assertThrows( @@ -133,7 +90,7 @@ public void shouldThrowExceptionOnSecondCallToAddValueWhenMultipleValuesNotAllow @Test public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleValues() { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); + final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); final IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, @@ -142,11 +99,25 @@ public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleV assertEquals("getValues() should be used when multiple values are allowed", exception.getMessage()); } - private static class TestParam extends SimpleDslParam + private static class TestArg extends SimpleDslArg + { + TestArg(final String name) + { + super(name, false); + } + + @Override + public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) + { + throw new UnsupportedOperationException("Not implemented"); + } + } + + private static class TestParam extends SimpleDslParam { TestParam(final String name) { - super(name); + super(new TestArg(name)); } } -} +} \ No newline at end of file From eee19590dd27c7f720dd9664307bb877ca378351 Mon Sep 17 00:00:00 2001 From: swarren12 <33803336+swarren12@users.noreply.github.com> Date: Fri, 5 Nov 2021 12:56:49 +0000 Subject: [PATCH 4/4] Clean up public API and parsing logic Clean up the public API by removing the deprecated methods that were only intended to be called during the parsing of the arguments. At the same time, migrate all the parsing logic into a dedicated class, rather than performing this in the constructor of the objects. --- .../java/com/lmax/simpledsl/api/DslArg.java | 24 +- .../com/lmax/simpledsl/api/DslParams.java | 43 +- .../com/lmax/simpledsl/api/DslValues.java | 7 + .../com/lmax/simpledsl/api/OptionalArg.java | 37 +- .../lmax/simpledsl/api/RepeatingArgGroup.java | 20 +- .../com/lmax/simpledsl/api/RequiredArg.java | 29 +- .../com/lmax/simpledsl/api/SimpleDslArg.java | 48 +- .../com/lmax/simpledsl/internal/DslParam.java | 16 +- .../simpledsl/internal/DslParamsImpl.java | 146 +--- .../simpledsl/internal/DslParamsParser.java | 358 ++++++++ .../simpledsl/internal/NameValuePair.java | 49 +- .../simpledsl/internal/OptionalParam.java | 52 -- .../internal/RepeatingParamGroup.java | 104 +-- .../internal/RepeatingParamValues.java | 25 +- .../simpledsl/internal/RequiredParam.java | 77 -- .../simpledsl/internal/SimpleDslParam.java | 122 +-- .../simpledsl/internal/DslParamsImplTest.java | 545 ++---------- .../internal/DslParamsParserTest.java | 808 ++++++++++++++++++ .../simpledsl/internal/NameValuePairTest.java | 36 +- .../simpledsl/internal/OptionalParamTest.java | 58 -- .../simpledsl/internal/RequiredParamTest.java | 107 --- .../internal/SimpleDslParamTest.java | 81 +- 22 files changed, 1452 insertions(+), 1340 deletions(-) create mode 100644 src/main/java/com/lmax/simpledsl/internal/DslParamsParser.java delete mode 100755 src/main/java/com/lmax/simpledsl/internal/OptionalParam.java delete mode 100755 src/main/java/com/lmax/simpledsl/internal/RequiredParam.java create mode 100644 src/test/java/com/lmax/simpledsl/internal/DslParamsParserTest.java delete mode 100755 src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.java delete mode 100644 src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java diff --git a/src/main/java/com/lmax/simpledsl/api/DslArg.java b/src/main/java/com/lmax/simpledsl/api/DslArg.java index 972cb54..a3e7d25 100644 --- a/src/main/java/com/lmax/simpledsl/api/DslArg.java +++ b/src/main/java/com/lmax/simpledsl/api/DslArg.java @@ -16,8 +16,6 @@ package com.lmax.simpledsl.api; -import java.util.function.Function; - /** * The base class for all arg types. */ @@ -38,14 +36,13 @@ public interface DslArg boolean isRequired(); /** - * Determine if a value is optional for this argument. + * Get a default value for this argument. + * + * If the argument is required, this method will throw an {@link IllegalArgumentException}. * - * @return {@literal true} if and only if this argument is optional. + * @return the default value for the argument */ - default boolean isOptional() - { - return !isRequired(); - } + String getDefaultValue(); /** * Check whether this argument can take multiple values. @@ -67,15 +64,4 @@ default boolean isOptional() * @return the values allowed by this argument, or {@literal null} if all values are allowed */ String[] getAllowedValues(); - - /** - * Transform this {@link DslArg} by applying an appropriate {@link Function}. - * - * @param ifRequired the {@link Function} to apply if this {@link DslArg} is compatible with a {@link RequiredArg}. - * @param ifOptional the {@link Function} to apply if this {@link DslArg} is compatible with an {@link OptionalArg}. - * @param ifGroup the {@link Function} to apply if this {@link DslArg} is compatible with a {@link RepeatingArgGroup}. - * @param the return type. - * @return the transformed object. - */ - T fold(Function ifRequired, Function ifOptional, Function ifGroup); } diff --git a/src/main/java/com/lmax/simpledsl/api/DslParams.java b/src/main/java/com/lmax/simpledsl/api/DslParams.java index f223a3a..f1f64c6 100644 --- a/src/main/java/com/lmax/simpledsl/api/DslParams.java +++ b/src/main/java/com/lmax/simpledsl/api/DslParams.java @@ -16,7 +16,7 @@ package com.lmax.simpledsl.api; -import com.lmax.simpledsl.internal.DslParamsImpl; +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. @@ -53,40 +53,31 @@ public interface DslParams extends DslValues RepeatingGroup[] valuesAsGroup(String groupName); /** - * 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. + * Create new {@link DslParams}. * - * @param args the arguments supplied by the test. - * @param requiredParamName the name of the required parameter. - * @return the value supplied for the parameter. + * @param args the values + * @param arguments the {@link DslArg args} + * @return the new {@link DslParams} */ - static String getSingleRequiredParamValue(final String[] args, final String requiredParamName) + static DslParams create(String[] args, DslArg... arguments) { - return create(args, new RequiredArg(requiredParamName)).value(requiredParamName); + return new DslParamsParser().parse(args, arguments); } - /** - * 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. - */ - static void checkEmpty(final String[] args) - { - new DslParamsImpl(args); - } /** - * Create a new DslParams to define the supported dsl language. + * 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 params the supported parameters. - * @return the parsed {@link DslParams} - * @throws IllegalArgumentException if an invalid parameter is specified + * @param args the arguments supplied by the test. + * @param requiredParamName the name of the required parameter. + * @return the value supplied for the parameter. */ - static DslParams create(final String[] args, final DslArg... params) + static String getSingleRequiredParamValue(final String[] args, final String requiredParamName) { - return new DslParamsImpl(args, params); + return new DslParamsParser() + .parse(args, new RequiredArg(requiredParamName)) + .value(requiredParamName); } + } diff --git a/src/main/java/com/lmax/simpledsl/api/DslValues.java b/src/main/java/com/lmax/simpledsl/api/DslValues.java index 7f889f2..79a5470 100644 --- a/src/main/java/com/lmax/simpledsl/api/DslValues.java +++ b/src/main/java/com/lmax/simpledsl/api/DslValues.java @@ -298,4 +298,11 @@ default double[] valuesAsDoubles(final String name) } return parsedValues; } + + /** + * Get the supported parameters. Supplied values will have been parsed into the parameters. + * + * @return the array of {@link DslArg DslArgs} supplied to the constructor. + */ + DslArg[] getParams(); } diff --git a/src/main/java/com/lmax/simpledsl/api/OptionalArg.java b/src/main/java/com/lmax/simpledsl/api/OptionalArg.java index 330d2d4..2020570 100644 --- a/src/main/java/com/lmax/simpledsl/api/OptionalArg.java +++ b/src/main/java/com/lmax/simpledsl/api/OptionalArg.java @@ -16,8 +16,6 @@ package com.lmax.simpledsl.api; -import java.util.function.Function; - /** * An optional argument. *

@@ -45,43 +43,10 @@ * * @see DslValues#valueAsOptional(String) */ -public class OptionalArg extends SimpleDslArg +public class OptionalArg extends SimpleDslArg { - private String defaultValue; - public OptionalArg(final String name) { super(name, false); } - - /** - * Get a default value for this argument. - * - * @return the default value for the argument - */ - public String getDefaultValue() - { - return defaultValue; - } - - /** - * 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 - */ - public OptionalArg setDefault(final String defaultValue) - { - this.defaultValue = defaultValue; - return this; - } - - @Override - public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) - { - return ifOptional.apply(this); - } } diff --git a/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java b/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java index d4721b3..f99d5ec 100644 --- a/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java +++ b/src/main/java/com/lmax/simpledsl/api/RepeatingArgGroup.java @@ -16,8 +16,6 @@ package com.lmax.simpledsl.api; -import java.util.function.Function; - /** * Define a group of arguments that can be repeated 0 or more times. *

@@ -45,9 +43,9 @@ public class RepeatingArgGroup implements DslArg { private final RequiredArg identity; - private final DslArg[] otherArgs; + private final SimpleDslArg[] otherArgs; - public RepeatingArgGroup(final RequiredArg firstArg, final SimpleDslArg... otherArgs) + public RepeatingArgGroup(final RequiredArg firstArg, final SimpleDslArg... otherArgs) { this.identity = firstArg; this.otherArgs = otherArgs; @@ -65,6 +63,12 @@ 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() { @@ -99,14 +103,8 @@ public RequiredArg getIdentity() * * @return the {@link DslArg DslArgs}. */ - public DslArg[] getOtherArgs() + public SimpleDslArg[] getOtherArgs() { return otherArgs; } - - @Override - public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) - { - return ifGroup.apply(this); - } } diff --git a/src/main/java/com/lmax/simpledsl/api/RequiredArg.java b/src/main/java/com/lmax/simpledsl/api/RequiredArg.java index 564d953..997906e 100644 --- a/src/main/java/com/lmax/simpledsl/api/RequiredArg.java +++ b/src/main/java/com/lmax/simpledsl/api/RequiredArg.java @@ -16,8 +16,6 @@ package com.lmax.simpledsl.api; -import java.util.function.Function; - /** * A required argument. *

@@ -50,7 +48,7 @@ * By default, only a single value is allowed. * Multiple values can be allowed by calling {@link #setAllowMultipleValues()}. */ -public class RequiredArg extends SimpleDslArg +public class RequiredArg extends SimpleDslArg { public RequiredArg(final String name) { @@ -58,8 +56,29 @@ public RequiredArg(final String name) } @Override - public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) + 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) { - return ifRequired.apply(this); + 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 index d39de04..d953cea 100644 --- a/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java +++ b/src/main/java/com/lmax/simpledsl/api/SimpleDslArg.java @@ -18,16 +18,15 @@ /** * The root type for all simple args. - * - * @param

the actual type of simple argument. */ -public abstract class SimpleDslArg

> implements DslArg +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; @@ -50,6 +49,17 @@ 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() { @@ -68,6 +78,22 @@ 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. @@ -75,10 +101,10 @@ public String[] getAllowedValues() * @param allowedValues the allowable values for this argument. * @return this argument */ - public P setAllowedValues(final String... allowedValues) + public SimpleDslArg setAllowedValues(final String... allowedValues) { this.allowedValues = allowedValues; - return me(); + return this; } /** @@ -93,7 +119,7 @@ public P setAllowedValues(final String... allowedValues) * @return this argument * @see #setAllowMultipleValues(String) */ - public P setAllowMultipleValues() + public SimpleDslArg setAllowMultipleValues() { return setAllowMultipleValues(DEFAULT_DELIMITER); } @@ -105,16 +131,10 @@ public P setAllowMultipleValues() * @return this argument * @see #setAllowMultipleValues() */ - public P setAllowMultipleValues(final String delimiter) + public SimpleDslArg setAllowMultipleValues(final String delimiter) { allowMultipleValues = true; multipleValueSeparator = delimiter; - return me(); - } - - @SuppressWarnings("unchecked") - protected P me() - { - return (P) this; + return this; } } diff --git a/src/main/java/com/lmax/simpledsl/internal/DslParam.java b/src/main/java/com/lmax/simpledsl/internal/DslParam.java index 7ea50e3..b029559 100644 --- a/src/main/java/com/lmax/simpledsl/internal/DslParam.java +++ b/src/main/java/com/lmax/simpledsl/internal/DslParam.java @@ -16,25 +16,13 @@ package com.lmax.simpledsl.internal; -import com.lmax.simpledsl.api.DslArg; - -/** - * The base class for all param types. - */ abstract class DslParam { - abstract DslArg getArg(); + abstract String getName(); - abstract SimpleDslParam getAsSimpleDslParam(); + abstract SimpleDslParam getAsSimpleDslParam(); abstract RepeatingParamGroup asRepeatingParamGroup(); - abstract int consume(int currentPosition, NameValuePair... args); - abstract boolean hasValue(); - - boolean isValid() - { - return true; - } } diff --git a/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java index e2dc2e8..2599eed 100644 --- a/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java +++ b/src/main/java/com/lmax/simpledsl/internal/DslParamsImpl.java @@ -19,172 +19,68 @@ import com.lmax.simpledsl.api.DslParams; import com.lmax.simpledsl.api.RepeatingGroup; -import java.util.HashMap; import java.util.Map; -import java.util.Objects; - -import static java.util.Arrays.stream; +import java.util.Optional; /** * The internal implementation of {@link DslParams}. */ -public final class DslParamsImpl implements DslParams +final class DslParamsImpl implements DslParams { - 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 DslParamsImpl(final String[] args, final DslArg... params) - { - this(args, stream(params) - .map(arg -> arg.fold(RequiredParam::new, OptionalParam::new, RepeatingParamGroup::new)) - .toArray(DslParam[]::new)); - } + private final DslArg[] args; + private final Map paramsByName; - DslParamsImpl(final String[] args, final DslParam... params) + DslParamsImpl(final DslArg[] args, final Map paramsByName) { - this.params = params; - - final NameValuePair[] arguments = parseArguments(args); - - for (final DslParam param : params) - { - paramsByName.put(param.getArg().getName().toLowerCase(), param); - } - - int currentPosition = 0; - - final boolean allParamsOptional = stream(params).map(DslParam::getArg).allMatch(DslArg::isOptional); - final boolean allArgsPositional = 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.getArg().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); - } - - private boolean matches(final NameValuePair argument, final DslParam param) - { - return argument.getName() == null || param.getArg().getName().equalsIgnoreCase(argument.getName()); + this.args = args; + this.paramsByName = paramsByName; } @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"); + return param.getAsSimpleDslParam().getValue(); } @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"); + return param.getAsSimpleDslParam().getValues(); } @Override 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; - } - @Override public boolean hasValue(final String name) { - final DslParam param = getDslParam(name); - return param != null && param.hasValue(); + return findDslParam(name) + .map(DslParam::hasValue) + .orElse(false); } - private void checkAllRequiredParamsSupplied(final DslParam[] params) + @Override + public DslArg[] getParams() { - for (final DslParam param : params) - { - if (!param.isValid()) - { - throw new IllegalArgumentException("Missing value for parameter: " + param.getArg().getName()); - } - } + return args; } - private NameValuePair[] parseArguments(final String[] args) + private DslParam getDslParam(final String name) { - 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; + return findDslParam(name).orElseThrow(() -> new IllegalArgumentException(name + " is not a parameter")); } - private DslParam getDslParam(final String name) + private Optional findDslParam(final String name) { - return name != null ? paramsByName.get(name.toLowerCase()) : null; + 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 index 855422a..d30713c 100644 --- a/src/main/java/com/lmax/simpledsl/internal/NameValuePair.java +++ b/src/main/java/com/lmax/simpledsl/internal/NameValuePair.java @@ -17,42 +17,33 @@ class NameValuePair { - private static final String SPLIT_NAME_VALUE_REGEX = "=|:"; + static final NameValuePair NULL = new NameValuePair(null, null, null); - private final String originalValue; - private final String name; - private final String value; + private static final String SPLIT_NAME_VALUE_REGEX = "[=:]"; - NameValuePair(final String argString) + 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) { - originalValue = argString; - final String[] splitArg = (argString + " ").split(SPLIT_NAME_VALUE_REGEX); - if (splitArg.length == 1) + if (argString == null) { - name = null; - value = argString.trim(); + return NULL; } 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(); + 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()); } } - - 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/internal/OptionalParam.java b/src/main/java/com/lmax/simpledsl/internal/OptionalParam.java deleted file mode 100755 index 7563e49..0000000 --- a/src/main/java/com/lmax/simpledsl/internal/OptionalParam.java +++ /dev/null @@ -1,52 +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.internal; - -import com.lmax.simpledsl.api.OptionalArg; - -class OptionalParam extends SimpleDslParam -{ - private String defaultValue; - - OptionalParam(final String name) - { - super(new OptionalArg(name)); - } - - OptionalParam(final OptionalArg arg) - { - super(arg); - } - - @Override - public String getDefaultValue() - { - return defaultValue; - } - - OptionalParam setDefault(final String defaultValue) - { - this.defaultValue = defaultValue; - return this; - } - - @Override - public String[] getValues() - { - final String[] values = super.getValues(); - return values.length > 0 ? values : defaultValue != null ? new String[]{defaultValue} : new String[0]; - } -} diff --git a/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java index 35da907..f7a50f0 100644 --- a/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamGroup.java @@ -16,62 +16,31 @@ package com.lmax.simpledsl.internal; -import com.lmax.simpledsl.api.RepeatingArgGroup; import com.lmax.simpledsl.api.RepeatingGroup; -import com.lmax.simpledsl.api.SimpleDslArg; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import static java.util.Arrays.stream; class RepeatingParamGroup extends DslParam { - protected final RepeatingArgGroup identity; - protected final Map> paramsByName = new HashMap<>(); - private final List values = new ArrayList<>(); + private final String name; + private final List values; - RepeatingParamGroup(final RequiredParam firstParam, final SimpleDslParam... params) + RepeatingParamGroup(final String name, final List values) { - this.identity = new RepeatingArgGroup(firstParam.getArg(), stream(params) - .map(SimpleDslParam::getArg) - .toArray(SimpleDslArg[]::new)); - - paramsByName.put(firstParam.getArg().getName(), firstParam); - Arrays.stream(params) - .forEach(param -> paramsByName.put(param.getArg().getName(), param)); - } - - RepeatingParamGroup(final RepeatingArgGroup group) - { - this( - new RequiredParam(group.getIdentity()), - stream(group.getOtherArgs()) - .map(arg -> (SimpleDslParam) arg.fold( - RequiredParam::new, - OptionalParam::new, - invalid -> - { - throw new IllegalArgumentException("Cannot nest RepeatingArgGroups"); - }) - ) - .toArray(SimpleDslParam[]::new) - ); + this.name = name; + this.values = values; } @Override - public RepeatingArgGroup getArg() + public String getName() { - return identity; + return name; } @Override - public SimpleDslParam getAsSimpleDslParam() + public SimpleDslParam getAsSimpleDslParam() { - return null; + throw new IllegalArgumentException(name + " is a repeating group"); } @Override @@ -90,64 +59,9 @@ public RepeatingGroup[] values() return values.toArray(new RepeatingParamValues[0]); } - /** - * {@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 RepeatingParamValues group = new RepeatingParamValues(); - 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.getArg().isAllowMultipleValues()) - { - 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.getArg().getName())) - { - if (param.getArg().isRequired()) - { - throw new IllegalArgumentException("Did not supply a value for " + param.getArg().getName() + " in group " + identity.getName()); - } - else if (param.getDefaultValue() != null) - { - group.addValue(param.getArg().getName(), param.getDefaultValue()); - } - } - } - values.add(group); - return currentPosition; - } - @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 index 13b91c6..eb52357 100644 --- a/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java +++ b/src/main/java/com/lmax/simpledsl/internal/RepeatingParamValues.java @@ -1,25 +1,20 @@ package com.lmax.simpledsl.internal; +import com.lmax.simpledsl.api.DslArg; import com.lmax.simpledsl.api.RepeatingGroup; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; class RepeatingParamValues implements RepeatingGroup { - private final Map> valuesByName = new HashMap<>(); + private final DslArg[] dslArgs; + private final Map> valuesByName; - void addValue(final String name, final String value) + RepeatingParamValues(final DslArg[] dslArgs, final Map> valuesByName) { - List values = getValues(name); - if (values == null) - { - values = new ArrayList<>(); - valuesByName.put(name.toLowerCase(), values); - } - values.add(value); + this.dslArgs = dslArgs; + this.valuesByName = valuesByName; } @Override @@ -39,7 +34,13 @@ public String value(final String name) public String[] values(final String name) { final List values = getValues(name); - return values != null ? values.toArray(new String[values.size()]) : new String[0]; + return values != null ? values.toArray(new String[0]) : new String[0]; + } + + @Override + public DslArg[] getParams() + { + return dslArgs; } private List getValues(final String name) diff --git a/src/main/java/com/lmax/simpledsl/internal/RequiredParam.java b/src/main/java/com/lmax/simpledsl/internal/RequiredParam.java deleted file mode 100755 index bb174ca..0000000 --- a/src/main/java/com/lmax/simpledsl/internal/RequiredParam.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.internal; - -import com.lmax.simpledsl.api.RequiredArg; - -class RequiredParam extends SimpleDslParam -{ - RequiredParam(final String name) - { - this(new RequiredArg(name)); - } - - RequiredParam(final RequiredArg arg) - { - super(arg); - } - - @Override - boolean isValid() - { - return getValues().length != 0; - } - - @Override - int consume(final int currentPosition, final NameValuePair... args) - { - int position = currentPosition; - while (position < args.length && consumeSingleParam(args, position)) - { - position++; - if (!getArg().isAllowMultipleValues()) - { - 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 || getArg().getName().equalsIgnoreCase(argName); - } - - private NameValuePair getArg(final NameValuePair[] args, final int position) - { - if (position >= args.length) - { - throw new IllegalArgumentException("Missing parameter: " + getArg().getName()); - } - return args[position]; - } -} diff --git a/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java b/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java index 30e55c0..50ab351 100644 --- a/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java +++ b/src/main/java/com/lmax/simpledsl/internal/SimpleDslParam.java @@ -15,48 +15,28 @@ */ package com.lmax.simpledsl.internal; -import com.lmax.simpledsl.api.SimpleDslArg; - -import java.util.Arrays; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; -abstract class SimpleDslParam, P extends SimpleDslParam> extends DslParam +class SimpleDslParam extends DslParam { - private final A arg; - private final List values = new LinkedList<>(); + private final String name; + private final List values; - SimpleDslParam(final A arg) + SimpleDslParam(final String name, final List values) { - this.arg = arg; + this.name = name; + this.values = values; } @Override - public A getArg() - { - return arg; - } - - P setAllowedValues(final String... allowedValues) - { - arg.setAllowedValues(allowedValues); - return me(); - } - - P setAllowMultipleValues() - { - arg.setAllowMultipleValues(); - return me(); - } - - P setAllowMultipleValues(final String delimiter) + public String getName() { - arg.setAllowMultipleValues(delimiter); - return me(); + return name; } @Override - SimpleDslParam getAsSimpleDslParam() + SimpleDslParam getAsSimpleDslParam() { return this; } @@ -64,77 +44,24 @@ P setAllowMultipleValues(final String delimiter) @Override RepeatingParamGroup asRepeatingParamGroup() { - return null; - } - - @Override - int consume(final int currentPosition, final NameValuePair... args) - { - final NameValuePair arg = args[currentPosition]; - addValue(null == arg ? null : arg.getValue()); - return currentPosition + 1; + throw new IllegalArgumentException(name + " is not a repeating group"); } @Override boolean hasValue() { - return arg.isAllowMultipleValues() ? getValues().length > 0 : getValue() != null; - } - - String getDefaultValue() - { - return null; + return !values.isEmpty(); } - void addValue(final String value) + /** + * 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 (arg.isAllowMultipleValues()) - { - final String[] values = value.split(arg.getMultipleValueSeparator()); - for (final String singleValue : values) - { - addSingleValue(singleValue.trim()); - } - } - else - { - addSingleValue(value); - } - } - - private void addSingleValue(final String value) - { - checkCanAddValue(); - values.add(checkValidValue(value)); - } - - String checkValidValue(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; - } - - private void checkCanAddValue() - { - if (!arg.isAllowMultipleValues() && values.size() == 1) - { - throw new IllegalArgumentException("Multiple " + arg.getName() + " parameters are not allowed"); - } - } - - String getValue() - { - if (arg.isAllowMultipleValues()) + if (values.size() > 1) { throw new IllegalArgumentException("getValues() should be used when multiple values are allowed"); } @@ -142,14 +69,13 @@ String getValue() return strings.length > 0 ? strings[0] : null; } - String[] getValues() + List getValuesAsList() { - return values.toArray(new String[0]); + return Collections.unmodifiableList(values); } - @SuppressWarnings("unchecked") - protected P me() + String[] getValues() { - return (P) this; + return getValuesAsList().toArray(new String[0]); } } diff --git a/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java index 1317b74..d1899ec 100644 --- a/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/DslParamsImplTest.java @@ -15,14 +15,19 @@ */ package com.lmax.simpledsl.internal; +import com.lmax.simpledsl.api.DslArg; import com.lmax.simpledsl.api.DslParams; -import com.lmax.simpledsl.api.RepeatingGroup; 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; @@ -35,10 +40,9 @@ public class DslParamsImplTest @Test public void shouldReturnValueAsInt() { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertEquals(1, params.valueAsInt("a")); } @@ -46,10 +50,9 @@ public void shouldReturnValueAsInt() @Test public void shouldThrowNumberFormatExceptionWhenValueAsIntCalledForParameterThatIsNotSupplied() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); final NumberFormatException exception = assertThrows( NumberFormatException.class, @@ -61,10 +64,9 @@ public void shouldThrowNumberFormatExceptionWhenValueAsIntCalledForParameterThat @Test public void shouldReturnValueAsLong() { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertEquals(1L, params.valueAsLong("a")); } @@ -72,10 +74,9 @@ public void shouldReturnValueAsLong() @Test public void shouldReturnValueAsBoolean() { - final String[] args = {"a=true"}; - final DslParam[] parameters = {new RequiredParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("true")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertTrue(params.valueAsBoolean("a")); } @@ -83,62 +84,59 @@ public void shouldReturnValueAsBoolean() @Test public void shouldReturnValueAsBigDecimal() { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - final DslParams params = new DslParamsImpl(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 SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1.23")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - final DslParams params = new DslParamsImpl(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 SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - final DslParams params = new DslParamsImpl(args, parameters); assertNull(params.valueAsBigDecimal("a")); } @Test public void shouldReturnValueAsParamForOptionalValueThatWasSpecified() { - final String[] args = {"a: value"}; - final DslParam[] parameters = {new OptionalParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("value")); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - final DslParams params = new DslParamsImpl(args, parameters); assertEquals("a: value", params.valueAsParam("a")); } @Test - public void shouldReturnNullValueAsParamWhenOptionalParamNotSpecified() + public void shouldReturnNullValueAsParamWhenSimpleDslParamNotSpecified() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); + + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - final DslParams params = new DslParamsImpl(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 SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2", "3")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertArrayEquals(new int[]{1, 2, 3}, params.valuesAsInts("a")); } @@ -146,12 +144,9 @@ public void shouldReturnValuesAsIntArray() @Test public void shouldReturnValuesAsLongArray() { - final String[] args = {"a: 1, 2, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2", "3")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertArrayEquals(new long[]{1, 2, 3}, params.valuesAsLongs("a")); } @@ -159,121 +154,72 @@ public void shouldReturnValuesAsLongArray() @Test public void shouldReturnValuesAsBigDecimalArray() { + final SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2.23", "3")); - final String[] args = {"a: 1, 2.23, 3"}; - final DslParam[] parameters = { - new OptionalParam("a").setAllowMultipleValues() - }; - - final DslParams params = new DslParamsImpl(args, parameters); + 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")); + 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 SimpleDslParam aParam = new SimpleDslParam("a", asList("1", "2.23", "3")); - final DslParams params = new DslParamsImpl(args, parameters); + 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 shouldExtractRequiredParametersWhenNamed() - { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new RequiredParam("b") - }; - - final DslParams params = new DslParamsImpl(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 DslParamsImpl(args, parameters); - - assertEquals("1", params.value("a")); - assertEquals("2", params.value("b")); - } - - @Test - public void shouldExtractMixedParameterTypesWhenNamed() + public void shouldReportOptionalValueAsPresentWhenValueProvided() { - final String[] args = {"a=1", "b=2"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; - - final DslParams params = new DslParamsImpl(args, parameters); + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + final SimpleDslParam bParam = new SimpleDslParam("b", singletonList("2")); - 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 Map paramsByName = new HashMap<>(); + paramsByName.put("a", aParam); + paramsByName.put("b", bParam); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], paramsByName); assertTrue(params.hasValue("b")); } @Test - public void shouldNotReportValueAsPresentWhenNotProvided() + public void shouldNotReportOptionalValueAsPresentWhenNoValueProvided() { - final String[] args = {"a=1"}; - final DslParam[] parameters = { - new RequiredParam("a"), - new OptionalParam("b") - }; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("1")); + final SimpleDslParam bParam = new SimpleDslParam("b", emptyList()); - final DslParams params = new DslParamsImpl(args, parameters); + 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 shouldReturnTrueFromHasValueIfAnEmptyValueIsSupplied() + public void shouldReportRequiredValueAsPresentWhenEmptyValueProvided() { - final String[] args = {"a="}; - final DslParam[] parameters = {new OptionalParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertTrue(params.hasValue("a")); } @Test - public void shouldReturnTrueFromHasValueIfADefaultValueIsUsed() + public void shouldReportOptionalValueAsPresentWhenEmptyValueProvided() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); assertTrue(params.hasValue("a")); } @@ -281,381 +227,40 @@ public void shouldReturnTrueFromHasValueIfADefaultValueIsUsed() @Test public void shouldReturnEmptyOptionalWhenValueIsNotSupplied() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a")}; + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - assertEquals(params.valueAsOptional("a"), Optional.empty()); + assertEquals(Optional.empty(), params.valueAsOptional("a")); } @Test public void shouldReturnOptionalWithValueWhenValueIsSupplied() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setDefault("value")}; + final SimpleDslParam aParam = new SimpleDslParam("a", singletonList("value")); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - assertEquals(params.valueAsOptional("a"), Optional.of("value")); + assertEquals(Optional.of("value"), params.valueAsOptional("a")); } @Test public void shouldReturnEmptyOptionalWhenMultipleParameterValueIsNotSupplied() { - final String[] args = new String[0]; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; + final SimpleDslParam aParam = new SimpleDslParam("a", emptyList()); - final DslParams params = new DslParamsImpl(args, parameters); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - assertEquals(params.valuesAsOptional("a"), Optional.empty()); + assertEquals(Optional.empty(), params.valuesAsOptional("a")); } @Test public void shouldReturnOptionalListWhenMultipleParameterValueIsSupplied() { - final String[] args = {"a=value1", "a=value2"}; - final DslParam[] parameters = {new OptionalParam("a").setAllowMultipleValues()}; - - final DslParams params = new DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 DslParamsImpl(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 shouldThrowAnExceptionWhenMissingAValueForARequiredParam() - { - final String[] args = {"a=1"}; - final DslParam[] parameters = {new RequiredParam("a"), new RequiredParam("b")}; - - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParamsImpl(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")}; + final SimpleDslParam aParam = new SimpleDslParam("a", asList("value1", "value2")); - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new DslParamsImpl(args, parameters)); + final DslParams params = new DslParamsImpl(new DslArg[0], Collections.singletonMap("a", aParam)); - assertEquals("Unexpected argument b=2", exception.getMessage()); + 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/internal/NameValuePairTest.java b/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java index 6b602a2..23b97de 100755 --- a/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/NameValuePairTest.java @@ -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/OptionalParamTest.java b/src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.java deleted file mode 100755 index 2421185..0000000 --- a/src/test/java/com/lmax/simpledsl/internal/OptionalParamTest.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.internal; - -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; - -public class OptionalParamTest -{ - @Test - public void shouldNotReportAsRequired() - { - final DslParam param = new OptionalParam("foo"); - assertFalse(param.getArg().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()); - } -} diff --git a/src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java b/src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java deleted file mode 100644 index 02e5af6..0000000 --- a/src/test/java/com/lmax/simpledsl/internal/RequiredParamTest.java +++ /dev/null @@ -1,107 +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.internal; - -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.assertTrue; - -public class RequiredParamTest -{ - @Test - public void shouldReportAsRequired() - { - final DslParam param = new RequiredParam("foo"); - assertTrue(param.getArg().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()); - } -} diff --git a/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java b/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java index ff761ab..590156d 100755 --- a/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java +++ b/src/test/java/com/lmax/simpledsl/internal/SimpleDslParamTest.java @@ -15,14 +15,11 @@ */ package com.lmax.simpledsl.internal; -import com.lmax.simpledsl.api.OptionalArg; -import com.lmax.simpledsl.api.RepeatingArgGroup; -import com.lmax.simpledsl.api.RequiredArg; -import com.lmax.simpledsl.api.SimpleDslArg; import org.junit.jupiter.api.Test; -import java.util.function.Function; +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; @@ -32,65 +29,23 @@ public class SimpleDslParamTest @Test public void shouldReturnTheValueAdded() { - final SimpleDslParam param = new TestParam("foo"); - param.addValue("12"); - assertEquals("12", param.getValue()); - } + final SimpleDslParam param = new SimpleDslParam("foo", Collections.singletonList("12")); - @Test - public void shouldAddMultipleValuesWhenMultipleValuesAreAllowed() - { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); - param.addValue("12"); - param.addValue("34"); + assertEquals("12", param.getValue()); } @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()); - } + final SimpleDslParam param = new SimpleDslParam("foo", asList("12", "34")); - @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 shouldThrowExceptionOnSecondCallToAddValueWhenMultipleValuesNotAllowed() - { - final SimpleDslParam param = new TestParam("foo"); - param.addValue("12"); - - assertThrows( - IllegalArgumentException.class, - () -> param.addValue("12"), - "Multiple foo parameters are not allowed"); + assertArrayEquals(new String[]{"12", "34"}, param.getValues()); } @Test public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleValues() { - final SimpleDslParam param = new TestParam("foo").setAllowMultipleValues(); + final SimpleDslParam param = new SimpleDslParam("foo", asList("value1", "value2")); final IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, @@ -98,26 +53,4 @@ public void shouldThrowExceptionOnAccessingASingleValueIfTheParamAllowsMultipleV assertEquals("getValues() should be used when multiple values are allowed", exception.getMessage()); } - - private static class TestArg extends SimpleDslArg - { - TestArg(final String name) - { - super(name, false); - } - - @Override - public T fold(final Function ifRequired, final Function ifOptional, final Function ifGroup) - { - throw new UnsupportedOperationException("Not implemented"); - } - } - - private static class TestParam extends SimpleDslParam - { - TestParam(final String name) - { - super(new TestArg(name)); - } - } } \ No newline at end of file