blob: 3fb79fac5d1d1fd490ae6e932e52f5470d0342b6 [file] [log] [blame]
/*
* 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.openjpa.lib.conf;
import java.awt.Image;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyDescriptor;
import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.log.LogFactory;
import org.apache.openjpa.lib.log.LogFactoryImpl;
import org.apache.openjpa.lib.log.NoneLogFactory;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.MultiClassLoader;
import org.apache.openjpa.lib.util.ParseException;
import org.apache.openjpa.lib.util.Services;
import org.apache.openjpa.lib.util.StringDistance;
import serp.util.Strings;
/**
* Default implementation of the {@link Configuration} interface.
* Subclasses can choose to obtain configuration
* information from JNDI, Properties, a Bean-builder, etc. This class
* provides base configuration functionality, including serialization,
* the <code>equals</code> and <code>hashCode</code> contracts, and default
* property loading.
* Property descriptors for {@link Value} instances are constructed from
* the {@link Localizer} for the package of the configuration class. The
* following localized strings will be used for describing a value, where
* <em>name</em> is the last token of the value's property string:
* <ul>
* <li><em>name</em>-name: The name that will be displayed for the
* option in a user interface; required.</li>
* <li><em>name</em>-desc: A brief description of the option; required.</li>
* <li><em>name</em>-type: The type or category name for this option;
* required.</li>
* <li><em>name</em>-expert: True if this is an expert option, false
* otherwise; defaults to false.</li>
* <li><em>name</em>-values: Set of expected or common values, excluding
* alias keys; optional.</li>
* <li><em>name</em>-interface: The class name of an interface whose
* discoverable implementations should be included in the set of expected
* or common values; optional.</li>
* <li><em>name</em>-cat: The hierarchical category for the property
* name, separated by ".".
* <li><em>name</em>-displayorder: The order in which the property should
* be displayer.</li>
* </ul>
*
* @author Abe White
*/
public class ConfigurationImpl
implements Configuration, Externalizable, ValueListener {
private static final String SEP = J2DoPrivHelper.getLineSeparator();
private static final Localizer _loc = Localizer.forPackage
(ConfigurationImpl.class);
public ObjectValue logFactoryPlugin;
public StringValue id;
private String _product = null;
private boolean _readOnly = false;
private Map _props = null;
private boolean _globals = false;
private String _auto = null;
private final List _vals = new ArrayList();
// property listener helper
private PropertyChangeSupport _changeSupport = null;
// cache descriptors
private PropertyDescriptor[] _pds = null;
private MethodDescriptor[] _mds = null;
/**
* Default constructor. Attempts to load default properties through
* system's configured {@link ProductDerivation}s.
*/
public ConfigurationImpl() {
this(true);
}
/**
* Constructor.
*
* @param loadGlobals whether to attempt to load the global properties
*/
public ConfigurationImpl(boolean loadGlobals) {
setProductName("openjpa");
logFactoryPlugin = addPlugin("Log", true);
String[] aliases = new String[]{
"true", LogFactoryImpl.class.getName(),
"openjpa", LogFactoryImpl.class.getName(),
"commons", "org.apache.openjpa.lib.log.CommonsLogFactory",
"log4j", "org.apache.openjpa.lib.log.Log4JLogFactory",
"none", NoneLogFactory.class.getName(),
"false", NoneLogFactory.class.getName(),
};
logFactoryPlugin.setAliases(aliases);
logFactoryPlugin.setDefault(aliases[0]);
logFactoryPlugin.setString(aliases[0]);
logFactoryPlugin.setInstantiatingGetter("getLogFactory");
id = addString("Id");
if (loadGlobals)
loadGlobals();
}
/**
* Automatically load global values from the system's
* {@link ProductDerivation}s, and from System properties.
*/
public boolean loadGlobals() {
MultiClassLoader loader = (MultiClassLoader) AccessController
.doPrivileged(J2DoPrivHelper.newMultiClassLoaderAction());
loader.addClassLoader((ClassLoader) AccessController.doPrivileged(
J2DoPrivHelper.getContextClassLoaderAction()));
loader.addClassLoader(getClass().getClassLoader());
ConfigurationProvider provider = ProductDerivations.loadGlobals(loader);
if (provider != null)
provider.setInto(this);
// let system properties override other globals
try {
fromProperties(new HashMap(
(Properties) AccessController.doPrivileged(
J2DoPrivHelper.getPropertiesAction())));
} catch (SecurityException se) {
// security manager might disallow
}
_globals = true;
if (provider == null) {
Log log = getConfigurationLog();
if (log.isTraceEnabled())
log.trace(_loc.get("no-default-providers"));
return false;
}
return true;
}
public String getProductName() {
return _product;
}
public void setProductName(String name) {
_product = name;
}
public LogFactory getLogFactory() {
if (logFactoryPlugin.get() == null)
logFactoryPlugin.instantiate(LogFactory.class, this);
return (LogFactory) logFactoryPlugin.get();
}
public void setLogFactory(LogFactory logFactory) {
assertNotReadOnly();
logFactoryPlugin.set(logFactory);
}
public String getLog() {
return logFactoryPlugin.getString();
}
public void setLog(String log) {
logFactoryPlugin.setString(log);
}
public Log getLog(String category) {
return getLogFactory().getLog(category);
}
public String getId() {
return id.get();
}
public void setId(String id) {
assertNotReadOnly();
this.id.set(id);
}
/**
* Returns the logging channel <code>openjpa.Runtime</code> by default.
*/
public Log getConfigurationLog() {
return getLog("openjpa.Runtime");
}
public Value[] getValues() {
return (Value[]) _vals.toArray(new Value[_vals.size()]);
}
public Value getValue(String property) {
if (property == null)
return null;
// search backwards so that custom values added after construction
// are found quickly, since this will be the std way of accessing them
Value val;
for (int i = _vals.size() - 1; i >= 0; i--) {
val = (Value) _vals.get(i);
if (val.getProperty().equals(property))
return val;
}
return null;
}
public void setReadOnly(boolean readOnly) {
_readOnly = readOnly;
}
public void instantiateAll() {
StringWriter errs = null;
PrintWriter stack = null;
Value val;
String getterName;
Method getter;
Object getterTarget;
for (int i = 0; i < _vals.size(); i++) {
val = (Value) _vals.get(i);
getterName = val.getInstantiatingGetter();
if (getterName == null)
continue;
getterTarget = this;
if (getterName.startsWith("this.")) {
getterName = getterName.substring("this.".length());
getterTarget = val;
}
try {
getter = getterTarget.getClass().getMethod(getterName,
(Class[]) null);
getter.invoke(getterTarget, (Object[]) null);
} catch (Throwable t) {
if (t instanceof InvocationTargetException)
t = ((InvocationTargetException) t).getTargetException();
if (errs == null) {
errs = new StringWriter();
stack = new PrintWriter(errs);
} else
errs.write(SEP);
t.printStackTrace(stack);
stack.flush();
}
}
if (errs != null)
throw new RuntimeException(_loc.get("get-prop-errs",
errs.toString()).getMessage());
}
public boolean isReadOnly() {
return _readOnly;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (_changeSupport == null)
_changeSupport = new PropertyChangeSupport(this);
_changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
if (_changeSupport != null)
_changeSupport.removePropertyChangeListener(listener);
}
public void valueChanged(Value val) {
if (_changeSupport == null && _props == null)
return;
String newString = val.getString();
if (_changeSupport != null)
_changeSupport.firePropertyChange(val.getProperty(), null,
newString);
// keep cached props up to date
if (_props != null) {
if (newString == null)
Configurations.removeProperty(val.getProperty(), _props);
else if (Configurations.containsProperty(val.getProperty(), _props)
|| val.getDefault() == null
|| !val.getDefault().equals(newString))
put(_props, val, newString);
}
}
/**
* Closes all closeable values and plugins.
*/
public final void close() {
ProductDerivations.beforeClose(this);
preClose();
ObjectValue val;
for (int i = 0; i < _vals.size(); i++) {
if (_vals.get(i) instanceof Closeable) {
try { ((Closeable) _vals.get(i)).close(); }
catch (Exception e) {}
continue;
}
if (!(_vals.get(i) instanceof ObjectValue))
continue;
val = (ObjectValue) _vals.get(i);
if (val.get() instanceof Closeable) {
try {
((Closeable) val.get()).close();
} catch (Exception e) {
}
}
}
}
/**
* Invoked by final method {@link #close} after invoking the
* {@link ProductDerivation#beforeConfigurationClose} callbacks
* but before performing internal close operations.
*
* @since 0.9.7
*/
protected void preClose() {
}
///////////////////////////
// BeanInfo implementation
///////////////////////////
public BeanInfo[] getAdditionalBeanInfo() {
return new BeanInfo[0];
}
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(getClass());
}
public int getDefaultEventIndex() {
return 0;
}
public int getDefaultPropertyIndex() {
return 0;
}
public EventSetDescriptor[] getEventSetDescriptors() {
return new EventSetDescriptor[0];
}
public Image getIcon(int kind) {
return null;
}
public synchronized MethodDescriptor[] getMethodDescriptors() {
if (_mds != null)
return _mds;
PropertyDescriptor[] pds = getPropertyDescriptors();
List descs = new ArrayList();
for (int i = 0; i < pds.length; i++) {
Method write = pds[i].getWriteMethod();
Method read = pds[i].getReadMethod();
if (read != null && write != null) {
descs.add(new MethodDescriptor(write));
descs.add(new MethodDescriptor(read));
}
}
_mds = (MethodDescriptor[]) descs.
toArray(new MethodDescriptor[descs.size()]);
return _mds;
}
public synchronized PropertyDescriptor[] getPropertyDescriptors() {
if (_pds != null)
return _pds;
_pds = new PropertyDescriptor[_vals.size()];
List failures = null;
Value val;
for (int i = 0; i < _vals.size(); i++) {
val = (Value) _vals.get(i);
try {
_pds[i] = getPropertyDescriptor(val);
} catch (MissingResourceException mre) {
if (failures == null)
failures = new ArrayList();
failures.add(val.getProperty());
} catch (IntrospectionException ie) {
if (failures == null)
failures = new ArrayList();
failures.add(val.getProperty());
}
}
if (failures != null)
throw new ParseException(_loc.get("invalid-property-descriptors",
failures));
return _pds;
}
/**
* Create a property descriptor for the given value.
*/
private PropertyDescriptor getPropertyDescriptor(Value val)
throws IntrospectionException {
String prop = val.getProperty();
prop = prop.substring(prop.lastIndexOf('.') + 1);
// set up property descriptor
PropertyDescriptor pd;
try {
pd = new PropertyDescriptor(Introspector.decapitalize(prop),
getClass());
} catch (IntrospectionException ie) {
// if there aren't any methods for this value(i.e., if it's a
// dynamically-added value), then an IntrospectionException will
// be thrown. Try to create a PD with no read or write methods.
pd = new PropertyDescriptor(Introspector.decapitalize(prop),
(Method) null, (Method) null);
}
pd.setDisplayName(findLocalized(prop + "-name", true, val.getScope()));
pd.setShortDescription(findLocalized(prop + "-desc", true,
val.getScope()));
pd.setExpert("true".equals(findLocalized(prop + "-expert", false,
val.getScope())));
try {
pd.setReadMethod(getClass().getMethod("get"
+ StringUtils.capitalize(prop), (Class[]) null));
pd.setWriteMethod(getClass().getMethod("set"
+ StringUtils.capitalize(prop), new Class[]
{ pd.getReadMethod().getReturnType() }));
} catch (Throwable t) {
// if an error occurs, it might be because the value is a
// dynamic property.
}
String type = findLocalized(prop + "-type", true, val.getScope());
if (type != null)
pd.setValue(ATTRIBUTE_TYPE, type);
String cat = findLocalized(prop + "-cat", false, val.getScope());
if (cat != null)
pd.setValue(ATTRIBUTE_CATEGORY, cat);
pd.setValue(ATTRIBUTE_XML, toXMLName(prop));
String order = findLocalized(prop + "-displayorder", false,
val.getScope());
if (order != null)
pd.setValue(ATTRIBUTE_ORDER, order);
// collect allowed values from alias keys, listed values, and
// interface implementors
Collection allowed = new TreeSet();
List aliases = Collections.EMPTY_LIST;
if (val.getAliases() != null) {
aliases = Arrays.asList(val.getAliases());
for (int i = 0; i < aliases.size(); i += 2)
allowed.add(aliases.get(i));
}
String[] vals = Strings.split(findLocalized(prop
+ "-values", false, val.getScope()), ",", 0);
for (int i = 0; i < vals.length; i++)
if (!aliases.contains(vals[i]))
allowed.add(vals[i]);
try {
Class intf = Class.forName(findLocalized(prop
+ "-interface", true, val.getScope()), false,
getClass().getClassLoader());
pd.setValue(ATTRIBUTE_INTERFACE, intf.getName());
String[] impls = Services.getImplementors(intf);
for (int i = 0; i < impls.length; i++)
if (!aliases.contains(impls[i]))
allowed.add(impls[i]);
} catch (Throwable t) {
}
if (!allowed.isEmpty())
pd.setValue(ATTRIBUTE_ALLOWED_VALUES, (String[]) allowed.toArray
(new String[allowed.size()]));
return pd;
}
/**
* Find the given localized string, or return null if not found.
*/
private String findLocalized(String key, boolean fatal, Class scope) {
// find the localizer package that contains this key
Localizer loc = null;
// check the package that the value claims to be defined in, if
// available, before we start guessing.
if (scope != null) {
loc = Localizer.forPackage(scope);
try {
return loc.getFatal(key).getMessage();
} catch (MissingResourceException mse) {
}
}
for (Class cls = getClass(); cls != Object.class;
cls = cls.getSuperclass()) {
loc = Localizer.forPackage(cls);
try {
return loc.getFatal(key).getMessage();
} catch (MissingResourceException mse) {
}
}
if (fatal)
throw new MissingResourceException(key, getClass().getName(), key);
return null;
}
////////////////
// To/from maps
////////////////
public Map toProperties(boolean storeDefaults) {
// clone properties before making any modifications; we need to keep
// the internal properties instance consistent to maintain equals and
// hashcode contracts
Map clone;
if (_props == null)
clone = new HashMap();
else if (_props instanceof Properties)
clone = (Map) ((Properties) _props).clone();
else
clone = new HashMap(_props);
// if no existing properties or the properties should contain entries
// with default values, add values to properties
if (_props == null || storeDefaults) {
Value val;
String str;
for (int i = 0; i < _vals.size(); i++) {
// if key in existing properties, we already know value is up
// to date
val = (Value) _vals.get(i);
if (_props != null && Configurations.containsProperty
(val.getProperty(), _props))
continue;
str = val.getString();
if (str != null && (storeDefaults
|| !str.equals(val.getDefault())))
put(clone, val, str);
}
if (_props == null)
_props = new HashMap(clone);
}
return clone;
}
public void fromProperties(Map map) {
if (map == null || map.isEmpty())
return;
assertNotReadOnly();
// if the only previous call was to load defaults, forget them.
// this way we preserve the original formatting of the user's props
// instead of the defaults. this is important for caching on
// configuration objects
if (_globals) {
_props = null;
_globals = false;
}
Map remaining = new HashMap(map);
boolean ser = true;
Value val;
Object o;
for (int i = 0; i < _vals.size(); i++) {
val = (Value) _vals.get(i);
o = get(map, val, true);
if (o == null)
continue;
if (o instanceof String) {
if (!StringUtils.equals((String) o, val.getString()))
val.setString((String) o);
} else {
ser &= o instanceof Serializable;
val.setObject(o);
}
Configurations.removeProperty(val.getProperty(), remaining);
}
// convention is to point product at a resource with the
// <prefix>.properties System property; remove that property so we
// we don't warn about it
Configurations.removeProperty("properties", remaining);
// now warn if there are any remaining properties that there
// is an unhandled prop
Map.Entry entry;
for (Iterator itr = remaining.entrySet().iterator(); itr.hasNext();) {
entry = (Map.Entry) itr.next();
if (entry.getKey() != null)
warnInvalidProperty((String) entry.getKey());
ser &= entry.getValue() instanceof Serializable;
}
// cache properties
if (_props == null && ser)
_props = map;
}
/**
* Adds <code>o</code> to <code>map</code> under key for <code>val</code>.
* Use this method instead of attempting to add the value directly because
* this will account for the property prefix.
*/
private void put(Map map, Value val, Object o) {
Object key = val.getLoadKey();
if (key == null)
key = "openjpa." + val.getProperty();
map.put(key, o);
}
/**
* Look up the given value, testing all available prefixes.
*/
private Object get(Map map, Value val, boolean setLoadKey) {
String key = ProductDerivations.getConfigurationKey(
val.getProperty(), map);
if (map.containsKey(key) && setLoadKey)
val.setLoadKey(key);
return map.get(key);
}
/**
* Issue a warning that the specified property is not valid.
*/
private void warnInvalidProperty(String propName) {
if (!isInvalidProperty(propName))
return;
Log log = getConfigurationLog();
if (log == null || !log.isWarnEnabled())
return;
// try to find the closest string to the invalid property
// so that we can provide a hint in case of a misspelling
String closest = StringDistance.getClosestLevenshteinDistance
(propName, newPropertyList(), 15);
if (closest == null)
log.warn(_loc.get("invalid-property", propName));
else
log.warn(_loc.get("invalid-property-hint", propName, closest));
}
/**
* Return a comprehensive list of recognized map keys.
*/
private Collection newPropertyList() {
String[] prefixes = ProductDerivations.getConfigurationPrefixes();
List l = new ArrayList(_vals.size() * prefixes.length);
for (int i = 0; i < _vals.size(); i++) {
for (int j = 0; j < prefixes.length; j++)
l.add(prefixes[j] + "." + ((Value) _vals.get(i)).getProperty());
}
return l;
}
/**
* Returns true if the specified property name should raise a warning
* if it is not found in the list of known properties.
*/
protected boolean isInvalidProperty(String propName) {
// handle warnings for openjpa.SomeString, but not for
// openjpa.some.subpackage.SomeString, since it might be valid for some
// specific implementation of OpenJPA
String[] prefixes = ProductDerivations.getConfigurationPrefixes();
for (int i = 0; i < prefixes.length; i++) {
if (propName.toLowerCase().startsWith(prefixes[i])
&& propName.length() > prefixes[i].length() + 1
&& propName.indexOf('.', prefixes[i].length())
== prefixes[i].length()
&& propName.indexOf('.', prefixes[i].length() + 1) == -1)
return true;
}
return false;
}
//////////////////////
// Auto-configuration
//////////////////////
/**
* This method loads the named resource as a properties file. It is
* useful for auto-configuration tools so users can specify a
* <code>properties</code> value with the name of a resource.
*/
public void setProperties(String resourceName) throws IOException {
ProductDerivations.load(resourceName, null,
getClass().getClassLoader()).setInto(this);
_auto = resourceName;
}
/**
* This method loads the named file as a properties file. It is
* useful for auto-configuration tools so users can specify a
* <code>propertiesFile</code> value with the name of a file.
*/
public void setPropertiesFile(File file) throws IOException {
ProductDerivations.load(file, null, getClass().getClassLoader()).
setInto(this);
_auto = file.toString();
}
/**
* Return the resource that was set via auto-configuration methods
* {@link #setProperties} or {@link #setPropertiesFile}, or null if none.
*/
public String getPropertiesResource() {
return _auto;
}
/////////////
// Utilities
/////////////
/**
* Checks if the configuration is read only and if so throws an
* exception, otherwise returns silently.
* Implementations should call this method before setting any state.
*/
public void assertNotReadOnly() {
if (isReadOnly())
throw new IllegalStateException(_loc.get("read-only").getMessage());
}
/**
* Performs an equality check based on the properties returned from
* {@link #toProperties}.
*/
public boolean equals(Object other) {
if (other == this)
return true;
if (other == null)
return false;
if (!getClass().equals(other.getClass()))
return false;
// compare properties
ConfigurationImpl conf = (ConfigurationImpl) other;
Map p1 = (_props == null) ? toProperties(false) : _props;
Map p2 = (conf._props == null) ? conf.toProperties(false) : conf._props;
return excludeDynamic(p1).equals(excludeDynamic(p2));
}
/**
* Computes hash code based on the properties returned from
* {@link #toProperties}.
*/
public int hashCode() {
Map copy = (_props == null) ? toProperties(false) : _props;
return excludeDynamic(copy).hashCode();
}
/**
* Convert <code>propName</code> to a lowercase-with-hyphens-style string.
* This algorithm is only designed for mixes of uppercase and lowercase
* letters and lone digits. A more sophisticated conversion should probably
* be handled by a proper parser generator or regular expressions.
*/
public static String toXMLName(String propName) {
if (propName == null)
return null;
StringBuffer buf = new StringBuffer();
char c;
for (int i = 0; i < propName.length(); i++) {
c = propName.charAt(i);
// convert sequences of all-caps to downcase with dashes around
// them. put a trailing cap that is followed by downcase into the
// downcase word.
if (i != 0 && Character.isUpperCase(c)
&& (Character.isLowerCase(propName.charAt(i-1))
|| (i > 1 && i < propName.length() - 1
&& Character.isUpperCase(propName.charAt(i-1))
&& Character.isLowerCase(propName.charAt(i+1)))))
buf.append('-');
// surround sequences of digits with dashes.
if (i != 0
&& ((!Character.isLetter(c) && Character.isLetter(propName
.charAt(i - 1)))
|| (Character.isLetter(c) && !Character.isLetter(propName
.charAt(i - 1)))))
buf.append('-');
buf.append(Character.toLowerCase(c));
}
return buf.toString();
}
/**
* Implementation of the {@link Externalizable} interface to read from
* the properties written by {@link #writeExternal}.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
fromProperties((Map) in.readObject());
_props = (Map) in.readObject();
_globals = in.readBoolean();
}
/**
* Implementation of the {@link Externalizable} interface to write
* the properties returned by {@link #toProperties}.
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(toProperties(true));
out.writeObject(_props);
out.writeBoolean(_globals);
}
/**
* Uses {@link #toProperties} and {@link #fromProperties} to clone
* configuration.
*/
public Object clone() {
try {
Constructor cons = getClass().getConstructor
(new Class[]{ boolean.class });
ConfigurationImpl clone = (ConfigurationImpl) cons.newInstance
(new Object[]{ Boolean.FALSE });
clone.fromProperties(toProperties(true));
clone._props = (_props == null) ? null : new HashMap(_props);
clone._globals = _globals;
return clone;
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new ParseException(e);
}
}
public boolean removeValue(Value val) {
if (!_vals.remove(val))
return false;
val.setListener(null);
return true;
}
public Value addValue(Value val) {
_vals.add(val);
val.setListener(this);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public StringValue addString(String property) {
StringValue val = new StringValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public FileValue addFile(String property) {
FileValue val = new FileValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public IntValue addInt(String property) {
IntValue val = new IntValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public DoubleValue addDouble(String property) {
DoubleValue val = new DoubleValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public BooleanValue addBoolean(String property) {
BooleanValue val = new BooleanValue(property);
addValue(val);
val.setDefault("false");
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public StringListValue addStringList(String property) {
StringListValue val = new StringListValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public ObjectValue addObject(String property) {
ObjectValue val = new ObjectValue(property);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public PluginValue addPlugin(String property, boolean singleton) {
PluginValue val = new PluginValue(property, singleton);
addValue(val);
return val;
}
/**
* Add the given value to the set of configuration properties.
*/
public PluginListValue addPluginList(String property) {
PluginListValue val = new PluginListValue(property);
addValue(val);
return val;
}
public void modifyDynamic(String property, Object newValue) {
if (!isDynamic(property))
throw new RuntimeException(_loc.get("not-dynamic", property)
.toString());
Value value = getValue(property);
value.setObject(newValue);
}
public boolean isDynamic(String property) {
Value[] dynamicValues = getDynamicValues();
for (int i=0; i<dynamicValues.length; i++)
if (dynamicValues[i].getProperty().equals(property))
return true;
return false;
}
public Value[] getDynamicValues() {
return new Value[0];
}
Map excludeDynamic(Map map) {
if (map == null)
return null;
Map copy = new HashMap(map);
Value[] dynamicValues = getDynamicValues();
for (int i=0; i<dynamicValues.length; i++) {
Configurations.removeProperty(dynamicValues[i].getProperty(), copy);
}
return copy;
}
}