org.conqat.lib.commons.options.Options Maven / Gradle / Ivy
Show all versions of teamscale-lib-commons Show documentation
/*
* Copyright (c) CQSE GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.conqat.lib.commons.options;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Properties;
import java.util.stream.Collectors;
import org.conqat.lib.commons.enums.EnumUtils;
import org.conqat.lib.commons.string.StringUtils;
/**
* This class offers a safe and flexible interface to Java properties files.
*
* Property files must follow the format for Java properties files. See Javadoc of
* {@link java.util.Properties} for details.
*
* @author Florian Deissenboeck
* @author Axel Gerster
*
* @see java.util.Properties
*/
public class Options {
/**
* Returned by countValues
when trying to count values of a non-present option.
*/
public static final int OPTION_NOT_PRESENT = -1;
/**
* This implementation is back by a Properties
object.
*/
private Properties properties;
/**
* Construct a new Option
object holding now options. Use methods
* {@link #init(String)}or {@link #setOption(String, String)}to store options.
*/
public Options() {
init();
}
/**
* This initializes the Options
object by reading a properties file.
*
* @param filename
* full-qualified name of the properties file
* @throws IOException
* Thrown if an I/O problem is encountered while reading properties file.
*/
public void init(String filename) throws IOException {
properties = new Properties();
try (InputStream inputStream = new FileInputStream(filename)) {
properties.load(inputStream);
}
}
/**
* Init empty Options
object. Existing options are cleared.
*/
public void init() {
properties = new Properties();
}
/**
* Sets and option. Setting an already existing option overwrites current value.
*
* @param option
* name of the option
* @param value
* option's value, must have same format as defined in the properties file
* @return true
if option was already present, false
otherwise
*/
public boolean setOption(String option, String value) {
boolean overridden = hasOption(option);
properties.setProperty(option, value);
return overridden;
}
/**
* Gets the value for a specified option.
*
* @param option
* the name of the option
* @return the option's value, if the option is not present or has a null
value
* null
is returned. If the option has a space separated value list, the whole
* list is returned. Use {@link #getValues(String)}to access single values.
*/
public String getValue(String option) {
if (!hasOption(option)) {
return null;
}
String value = properties.getProperty(option);
if ("".equals(value)) {
return null;
}
return value;
}
/**
* Return the value for a specified option or a default value if option is not present.
*
* @param option
* name of the option
* @param defaultValue
* default value to use, if option is not present
* @return the option's value or the default value
*/
public String getValue(String option, String defaultValue) {
if (hasOption(option)) {
return getValue(option);
}
return defaultValue;
}
/**
* Returns the space separated value of an option as array. An option might have more the one space
* separated value. This method returns them as an array. To allow values containing spaces use
* double quotes.
*
* Example: For the following line in a properties file
*
* option=value1 value2 "value 3" value4
*
* the method returns this array
*
*
* a[0] = "value1"
* a[1] = "value2"
* a[2] = "value 3"
* a[3] = "value4"
*
*
* @param option
* name of the option
* @return the array as described above
*/
public String[] getValues(String option) {
if (!hasOption(option)) {
return null;
}
String values = properties.getProperty(option);
if ("".equals(values)) {
return null;
}
return parse(values);
}
/**
* Checks if the specified option is present and has a boolean value. Boolean values are
* true
,false
, yes
and no
*
* @param option
* name of the option
* @return if the option is present and has a boolean value true
is returned, otherwise
* false
*/
public boolean hasBooleanValue(String option) {
if (!hasValue(option)) {
return false;
}
String value = getValue(option);
return checkTrue(value) || checkFalse(value);
}
/**
* Get the value for an option as boolean
.
*
* @param option
* name of the option
* @return the value of this option
* @throws ValueConversionException
* if the option doesn't have a boolean value. Use
* {@link #hasBooleanValue(String)}method or default value enabled version
* {@link #getBooleanValue(String, boolean)}of this method to avoid conversion problems.
*/
public boolean getBooleanValue(String option) throws ValueConversionException {
if (!hasBooleanValue(option)) {
throw new ValueConversionException(option);
}
String value = getValue(option);
return checkTrue(value);
}
/**
* Get the value for an option as instance of an enumeration. Enumeration names are matched
* case-insensitively. Dashes in values are replaced by underscores.
*
* Typical usage is:
*
*
*
* Colors color = options.getEnumValue("enum1", Colors.class);
*
*
* where Colors
is an enumeration.
*
* @param
* the enumeration
* @param option
* the name of the option
* @param enumType
* the enumeration type
* @return the enumeration entry
* @throws ValueConversionException
* if the option doesn't have a value of the specified enumeration. Use
* {@link #hasEnumValue(String, Class)}method or default value enabled version
* {@link #getEnumValue(String, Enum, Class)}of this method to avoid conversion
* problems.
*/
public > T getEnumValue(String option, Class enumType) throws ValueConversionException {
if (!hasEnumValue(option, enumType)) {
throw new ValueConversionException(option);
}
String value = getValue(option);
return EnumUtils.valueOfIgnoreCase(enumType, normalizeEnumConstantName(value));
}
/**
* Same as {@link #getEnumValue(String, Class)} but allows to specify default value.
*
* @param
* the enumeration
* @param option
* the name of the option
* @param enumType
* the enumeration type
* @return the enumeration entry
*
*/
public > T getEnumValue(String option, T defaultValue, Class enumType) {
try {
return getEnumValue(option, enumType);
} catch (ValueConversionException e) {
return defaultValue;
}
}
/**
* Checks if the specified option is present and has a legal value.
*
* @param option
* name of the option
* @return if the option is present and has a legal value true
is returned, otherwise
* false
*/
public > boolean hasEnumValue(String option, Class enumType) {
if (!hasValue(option)) {
return false;
}
String value = getValue(option);
return checkEnum(value, enumType);
}
/**
* Check if value describes an element of the enumeration (case-insensitive match).
*
*/
private static > boolean checkEnum(String value, Class enumType) {
T t = EnumUtils.valueOfIgnoreCase(enumType, normalizeEnumConstantName(value));
return t != null;
}
/**
* Get the value for an option as int
.
*
* @param option
* name of the option
* @return the value of this option
* @throws ValueConversionException
* if the option doesn't have a int
value. Use
* {@link #hasIntValue(String)}method or default value enabled version
* {@link #getIntValue(String, int)}of this method to avoid conversion problems.
*/
public int getIntValue(String option) throws ValueConversionException {
if (!hasIntValue(option)) {
throw new ValueConversionException(option);
}
String value = getValue(option);
return Integer.parseInt(value);
}
/**
* Checks if the specified option is present and has a int
value.
*
* @param option
* name of the option
* @return if the option is present and has a int
value true
is returned,
* otherwise false
*/
public boolean hasIntValue(String option) {
if (!hasValue(option)) {
return false;
}
String value = getValue(option);
return checkInt(value);
}
/**
* Same as {@link #getBooleanValue(String)}but allows to specify a default value.
*
* @param option
* name of the option
* @param defaultValue
* default value
* @return return the value of the option if option is present and has a boolean value, otherwise
* the default value is returned
*/
public boolean getBooleanValue(String option, boolean defaultValue) {
try {
return getBooleanValue(option);
} catch (ValueConversionException e) {
return defaultValue;
}
}
/**
* Same as {@link #getIntValue(String)}but allows to specify a default value.
*
* @param option
* name of the option
* @param defaultValue
* default value
* @return return the value of the option if option is present and has an integer value, otherwise
* the default value is returned
*/
public int getIntValue(String option, int defaultValue) {
try {
return getIntValue(option);
} catch (ValueConversionException e) {
return defaultValue;
}
}
/**
* Checks if a given string represent an integer.
*
* @param value
* - the string to check
* @return true
if the string represents an integer, false
otherwise
*/
private static boolean checkInt(String value) {
try {
Integer.parseInt(value);
} catch (NumberFormatException ex) {
return false;
}
return true;
}
/**
* Checks if the string is a boolean literal with value false
. Literals
* false
and no
are allowed.
*
* @param value
* the string to check
* @return true
if the string represents a boolean literal with value
* false
,false
otherwise
*/
private static boolean checkFalse(String value) {
value = value.trim();
return value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no");
}
/**
* Checks if the string is a boolean literal with value true
. Literals
* true
and yes
are allowed.
*
* @param value
* the string to check
* @return true
if the string represents a boolean literal with value
* true
,false
otherwise
*/
private static boolean checkTrue(String value) {
value = value.trim();
return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes");
}
/**
* Parses a space separated value list. To use values with spaces, use double quotes.
*
* @param string
* the value list to parse
* @return an array containing the values, quotes are omitted
*/
private static String[] parse(String string) {
string = string.trim();
int length = string.length();
char[] content = new char[length];
string.getChars(0, length, content, 0);
ArrayList list = new ArrayList<>();
int i = 0;
int lastPos = 0;
boolean inQM = false;
boolean inToken = false;
while (i < length) {
switch (content[i]) {
case ' ':
case '\t':
if (inToken && !inQM) {
// parameter found
String parameter = string.substring(lastPos, i).trim();
parameter = parameter.replaceAll("\"", "");
list.add(parameter);
lastPos = i;
}
inToken = false;
break;
case '\"':
inQM = !inQM;
break;
default:
inToken = true;
}
i++;
}
String parameter = string.substring(lastPos, i).trim();
parameter = parameter.replaceAll("\"", "");
list.add(parameter);
String[] result = new String[list.size()];
list.toArray(result);
return result;
}
/**
* Checks if a specified option is present.
*
* @param option
* name of the option
* @return true
if option is present, false
otherwise
*/
public boolean hasOption(String option) {
return properties.getProperty(option) != null;
}
/**
* Checks if specified option has a value.
*
* @param option
* name of the option
* @return true
if option is present and has a value, false
otherwise
* (even if option is present but doesn't have a value)
*/
public boolean hasValue(String option) {
return countValues(option) > 0;
}
/**
* Count the space separated values of an option. Double quotes are taken into account.
*
* @param option
* name of the option
* @return value count
*/
public int countValues(String option) {
if (!hasOption(option)) {
return OPTION_NOT_PRESENT;
}
String[] values = getValues(option);
if (values == null) {
return 0;
}
return values.length;
}
/**
* Returns a list with key-value-pairs as string.
*
* @return key-value-pairs as string
*/
@Override
public String toString() {
return properties.entrySet().stream().map(entry -> entry.getKey() + " = " + entry.getValue())
.collect(Collectors.joining(StringUtils.LINE_SEPARATOR));
}
/**
* Exception objects of this class are possibly returned by
* {@link Options#getBooleanValue(String)}and {@link Options#getIntValue(String)}, if corresponding
* options don't have a boolean respectively integer value.
*
*/
public static class ValueConversionException extends Exception {
/**
* Construct new conversion exception.
*
* @param option
* name of the option causing the exception
*/
public ValueConversionException(String option) {
super("Option: " + option);
}
}
/**
* Get the value for an option as float
.
*
* @param option
* name of the option
* @return the value of this option
* @throws ValueConversionException
* if the option doesn't have a float value.
*/
public float getFloatValue(String option) throws ValueConversionException {
if (!hasFloatValue(option)) {
throw new ValueConversionException(option);
}
String value = getValue(option);
return Float.parseFloat(value);
}
/**
* Checks if the specified option is present and has a float value.
*
*
* @param option
* name of the option
* @return if the option is present and has a float value true
is returned, otherwise
* false
*/
public boolean hasFloatValue(String option) {
if (!hasValue(option)) {
return false;
}
String value = getValue(option);
return checkFloat(value);
}
/**
* Checks if a string contains a float.
*/
private static boolean checkFloat(String value) {
try {
Float.parseFloat(value);
} catch (NumberFormatException ex) {
return false;
}
return true;
}
/**
* Normalize enum constant name. This replaces all dashes with underscores.
*/
private static String normalizeEnumConstantName(String constantName) {
return constantName.replace("-", "_");
}
}