diff --git a/src/main/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProvider.java b/src/main/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProvider.java new file mode 100644 index 00000000..3b9d35c1 --- /dev/null +++ b/src/main/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProvider.java @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2019 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * 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.beust.jcommander.defaultprovider; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.beust.jcommander.IDefaultProvider; + +/** + * A default provider that reads its default values from an environment + * variable. + * + * A prefix pattern can be provided to indicate how options are identified. + * The default pattern {@code -/} mandates that options MUST start with either a dash or a slash. + * Options can have values separated by whitespace. + * Values can contain whitespace as long as they are single-quoted or double-quoted. + * Otherwhise whitespace identifies the end of a value. + * + * @author Markus KARG (markus@headcrashing.eu) + */ +public final class EnvironmentVariableDefaultProvider implements IDefaultProvider { + + private static final String DEFAULT_VARIABLE_NAME = "JCOMMANDER_OPTS"; + + private static final String DEFAULT_PREFIXES_PATTERN = "-/"; + + private final String environmentVariableValue; + + private final String optionPrefixesPattern; + + /** + * Creates a default provider reading the environment variable {@code JCOMMANDER_OPTS} using the prefixes pattern {@code -/}. + */ + public EnvironmentVariableDefaultProvider() { + this(DEFAULT_VARIABLE_NAME, DEFAULT_PREFIXES_PATTERN); + } + + /** + * Creates a default provider reading the specified environment variable using the specified prefixes pattern. + * + * @param environmentVariableName + * The name of the environment variable to read (e. g. {@code "JCOMMANDER_OPTS"}). Must not be {@code null}. + * @param optionPrefix + * A set of characters used to indicate the start of an option (e. g. {@code "-/"} if option names may start with either dash or slash). Must not be {@code null}. + */ + public EnvironmentVariableDefaultProvider(final String environmentVariableName, final String optionPrefixes) { + this(requireNonNull(environmentVariableName), requireNonNull(optionPrefixes), System::getenv); + } + + /** + * For Unit Tests Only: Allows to mock the resolver, as Java cannot set environment variables. + * + * @param environmentVariableName + * The name of the environment variable to read. May be {@code null} if the passed resolver doesn't use it (e. g. Unit Test). + * @param optionPrefix + * A set of characters used to indicate the start of an option (e. g. {@code "-/"} if option names may start with either dash or slash). Must not be {@code null}. + * @param resolver + * Reads the value from the environment variable (e. g. {@code System::getenv}). Must not be {@code null}. + */ + EnvironmentVariableDefaultProvider(final String environmentVariableName, final String optionPrefixes, final Function resolver) { + this.environmentVariableValue = resolver.apply(environmentVariableName); + this.optionPrefixesPattern = requireNonNull(optionPrefixes); + } + + @Override + public final String getDefaultValueFor(final String optionName) { + if (this.environmentVariableValue == null) + return null; + final Matcher matcher = Pattern + .compile("(?:(?:.*\\s+)|(?:^))(" + Pattern.quote(optionName) + ")\\s*((?:'[^']*(?='))|(?:\"[^\"]*(?=\"))|(?:[^" + this.optionPrefixesPattern + "\\s]+))?.*") + .matcher(this.environmentVariableValue); + if (!matcher.matches()) + return null; + String value = matcher.group(2); + if (value == null) + return "true"; + final char firstCharacter = value.charAt(0); + if (firstCharacter == '\'' || firstCharacter == '"') + value = value.substring(1); + return value; + } + +} diff --git a/src/test/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProviderTest.java b/src/test/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProviderTest.java new file mode 100644 index 00000000..1cae14bd --- /dev/null +++ b/src/test/java/com/beust/jcommander/defaultprovider/EnvironmentVariableDefaultProviderTest.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2019 the original author or authors. + * See the notice.md file distributed with this work for additional + * information regarding copyright ownership. + * + * 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.beust.jcommander.defaultprovider; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.util.function.Function; + +import org.testng.annotations.Test; + +import com.beust.jcommander.IDefaultProvider; + +public final class EnvironmentVariableDefaultProviderTest { + + @Test + public final void shouldParseEnvironmentVariable() { + // given + final Function variableResolver = name -> "--some-option --simple-value ABC --quoted-value 'A BC' --double-quoted-value \"AB C\""; + final IDefaultProvider defaultProvider = new EnvironmentVariableDefaultProvider(null, "-", variableResolver); + + // when + final String nonExistentValue = defaultProvider.getDefaultValueFor("--non-existent-option"); + final String someOption = defaultProvider.getDefaultValueFor("--some-option"); + final String simpleValue = defaultProvider.getDefaultValueFor("--simple-value"); + final String quotedValue = defaultProvider.getDefaultValueFor("--quoted-value"); + final String doubleQuotedValue = defaultProvider.getDefaultValueFor("--double-quoted-value"); + + // then + assertNull(nonExistentValue); + assertEquals(someOption, "true"); + assertEquals(simpleValue, "ABC"); + assertEquals(quotedValue, "A BC"); + assertEquals(doubleQuotedValue, "AB C"); + } + +} diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml index 7dc0f3b2..964906d1 100644 --- a/src/test/resources/testng.xml +++ b/src/test/resources/testng.xml @@ -20,6 +20,7 @@ +