Class PropertiesConfiguration
- java.lang.Object
-
- org.apache.commons.configuration2.event.BaseEventSource
-
- org.apache.commons.configuration2.AbstractConfiguration
-
- org.apache.commons.configuration2.BaseConfiguration
-
- org.apache.commons.configuration2.PropertiesConfiguration
-
- All Implemented Interfaces:
java.lang.Cloneable
,Configuration
,EventSource
,FileBasedConfiguration
,ImmutableConfiguration
,FileBased
,FileLocatorAware
,SynchronizerSupport
public class PropertiesConfiguration extends BaseConfiguration implements FileBasedConfiguration, FileLocatorAware
This is the "classic" Properties loader which loads the values from a single or multiple files (which can be chained with "include =". All given path references are either absolute or relative to the file name supplied in the constructor.In this class, empty PropertyConfigurations can be built, properties added and later saved. include statements are (obviously) not supported if you don't construct a PropertyConfiguration from a file.
The properties file syntax is explained here, basically it follows the syntax of the stream parsed by
Properties.load(java.io.Reader)
and adds several useful extensions:- Each property has the syntax
key <separator> value
. The separators accepted are'='
,':'
and any white space character. Examples:key1 = value1 key2 : value2 key3 value3
- The key may use any character, separators must be escaped:
key\:foo = bar
- value may be separated on different lines if a backslash is placed at the end of the line that continues below.
- The list delimiter facilities provided by
AbstractConfiguration
are supported, too. If an appropriateListDelimiterHandler
is set (for instance aD efaultListDelimiterHandler
object configured with a comma as delimiter character), value can contain value delimiters and will then be interpreted as a list of tokens. So the following property definitionkey = This property, has multiple, values
will result in a property with three values. You can change the handling of delimiters using theAbstractConfiguration.setListDelimiterHandler(ListDelimiterHandler)
method. Per default, list splitting is disabled. - Commas in each token are escaped placing a backslash right before the comma.
- If a key is used more than once, the values are appended like if they were on the same line separated with
commas. Note: When the configuration file is written back to disk the associated
PropertiesConfigurationLayout
object (see below) will try to preserve as much of the original format as possible, i.e. properties with multiple values defined on a single line will also be written back on a single line, and multiple occurrences of a single key will be written on multiple lines. If theaddProperty()
method was called multiple times for adding multiple values to a property, these properties will per default be written on multiple lines in the output file, too. Some options of thePropertiesConfigurationLayout
class have influence on that behavior. - Blank lines and lines starting with character '#' or '!' are skipped.
- If a property is named "include" (or whatever is defined by setInclude() and getInclude() and the value of that property is the full path to a file on disk, that file will be included into the configuration. You can also pull in files relative to the parent configuration file. So if you have something like the following: include = additional.properties Then "additional.properties" is expected to be in the same directory as the parent configuration file. The properties in the included file are added to the parent configuration, they do not replace existing properties with the same key.
- You can define custom error handling for the special key
"include"
by usingsetIncludeListener(ConfigurationConsumer)
.
Here is an example of a valid extended 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? # properties can reference other properties base.prop = /base first.prop = ${base.prop}/first second.prop = ${first.prop}/second
A
PropertiesConfiguration
object is associated with an instance of thePropertiesConfigurationLayout
class, which is responsible for storing the layout of the parsed properties file (i.e. empty lines, comments, and such things). ThegetLayout()
method can be used to obtain this layout object. WithsetLayout()
a new layout object can be set. This should be done before a properties file was loaded.Like other
Configuration
implementations, this class uses aSynchronizer
object to control concurrent access. By choosing a suitable implementation of theSynchronizer
interface, an instance can be made thread-safe or not. Note that access to most of the properties typically set through a builder is not protected by theSynchronizer
. The intended usage is that these properties are set once at construction time through the builder and after that remain constant. If you wish to change such properties during life time of an instance, you have to use thelock()
andunlock()
methods manually to ensure that other threads see your changes.As this class extends
AbstractConfiguration
, all basic features like variable interpolation, list handling, or data type conversions are available as well. This is described in the chapter Basic features and AbstractConfiguration of the user's guide. There is also a separate chapter dealing with Properties files in special.- See Also:
Properties.load(java.io.Reader)
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static class
PropertiesConfiguration.DefaultIOFactory
A default implementation of theIOFactory
interface.static interface
PropertiesConfiguration.IOFactory
Definition of an interface that allows customization of read and write operations.static class
PropertiesConfiguration.JupIOFactory
An alternativePropertiesConfiguration.IOFactory
that tries to mimic the behavior ofProperties
(Jup) more closely.static class
PropertiesConfiguration.JupPropertiesReader
APropertiesConfiguration.PropertiesReader
that tries to mimic the behavior ofProperties
.static class
PropertiesConfiguration.JupPropertiesWriter
APropertiesConfiguration.PropertiesWriter
that tries to mimic the behavior ofProperties
.static class
PropertiesConfiguration.PropertiesReader
This class is used to read properties lines.static class
PropertiesConfiguration.PropertiesWriter
This class is used to write properties lines.
-
Field Summary
Fields Modifier and Type Field Description (package private) static java.lang.String
COMMENT_CHARS
Constant for the supported comment characters.static java.lang.String
DEFAULT_ENCODING
The default encoding (ISO-8859-1 as specified by https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html)static ConfigurationConsumer<ConfigurationException>
DEFAULT_INCLUDE_LISTENER
Defines default error handling for the special"include"
key by throwing the given exception.(package private) static java.lang.String
DEFAULT_SEPARATOR
Constant for the default properties separator.private static int
HEX_RADIX
Constant for the radix of hex numbers.private static java.lang.String
include
This is the name of the property that can point to other properties file for including other properties files.private ConfigurationConsumer<ConfigurationException>
includeListener
The include listener for the special"include"
key.private static java.lang.String
includeOptional
This is the name of the property that can point to other properties file for including other properties files.private boolean
includesAllowed
Allow file inclusion or notprivate PropertiesConfiguration.IOFactory
ioFactory
The IOFactory for creating readers and writers.private PropertiesConfigurationLayout
layout
Stores the layout object.private static java.lang.String
LINE_SEPARATOR
Constant for the platform specific line separator.private FileLocator
locator
The currentFileLocator
.static ConfigurationConsumer<ConfigurationException>
NOOP_INCLUDE_LISTENER
Defines error handling as a noop for the special"include"
key.private static char[]
SEPARATORS
The list of possible key/value separatorsprivate static java.lang.String
UNESCAPE_CHARACTERS
A string with special characters that need to be unescaped when reading a properties file.private static int
UNICODE_LEN
Constant for the length of a unicode literal.private static char[]
WHITE_SPACE
The white space characters used as key/value separators.
-
Constructor Summary
Constructors Constructor Description PropertiesConfiguration()
Creates an empty PropertyConfiguration object which can be used to synthesize a new Properties file by adding values and then saving().
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description java.lang.Object
clone()
Creates a copy of this object.private static int
countTrailingBS(java.lang.String line)
Returns the number of trailing backslashes.private PropertiesConfigurationLayout
createLayout()
Creates a standard layout object.java.lang.String
getFooter()
Gets the footer comment.java.lang.String
getHeader()
Gets the comment header.static java.lang.String
getInclude()
Gets the property value for including other properties files.ConfigurationConsumer<ConfigurationException>
getIncludeListener()
Gets the current include listener, never null.static java.lang.String
getIncludeOptional()
Gets the property value for including other properties files.PropertiesConfiguration.IOFactory
getIOFactory()
Gets theIOFactory
to be used for creating readers and writers when loading or saving this configuration.PropertiesConfigurationLayout
getLayout()
Gets the associated layout object.void
initFileLocator(FileLocator locator)
Stores the currentFileLocator
for a following IO operation.private void
installLayout(PropertiesConfigurationLayout layout)
Installs a layout object.(package private) static boolean
isCommentLine(java.lang.String line)
Tests whether a line is a comment, i.e.boolean
isIncludesAllowed()
Reports the status of file inclusion.private void
loadIncludeFile(java.lang.String fileName, boolean optional, java.util.Deque<java.net.URL> seenStack)
Helper method for loading an included properties file.private java.net.URL
locateIncludeFile(java.lang.String basePath, java.lang.String fileName)
Tries to obtain the URL of an include file using the specified (optional) base path and file name.private static boolean
needsUnescape(char ch)
Checks whether the specified character needs to be unescaped.(package private) boolean
propertyLoaded(java.lang.String key, java.lang.String value, java.util.Deque<java.net.URL> seenStack)
This method is invoked by the associatedPropertiesConfigurationLayout
object for each property definition detected in the parsed properties file.void
read(java.io.Reader in)
Reads the content of this object from the given reader.void
setFooter(java.lang.String footer)
Sets the footer comment.void
setHeader(java.lang.String header)
Sets the comment header.static void
setInclude(java.lang.String inc)
Sets the property value for including other properties files.void
setIncludeListener(ConfigurationConsumer<ConfigurationException> includeListener)
Sets the current include listener, may not be null.static void
setIncludeOptional(java.lang.String inc)
Sets the property value for including other properties files.void
setIncludesAllowed(boolean includesAllowed)
Controls whether additional files can be loaded by theinclude = <xxx>
statement or not.void
setIOFactory(PropertiesConfiguration.IOFactory ioFactory)
Sets theIOFactory
to be used for creating readers and writers when loading or saving this configuration.void
setLayout(PropertiesConfigurationLayout layout)
Sets the associated layout object.protected static java.lang.String
unescapeJava(java.lang.String str)
Unescapes any Java literals found in theString
to aWriter
.protected static java.lang.String
unescapeJava(java.lang.String str, boolean jupCompatible)
Unescapes Java literals found in theString
to aWriter
.void
write(java.io.Writer out)
Writes the content of this object to the given writer.-
Methods inherited from class org.apache.commons.configuration2.BaseConfiguration
addPropertyDirect, clearInternal, clearPropertyDirect, containsKeyInternal, containsValueInternal, getKeysInternal, getPropertyInternal, isEmptyInternal, sizeInternal
-
Methods inherited from class org.apache.commons.configuration2.AbstractConfiguration
addErrorLogListener, addProperty, addPropertyInternal, append, beginRead, beginWrite, clear, clearProperty, cloneInterpolator, contains, containsKey, containsValue, copy, endRead, endWrite, get, get, getArray, getArray, getBigDecimal, getBigDecimal, getBigInteger, getBigInteger, getBoolean, getBoolean, getBoolean, getByte, getByte, getByte, getCollection, getCollection, getConfigurationDecoder, getConversionHandler, getDouble, getDouble, getDouble, getDuration, getDuration, getEncodedString, getEncodedString, getFloat, getFloat, getFloat, getInt, getInt, getInteger, getInterpolator, getKeys, getKeys, getKeys, getKeysInternal, getKeysInternal, getList, getList, getList, getList, getListDelimiterHandler, getLogger, getLong, getLong, getLong, getProperties, getProperties, getProperty, getShort, getShort, getShort, getString, getString, getStringArray, getSynchronizer, immutableSubset, initLogger, installInterpolator, interpolate, interpolate, interpolatedConfiguration, isEmpty, isScalarValue, isThrowExceptionOnMissing, lock, setConfigurationDecoder, setConversionHandler, setDefaultLookups, setInterpolator, setListDelimiterHandler, setLogger, setParentInterpolator, setPrefixLookups, setProperty, setPropertyInternal, setSynchronizer, setThrowExceptionOnMissing, size, subset, unlock
-
Methods inherited from class org.apache.commons.configuration2.event.BaseEventSource
addEventListener, clearErrorListeners, clearEventListeners, copyEventListeners, createErrorEvent, createEvent, fireError, fireEvent, getEventListenerRegistrations, getEventListeners, isDetailEvents, removeEventListener, setDetailEvents
-
Methods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
-
Methods inherited from interface org.apache.commons.configuration2.Configuration
addProperty, clear, clearProperty, getInterpolator, installInterpolator, setInterpolator, setProperty, subset
-
Methods inherited from interface org.apache.commons.configuration2.ImmutableConfiguration
containsKey, containsValue, get, get, getArray, getArray, getBigDecimal, getBigDecimal, getBigInteger, getBigInteger, getBoolean, getBoolean, getBoolean, getByte, getByte, getByte, getCollection, getCollection, getDouble, getDouble, getDouble, getDuration, getDuration, getEncodedString, getEncodedString, getEnum, getEnum, getFloat, getFloat, getFloat, getInt, getInt, getInteger, getKeys, getKeys, getKeys, getList, getList, getList, getList, getLong, getLong, getLong, getProperties, getProperty, getShort, getShort, getShort, getString, getString, getStringArray, immutableSubset, isEmpty, size
-
Methods inherited from interface org.apache.commons.configuration2.sync.SynchronizerSupport
getSynchronizer, lock, setSynchronizer, unlock
-
-
-
-
Field Detail
-
DEFAULT_INCLUDE_LISTENER
public static final ConfigurationConsumer<ConfigurationException> DEFAULT_INCLUDE_LISTENER
Defines default error handling for the special"include"
key by throwing the given exception.- Since:
- 2.6
-
NOOP_INCLUDE_LISTENER
public static final ConfigurationConsumer<ConfigurationException> NOOP_INCLUDE_LISTENER
Defines error handling as a noop for the special"include"
key.- Since:
- 2.6
-
DEFAULT_ENCODING
public static final java.lang.String DEFAULT_ENCODING
The default encoding (ISO-8859-1 as specified by https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html)
-
COMMENT_CHARS
static final java.lang.String COMMENT_CHARS
Constant for the supported comment characters.- See Also:
- Constant Field Values
-
DEFAULT_SEPARATOR
static final java.lang.String DEFAULT_SEPARATOR
Constant for the default properties separator.- See Also:
- Constant Field Values
-
UNESCAPE_CHARACTERS
private static final java.lang.String UNESCAPE_CHARACTERS
A string with special characters that need to be unescaped when reading a properties file.Properties
escapes these characters when writing out a properties file.- See Also:
- Constant Field Values
-
include
private static java.lang.String include
This is the name of the property that can point to other properties file for including other properties files.
-
includeOptional
private static java.lang.String includeOptional
This is the name of the property that can point to other properties file for including other properties files.If the file is absent, processing continues normally.
-
SEPARATORS
private static final char[] SEPARATORS
The list of possible key/value separators
-
WHITE_SPACE
private static final char[] WHITE_SPACE
The white space characters used as key/value separators.
-
LINE_SEPARATOR
private static final java.lang.String LINE_SEPARATOR
Constant for the platform specific line separator.
-
HEX_RADIX
private static final int HEX_RADIX
Constant for the radix of hex numbers.- See Also:
- Constant Field Values
-
UNICODE_LEN
private static final int UNICODE_LEN
Constant for the length of a unicode literal.- See Also:
- Constant Field Values
-
layout
private PropertiesConfigurationLayout layout
Stores the layout object.
-
includeListener
private ConfigurationConsumer<ConfigurationException> includeListener
The include listener for the special"include"
key.
-
ioFactory
private PropertiesConfiguration.IOFactory ioFactory
The IOFactory for creating readers and writers.
-
locator
private FileLocator locator
The currentFileLocator
.
-
includesAllowed
private boolean includesAllowed
Allow file inclusion or not
-
-
Method Detail
-
countTrailingBS
private static int countTrailingBS(java.lang.String line)
Returns the number of trailing backslashes. This is sometimes needed for the correct handling of escape characters.- Parameters:
line
- the string to investigate- Returns:
- the number of trailing backslashes
-
getInclude
public static java.lang.String getInclude()
Gets the property value for including other properties files. By default it is "include".- Returns:
- A String.
-
getIncludeOptional
public static java.lang.String getIncludeOptional()
Gets the property value for including other properties files. By default it is "includeoptional".If the file is absent, processing continues normally.
- Returns:
- A String.
- Since:
- 2.5
-
isCommentLine
static boolean isCommentLine(java.lang.String line)
Tests whether a line is a comment, i.e. whether it starts with a comment character.- Parameters:
line
- the line- Returns:
- a flag if this is a comment line
- Since:
- 1.3
-
needsUnescape
private static boolean needsUnescape(char ch)
Checks whether the specified character needs to be unescaped. This method is called when during reading a property file an escape character ('\') is detected. If the character following the escape character is recognized as a special character which is escaped per default in a Java properties file, it has to be unescaped.- Parameters:
ch
- the character in question- Returns:
- a flag whether this character has to be unescaped
-
setInclude
public static void setInclude(java.lang.String inc)
Sets the property value for including other properties files. By default it is "include".- Parameters:
inc
- A String.
-
setIncludeOptional
public static void setIncludeOptional(java.lang.String inc)
Sets the property value for including other properties files. By default it is "include".If the file is absent, processing continues normally.
- Parameters:
inc
- A String.- Since:
- 2.5
-
unescapeJava
protected static java.lang.String unescapeJava(java.lang.String str)
Unescapes any Java literals found in the
This is a slightly modified version of the StringEscapeUtils.unescapeJava() function in commons-lang that doesn't drop escaped separators (i.e '\,').String
to aWriter
.- Parameters:
str
- theString
to unescape, may be null- Returns:
- the processed string
- Throws:
java.lang.IllegalArgumentException
- if the Writer isnull
-
unescapeJava
protected static java.lang.String unescapeJava(java.lang.String str, boolean jupCompatible)
Unescapes Java literals found in theString
to aWriter
.When the parameter
jupCompatible
isfalse
, the classic behavior is used (seeunescapeJava(String)
). When it'strue
a slightly different behavior that's compatible withProperties
is used (seePropertiesConfiguration.JupIOFactory
).- Parameters:
str
- theString
to unescape, may be nulljupCompatible
- whether unescaping is compatible withProperties
; otherwise the classic behavior is used- Returns:
- the processed string
- Throws:
java.lang.IllegalArgumentException
- if the Writer isnull
-
clone
public java.lang.Object clone()
Creates a copy of this object.- Overrides:
clone
in classBaseConfiguration
- Returns:
- the copy
-
createLayout
private PropertiesConfigurationLayout createLayout()
Creates a standard layout object. This configuration is initialized with such a standard layout.- Returns:
- the newly created layout object
-
getFooter
public java.lang.String getFooter()
Gets the footer comment. This is a comment at the very end of the file.- Returns:
- the footer comment
- Since:
- 2.0
-
getHeader
public java.lang.String getHeader()
Gets the comment header.- Returns:
- the comment header
- Since:
- 1.1
-
getIncludeListener
public ConfigurationConsumer<ConfigurationException> getIncludeListener()
Gets the current include listener, never null.- Returns:
- the current include listener, never null.
- Since:
- 2.6
-
getIOFactory
public PropertiesConfiguration.IOFactory getIOFactory()
Gets theIOFactory
to be used for creating readers and writers when loading or saving this configuration.- Returns:
- the
IOFactory
- Since:
- 1.7
-
getLayout
public PropertiesConfigurationLayout getLayout()
Gets the associated layout object.- Returns:
- the associated layout object
- Since:
- 1.3
-
initFileLocator
public void initFileLocator(FileLocator locator)
Stores the currentFileLocator
for a following IO operation. TheFileLocator
is needed to resolve include files with relative file names.- Specified by:
initFileLocator
in interfaceFileLocatorAware
- Parameters:
locator
- the currentFileLocator
- Since:
- 2.0
-
installLayout
private void installLayout(PropertiesConfigurationLayout layout)
Installs a layout object. It has to be ensured that the layout is registered as change listener at this configuration. If there is already a layout object installed, it has to be removed properly.- Parameters:
layout
- the layout object to be installed
-
isIncludesAllowed
public boolean isIncludesAllowed()
Reports the status of file inclusion.- Returns:
- True if include files are loaded.
-
loadIncludeFile
private void loadIncludeFile(java.lang.String fileName, boolean optional, java.util.Deque<java.net.URL> seenStack) throws ConfigurationException
Helper method for loading an included properties file. This method is called byload()
when aninclude
property is encountered. It tries to resolve relative file names based on the current base path. If this fails, a resolution based on the location of this properties file is tried.- Parameters:
fileName
- the name of the file to loadoptional
- whether or not thefileName
is optionalseenStack
- Stack of seen include URLs- Throws:
ConfigurationException
- if loading fails
-
locateIncludeFile
private java.net.URL locateIncludeFile(java.lang.String basePath, java.lang.String fileName)
Tries to obtain the URL of an include file using the specified (optional) base path and file name.- Parameters:
basePath
- the base pathfileName
- the file name- Returns:
- the URL of the include file or null if it cannot be resolved
-
propertyLoaded
boolean propertyLoaded(java.lang.String key, java.lang.String value, java.util.Deque<java.net.URL> seenStack) throws ConfigurationException
This method is invoked by the associatedPropertiesConfigurationLayout
object for each property definition detected in the parsed properties file. Its task is to check whether this is a special property definition (e.g. theinclude
property). If not, the property must be added to this configuration. The return value indicates whether the property should be treated as a normal property. If it is false, the layout object will ignore this property.- Parameters:
key
- the property keyvalue
- the property valueseenStack
- the stack of seen include URLs- Returns:
- a flag whether this is a normal property
- Throws:
ConfigurationException
- if an error occurs- Since:
- 1.3
-
read
public void read(java.io.Reader in) throws ConfigurationException, java.io.IOException
Reads the content of this object from the given reader. Client code should not call this method directly, but use aFileHandler
for reading data. This implementation delegates to the associated layout object which does the actual loading. Note that this method does not do any synchronization. This lies in the responsibility of the caller. (Typically, the caller is aFileHandler
object which takes care for proper synchronization.)- Specified by:
read
in interfaceFileBased
- Parameters:
in
- the reader- Throws:
ConfigurationException
- if a non-I/O related problem occurs, e.g. the data read does not have the expected formatjava.io.IOException
- if an I/O error occurs.- Since:
- 2.0
-
setFooter
public void setFooter(java.lang.String footer)
Sets the footer comment. If set, this comment is written after all properties at the end of the file.- Parameters:
footer
- the footer comment- Since:
- 2.0
-
setHeader
public void setHeader(java.lang.String header)
Sets the comment header.- Parameters:
header
- the header to use- Since:
- 1.1
-
setIncludeListener
public void setIncludeListener(ConfigurationConsumer<ConfigurationException> includeListener)
Sets the current include listener, may not be null.- Parameters:
includeListener
- the current include listener, may not be null.- Throws:
java.lang.IllegalArgumentException
- if theincludeListener
is null.- Since:
- 2.6
-
setIncludesAllowed
public void setIncludesAllowed(boolean includesAllowed)
Controls whether additional files can be loaded by theinclude = <xxx>
statement or not. This is true per default.- Parameters:
includesAllowed
- True if Includes are allowed.
-
setIOFactory
public void setIOFactory(PropertiesConfiguration.IOFactory ioFactory)
Sets theIOFactory
to be used for creating readers and writers when loading or saving this configuration. Using this method a client can customize the reader and writer classes used by the load and save operations. Note that this method must be called before invoking one of theload()
andsave()
methods. Especially, if you want to use a customIOFactory
for changing thePropertiesReader
, you cannot load the configuration data in the constructor.- Parameters:
ioFactory
- the newIOFactory
(must not be null)- Throws:
java.lang.IllegalArgumentException
- if theIOFactory
is null- Since:
- 1.7
-
setLayout
public void setLayout(PropertiesConfigurationLayout layout)
Sets the associated layout object.- Parameters:
layout
- the new layout object; can be null, then a new layout object will be created- Since:
- 1.3
-
write
public void write(java.io.Writer out) throws ConfigurationException, java.io.IOException
Writes the content of this object to the given writer. Client code should not call this method directly, but use aFileHandler
for writing data. This implementation delegates to the associated layout object which does the actual saving. Note that, analogous toread(Reader)
, this method does not do any synchronization.- Specified by:
write
in interfaceFileBased
- Parameters:
out
- the writer- Throws:
ConfigurationException
- if a non-I/O related problem occurs, e.g. the data read does not have the expected formatjava.io.IOException
- if an I/O error occurs.- Since:
- 2.0
-
-