public class Configuration extends java.lang.Object implements VariableDereferencer, VariableNameChecker
Configuration implements a parser, generator and in-memory store for a configuration file whose syntax is reminiscent of classic Windows .INI files, though with many extensions.
A configuration file is broken into sections, and each section is introduced by a section name in brackets. For example:
[main] installation.directory=/usr/local/foo program.directory: /usr/local/foo/programs [search] searchCommand: find /usr/local/foo -type f -name '*.class' [display] searchFailedMessage=Search failed, sorry.
Notes and caveats:
There can be any amount of whitespace before and after the brackets in a section name; the whitespace is ignored. Section names may consist of alphanumeric characters and periods. Anything else is not permitted.
Each section contains zero or more variable settings. Similar to a Properties file, the variables are specified as name/value pairs, separated by an equal sign ("=") or a colon (":"). Variable names are case-sensitive and may contain any printable character (including white space), other than '$', '{', and '}' Variable values may contain anything at all. The parser ignores whitespace on either side of the "=" or ":"; that is, leading whitespace in the value is skipped. The way to include leading whitespace in a value is escape the whitespace characters with backslashes. (See below).
Variable definitions may span multiple lines; each line to be continued must end with a backslash ("\") character, which escapes the meaning of the newline, causing it to be treated like a space character. The following line is treated as a logical continuation of the first line; however, any leading whitespace is removed from continued lines. For example, the following four variable assignments all have the same value:
[test] a: one two three b: one two three c: one two \ three d: one \ two \ three
Because leading whitespace is skipped, all four variables have the value "one two three".
Only variable definition lines may be continued. Section header lines, comment lines (see below) and include directives (see below) cannot span multiple lines.
The configuration parser preprocesses each variable's value, replacing embedded metacharacter sequences and substituting variable references. You can use backslashes to escape the special characters that the parser uses to recognize metacharacter and variable sequences; you can also use single quotes. See Suppressing Metacharacter Expansion and Variable Substitution, below, for more details.
The parser recognizes Java-style ASCII escape sequences \t, \n, \r, \\, \", \', \ (a backslash and a space), and \uxxxx are recognized and converted to single characters. Note that metacharacter expansion is performed before variable substitution.
A variable value can interpolate the values of other variables, using a variable substitution syntax. The general form of a variable reference is ${sectionName:varName?default}.
setAbortOnUndefinedVariable(boolean)
.
If a variable reference specifies a section name, the referenced section must precede the current section. It is not possible to substitute the value of a variable in a section that occurs later in the file.
The section names "system", "env", and "program" are reserved for special "pseudosections."
The "system" pseudosection is used to interpolate values from Java's System.properties class. For instance, ${system:user.home} substitutes the value of the user.home system property (typically, the home directory of the user running curn). Similarly, ${system:user.name} substitutes the user's name.
For example:
[main] installation.directory=${system:user.home?/tmp}/this_package program.directory: ${installation.directory}/foo/programs [search] searchCommand: find ${main:installation.directory} -type f -name '*.class' [display] searchFailedMessage=Search failed, sorry.
The "env" pseudosection is used to interpolate values from the environment. On UNIX systems, for instance, ${env:HOME} substitutes user's home directory (and is, therefore, a synonym for ${system:user.home}. On some versions of Windows, ${env:USERNAME} will substitute the name of the user running curn. Note: On UNIX systems, environment variable names are typically case-sensitive; for instance, ${env:USER} and ${env:user} refer to different environment variables. On Windows systems, environment variable names are typically case-insensitive; ${env:USERNAME} and ${env:username} are equivalent.
The "program" pseudosection is a placeholder for various special variables provided by the Configuration class. Those variables are:
Variable | Description | Examples |
---|---|---|
cwd | the program's current working directory. Thus, ${program:cwd} will substitute the working directory, with the appropriate system-specific file separator. On a Windows system, the file separator character (a backslash) will be doubled, to ensure that it is properly interpreted by the configuration file parsing logic. | |
cwd.url |
the program's current working directory as a file URL,
without the trailing "/". Useful when you need to create a URL
reference to something relative to the current directory. This is
especially useful on Windows, where
produces an invalid URL, with a mixture of backslashes and forward slashes. By contrast,file://${program:cwd}/something.txt always produces a valid URL, regardless of the underlying host operating system.${program:cwd.url}/something.txt |
|
now | the current time, formatted by calling java.util.Date.toString() with the default locale. | |
now delim fmt [delim lang delim country]] |
The current date/time, formatted with the specified java.text.SimpleDateFormat format string. If specified, the given locale and country code will be used; otherwise, the default system locale will be used. lang is a Java language code, such as "en", "fr", etc. country is a 2-letter country code, e.g., "UK", "US", "CA", etc. delim is a user-chosen delimiter that separates the variable name ("now") from the format and the optional locale fields. The delimiter can be anything that doesn't appear in the format string, the variable name, or the locale. Note: SimpleDateFormat requires that literal strings (i.e., strings that should not be processed as part of the format) be enclosed in quotes. For instance: yyyy.MM.dd 'at' hh:mm:ss z Because single quotes are special characters in configuration files, it's important to escape them if you use them inside date formats. So, to include the above string in a configuration file's ${program:now} reference, use the following: ${program:now/yyyy.MM.dd \'at\' hh:mm:ss z} See Suppressing Metacharacter Expansion and Variable Substitution, below, for more details. |
${program:now|yyyy.MM.dd 'at' hh:mm:ss z} ${program:now|yyyy/MM/dd 'at' HH:mm:ss z|en|US} ${program:now|dd MMM, yyyy hh:mm:ss z|fr|FR} |
Notes and caveats:
UnixShellVariableSubstituter
class to do variable
substitution, so it honors all the syntax conventions supported
by that class.
To prevent the parser from interpreting metacharacter sequences, variable substitutions and other special characters, enclose part or all of the value in single quotes.
For example, suppose you want to set variable "prompt" to the literal value "Enter value. To specify a newline, use \n." The following configuration file line will do the trick:
prompt: 'Enter value. To specify a newline, use \n'
Similarly, to set variable "abc" to the literal string "${foo}" suppressing the parser's attempts to expand "${foo}" as a variable reference, you could use:
abc: '${foo}'
To include a literal single quote, you must escape it with a backslash.
Note: It's also possible, though hairy, to escape the special meaning
of special characters via the backslash character. For instance, you can
escape the variable substitution lead-in character, '$', with a
backslash. e.g., "\$". This technique is not recommended, however,
because you have to double-escape any backslash characters that you want
to be preserved literally. For instance, to get "\t", you must specify
"\\\\t". To get a literal backslash, specify "\\\\". (Yes, that's four
backslashes, just to get a single unescaped one.) This double-escaping
is a regrettable side effect of how the configuration file parses
variable values: It makes two separate passes over the value (one for
metacharacter expansion and another for variable expansion). Each of
those passes honors and processes backslash escapes. This problem would
go away if the configuration file parser parsed both metacharacter
sequences and variable substitutions itself, in one pass. It doesn't
currently do that, because I wanted to make use of the existing
XStringBufBase.decodeMetacharacters()
method and the
UnixShellVariableSubstituter
class. In general, you're better off
just sticking with single quotes.
Double quotes can be used to escape the special meaning of white
space, while still permitting metacharacters and variable references to
be expanded. (Metacharacter and variable references are not expanded
between single quotes.) When retrieving a variable's value via
getConfigurationValue(java.lang.String, java.lang.String)
, a program will not be able to tell whether
double quotes were used or not, since getConfigurationValue(java.lang.String, java.lang.String)
returns the "cooked" value as a single string. However, callers can use
the getConfigurationTokens(java.lang.String, java.lang.String)
method to retrieve the parsed tokens
that comprise a configuration value. Double- and single-quoted strings are
returned as individual tokens.
A special include directive permits inline inclusion of another configuration file. The include directive takes two forms:
%include "path" %include "URL"
For example:
%include "/home/bmc/mytools/common.cfg" %include "http://configs.example.com/mytools/common.cfg"
The included file may contain any content that is valid for this parser. It may contain just variable definitions (i.e., the contents of a section, without the section header), or it may contain a complete configuration file, with individual sections. Since Configuration recognizes a variable syntax that is essentially identical to Java's properties file syntax, it's also legal to include a properties file, provided it's included within a valid section.
Note: Attempting to include a file from itself, either directly or indirectly, will cause the parser to throw an exception.
A comment line is a one whose first non-whitespace character is a "#" or a "!". This comment syntax is identical to the one supported by a Java properties file. A blank line is a line containing no content, or one containing only whitespace. Blank lines and comments are ignored.
Constructor and Description |
---|
Configuration()
Construct an empty Configuration object.
|
Modifier and Type | Method and Description |
---|---|
void |
addSection(java.lang.String sectionName)
Add a new section to this configuration data.
|
void |
clear()
Clear this object of all configuration data.
|
boolean |
containsSection(java.lang.String sectionName)
Determine whether this object contains a specified section.
|
boolean |
getAbortOnUndefinedVariable()
Get the value of the flag that controls whether the
Configuration object will abort when it encounters an
undefined variable.
|
java.net.URL |
getConfigurationFileURL()
Get the URL of the configuration file, if available.
|
java.lang.String[] |
getConfigurationTokens(java.lang.String sectionName,
java.lang.String variableName)
Get the value for a variable as a series of tokens.
|
java.lang.String |
getConfigurationValue(java.lang.String sectionName,
java.lang.String variableName)
Get the value for a variable.
|
boolean |
getOptionalBooleanValue(java.lang.String sectionName,
java.lang.String variableName,
boolean defaultValue)
Convenience method to get and convert an optional boolean parameter.
|
int |
getOptionalCardinalValue(java.lang.String sectionName,
java.lang.String variableName,
int defaultValue)
Convenience method to get and convert an optional non-negative integer
parameter.
|
double |
getOptionalDoubleValue(java.lang.String sectionName,
java.lang.String variableName,
double defaultValue)
Convenience method to get and convert an optional floating point
numeric parameter.
|
int |
getOptionalIntegerValue(java.lang.String sectionName,
java.lang.String variableName,
int defaultValue)
Convenience method to get and convert an optional integer parameter.
|
java.lang.String |
getOptionalStringValue(java.lang.String sectionName,
java.lang.String variableName,
java.lang.String defaultValue)
Convenience method to get an optional string value.
|
java.lang.String |
getRawValue(java.lang.String sectionName,
java.lang.String variableName)
Get the raw value (i.e., without any substitutions) for a variable.
|
boolean |
getRequiredBooleanValue(java.lang.String sectionName,
java.lang.String variableName)
Convenience method to get and convert a required boolean parameter.
|
int |
getRequiredCardinalValue(java.lang.String sectionName,
java.lang.String variableName)
Convenience method to get and convert a required integer parameter.
|
double |
getRequiredDoubleValue(java.lang.String sectionName,
java.lang.String variableName)
Convenience method to get and convert a required floating point
numeric parameter.
|
int |
getRequiredIntegerValue(java.lang.String sectionName,
java.lang.String variableName)
Convenience method to get and convert a required integer parameter.
|
java.util.Collection<java.lang.String> |
getSectionNames()
Get the names of the sections in this object, in the order they were
parsed and/or added.
|
java.util.Collection<java.lang.String> |
getSectionNames(java.util.Collection<java.lang.String> collection)
Get the names of the sections in this object, in the order they were
parsed and/or added.
|
java.util.Collection<java.lang.String> |
getVariableNames(java.lang.String sectionName)
Get the names of the all the variables in a section, in the order
they were parsed and/or added.
|
java.util.Collection<java.lang.String> |
getVariableNames(java.lang.String sectionName,
java.util.Collection<java.lang.String> collection)
Get the names of the all the variables in a section, in the order
they were parsed and/or added.
|
java.lang.String |
getVariableValue(java.lang.String varName,
java.lang.Object context)
Get the value associated with a given variable.
|
boolean |
legalVariableCharacter(char c)
Required by the
VariableNameChecker interface, this method
determines whether a character may legally be used in a variable name
or not. |
void |
load(java.io.File file)
Load configuration from a File.
|
void |
load(java.io.File file,
java.lang.String encoding)
Load configuration from a File.
|
void |
load(java.io.InputStream iStream)
Load configuration from an InputStream.
|
void |
load(java.io.InputStream iStream,
java.lang.String encoding)
Load configuration from an InputStream.
|
void |
load(java.lang.String path)
Load configuration from a file specified as a pathname.
|
void |
load(java.lang.String path,
java.lang.String encoding)
Load configuration from a file specified as a pathname.
|
void |
load(java.net.URL url)
Load the configuration from a URL.
|
void |
load(java.net.URL url,
java.lang.String encoding)
Load the configuration from a URL.
|
void |
setAbortOnUndefinedVariable(boolean enable)
Set or clear the flag that controls whether the Configuration
object will abort when it encounters an undefined variable.
|
void |
setVariable(java.lang.String sectionName,
java.lang.String variableName,
java.lang.String value,
boolean expand)
Set a variable's value.
|
void |
write(java.io.PrintStream out)
Writes the configuration data to a PrintStream.
|
void |
write(java.io.PrintWriter out)
Writes the configuration data to a PrintWriter.
|
public Configuration()
addSection()
and
setVariable()
.public void addSection(java.lang.String sectionName) throws SectionExistsException
sectionName
- the name of the new sectionSectionExistsException
- a section by that name already existscontainsSection(java.lang.String)
,
getSectionNames(java.util.Collection<java.lang.String>)
,
setVariable(java.lang.String, java.lang.String, java.lang.String, boolean)
public void clear()
public final boolean containsSection(java.lang.String sectionName)
sectionName
- the section namegetSectionNames(java.util.Collection<java.lang.String>)
,
addSection(java.lang.String)
public java.net.URL getConfigurationFileURL()
public java.util.Collection<java.lang.String> getSectionNames(java.util.Collection<java.lang.String> collection)
collection
- the Collection to which to add the section
names. The names are added in the order they were
parsed and/or added to this object; of course, the
Collection may reorder them.getVariableNames(java.lang.String, java.util.Collection<java.lang.String>)
public java.util.Collection<java.lang.String> getSectionNames()
getVariableNames(java.lang.String, java.util.Collection<java.lang.String>)
public java.util.Collection<java.lang.String> getVariableNames(java.lang.String sectionName, java.util.Collection<java.lang.String> collection) throws NoSuchSectionException
sectionName
- the name of the section to accesscollection
- the Collection to which to add the variable
names. The names are added in the order they were
parsed and/or added to this object; of course, the
Collection may reorder them.NoSuchSectionException
- no such sectiongetSectionNames(java.util.Collection<java.lang.String>)
,
containsSection(java.lang.String)
,
getVariableValue(java.lang.String, java.lang.Object)
public java.util.Collection<java.lang.String> getVariableNames(java.lang.String sectionName) throws NoSuchSectionException
sectionName
- the name of the section to accessNoSuchSectionException
- no such sectiongetSectionNames(java.util.Collection<java.lang.String>)
,
containsSection(java.lang.String)
,
getVariableValue(java.lang.String, java.lang.Object)
public java.lang.String getConfigurationValue(java.lang.String sectionName, java.lang.String variableName) throws NoSuchSectionException, NoSuchVariableException
getConfigurationTokens(java.lang.String, java.lang.String)
method. For example, if the configuration line looks like this:
this method will return the stringfoo: abc "def ghi" jkl
whereasabc def ghi jkl
getConfigurationTokens(java.lang.String, java.lang.String)
will return the following
individual tokens:
Getting the tokens preserves the white space-escaping properties of the double quotes.abc def ghi jkl
sectionName
- the name of the section containing the variablevariableName
- the variable nameNoSuchSectionException
- the named section does not existNoSuchVariableException
- the section has no such variablegetConfigurationTokens(java.lang.String, java.lang.String)
public java.lang.String getRawValue(java.lang.String sectionName, java.lang.String variableName) throws NoSuchSectionException, NoSuchVariableException
sectionName
- the name of the section containing the variablevariableName
- the variable nameNoSuchSectionException
- the named section does not existNoSuchVariableException
- the section has no such variablegetConfigurationValue(java.lang.String, java.lang.String)
public java.lang.String[] getConfigurationTokens(java.lang.String sectionName, java.lang.String variableName) throws ConfigurationException
getConfigurationValue(java.lang.String, java.lang.String)
method, it's impossible to
tell where quoted substrings appeared. For example, if the
configuration line looks like this:
foo: abc "def ghi" jkl mno
getConfigurationValue(java.lang.String, java.lang.String)
will return the string
whereas this method will return the following individual tokens:abc def ghi jkl mno
abc def ghi jkl mno
Getting the tokens preserves the white space-escaping properties of the double quotes.
sectionName
- the name of the section containing the variablevariableName
- the variable nameConfigurationException
- some configuration errorgetConfigurationValue(java.lang.String, java.lang.String)
public int getOptionalIntegerValue(java.lang.String sectionName, java.lang.String variableName, int defaultValue) throws ConfigurationException
sectionName
- section namevariableName
- variable namedefaultValue
- default value if not foundConfigurationException
- some configuration errorgetOptionalCardinalValue(java.lang.String, java.lang.String, int)
,
getRequiredIntegerValue(java.lang.String, java.lang.String)
public int getRequiredIntegerValue(java.lang.String sectionName, java.lang.String variableName) throws ConfigurationException
sectionName
- section namevariableName
- variable nameConfigurationException
- some configuration error, including bad
numeric valuegetRequiredCardinalValue(java.lang.String, java.lang.String)
,
getOptionalIntegerValue(java.lang.String, java.lang.String, int)
public int getOptionalCardinalValue(java.lang.String sectionName, java.lang.String variableName, int defaultValue) throws ConfigurationException
sectionName
- section namevariableName
- variable namedefaultValue
- default value if not found. Must be non-negative.ConfigurationException
- bad numeric value, or other config errorgetOptionalIntegerValue(java.lang.String, java.lang.String, int)
,
getRequiredCardinalValue(java.lang.String, java.lang.String)
public int getRequiredCardinalValue(java.lang.String sectionName, java.lang.String variableName) throws ConfigurationException
sectionName
- section namevariableName
- variable nameConfigurationException
- bad numeric value, or other config errorgetOptionalCardinalValue(java.lang.String, java.lang.String, int)
,
getRequiredIntegerValue(java.lang.String, java.lang.String)
public double getOptionalDoubleValue(java.lang.String sectionName, java.lang.String variableName, double defaultValue) throws ConfigurationException
sectionName
- section namevariableName
- variable namedefaultValue
- default value if not foundConfigurationException
- bad numeric value or other config errorpublic double getRequiredDoubleValue(java.lang.String sectionName, java.lang.String variableName) throws ConfigurationException
sectionName
- section namevariableName
- variable nameConfigurationException
- bad numeric value or other config errorpublic boolean getOptionalBooleanValue(java.lang.String sectionName, java.lang.String variableName, boolean defaultValue) throws ConfigurationException
sectionName
- section namevariableName
- variable namedefaultValue
- default value if not foundConfigurationException
- bad numeric value, or other config errorpublic boolean getRequiredBooleanValue(java.lang.String sectionName, java.lang.String variableName) throws ConfigurationException
sectionName
- section namevariableName
- variable nameConfigurationException
- bad numeric value, or other config errorpublic java.lang.String getOptionalStringValue(java.lang.String sectionName, java.lang.String variableName, java.lang.String defaultValue) throws ConfigurationException
sectionName
- section namevariableName
- variable namedefaultValue
- default value if not foundConfigurationException
- bad numeric value or other config errorpublic java.lang.String getVariableValue(java.lang.String varName, java.lang.Object context) throws VariableSubstitutionException
VariableDereferencer
interface, this method is used during
parsing to handle variable substitutions (but also potentially
useful by other applications). See this class's documentation for
details on variable references.getVariableValue
in interface VariableDereferencer
varName
- The name of the variable for which the value is
desired.context
- a context object, passed through from the caller
to the dereferencer, or null if there isn't one.
For this class, the context object is a
SubstitutionContext variable.VariableSubstitutionException
- variable references itselfpublic boolean legalVariableCharacter(char c)
VariableNameChecker
interface, this method
determines whether a character may legally be used in a variable name
or not.legalVariableCharacter
in interface VariableNameChecker
c
- The character to testVariableSubstituter.substitute(java.lang.String, org.clapper.util.text.VariableDereferencer, java.lang.Object)
public void load(java.io.File file) throws java.io.IOException, ConfigurationException
file
- the filejava.io.IOException
- read errorConfigurationException
- parse errorpublic void load(java.io.File file, java.lang.String encoding) throws java.io.IOException, ConfigurationException
file
- the fileencoding
- the encoding to use, or null for the defaultjava.io.IOException
- read errorConfigurationException
- parse errorpublic void load(java.lang.String path) throws java.io.IOException, ConfigurationException
path
- the pathjava.io.FileNotFoundException
- specified file doesn't existjava.io.IOException
- can't open or read fileConfigurationException
- error in configuration datapublic void load(java.lang.String path, java.lang.String encoding) throws java.io.IOException, ConfigurationException
path
- the pathencoding
- the encoding to use, or null for the defaultjava.io.FileNotFoundException
- specified file doesn't existjava.io.IOException
- can't open or read fileConfigurationException
- error in configuration datajava.io.UnsupportedEncodingException
- bad encodingpublic void load(java.net.URL url) throws java.io.IOException, ConfigurationException
url
- the URLjava.io.IOException
- on I/O errorConfigurationException
- on configuration errorpublic void load(java.net.URL url, java.lang.String encoding) throws java.io.IOException, ConfigurationException
url
- the URLencoding
- the encoding, if known, or nulljava.io.IOException
- on I/O errorConfigurationException
- on configuration errorpublic void load(java.io.InputStream iStream) throws java.io.IOException, ConfigurationException
iStream
- the InputStreamjava.io.IOException
- can't open or read URLConfigurationException
- error in configuration datapublic void load(java.io.InputStream iStream, java.lang.String encoding) throws java.io.IOException, ConfigurationException
iStream
- the InputStreamencoding
- the encoding to use, or null for the defaultjava.io.IOException
- can't open or read URLConfigurationException
- error in configuration datajava.io.UnsupportedEncodingException
- bad encodingpublic void setVariable(java.lang.String sectionName, java.lang.String variableName, java.lang.String value, boolean expand) throws NoSuchSectionException, VariableSubstitutionException
sectionName
- name of existing section to contain the variablevariableName
- name of variable to setvalue
- variable's valueexpand
- true to expand metacharacters and variable
references in the value, false to leave
the value untouched.NoSuchSectionException
- section does not existVariableSubstitutionException
- variable substitution errorpublic boolean getAbortOnUndefinedVariable()
ConfigurationException
.setAbortOnUndefinedVariable(boolean)
public void setAbortOnUndefinedVariable(boolean enable)
ConfigurationException
. The flag defaults to true.enable
- true to enable the "abort on undefined variable"
flag, false to disable it.getAbortOnUndefinedVariable()
public void write(java.io.PrintWriter out) throws ConfigurationException
out
- where to write the configuration dataConfigurationException
- on errorXStringBufBase.encodeMetacharacters()
public void write(java.io.PrintStream out) throws ConfigurationException
out
- where to write the configuration dataConfigurationException
- on errorXStringBufBase.encodeMetacharacters()