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

org.opencms.configuration.CmsParameterConfiguration Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.configuration;

import org.opencms.i18n.CmsEncoder;
import org.opencms.util.CmsStringUtil;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;

import org.dom4j.Element;

/**
 * Provides convenient access to configuration parameters.

* * Usually the parameters are configured in some sort of String based file, * either in an XML configuration, or in a .property file. * This wrapper allows accessing such String values directly * as int, boolean or other data types, without * worrying about the type conversion.

* * It can also read a configuration from a special property file format, * which is explained here: * *

    *
  • * Each parameter in the file has the syntax key = value *
  • *
  • * The key may use any character but the equal sign '='. *
  • *
  • * value may be separated on different lines if a backslash * is placed at the end of the line that continues below. *
  • *
  • * If value is a list of strings, each token is separated * by a comma ','. *
  • *
  • * Commas in each token are escaped placing a backslash right before * the comma. *
  • *
  • * Backslashes are escaped by using two consecutive backslashes i.e. \\. * Note: Unlike in regular Java properties files, you don't need to escape Backslashes. *
  • *
  • * If a key is used more than once, the values are appended * as if they were on the same line separated with commas. *
  • *
  • * Blank lines and lines starting with character '#' are skipped. *
  • *
* * Here is an example of a valid parameter properties file:

* *

 *      # lines starting with # are comments
 *
 *      # This is the simplest property
 *      key = value
 *
 *      # A long property may be separated on multiple lines
 *      longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
 *                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 *
 *      # This is a property with many tokens
 *      tokens_on_a_line = first token, second token
 *
 *      # This sequence generates exactly the same result
 *      tokens_on_multiple_lines = first token
 *      tokens_on_multiple_lines = second token
 *
 *      # commas may be escaped in tokens
 *      commas.escaped = Hi\, what'up?
 * 
*/ public class CmsParameterConfiguration extends AbstractMap implements Serializable { /** * Used to read parameter lines from a property file.

* * The lines do not terminate with new-line chars but rather when there is no * backslash sign a the end of the line. This is used to * concatenate multiple lines for readability in the input file.

*/ protected static class ParameterReader extends LineNumberReader { /** * Constructor.

* * @param reader a reader */ public ParameterReader(Reader reader) { super(reader); } /** * Reads a parameter line.

* * @return the parameter line read * * @throws IOException in case of IO errors */ public String readParameter() throws IOException { StringBuffer buffer = new StringBuffer(); String line = readLine(); while (line != null) { line = line.trim(); if ((line.length() != 0) && (line.charAt(0) != '#')) { if (endsWithSlash(line)) { line = line.substring(0, line.length() - 1); buffer.append(line); } else { buffer.append(line); return buffer.toString(); // normal method end } } line = readLine(); } return null; // EOF reached } } /** * This class divides property value into tokens separated by ",".

* * Commas in the property value that are wanted * can be escaped using the backslash in front like this "\,". */ protected static class ParameterTokenizer extends StringTokenizer { /** The property delimiter used while parsing (a comma). */ static final String COMMA = ","; /** * Constructor.

* * @param string the String to break into tokens */ public ParameterTokenizer(String string) { super(string, COMMA); } /** * Returns the next token.

* * @return the next token */ @Override public String nextToken() { StringBuffer buffer = new StringBuffer(); while (hasMoreTokens()) { String token = super.nextToken(); if (endsWithSlash(token)) { buffer.append(token.substring(0, token.length() - 1)); buffer.append(COMMA); } else { buffer.append(token); break; } } return buffer.toString().trim(); } } /** * An empty, immutable parameter configuration.

*/ public static final CmsParameterConfiguration EMPTY_PARAMETERS = new CmsParameterConfiguration( Collections. emptyMap(), Collections. emptyMap()); /** The serial version id. */ private static final long serialVersionUID = 294679648036460877L; /** The parsed map of parameters where the Strings may have become Objects. */ private transient Map m_configurationObjects; /** The original map of parameters that contains only String values. */ private Map m_configurationStrings; /** * Creates an empty parameter configuration.

*/ public CmsParameterConfiguration() { this(new TreeMap(), new TreeMap()); } /** * Creates a parameter configuration from an input stream.

* * @param in the input stream to create the parameter configuration from * * @throws IOException in case of errors loading the parameters from the input stream */ public CmsParameterConfiguration(InputStream in) throws IOException { this(); load(in); } /** * Creates a parameter configuration from a Map of Strings.

* * @param configuration the map of Strings to create the parameter configuration from */ public CmsParameterConfiguration(Map configuration) { this(); for (String key : configuration.keySet()) { String value = configuration.get(key); add(key, value); } } /** * Creates a parameter wrapper by loading the parameters from the specified property file.

* * @param file the path of the file to load * * @throws IOException in case of errors loading the parameters from the specified property file */ public CmsParameterConfiguration(String file) throws IOException { this(); FileInputStream in = null; try { in = new FileInputStream(file); load(in); } finally { try { if (in != null) { in.close(); } } catch (IOException ex) { // ignore error on close() only } } } /** * Creates a parameter configuration from the given maps.

* * @param strings the String map * @param objects the object map */ private CmsParameterConfiguration(Map strings, Map objects) { m_configurationStrings = strings; m_configurationObjects = objects; } /** * Returns an unmodifiable version of this parameter configuration.

* * @param original the configuration to make unmodifiable * * @return an unmodifiable version of this parameter configuration */ public static CmsParameterConfiguration unmodifiableVersion(CmsParameterConfiguration original) { return new CmsParameterConfiguration( Collections.unmodifiableMap(original.m_configurationStrings), original.m_configurationObjects); } /** * Counts the number of successive times 'ch' appears in the * 'line' before the position indicated by the 'index'.

* * @param line the line to count * @param index the index position to start * @param ch the character to count * * @return the number of successive times 'ch' appears in the 'line' * before the position indicated by the 'index' */ protected static int countPreceding(String line, int index, char ch) { int i; for (i = index - 1; i >= 0; i--) { if (line.charAt(i) != ch) { break; } } return index - 1 - i; } /** * Checks if the line ends with odd number of backslashes.

* * @param line the line to check * * @return true if the line ends with odd number of backslashes */ protected static boolean endsWithSlash(String line) { if (!line.endsWith("\\")) { return false; } return ((countPreceding(line, line.length() - 1, '\\') % 2) == 0); } /** * Replaces escaped char sequences in the input value.

* * @param value the value to unescape * * @return the unescaped String */ protected static String unescape(String value) { value = CmsStringUtil.substitute(value, "\\,", ","); value = CmsStringUtil.substitute(value, "\\=", "="); value = CmsStringUtil.substitute(value, "\\\\", "\\"); return value; } /** * Add a parameter to this configuration.

* * If the parameter already exists then the value will be added * to the existing configuration entry and a List will be created for the values.

* * String values separated by a comma "," will NOT be tokenized when this * method is used. To create a List of String values for a parameter, call this method * multiple times with the same parameter name.

* * @param key the parameter to add * @param value the value to add */ public void add(String key, String value) { add(key, value, false); } /** * Serializes this parameter configuration for the OpenCms XML configuration.

* * For each parameter, a XML node like this
* * <param name="theName">theValue</param> *
* is generated and appended to the provided parent node.

* * @param parentNode the parent node where the parameter nodes are appended to * * @return the parent node */ public Element appendToXml(Element parentNode) { return appendToXml(parentNode, null); } /** * Serializes this parameter configuration for the OpenCms XML configuration.

* * For each parameter, a XML node like this
* * <param name="theName">theValue</param> *
* is generated and appended to the provided parent node.

* * @param parentNode the parent node where the parameter nodes are appended to * @param parametersToIgnore if not null, * all parameters in this list are not written to the XML * * @return the parent node */ public Element appendToXml(Element parentNode, List parametersToIgnore) { for (Map.Entry entry : m_configurationObjects.entrySet()) { String name = entry.getKey(); // check if the parameter should be ignored if ((parametersToIgnore == null) || !parametersToIgnore.contains(name)) { // now serialize the parameter name and value Object value = entry.getValue(); if (value instanceof List) { @SuppressWarnings("unchecked") List values = (List)value; for (String strValue : values) { // use the original String as value Element paramNode = parentNode.addElement(I_CmsXmlConfiguration.N_PARAM); // set the name attribute paramNode.addAttribute(I_CmsXmlConfiguration.A_NAME, name); // set the text of node paramNode.addText(strValue); } } else { // use the original String as value String strValue = get(name); Element paramNode = parentNode.addElement(I_CmsXmlConfiguration.N_PARAM); // set the name attribute paramNode.addAttribute(I_CmsXmlConfiguration.A_NAME, name); // set the text of node paramNode.addText(strValue); } } } return parentNode; } /** * @see java.util.Map#clear() */ @Override public void clear() { m_configurationStrings.clear(); m_configurationObjects.clear(); } /** * @see java.util.Map#containsKey(java.lang.Object) */ @Override public boolean containsKey(Object key) { return m_configurationStrings.containsKey(key); } /** * @see java.util.Map#containsValue(java.lang.Object) */ @Override public boolean containsValue(Object value) { return m_configurationStrings.containsValue(value) || m_configurationObjects.containsValue(value); } /** * @see java.util.Map#entrySet() */ @Override public Set> entrySet() { return m_configurationStrings.entrySet(); } /** * Returns the String associated with the given parameter.

* * @param key the parameter to look up the value for * * @return the String associated with the given parameter */ @Override public String get(Object key) { return m_configurationStrings.get(key); } /** * Returns the boolean associated with the given parameter, * or the default value in case there is no boolean value for this parameter.

* * @param key the parameter to look up the value for * @param defaultValue the default value * * @return the boolean associated with the given parameter, * or the default value in case there is no boolean value for this parameter */ public boolean getBoolean(String key, boolean defaultValue) { Object value = m_configurationObjects.get(key); if (value instanceof Boolean) { return ((Boolean)value).booleanValue(); } else if (value instanceof String) { Boolean b = Boolean.valueOf((String)value); m_configurationObjects.put(key, b); return b.booleanValue(); } else { return defaultValue; } } /** * Returns the integer associated with the given parameter, * or the default value in case there is no integer value for this parameter.

* * @param key the parameter to look up the value for * @param defaultValue the default value * * @return the integer associated with the given parameter, * or the default value in case there is no integer value for this parameter */ public int getInteger(String key, int defaultValue) { Object value = m_configurationObjects.get(key); if (value instanceof Integer) { return ((Integer)value).intValue(); } else if (value instanceof String) { Integer i = new Integer((String)value); m_configurationObjects.put(key, i); return i.intValue(); } else { return defaultValue; } } /** * Returns the List of Strings associated with the given parameter, * or an empty List in case there is no List of Strings for this parameter.

* * The list returned is a copy of the internal data of this object, and as * such you may alter it freely.

* * @param key the parameter to look up the value for * * @return the List of Strings associated with the given parameter, * or an empty List in case there is no List of Strings for this parameter */ public List getList(String key) { return getList(key, null); } /** * Returns the List of Strings associated with the given parameter, * or the default value in case there is no List of Strings for this parameter.

* * The list returned is a copy of the internal data of this object, and as * such you may alter it freely.

* * @param key the parameter to look up the value for * @param defaultValue the default value * * @return the List of Strings associated with the given parameter, * or the default value in case there is no List of Strings for this parameter */ public List getList(String key, List defaultValue) { Object value = m_configurationObjects.get(key); if (value instanceof List) { @SuppressWarnings("unchecked") List result = (List)value; return new ArrayList(result); } else if (value instanceof String) { ArrayList values = new ArrayList(1); values.add((String)value); m_configurationObjects.put(key, values); return values; } else { if (defaultValue == null) { return new ArrayList(); } else { return defaultValue; } } } /** * Returns the raw Object associated with the given parameter, * or null in case there is no Object for this parameter.

* * @param key the parameter to look up the value for * * @return the raw Object associated with the given parameter, * or null in case there is no Object for this parameter.

*/ public Object getObject(String key) { return m_configurationObjects.get(key); } /** * Creates a new Properties object from the existing configuration * extracting all key-value pars whose key are prefixed * with keyPrefix.

* * For this example config: * *

     *      # lines starting with # are comments
     *      db.pool.default.jdbcDriver=net.bull.javamelody.JdbcDriver
     *      db.pool.default.connectionProperties.driver=com.mysql.cj.jdbc.Driver
     * 
* * getPrefixedProperties("db.pool.default.connectionProperties") * will return a Properties object with one single entry: *
     *      key:"driver", value:"com.mysql.cj.jdbc.Driver"
     * 
* * @param keyPrefix prefix to match. If it isn't already, it will be * terminated with a dot. If null, it will return * an empty Properties instance * @return a new Properties object with all the entries from this * configuration whose keys math the prefix */ public Properties getPrefixedProperties(String keyPrefix) { Properties props = new Properties(); if (null == keyPrefix) { return props; } String dotTerminatedKeyPrefix = keyPrefix + (keyPrefix.endsWith(".") ? "" : "."); for (Map.Entry e : entrySet()) { String key = e.getKey(); if ((null != key) && key.startsWith(dotTerminatedKeyPrefix)) { String subKey = key.substring(dotTerminatedKeyPrefix.length()); props.put(subKey, e.getValue()); } } return props; } /** * Returns the String associated with the given parameter, * or the given default value in case there is no value for this parameter.

* * @param key the parameter to look up the value for * @param defaultValue the default value * * @return the String associated with the given parameter, * or the given default value in case there is no value for this parameter.

*/ public String getString(String key, String defaultValue) { String result = get(key); return result == null ? defaultValue : result; } /** * @see java.util.Map#hashCode() */ @Override public int hashCode() { return m_configurationStrings.hashCode(); } /** * @see java.util.Map#keySet() */ @Override public Set keySet() { return m_configurationStrings.keySet(); } /** * Load the parameters from the given input stream, which must be in property file format.

* * @param input the stream to load the input from * * @throws IOException in case of IO errors reading from the stream */ public void load(InputStream input) throws IOException { ParameterReader reader = null; try { reader = new ParameterReader(new InputStreamReader(input, CmsEncoder.ENCODING_ISO_8859_1)); } catch (UnsupportedEncodingException ex) { reader = new ParameterReader(new InputStreamReader(input)); } while (true) { String line = reader.readParameter(); if (line == null) { return; // EOF } int equalSign = line.indexOf('='); if (equalSign > 0) { String key = line.substring(0, equalSign).trim(); String value = line.substring(equalSign + 1).trim(); if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) { continue; } add(key, value, true); } } } /** * Set a parameter for this configuration.

* * If the parameter already exists then the existing value will be replaced.

* * @param key the parameter to set * @param value the value to set * * @return the previous String value from the parameter map */ @Override public String put(String key, String value) { String result = remove(key); add(key, value, false); return result; } /** * Merges this parameter configuration with the provided other parameter configuration.

* * The difference form a simple Map<String, String> is that for the parameter * configuration, the values of the keys in both maps are merged and kept in the Object store * as a List.

* * As result, this configuration will be altered, the other configuration will * stay unchanged.

* * @param other the other parameter configuration to merge this configuration with */ @Override public void putAll(Map other) { for (String key : other.keySet()) { boolean tokenize = false; if (other instanceof CmsParameterConfiguration) { Object o = ((CmsParameterConfiguration)other).getObject(key); if (o instanceof List) { tokenize = true; } } add(key, other.get(key), tokenize); } } /** * Removes a parameter from this configuration. * * @param key the parameter to remove */ @Override public String remove(Object key) { String result = m_configurationStrings.remove(key); m_configurationObjects.remove(key); return result; } /** * @see java.util.Map#toString() */ @Override public String toString() { return m_configurationStrings.toString(); } /** * @see java.util.Map#values() */ @Override public Collection values() { return m_configurationStrings.values(); } /** * Add a parameter to this configuration.

* * If the parameter already exists then the value will be added * to the existing configuration entry and a List will be created for the values.

* * @param key the parameter to add * @param value the value to add * @param tokenize decides if a String value should be tokenized or nor */ private void add(String key, String value, boolean tokenize) { if (tokenize && (value.indexOf(ParameterTokenizer.COMMA) > 0)) { // token contains commas, so must be split apart then added ParameterTokenizer tokenizer = new ParameterTokenizer(value); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); addInternal(key, unescape(token)); } } else if (tokenize) { addInternal(key, unescape(value)); } else { // token contains no commas, so can be simply added addInternal(key, value); } } /** * Adds a parameter, parsing the value if required.

* * @param key the parameter to add * @param value the value of the parameter */ private void addInternal(String key, String value) { Object currentObj = m_configurationObjects.get(key); String currentStr = get(key); if (currentObj instanceof String) { // one object already in map - convert it to a list ArrayList values = new ArrayList(2); values.add(currentStr); values.add(value); m_configurationObjects.put(key, values); m_configurationStrings.put(key, currentStr + ParameterTokenizer.COMMA + value); } else if (currentObj instanceof List) { // already a list - just add the new token @SuppressWarnings("unchecked") List list = (List)currentObj; list.add(value); m_configurationStrings.put(key, currentStr + ParameterTokenizer.COMMA + value); } else { m_configurationObjects.put(key, value); m_configurationStrings.put(key, value); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy