All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.norconex.commons.lang.map.Properties Maven / Gradle / Ivy

Go to download

Norconex Commons Lang is a Java library containing utility classes that complements the Java API and are not found in commonly available libraries (such as the great Apache Commons Lang, which it relies on).

There is a newer version: 2.0.2
Show newest version
/* Copyright 2010-2016 Norconex Inc.
 *
 * 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.norconex.commons.lang.map;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * 

This class is a enhanced version of {@link java.util.Properties} * that enforces the use of String keys and values internally, but offers many * convenience methods for storing and retrieving multiple values of different * types (e.g. Integer, Locale, File, etc). You can also see it as a * string-based multi-value map with helpful methods. While it does not extend * {@link java.util.Properties}, it offers similar load and store * methods and can be used as a replacement for it in many cases.

* *

As of 1.4, this class no longer extends {@code TreeMap}. * It now extends {@link ObservableMap} which means you can listen * for map changes.

* *

To insert values, there are set methods and add methods. * The set methods will replace any value(s) already present under the * given key. It is essentially the same behavior as * {@link Map#put(Object, Object)}. The add method will add the * new value(s) to the list of already existing ones (if any). *

* *

Upon encountering a problem in parsing the * data to its target format, a {@link PropertiesException} is thrown.

* @author Pascal Essiembre */ @SuppressWarnings("nls") public class Properties extends ObservableMap> implements Serializable { //TODO remove support for case sensitivity and provide a utility //class that does it instead on any string-key maps? // OR, store it in a case sensitive way instead of keeping // multiple keys of different cases around. Could provide // options to put everyting lower, upper, or rely on first key // entered (to know which case-version to use). private static final long serialVersionUID = -7215126924574341L; private static final Logger LOG = LogManager.getLogger(Properties.class); /** * Default delimiter when storing/loading multi-values to/from * .properties files. */ public static final String DEFAULT_MULTIVALUE_DELIMITER = "^|~"; private final boolean caseInsensitiveKeys; private String multiValueDelimiter = DEFAULT_MULTIVALUE_DELIMITER; /** * Create a new instance with case-sensitive keys. * Internally wraps a {@link HashMap} to store keys and values. */ public Properties() { this(false); } /** * Creates a new instance. Internally wraps a {@link HashMap} to * store keys and values. * @param caseInsensitiveKeys when true methods taking a * key argument will consider the key being passed without * consideration for character case. */ public Properties(boolean caseInsensitiveKeys) { this(null, caseInsensitiveKeys); } /** * Decorates {@code Map} as a {@code Properties}. * As of version 1.4 the {@code Map} argument is decorated so that * modifications to this instance will also modify the supplied {@code Map}. * To use a {@code Map} to initialize values only, use the * {@link #load(Map)} method. * @param map the Map to decorate */ public Properties(Map> map) { this(map, false); } /** * Decorates a {@code Map} argument as a {@code Properties}. * As of version 1.4 the {@code Map} argument is decorated so that * modifications to this instance will also modify the supplied {@code Map}. * To use a {@code Map} to initialize values only, use the * {@link #load(Map)} method. * @param map the Map to decorate * @param caseInsensitiveKeys when true methods taking a * key argument will consider the key being passed without * consideration for character case. */ public Properties( Map> map, boolean caseInsensitiveKeys) { super(map); this.caseInsensitiveKeys = caseInsensitiveKeys; } /** * Gets whether keys are case sensitive or not. * @return true if case sensitive * @since 1.4 * @deprecated Since 1.8.0, use {@link #isCaseInsensitiveKeys()} */ @Deprecated public boolean isCaseSensitiveKeys() { return !caseInsensitiveKeys; } /** * Gets whether keys are case sensitive or not. * @return true if case insensitive * @since 1.8 */ public boolean isCaseInsensitiveKeys() { return caseInsensitiveKeys; } /** * Gets multiple value string delimiter. * @return multiple value string delimiter * @since 1.4 */ public String getMultiValueDelimiter() { return multiValueDelimiter; } /** * Sets multiple value string delimiter. * @param multiValueDelimiter multiple value string delimiter * @since 1.4 */ public void setMultiValueDelimiter(String multiValueDelimiter) { this.multiValueDelimiter = multiValueDelimiter; } //--- Store ---------------------------------------------------------------- /** * Writes this property list (key and element pairs) in this * Properties table to the output stream as UTF-8 in a format * suitable for loading into a Properties table using the * {@link #load(InputStream) load} method. * Otherwise, the same considerations as * {@link #store(OutputStream, String)} apply. * @param comments a description of the property list. * @return the properties as string * @throws IOException problem storing to string */ public String storeToString(String comments) throws IOException { StringWriter writer = new StringWriter(); store(writer, comments); String str = writer.toString(); writer.close(); return str; } /** * Writes this {@link Map} (key and element pairs) to the output character * stream in a format suitable for using the * {@link #load(Reader)} method. * If a key only has one value, then this method behavior is the * exact same as the {@link Properties#store(Writer, String)} method. * Keys with multi-values are joined into a single string, using * the default delimiter: * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * @param writer an output character stream writer. * @param comments a description of the property list. * @throws IOException i/o problem * @see Properties#store(Writer, String) */ public void store(Writer writer, String comments) throws IOException { store(writer, comments, DEFAULT_MULTIVALUE_DELIMITER); } /** * Writes this {@link Map} (key and element pairs) to the output character * stream in a format suitable for using the * {@link #load(Reader, String)} method. * If a key only has one value, then this method behavior is the * exact same as the {@link Properties#store(Writer, String)} method. * Keys with multi-values are joined into a single string, using * the delimiter provided. * @param writer an output character stream writer. * @param comments a description of the property list. * @param delimiter string to used as a separator when joining * multiple values for the same key. * @throws IOException i/o problem * @see Properties#store(Writer, String) */ public void store(Writer writer, String comments, String delimiter) throws IOException { java.util.Properties p = new java.util.Properties(); for (String key : keySet()) { List values = getStrings(key); p.put(key, StringUtils.join(values, delimiter)); } p.store(writer, comments); p = null; } /** * Writes this {@link Map} (key and element pairs) to the output character * stream in a format suitable for using the * {@link #load(InputStream)} method. * If a key only has one value, then this method behavior is the * exact same as the {@link Properties#store(OutputStream, String)} method. * Keys with multi-values are joined into a single string, using * the default delimiter: * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * @param out an output stream. * @param comments a description of the property list. * @throws IOException i/o problem * @see Properties#store(OutputStream, String) */ public void store(OutputStream out, String comments) throws IOException { store(out, comments, DEFAULT_MULTIVALUE_DELIMITER); } /** * Writes this {@link Map} (key and element pairs) to the output character * stream as UTF-8 in a format suitable for using the * {@link #load(InputStream, String)} method. * If a key only has one value, then this method behavior is the * exact same as the {@link Properties#store(OutputStream, String)} method. * Keys with multi-values are joined into a single string, using * the delimiter provided. * @param out an output stream. * @param comments a description of the property list. * @param delimiter delimiter string to used as a separator when joining * multiple values for the same key. * @throws IOException i/o problem * @see Properties#store(OutputStream, String) */ public void store(OutputStream out, String comments, String delimiter) throws IOException { store(new OutputStreamWriter( out, CharEncoding.UTF_8), comments, delimiter); } /** * Emits an XML document representing all of the properties contained * in this {@link Map}, using the specified encoding. * If a key only has one value, then this method behavior is the * exact same as the * {@link Properties#storeToXML(OutputStream, String, String)} method, * where the character encoding is "UTF-8". * Keys with multi-values are joined into a single string, using * the default delimiter: * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * @param os the output stream on which to emit the XML document. * @param comment a description of the property list, or null * if no comment is desired. * @throws IOException i/o problem * @see Properties#storeToXML(OutputStream, String, String) */ public synchronized void storeToXML( OutputStream os, String comment) throws IOException { storeToXML(os, comment, CharEncoding.UTF_8); } /** * Emits an XML document representing all of the properties contained * in this {@link Map}, using the specified encoding. * If a key only has one value, then this method behavior is the * exact same as the * {@link Properties#storeToXML(OutputStream, String, String)} method. * Keys with multi-values are joined into a single string, using * the default delimiter: * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * @param os the output stream on which to emit the XML document. * @param comment a description of the property list, or null * if no comment is desired. * @param encoding character encoding * @throws IOException i/o problem * @see Properties#storeToXML(OutputStream, String, String) */ public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException { storeToXML(os, comment, encoding, DEFAULT_MULTIVALUE_DELIMITER); } /** * Emits an XML document representing all of the properties contained * in this {@link Map}, using the specified encoding. * If a key only has one value, then this method behavior is the * exact same as the * {@link Properties#storeToXML(OutputStream, String, String)} method. * Keys with multi-values are joined into a single string, using * the delimiter provided. * @param os the output stream on which to emit the XML document. * @param comment a description of the property list, or null * if no comment is desired. * @param encoding character encoding * @param delimiter delimiter string to used as a separator when joining * multiple values for the same key. * @throws IOException i/o problem * @see Properties#storeToXML(OutputStream, String, String) */ public synchronized void storeToXML(OutputStream os, String comment, String encoding, String delimiter) throws IOException { java.util.Properties p = new java.util.Properties(); for (String key : keySet()) { List values = getStrings(key); p.put(key, StringUtils.join(values, delimiter)); } p.storeToXML(os, comment, encoding); p = null; } //--- Load ----------------------------------------------------------------- /** * Reads a property list (key and element pairs) from the input * character stream in a simple line-oriented format. * If a key was stored with multiple values using a delimiter, this, * method will split these values appropriately assuming the delimiter is * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#load(Reader)} method. * @param reader the input character stream. * @throws IOException i/o problem * @see Properties#load(Reader) */ public synchronized void load(Reader reader) throws IOException { load(reader, DEFAULT_MULTIVALUE_DELIMITER); } /** * Reads a property list (key and element pairs) from the input * character stream in a simple line-oriented format. * If a key was stored with multiple values using a delimiter, * this method will split these values appropriately provided the * supplied delimiter is the same. If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#load(Reader)} method. * @param reader the input character stream. * @param delimiter delimiter string to used to parse a multi value * key. * @throws IOException i/o problem * @see Properties#load(Reader) */ public synchronized void load(Reader reader, String delimiter) throws IOException { java.util.Properties p = new java.util.Properties(); p.load(reader); for (String key : p.stringPropertyNames()) { List values = new ArrayList<>(); String value = p.getProperty(key); if (value != null) { values.addAll(Arrays.asList( StringUtils.splitByWholeSeparator(value, delimiter))); } put(key, values); } p = null; } /** *

Reads all key/value pairs in the given map, and * add them to this Map. Keys are converted to strings * using their toString() method, with exception * of values being arrays or collections. In such case, the entry * is considered a multi-value one and each value will be converted * to individual strings. null keys are ignored. * null values are converted to an empty string.

*

Changes to this instance * won't be reflected in the given Map. If you want otherwise, * use invoke the constructor with a Map argument.

* * @param map the map containing values to load */ public synchronized void load(Map map) { if (map != null) { for (Entry entry : map.entrySet()) { Object keyObj = entry.getKey(); if (keyObj == null) { continue; } String key = Objects.toString(keyObj, null); Object valObj = entry.getValue(); if (valObj == null) { valObj = StringUtils.EMPTY; } Iterable it = null; if (valObj.getClass().isArray()) { it = Arrays.asList((Object[]) valObj); } else if (valObj instanceof Iterable) { it = (Iterable) valObj; } if (it == null) { addString(key, Objects.toString(valObj, null)); } else { for (Object val : it) { addString(key, Objects.toString(val, null)); } } } } } /** * Reads a property list (key and element pairs) from the input * character stream (UTF-8) in a simple line-oriented format. * If a key was stored with multiple values using a delimiter, this, * method will split these values appropriately assuming the delimiter is * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#load(InputStream)} method. * @param inStream the input stream. * @throws IOException i/o problem * @see Properties#load(InputStream) */ public synchronized void load(InputStream inStream) throws IOException { load(new InputStreamReader(inStream, CharEncoding.UTF_8), DEFAULT_MULTIVALUE_DELIMITER); } /** * Reads a property list (key and element pairs) from the input * character stream (UTF8) in a simple line-oriented format. * If a key was stored with multiple values using a delimiter, * this method will split these values appropriately provided the * supplied delimiter is the same. If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#load(InputStream)} method. * @param inStream the input stream. * @param delimiter delimiter string to used to parse a multi value * key. * @throws IOException i/o problem * @see Properties#load(InputStream) */ public synchronized void load(InputStream inStream, String delimiter) throws IOException { load(new InputStreamReader(inStream, CharEncoding.UTF_8), delimiter); } /** * Loads all of the properties represented by the XML document on the * specified input stream into this instance. * If a key was stored with multiple values using a delimiter, * method will split these values appropriately assuming the delimiter is * {@link Properties#DEFAULT_MULTIVALUE_DELIMITER} * If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#loadFromXML(InputStream)} method. * @param in in the input stream from which to read the XML document. * @throws IOException i/o problem */ public synchronized void loadFromXML(InputStream in) throws IOException { loadFromXML(in, DEFAULT_MULTIVALUE_DELIMITER); } /** * Loads all of the properties represented by the XML document on the * specified input stream into this instance. * If a key was stored with multiple values using a delimiter, * this method will split these values appropriately provided the * supplied delimiter is the same. If the key value was stored as a * single value, then this method behavior is the * exact same as the * {@link Properties#loadFromXML(InputStream)} method. * @param in in the input stream from which to read the XML document. * @param delimiter delimiter string to used to parse a multi value * key. * @throws IOException i/o problem */ public synchronized void loadFromXML(InputStream in, String delimiter) throws IOException { java.util.Properties p = new java.util.Properties(); p.loadFromXML(in); List values = new ArrayList<>(); for (String key : p.stringPropertyNames()) { String value = p.getProperty(key); if (value != null) { values.addAll(Arrays.asList( StringUtils.splitByWholeSeparator(value, delimiter))); } put(key, values); } p = null; } /** * Reads a property list (key and element pairs) from the UTF-8 input * string. Otherwise, the same considerations as * {@link #load(InputStream)} apply. * @param str the string to load * @throws IOException problem loading string */ public void loadFromString(String str) throws IOException { InputStream is = new ByteArrayInputStream( str.getBytes(CharEncoding.UTF_8)); load(is); is.close(); } //--- String --------------------------------------------------------------- /** * Gets value as string. * @param key property key * @return the value */ public final String getString(String key) { List list = get(key); if (list != null && !list.isEmpty()) { return list.get(0); } return null; } /** * Gets value as string. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final String getString(String key, String defaultValue) { String s = getString(key); if (s == null) { return defaultValue; } return s; } /** * Gets values as a list of strings. This method is null-safe. No matches * returns an empty list. * @param key property key * @return the values */ public final List getStrings(String key) { List values = get(key); if (values == null) { return new ArrayList<>(); } return new ArrayList<>(values); } /** * Sets one or multiple string values replacing existing ones. * Setting a single null value or an empty string array is * the same as calling {@link #remove(Object)} with the same key. * When setting multiple values, null values are converted * to blank strings. * @param key the key of the value to set * @param values the values to set */ public final void setString(String key, String... values) { if (ArrayUtils.isEmpty(values)) { remove(key); } put(key, new ArrayList<>(Arrays.asList(values))); } /** * Adds one or multiple string values. * Adding a single null value has no effect. * When adding multiple values, null values are converted * to blank strings. * @param key the key of the value to set * @param values the values to set */ public final void addString(String key, String... values) { if (ArrayUtils.isEmpty(values)) { return; } List list = get(key); if (list == null) { list = new ArrayList<>(); } list.addAll(Arrays.asList(values)); put(key, list); } //--- Integer -------------------------------------------------------------- /** * Gets value as an integer. * @param key property key * @return the value */ public final int getInt(String key) { try { return Integer.parseInt(getString(key)); } catch (NumberFormatException e) { throw createTypedException( "Could not parse integer value.", key, getString(key), e); } } /** * Gets value as an integer. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final int getInt(String key, int defaultValue) { String value = getString(key, Integer.toString(defaultValue)); try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw createTypedException( "Could not parse integer value.", key, value, e); } } /** * Gets values as a list of integers. * @param key property key * @return the values */ public final List getInts(String key) { List values = getStrings(key); String errVal = null; try { List ints = new ArrayList<>(values.size()); for (String value : values) { errVal = value; ints.add(Integer.parseInt(value)); } return ints; } catch (NumberFormatException e) { throw createTypedException( "Could not parse integer value.", key, errVal, e); } } /** * Sets one or multiple integer values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setInt(String key, int... values) { setString(key, toStringArray(ArrayUtils.toObject(values))); } /** * Adds one or multiple integer values values. * @param key the key of the values to set * @param values the values to set */ public final void addInt(String key, int... values) { addString(key, toStringArray(ArrayUtils.toObject(values))); } //--- Double --------------------------------------------------------------- /** * Gets value as a double. * @param key property key * @return the value */ public final double getDouble(String key) { try { return Double.parseDouble(getString(key)); } catch (NumberFormatException e) { throw createTypedException( "Could not parse double value.", key, getString(key), e); } } /** * Gets value as a double. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final double getDouble(String key, double defaultValue) { String value = getString(key, Double.toString(defaultValue)); try { return Double.parseDouble(value); } catch (NumberFormatException e) { throw createTypedException( "Could not parse double value.", key, value, e); } } /** * Gets values as a list of doubles. * @param key property key * @return the values */ public final List getDoubles(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(Double.parseDouble(value)); } return list; } catch (NumberFormatException e) { throw createTypedException( "Could not parse double value.", key, errVal, e); } } /** * Sets one or multiple double values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setDouble(String key, double... values) { setString(key, toStringArray(ArrayUtils.toObject(values))); } /** * Adds one or multiple double values. * @param key the key of the values to set * @param values the values to set */ public final void addDouble(String key, double... values) { addString(key, toStringArray(ArrayUtils.toObject(values))); } //--- Long ----------------------------------------------------------------- /** * Gets value as a long. * @param key property key * @return the value */ public final long getLong(String key) { try { return Long.parseLong(getString(key)); } catch (NumberFormatException e) { throw createTypedException( "Could not parse long value.", key, getString(key), e); } } /** * Gets value as a long. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final long getLong(String key, long defaultValue) { String value = getString(key, Long.toString(defaultValue)); try { return Long.parseLong(value); } catch (NumberFormatException e) { throw createTypedException( "Could not parse long value.", key, value, e); } } /** * Gets values as a list of longs. * @param key property key * @return the values */ public final List getLongs(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(Long.parseLong(value)); } return list; } catch (NumberFormatException e) { throw createTypedException( "Could not parse long value.", key, errVal, e); } } /** * Sets one or multiple long values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setLong(String key, long... values) { setString(key, toStringArray(ArrayUtils.toObject(values))); } /** * Add one or multiple long values. * @param key the key of the values to set * @param values the values to set */ public final void addLong(String key, long... values) { addString(key, toStringArray(ArrayUtils.toObject(values))); } //--- Float ---------------------------------------------------------------- /** * Gets value as a float. * @param key property key * @return the value */ public final float getFloat(String key) { try { return Float.parseFloat(getString(key)); } catch (NumberFormatException e) { throw createTypedException( "Could not parse float value.", key, getString(key), e); } } /** * Gets value as a float. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final float getFloat(String key, float defaultValue) { String value = getString(key, Float.toString(defaultValue)); try { return Float.parseFloat(value); } catch (NumberFormatException e) { throw createTypedException( "Could not parse float value.", key, value, e); } } /** * Gets values as a list of floats. * @param key property key * @return the values */ public final List getFloats(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(Float.parseFloat(value)); } return list; } catch (NumberFormatException e) { throw createTypedException( "Could not parse float value.", key, errVal, e); } } /** * Sets one or multiple float values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setFloat(String key, float... values) { setString(key, toStringArray(ArrayUtils.toObject(values))); } /** * Adds one or multiple long values. * @param key the key of the values to set * @param values the values to set */ public final void addFloat(String key, float... values) { addString(key, toStringArray(ArrayUtils.toObject(values))); } //--- BigDecimal ----------------------------------------------------------- /** * Gets value as a BigDecimal. * @param key property key * @return the value */ public final BigDecimal getBigDecimal(String key) { String value = getString(key); if (value == null || value.trim().length() == 0) { return null; } try { return new BigDecimal(value); } catch (NumberFormatException e) { throw createTypedException( "Could not parse BigDecimal value.", key, value, e); } } /** * Gets value as a BigDecimal. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final BigDecimal getBigDecimal(String key, BigDecimal defaultValue) { BigDecimal value = getBigDecimal(key); if (value == null) { return defaultValue; } return value; } /** * Gets values as a list of BigDecimals. * @param key property key * @return the values */ public final List getBigDecimals(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(new BigDecimal(value)); } return list; } catch (NumberFormatException e) { throw createTypedException( "Could not parse BigDecimal value.", key, errVal, e); } } /** * Sets one or multiple BigDecimal values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setBigDecimal(String key, BigDecimal... values) { setString(key, toStringArray(values)); } /** * Add one or multiple BigDecimal values. * @param key the key of the values to set * @param values the values to set */ public final void addBigDecimal(String key, BigDecimal... values) { addString(key, toStringArray(values)); } //--- Date ----------------------------------------------------------------- /** * Gets value as a date. * @param key property key * @return the value */ public final Date getDate(String key) { String value = getString(key); if (StringUtils.isBlank(value)) { return null; } try { return new Date(Long.parseLong(value)); } catch (NumberFormatException e) { throw createTypedException( "Could not parse Date value.", key, value, e); } } /** * Gets value as a date. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final Date getDate(String key, Date defaultValue) { Date value = getDate(key); if (value == null) { return defaultValue; } return value; } /** * Gets values as a list of dates. * @param key property key * @return the values */ public final List getDates(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(new Date(Long.parseLong(value))); } return list; } catch (NumberFormatException e) { throw createTypedException( "Could not parse Date value.", key, errVal, e); } } /** * Sets one or multiple date values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setDate(String key, Date... values) { setString(key, datesToStringArray(values)); } /** * Add one or multiple date values. * @param key the key of the values to set * @param values the values to set */ public final void addDate(String key, Date... values) { addString(key, datesToStringArray(values)); } private String[] datesToStringArray(Date... values) { if (values == null) { return null; } String[] array = new String[values.length]; for (int i = 0; i < array.length; i++) { Date value = values[i]; if (value == null) { array[i] = null; } else { array[i] = Long.toString(value.getTime()); } } return array; } //--- Boolean -------------------------------------------------------------- /** * Gets value as a boolean. The underlying string value matching * the key must exist and equal "true" (ignoring case) to return * true. Any other value (including null) * will return false. * @param key property key * @return the value */ public final boolean getBoolean(String key) { return Boolean.parseBoolean(getString(key)); } /** * Gets value as a boolean. The underlying string value matching * the key must exist and equal "true" (ignoring case) to return * true. Any other value (including null) * will return false. If there are no entries for the given * key, the default value is returned instead. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final boolean getBoolean(String key, boolean defaultValue) { return Boolean.parseBoolean(getString(key, Boolean.toString(defaultValue))); } /** * Gets values as a list of booleans. * @param key property key * @return the values */ public final List getBooleans(String key) { List values = getStrings(key); List list = new ArrayList<>(values.size()); for (String value : values) { list.add(Boolean.parseBoolean(value)); } return list; } /** * Sets one or multiple boolean values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setBoolean(String key, boolean... values) { setString(key, toStringArray(ArrayUtils.toObject(values))); } /** * Adds one or multiple boolean values. * @param key the key of the values to set * @param values the values to set */ public final void addBoolean(String key, boolean... values) { addString(key, toStringArray(ArrayUtils.toObject(values))); } //--- Locale --------------------------------------------------------------- /** * Gets value as a locale. * @param key property key * @return the value */ public final Locale getLocale(String key) { try { return LocaleUtils.toLocale(getString(key)); } catch (IllegalArgumentException e) { throw createTypedException( "Could not parse Locale value.", key, getString(key), e); } } /** * Gets value as a locale. * @param key property key * @param defaultValue default value to return when original value is null. * @return the value */ public final Locale getLocale(String key, Locale defaultValue) { try { return LocaleUtils.toLocale(getString(key)); } catch (IllegalArgumentException e) { return defaultValue; } } /** * Gets values as a list of locales. * @param key property key * @return the values */ public final List getLocales(String key) { List values = getStrings(key); String errVal = null; try { List list = new ArrayList<>(values.size()); for (String value : values) { errVal = value; list.add(LocaleUtils.toLocale(value)); } return list; } catch (IllegalArgumentException e) { throw createTypedException( "Could not parse locale value.", key, errVal, e); } } /** * Sets one or multiple locale values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setLocale(String key, Locale... values) { setString(key, toStringArray(values)); } /** * Adds one or multiple locale values. * @param key the key of the values to set * @param values the values to set */ public final void addLocale(String key, Locale... values) { addString(key, toStringArray(values)); } //--- File ----------------------------------------------------------------- /** * Gets a file, assuming key value is a file system path. * @param key properties key * @return a File */ public final File getFile(String key) { String filePath = getString(key); if (filePath == null) { return null; } return new File(filePath); } /** * Gets a file, assuming key value is a file system path. * @param key properties key * @param defaultValue default file being returned if no file has been * defined for the given key in the properties. * @return a File */ public final File getFile(String key, File defaultValue) { File value = getFile(key); if (value == null) { return defaultValue; } return value; } /** * Gets values as a list of files. * @param key property key * @return the values */ public final List getFiles(String key) { List values = getStrings(key); List list = new ArrayList<>(values.size()); for (String value : values) { list.add(new File(value)); } return list; } /** * Sets one or multiple file values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setFile(String key, File... values) { setString(key, filesToStringArray(values)); } /** * Adds one or multiple file values. * @param key the key of the values to set * @param values the values to set */ public final void addFile(String key, File... values) { addString(key, filesToStringArray(values)); } private String[] filesToStringArray(File... values) { if (values == null) { return null; } String[] array = new String[values.length]; for (int i = 0; i < array.length; i++) { array[i] = values[i].getPath(); } return array; } //--- Class ---------------------------------------------------------------- /** * Gets a class, assuming key value is a fully qualified class name * available in the classloader. * @param key properties key * @return initialized class */ public final Class getClass(String key) { String value = getString(key); try { return Class.forName(value); } catch (ClassNotFoundException e) { throw createTypedException( "Could not parse class value.", key, value, e); } } /** * Gets a class, assuming key value is a fully qualified class name * available in the classloader. * @param key properties key * @param defaultValue default file being returned if no class has been * defined for the given key in the properties. * @return initialized class */ public final Class getClass(String key, Class defaultValue) { Class value = getClass(key); if (value == null) { return defaultValue; } return value; } /** * Gets values as a list of initialized classes. * @param key property key * @return the values */ public final List> getClasses(String key) { List values = getStrings(key); List> list = new ArrayList<>(values.size()); for (String value : values) { list.add(getClass(value)); } return list; } /** * Sets one or multiple class values, replacing existing ones. * @param key the key of the values to set * @param values the values to set */ public final void setClass(String key, Class... values) { setString(key, classesToStringArray(values)); } /** * Adds one or multiple class values. * @param key the key of the values to set * @param values the values to set */ public final void addClass(String key, Class... values) { addString(key, classesToStringArray(values)); } //--- Other ---------------------------------------------------------------- @Override public final List get(Object key) { return super.get(caseResolvedKey(key)); } @Override public final List remove(Object key) { return super.remove(caseResolvedKey(key)); } /* * Puts the list of values to the given key. Supplying a null * list has the same effect as calling {@link #remove(Object)} with * the same key. */ @Override public List put(String key, List values) { if (values == null) { return remove(key); } List nullSafeValues = new ArrayList<>(values.size()); for (String value : nullSafeValues) { if (value == null) { nullSafeValues.add(StringUtils.EMPTY); } else { nullSafeValues.add(value); } } return super.put(caseResolvedKey(key), values); } /* * When case insensitive, values of equal keys are merged into the first * key encountered. */ @Override public void putAll(Map> m) { if (!caseInsensitiveKeys) { super.putAll(m); return; } if (m == null) { return; } for (Entry> entry : m.entrySet()) { String key = entry.getKey(); List values = entry.getValue(); if (values != null) { addString(key, values.toArray(ArrayUtils.EMPTY_STRING_ARRAY)); } } } //--- Privates ------------------------------------------------------------- // If a key already exists under a different case, return it, else // return the one passed as argument private String caseResolvedKey(Object key) { String resolvedKey = Objects.toString(key, null); if (!isCaseInsensitiveKeys()) { return resolvedKey; } for (String existingKey : super.keySet()) { if (StringUtils.equalsIgnoreCase(existingKey, resolvedKey)) { resolvedKey = existingKey; break; } } return resolvedKey; } private String[] classesToStringArray(Class... values) { if (values == null) { return null; } String[] array = new String[values.length]; for (int i = 0; i < array.length; i++) { array[i] = values[i].getName(); } return array; } private PropertiesException createTypedException( String msg, String key, String value, Exception cause) { String message = msg + " [key=" + key + "; value=" + value + "]."; LOG.error(message, cause); return new PropertiesException(message, cause); } private String[] toStringArray(Object[] array) { if (array == null) { return null; } String[] strArray = new String[array.length]; for (int i = 0; i < array.length; i++) { strArray[i] = Objects.toString(array[i], StringUtils.EMPTY); } return strArray; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy