/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.configuration;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.configuration.event.ConfigurationEvent;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.apache.commons.lang.StringUtils;

A helper class used by PropertiesConfiguration to keep the layout of a properties file.

Instances of this class are associated with a PropertiesConfiguration object. They are responsible for analyzing properties files and for extracting as much information about the file layout (e.g. empty lines, comments) as possible. When the properties file is written back again it should be close to the original.

The PropertiesConfigurationLayout object associated with a PropertiesConfiguration object can be obtained using the getLayout() method of the configuration. Then the methods provided by this class can be used to alter the properties file's layout.

Implementation note: This is a very simple implementation, which is far away from being perfect, i.e. the original layout of a properties file won't be reproduced in all cases. One limitation is that comments for multi-valued property keys are concatenated. Maybe this implementation can later be improved.

To get an impression how this class works consider the following properties file:

# A demo configuration file
# for Demo App 1.42
# Application name
AppName=Demo App
# Application vendor
AppVendor=DemoSoft
# GUI properties
# Window Color
windowColors=0xFFFFFF,0x000000
# Include some setting
include=settings.properties
# Another vendor
AppVendor=TestSoft

For this example the following points are relevant:

  • The first two lines are set as header comment. The header comment is determined by the last blanc line before the first property definition.
  • For the property AppName one comment line and one leading blanc line is stored.
  • For the property windowColors two comment lines and two leading blanc lines are stored.
  • Include files is something this class cannot deal with well. When saving the properties configuration back, the included properties are simply contained in the original file. The comment before the include property is skipped.
  • For all properties except for AppVendor the "single line" flag is set. This is relevant only for windowColors, which has multiple values defined in one line using the separator character.
  • The AppVendor property appears twice. The comment lines are concatenated, so that layout.getComment("AppVendor"); will result in Application vendor<CR>Another vendor, with <CR> meaning the line separator. In addition the "single line" flag is set to false for this property. When the file is saved, two property definitions will be written (in series).

Author:Commons Configuration team
Version:$Id: PropertiesConfigurationLayout.java 1534402 2013-10-21 22:35:52Z henning $
Since:1.3
/** * <p> * A helper class used by {@link PropertiesConfiguration} to keep * the layout of a properties file. * </p> * <p> * Instances of this class are associated with a * {@code PropertiesConfiguration} object. They are responsible for * analyzing properties files and for extracting as much information about the * file layout (e.g. empty lines, comments) as possible. When the properties * file is written back again it should be close to the original. * </p> * <p> * The {@code PropertiesConfigurationLayout} object associated with a * {@code PropertiesConfiguration} object can be obtained using the * {@code getLayout()} method of the configuration. Then the methods * provided by this class can be used to alter the properties file's layout. * </p> * <p> * Implementation note: This is a very simple implementation, which is far away * from being perfect, i.e. the original layout of a properties file won't be * reproduced in all cases. One limitation is that comments for multi-valued * property keys are concatenated. Maybe this implementation can later be * improved. * </p> * <p> * To get an impression how this class works consider the following properties * file: * </p> * <p> * * <pre> * # A demo configuration file * # for Demo App 1.42 * * # Application name * AppName=Demo App * * # Application vendor * AppVendor=DemoSoft * * * # GUI properties * # Window Color * windowColors=0xFFFFFF,0x000000 * * # Include some setting * include=settings.properties * # Another vendor * AppVendor=TestSoft * </pre> * * </p> * <p> * For this example the following points are relevant: * </p> * <p> * <ul> * <li>The first two lines are set as header comment. The header comment is * determined by the last blanc line before the first property definition.</li> * <li>For the property {@code AppName} one comment line and one * leading blanc line is stored.</li> * <li>For the property {@code windowColors} two comment lines and two * leading blanc lines are stored.</li> * <li>Include files is something this class cannot deal with well. When saving * the properties configuration back, the included properties are simply * contained in the original file. The comment before the include property is * skipped.</li> * <li>For all properties except for {@code AppVendor} the &quot;single * line&quot; flag is set. This is relevant only for {@code windowColors}, * which has multiple values defined in one line using the separator character.</li> * <li>The {@code AppVendor} property appears twice. The comment lines * are concatenated, so that {@code layout.getComment("AppVendor");} will * result in <code>Application vendor&lt;CR&gt;Another vendor</code>, with * <code>&lt;CR&gt;</code> meaning the line separator. In addition the * &quot;single line&quot; flag is set to <b>false</b> for this property. When * the file is saved, two property definitions will be written (in series).</li> * </ul> * </p> * * @author <a * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team</a> * @version $Id: PropertiesConfigurationLayout.java 1534402 2013-10-21 22:35:52Z henning $ * @since 1.3 */
public class PropertiesConfigurationLayout implements ConfigurationListener {
Constant for the line break character.
/** Constant for the line break character. */
private static final String CR = "\n";
Constant for the default comment prefix.
/** Constant for the default comment prefix. */
private static final String COMMENT_PREFIX = "# ";
Stores the associated configuration object.
/** Stores the associated configuration object. */
private PropertiesConfiguration configuration;
Stores a map with the contained layout information.
/** Stores a map with the contained layout information. */
private Map<String, PropertyLayoutData> layoutData;
Stores the header comment.
/** Stores the header comment. */
private String headerComment;
Stores the footer comment.
/** Stores the footer comment. */
private String footerComment;
The global separator that will be used for all properties.
/** The global separator that will be used for all properties. */
private String globalSeparator;
The line separator.
/** The line separator.*/
private String lineSeparator;
A counter for determining nested load calls.
/** A counter for determining nested load calls. */
private int loadCounter;
Stores the force single line flag.
/** Stores the force single line flag. */
private boolean forceSingleLine;
Creates a new instance of PropertiesConfigurationLayout and initializes it with the associated configuration object.
Params:
  • config – the configuration (must not be null)
/** * Creates a new instance of {@code PropertiesConfigurationLayout} * and initializes it with the associated configuration object. * * @param config the configuration (must not be <b>null</b>) */
public PropertiesConfigurationLayout(PropertiesConfiguration config) { this(config, null); }
Creates a new instance of PropertiesConfigurationLayout and initializes it with the given configuration object. The data of the specified layout object is copied.
Params:
  • config – the configuration (must not be null)
  • c – the layout object to be copied
/** * Creates a new instance of {@code PropertiesConfigurationLayout} * and initializes it with the given configuration object. The data of the * specified layout object is copied. * * @param config the configuration (must not be <b>null</b>) * @param c the layout object to be copied */
public PropertiesConfigurationLayout(PropertiesConfiguration config, PropertiesConfigurationLayout c) { if (config == null) { throw new IllegalArgumentException( "Configuration must not be null!"); } configuration = config; layoutData = new LinkedHashMap<String, PropertyLayoutData>(); config.addConfigurationListener(this); if (c != null) { copyFrom(c); } }
Returns the associated configuration object.
Returns:the associated configuration
/** * Returns the associated configuration object. * * @return the associated configuration */
public PropertiesConfiguration getConfiguration() { return configuration; }
Returns the comment for the specified property key in a canonical form. "Canonical" means that either all lines start with a comment character or none. If the commentChar parameter is false, all comment characters are removed, so that the result is only the plain text of the comment. Otherwise it is ensured that each line of the comment starts with a comment character. Also, line breaks in the comment are normalized to the line separator "\n".
Params:
  • key – the key of the property
  • commentChar – determines whether all lines should start with comment characters or not
Returns:the canonical comment for this key (can be null)
/** * Returns the comment for the specified property key in a canonical form. * &quot;Canonical&quot; means that either all lines start with a comment * character or none. If the {@code commentChar} parameter is <b>false</b>, * all comment characters are removed, so that the result is only the plain * text of the comment. Otherwise it is ensured that each line of the * comment starts with a comment character. Also, line breaks in the comment * are normalized to the line separator &quot;\n&quot;. * * @param key the key of the property * @param commentChar determines whether all lines should start with comment * characters or not * @return the canonical comment for this key (can be <b>null</b>) */
public String getCanonicalComment(String key, boolean commentChar) { return constructCanonicalComment(getComment(key), commentChar); }
Returns the comment for the specified property key. The comment is returned as it was set (either manually by calling setComment() or when it was loaded from a properties file). No modifications are performed.
Params:
  • key – the key of the property
Returns:the comment for this key (can be null)
/** * Returns the comment for the specified property key. The comment is * returned as it was set (either manually by calling * {@code setComment()} or when it was loaded from a properties * file). No modifications are performed. * * @param key the key of the property * @return the comment for this key (can be <b>null</b>) */
public String getComment(String key) { return fetchLayoutData(key).getComment(); }
Sets the comment for the specified property key. The comment (or its single lines if it is a multi-line comment) can start with a comment character. If this is the case, it will be written without changes. Otherwise a default comment character is added automatically.
Params:
  • key – the key of the property
  • comment – the comment for this key (can be null, then the comment will be removed)
/** * Sets the comment for the specified property key. The comment (or its * single lines if it is a multi-line comment) can start with a comment * character. If this is the case, it will be written without changes. * Otherwise a default comment character is added automatically. * * @param key the key of the property * @param comment the comment for this key (can be <b>null</b>, then the * comment will be removed) */
public void setComment(String key, String comment) { fetchLayoutData(key).setComment(comment); }
Returns the number of blanc lines before this property key. If this key does not exist, 0 will be returned.
Params:
  • key – the property key
Returns:the number of blanc lines before the property definition for this key
/** * Returns the number of blanc lines before this property key. If this key * does not exist, 0 will be returned. * * @param key the property key * @return the number of blanc lines before the property definition for this * key */
public int getBlancLinesBefore(String key) { return fetchLayoutData(key).getBlancLines(); }
Sets the number of blanc lines before the given property key. This can be used for a logical grouping of properties.
Params:
  • key – the property key
  • number – the number of blanc lines to add before this property definition
/** * Sets the number of blanc lines before the given property key. This can be * used for a logical grouping of properties. * * @param key the property key * @param number the number of blanc lines to add before this property * definition */
public void setBlancLinesBefore(String key, int number) { fetchLayoutData(key).setBlancLines(number); }
Returns the header comment of the represented properties file in a canonical form. With the commentChar parameter it can be specified whether comment characters should be stripped or be always present.
Params:
  • commentChar – determines the presence of comment characters
Returns:the header comment (can be null)
/** * Returns the header comment of the represented properties file in a * canonical form. With the {@code commentChar} parameter it can be * specified whether comment characters should be stripped or be always * present. * * @param commentChar determines the presence of comment characters * @return the header comment (can be <b>null</b>) */
public String getCanonicalHeaderComment(boolean commentChar) { return constructCanonicalComment(getHeaderComment(), commentChar); }
Returns the header comment of the represented properties file. This method returns the header comment exactly as it was set using setHeaderComment() or extracted from the loaded properties file.
Returns:the header comment (can be null)
/** * Returns the header comment of the represented properties file. This * method returns the header comment exactly as it was set using * {@code setHeaderComment()} or extracted from the loaded properties * file. * * @return the header comment (can be <b>null</b>) */
public String getHeaderComment() { return headerComment; }
Sets the header comment for the represented properties file. This comment will be output on top of the file.
Params:
  • comment – the comment
/** * Sets the header comment for the represented properties file. This comment * will be output on top of the file. * * @param comment the comment */
public void setHeaderComment(String comment) { headerComment = comment; }
Returns the footer comment of the represented properties file in a canonical form. This method works like getCanonicalHeaderComment(), but reads the footer comment.
Params:
  • commentChar – determines the presence of comment characters
See Also:
Returns:the footer comment (can be null)
Since:1.10
/** * Returns the footer comment of the represented properties file in a * canonical form. This method works like * {@code getCanonicalHeaderComment()}, but reads the footer comment. * * @param commentChar determines the presence of comment characters * @return the footer comment (can be <b>null</b>) * @see #getCanonicalHeaderComment(boolean) * @since 1.10 */
public String getCanonicalFooterCooment(boolean commentChar) { return constructCanonicalComment(getFooterComment(), commentChar); }
Returns the footer comment of the represented properties file. This method returns the footer comment exactly as it was set using setFooterComment() or extracted from the loaded properties file.
Returns:the footer comment (can be null)
Since:1.10
/** * Returns the footer comment of the represented properties file. This * method returns the footer comment exactly as it was set using * {@code setFooterComment()} or extracted from the loaded properties * file. * * @return the footer comment (can be <b>null</b>) * @since 1.10 */
public String getFooterComment() { return footerComment; }
Sets the footer comment for the represented properties file. This comment will be output at the bottom of the file.
Params:
  • footerComment – the footer comment
Since:1.10
/** * Sets the footer comment for the represented properties file. This comment * will be output at the bottom of the file. * * @param footerComment the footer comment * @since 1.10 */
public void setFooterComment(String footerComment) { this.footerComment = footerComment; }
Returns a flag whether the specified property is defined on a single line. This is meaningful only if this property has multiple values.
Params:
  • key – the property key
Returns:a flag if this property is defined on a single line
/** * Returns a flag whether the specified property is defined on a single * line. This is meaningful only if this property has multiple values. * * @param key the property key * @return a flag if this property is defined on a single line */
public boolean isSingleLine(String key) { return fetchLayoutData(key).isSingleLine(); }
Sets the "single line flag" for the specified property key. This flag is evaluated if the property has multiple values (i.e. if it is a list property). In this case, if the flag is set, all values will be written in a single property definition using the list delimiter as separator. Otherwise multiple lines will be written for this property, each line containing one property value.
Params:
  • key – the property key
  • f – the single line flag
/** * Sets the &quot;single line flag&quot; for the specified property key. * This flag is evaluated if the property has multiple values (i.e. if it is * a list property). In this case, if the flag is set, all values will be * written in a single property definition using the list delimiter as * separator. Otherwise multiple lines will be written for this property, * each line containing one property value. * * @param key the property key * @param f the single line flag */
public void setSingleLine(String key, boolean f) { fetchLayoutData(key).setSingleLine(f); }
Returns the "force single line" flag.
See Also:
Returns:the force single line flag
/** * Returns the &quot;force single line&quot; flag. * * @return the force single line flag * @see #setForceSingleLine(boolean) */
public boolean isForceSingleLine() { return forceSingleLine; }
Sets the "force single line" flag. If this flag is set, all properties with multiple values are written on single lines. This mode provides more compatibility with java.lang.Properties, which cannot deal with multiple definitions of a single property. This mode has no effect if the list delimiter parsing is disabled.
Params:
  • f – the force single line flag
/** * Sets the &quot;force single line&quot; flag. If this flag is set, all * properties with multiple values are written on single lines. This mode * provides more compatibility with {@code java.lang.Properties}, * which cannot deal with multiple definitions of a single property. This * mode has no effect if the list delimiter parsing is disabled. * * @param f the force single line flag */
public void setForceSingleLine(boolean f) { forceSingleLine = f; }
Returns the separator for the property with the given key.
Params:
  • key – the property key
Returns:the property separator for this property
Since:1.7
/** * Returns the separator for the property with the given key. * * @param key the property key * @return the property separator for this property * @since 1.7 */
public String getSeparator(String key) { return fetchLayoutData(key).getSeparator(); }
Sets the separator to be used for the property with the given key. The separator is the string between the property key and its value. For new properties " = " is used. When a properties file is read, the layout tries to determine the separator for each property. With this method the separator can be changed. To be compatible with the properties format only the characters = and : (with or without whitespace) should be used, but this method does not enforce this - it accepts arbitrary strings. If the key refers to a property with multiple values that are written on multiple lines, this separator will be used on all lines.
Params:
  • key – the key for the property
  • sep – the separator to be used for this property
Since:1.7
/** * Sets the separator to be used for the property with the given key. The * separator is the string between the property key and its value. For new * properties &quot; = &quot; is used. When a properties file is read, the * layout tries to determine the separator for each property. With this * method the separator can be changed. To be compatible with the properties * format only the characters {@code =} and {@code :} (with or * without whitespace) should be used, but this method does not enforce this * - it accepts arbitrary strings. If the key refers to a property with * multiple values that are written on multiple lines, this separator will * be used on all lines. * * @param key the key for the property * @param sep the separator to be used for this property * @since 1.7 */
public void setSeparator(String key, String sep) { fetchLayoutData(key).setSeparator(sep); }
Returns the global separator.
Returns:the global properties separator
Since:1.7
/** * Returns the global separator. * * @return the global properties separator * @since 1.7 */
public String getGlobalSeparator() { return globalSeparator; }
Sets the global separator for properties. With this method a separator can be set that will be used for all properties when writing the configuration. This is an easy way of determining the properties separator globally. To be compatible with the properties format only the characters = and : (with or without whitespace) should be used, but this method does not enforce this - it accepts arbitrary strings. If the global separator is set to null, property separators are not changed. This is the default behavior as it produces results that are closer to the original properties file.
Params:
  • globalSeparator – the separator to be used for all properties
Since:1.7
/** * Sets the global separator for properties. With this method a separator * can be set that will be used for all properties when writing the * configuration. This is an easy way of determining the properties * separator globally. To be compatible with the properties format only the * characters {@code =} and {@code :} (with or without whitespace) * should be used, but this method does not enforce this - it accepts * arbitrary strings. If the global separator is set to <b>null</b>, * property separators are not changed. This is the default behavior as it * produces results that are closer to the original properties file. * * @param globalSeparator the separator to be used for all properties * @since 1.7 */
public void setGlobalSeparator(String globalSeparator) { this.globalSeparator = globalSeparator; }
Returns the line separator.
Returns:the line separator
Since:1.7
/** * Returns the line separator. * * @return the line separator * @since 1.7 */
public String getLineSeparator() { return lineSeparator; }
Sets the line separator. When writing the properties configuration, all lines are terminated with this separator. If no separator was set, the platform-specific default line separator is used.
Params:
  • lineSeparator – the line separator
Since:1.7
/** * Sets the line separator. When writing the properties configuration, all * lines are terminated with this separator. If no separator was set, the * platform-specific default line separator is used. * * @param lineSeparator the line separator * @since 1.7 */
public void setLineSeparator(String lineSeparator) { this.lineSeparator = lineSeparator; }
Returns a set with all property keys managed by this object.
Returns:a set with all contained property keys
/** * Returns a set with all property keys managed by this object. * * @return a set with all contained property keys */
public Set<String> getKeys() { return layoutData.keySet(); }
Reads a properties file and stores its internal structure. The found properties will be added to the associated configuration object.
Params:
  • in – the reader to the properties file
Throws:
/** * Reads a properties file and stores its internal structure. The found * properties will be added to the associated configuration object. * * @param in the reader to the properties file * @throws ConfigurationException if an error occurs */
public void load(Reader in) throws ConfigurationException { if (++loadCounter == 1) { getConfiguration().removeConfigurationListener(this); } PropertiesConfiguration.PropertiesReader reader = getConfiguration() .getIOFactory().createPropertiesReader(in, getConfiguration().getListDelimiter()); try { while (reader.nextProperty()) { if (getConfiguration().propertyLoaded(reader.getPropertyName(), reader.getPropertyValue())) { boolean contained = layoutData.containsKey(reader .getPropertyName()); int blancLines = 0; int idx = checkHeaderComment(reader.getCommentLines()); while (idx < reader.getCommentLines().size() && reader.getCommentLines().get(idx).length() < 1) { idx++; blancLines++; } String comment = extractComment(reader.getCommentLines(), idx, reader.getCommentLines().size() - 1); PropertyLayoutData data = fetchLayoutData(reader .getPropertyName()); if (contained) { data.addComment(comment); data.setSingleLine(false); } else { data.setComment(comment); data.setBlancLines(blancLines); data.setSeparator(reader.getPropertySeparator()); } } } setFooterComment(extractComment(reader.getCommentLines(), 0, reader .getCommentLines().size() - 1)); } catch (IOException ioex) { throw new ConfigurationException(ioex); } finally { if (--loadCounter == 0) { getConfiguration().addConfigurationListener(this); } } }
Writes the properties file to the given writer, preserving as much of its structure as possible.
Params:
  • out – the writer
Throws:
/** * Writes the properties file to the given writer, preserving as much of its * structure as possible. * * @param out the writer * @throws ConfigurationException if an error occurs */
public void save(Writer out) throws ConfigurationException { try { char delimiter = getConfiguration().isDelimiterParsingDisabled() ? 0 : getConfiguration().getListDelimiter(); PropertiesConfiguration.PropertiesWriter writer = getConfiguration() .getIOFactory().createPropertiesWriter(out, delimiter); writer.setGlobalSeparator(getGlobalSeparator()); if (getLineSeparator() != null) { writer.setLineSeparator(getLineSeparator()); } if (headerComment != null) { writeComment(writer, getCanonicalHeaderComment(true)); writer.writeln(null); } for (String key : layoutData.keySet()) { if (getConfiguration().containsKey(key)) { // Output blank lines before property for (int i = 0; i < getBlancLinesBefore(key); i++) { writer.writeln(null); } // Output the comment writeComment(writer, getCanonicalComment(key, true)); // Output the property and its value boolean singleLine = (isForceSingleLine() || isSingleLine(key)) && !getConfiguration().isDelimiterParsingDisabled(); writer.setCurrentSeparator(getSeparator(key)); writer.writeProperty(key, getConfiguration().getProperty( key), singleLine); } } writeComment(writer, getCanonicalFooterCooment(true)); writer.flush(); } catch (IOException ioex) { throw new ConfigurationException(ioex); } }
The event listener callback. Here event notifications of the configuration object are processed to update the layout object properly.
Params:
  • event – the event object
/** * The event listener callback. Here event notifications of the * configuration object are processed to update the layout object properly. * * @param event the event object */
public void configurationChanged(ConfigurationEvent event) { if (event.isBeforeUpdate()) { if (AbstractFileConfiguration.EVENT_RELOAD == event.getType()) { clear(); } } else { switch (event.getType()) { case AbstractConfiguration.EVENT_ADD_PROPERTY: boolean contained = layoutData.containsKey(event .getPropertyName()); PropertyLayoutData data = fetchLayoutData(event .getPropertyName()); data.setSingleLine(!contained); break; case AbstractConfiguration.EVENT_CLEAR_PROPERTY: layoutData.remove(event.getPropertyName()); break; case AbstractConfiguration.EVENT_CLEAR: clear(); break; case AbstractConfiguration.EVENT_SET_PROPERTY: fetchLayoutData(event.getPropertyName()); break; } } }
Returns a layout data object for the specified key. If this is a new key, a new object is created and initialized with default values.
Params:
  • key – the key
Returns:the corresponding layout data object
/** * Returns a layout data object for the specified key. If this is a new key, * a new object is created and initialized with default values. * * @param key the key * @return the corresponding layout data object */
private PropertyLayoutData fetchLayoutData(String key) { if (key == null) { throw new IllegalArgumentException("Property key must not be null!"); } PropertyLayoutData data = layoutData.get(key); if (data == null) { data = new PropertyLayoutData(); data.setSingleLine(true); layoutData.put(key, data); } return data; }
Removes all content from this layout object.
/** * Removes all content from this layout object. */
private void clear() { layoutData.clear(); setHeaderComment(null); }
Tests whether a line is a comment, i.e. whether it starts with a comment character.
Params:
  • line – the line
Returns:a flag if this is a comment line
/** * Tests whether a line is a comment, i.e. whether it starts with a comment * character. * * @param line the line * @return a flag if this is a comment line */
static boolean isCommentLine(String line) { return PropertiesConfiguration.isCommentLine(line); }
Trims a comment. This method either removes all comment characters from the given string, leaving only the plain comment text or ensures that every line starts with a valid comment character.
Params:
  • s – the string to be processed
  • comment – if true, a comment character will always be enforced; if false, it will be removed
Returns:the trimmed comment
/** * Trims a comment. This method either removes all comment characters from * the given string, leaving only the plain comment text or ensures that * every line starts with a valid comment character. * * @param s the string to be processed * @param comment if <b>true</b>, a comment character will always be * enforced; if <b>false</b>, it will be removed * @return the trimmed comment */
static String trimComment(String s, boolean comment) { StringBuilder buf = new StringBuilder(s.length()); int lastPos = 0; int pos; do { pos = s.indexOf(CR, lastPos); if (pos >= 0) { String line = s.substring(lastPos, pos); buf.append(stripCommentChar(line, comment)).append(CR); lastPos = pos + CR.length(); } } while (pos >= 0); if (lastPos < s.length()) { buf.append(stripCommentChar(s.substring(lastPos), comment)); } return buf.toString(); }
Either removes the comment character from the given comment line or ensures that the line starts with a comment character.
Params:
  • s – the comment line
  • comment – if true, a comment character will always be enforced; if false, it will be removed
Returns:the line without comment character
/** * Either removes the comment character from the given comment line or * ensures that the line starts with a comment character. * * @param s the comment line * @param comment if <b>true</b>, a comment character will always be * enforced; if <b>false</b>, it will be removed * @return the line without comment character */
static String stripCommentChar(String s, boolean comment) { if (s.length() < 1 || (isCommentLine(s) == comment)) { return s; } else { if (!comment) { int pos = 0; // find first comment character while (PropertiesConfiguration.COMMENT_CHARS.indexOf(s .charAt(pos)) < 0) { pos++; } // Remove leading spaces pos++; while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) { pos++; } return (pos < s.length()) ? s.substring(pos) : StringUtils.EMPTY; } else { return COMMENT_PREFIX + s; } } }
Extracts a comment string from the given range of the specified comment lines. The single lines are added using a line feed as separator.
Params:
  • commentLines – a list with comment lines
  • from – the start index
  • to – the end index (inclusive)
Returns:the comment string (null if it is undefined)
/** * Extracts a comment string from the given range of the specified comment * lines. The single lines are added using a line feed as separator. * * @param commentLines a list with comment lines * @param from the start index * @param to the end index (inclusive) * @return the comment string (<b>null</b> if it is undefined) */
private String extractComment(List<String> commentLines, int from, int to) { if (to < from) { return null; } else { StringBuilder buf = new StringBuilder(commentLines.get(from)); for (int i = from + 1; i <= to; i++) { buf.append(CR); buf.append(commentLines.get(i)); } return buf.toString(); } }
Checks if parts of the passed in comment can be used as header comment. This method checks whether a header comment can be defined (i.e. whether this is the first comment in the loaded file). If this is the case, it is searched for the latest blanc line. This line will mark the end of the header comment. The return value is the index of the first line in the passed in list, which does not belong to the header comment.
Params:
  • commentLines – the comment lines
Returns:the index of the next line after the header comment
/** * Checks if parts of the passed in comment can be used as header comment. * This method checks whether a header comment can be defined (i.e. whether * this is the first comment in the loaded file). If this is the case, it is * searched for the latest blanc line. This line will mark the end of the * header comment. The return value is the index of the first line in the * passed in list, which does not belong to the header comment. * * @param commentLines the comment lines * @return the index of the next line after the header comment */
private int checkHeaderComment(List<String> commentLines) { if (loadCounter == 1 && getHeaderComment() == null && layoutData.isEmpty()) { // This is the first comment. Search for blanc lines. int index = commentLines.size() - 1; while (index >= 0 && commentLines.get(index).length() > 0) { index--; } setHeaderComment(extractComment(commentLines, 0, index - 1)); return index + 1; } else { return 0; } }
Copies the data from the given layout object.
Params:
  • c – the layout object to copy
/** * Copies the data from the given layout object. * * @param c the layout object to copy */
private void copyFrom(PropertiesConfigurationLayout c) { for (String key : c.getKeys()) { PropertyLayoutData data = c.layoutData.get(key); layoutData.put(key, data.clone()); } setHeaderComment(c.getHeaderComment()); setFooterComment(c.getFooterComment()); }
Helper method for writing a comment line. This method ensures that the correct line separator is used if the comment spans multiple lines.
Params:
  • writer – the writer
  • comment – the comment to write
Throws:
/** * Helper method for writing a comment line. This method ensures that the * correct line separator is used if the comment spans multiple lines. * * @param writer the writer * @param comment the comment to write * @throws IOException if an IO error occurs */
private static void writeComment( PropertiesConfiguration.PropertiesWriter writer, String comment) throws IOException { if (comment != null) { writer.writeln(StringUtils.replace(comment, CR, writer .getLineSeparator())); } }
Helper method for generating a comment string. Depending on the boolean argument the resulting string either has no comment characters or a leading comment character at each line.
Params:
  • comment – the comment string to be processed
  • commentChar – determines the presence of comment characters
Returns:the canonical comment string (can be null)
/** * Helper method for generating a comment string. Depending on the boolean * argument the resulting string either has no comment characters or a * leading comment character at each line. * * @param comment the comment string to be processed * @param commentChar determines the presence of comment characters * @return the canonical comment string (can be <b>null</b>) */
private static String constructCanonicalComment(String comment, boolean commentChar) { return (comment == null) ? null : trimComment(comment, commentChar); }
A helper class for storing all layout related information for a configuration property.
/** * A helper class for storing all layout related information for a * configuration property. */
static class PropertyLayoutData implements Cloneable {
Stores the comment for the property.
/** Stores the comment for the property. */
private StringBuffer comment;
The separator to be used for this property.
/** The separator to be used for this property. */
private String separator;
Stores the number of blanc lines before this property.
/** Stores the number of blanc lines before this property. */
private int blancLines;
Stores the single line property.
/** Stores the single line property. */
private boolean singleLine;
Creates a new instance of PropertyLayoutData.
/** * Creates a new instance of {@code PropertyLayoutData}. */
public PropertyLayoutData() { singleLine = true; separator = PropertiesConfiguration.DEFAULT_SEPARATOR; }
Returns the number of blanc lines before this property.
Returns:the number of blanc lines before this property
/** * Returns the number of blanc lines before this property. * * @return the number of blanc lines before this property */
public int getBlancLines() { return blancLines; }
Sets the number of properties before this property.
Params:
  • blancLines – the number of properties before this property
/** * Sets the number of properties before this property. * * @param blancLines the number of properties before this property */
public void setBlancLines(int blancLines) { this.blancLines = blancLines; }
Returns the single line flag.
Returns:the single line flag
/** * Returns the single line flag. * * @return the single line flag */
public boolean isSingleLine() { return singleLine; }
Sets the single line flag.
Params:
  • singleLine – the single line flag
/** * Sets the single line flag. * * @param singleLine the single line flag */
public void setSingleLine(boolean singleLine) { this.singleLine = singleLine; }
Adds a comment for this property. If already a comment exists, the new comment is added (separated by a newline).
Params:
  • s – the comment to add
/** * Adds a comment for this property. If already a comment exists, the * new comment is added (separated by a newline). * * @param s the comment to add */
public void addComment(String s) { if (s != null) { if (comment == null) { comment = new StringBuffer(s); } else { comment.append(CR).append(s); } } }
Sets the comment for this property.
Params:
  • s – the new comment (can be null)
/** * Sets the comment for this property. * * @param s the new comment (can be <b>null</b>) */
public void setComment(String s) { if (s == null) { comment = null; } else { comment = new StringBuffer(s); } }
Returns the comment for this property. The comment is returned as it is, without processing of comment characters.
Returns:the comment (can be null)
/** * Returns the comment for this property. The comment is returned as it * is, without processing of comment characters. * * @return the comment (can be <b>null</b>) */
public String getComment() { return (comment == null) ? null : comment.toString(); }
Returns the separator that was used for this property.
Returns:the property separator
/** * Returns the separator that was used for this property. * * @return the property separator */
public String getSeparator() { return separator; }
Sets the separator to be used for the represented property.
Params:
  • separator – the property separator
/** * Sets the separator to be used for the represented property. * * @param separator the property separator */
public void setSeparator(String separator) { this.separator = separator; }
Creates a copy of this object.
Returns:the copy
/** * Creates a copy of this object. * * @return the copy */
@Override public PropertyLayoutData clone() { try { PropertyLayoutData copy = (PropertyLayoutData) super.clone(); if (comment != null) { // must copy string buffer, too copy.comment = new StringBuffer(getComment()); } return copy; } catch (CloneNotSupportedException cnex) { // This cannot happen! throw new ConfigurationRuntimeException(cnex); } } } }