Return-Path: Delivered-To: apmail-jakarta-commons-user-archive@www.apache.org Received: (qmail 67007 invoked from network); 24 Aug 2005 17:40:54 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 24 Aug 2005 17:40:54 -0000 Received: (qmail 46403 invoked by uid 500); 24 Aug 2005 17:40:48 -0000 Delivered-To: apmail-jakarta-commons-user-archive@jakarta.apache.org Received: (qmail 46332 invoked by uid 500); 24 Aug 2005 17:40:47 -0000 Mailing-List: contact commons-user-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Jakarta Commons Users List" Reply-To: "Jakarta Commons Users List" Delivered-To: mailing list commons-user@jakarta.apache.org Received: (qmail 46315 invoked by uid 99); 24 Aug 2005 17:40:47 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 24 Aug 2005 10:40:47 -0700 X-ASF-Spam-Status: No, hits=-0.0 required=10.0 tests=SPF_HELO_PASS X-Spam-Check-By: apache.org Received-SPF: neutral (asf.osuosl.org: local policy) Received: from [207.97.227.212] (HELO iad2.emailsrvr.com) (207.97.227.212) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 24 Aug 2005 10:41:02 -0700 Received: from jackie (CPE000f66366977-CM0013711451aa.cpe.net.cable.rogers.com [69.199.52.234]) (Authenticated sender: moran@place-base.com) by relay1.r1.iad.emailsrvr.com (SMTP Server) with ESMTP id 6B57F44C4FE; Wed, 24 Aug 2005 13:28:45 -0400 (EDT) From: "Moran Ben-David" To: "'Jakarta Commons Users List'" , , Subject: RE: [configuration] Property Substitution Policy Date: Wed, 24 Aug 2005 13:28:58 -0400 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0088_01C5A8AF.C941E810" X-Mailer: Microsoft Office Outlook, Build 11.0.6353 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2527 Thread-Index: AcWn4OZnzzBO/Na8TZK22FrL+Hty7QA7k50Q In-Reply-To: <430B1B3F.90303@apache.org> Message-Id: <20050824172845.6B57F44C4FE@relay1.r1.iad.emailsrvr.com> X-Virus-Scanned: OK X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N ------=_NextPart_000_0088_01C5A8AF.C941E810 Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Hi guys, Thanks again for inspecting this problem with me and hashing out my various options. I have taken the liberty of making some modifications to 2 classes in Commons Configuration (attached) in order to get it to do what I wanted. Is it possible to contribute this to Commons Configuration? I've tried to do a good job in this code change by providing the test case. I plan to use commons configuration for as long as I can think and would hate to have to make this change for every new version I get. The summary of these changes is that I added a Boolean flag to tell the interpolation algorithm whether to substitute using the entire multi-valued property or just the first value. I.e., an if statement determining whether getString or getPoroperty are used. I added a test case for this in what I think is the appropriate place. My exact changes are as follows: TestBaseConfiguration.java(592): test case for making sure flag switches behaviour properly. public void testMultiValuedPropertyInterpolation() throws Exception { config.setProperty("multi", "value1"); config.addProperty("multi", "value2"); config.setProperty("interpolated", "${multi}"); config.setInterpolateAllValues(false); String expectedValue = "value1"; assertEquals(config.getString("interpolated"), expectedValue); config.setInterpolateAllValues(true); expectedValue = "" + config.getProperty("multi"); assertEquals(config.getString("interpolated"), expectedValue); } Abstractconfiguration.java(59): added flag /** * Determines whether all values of a property are used or just *the first when * substituting tokens. */ private boolean interpolateAllValues = true; Abstractconfiguration.java(209): the if statement in interpolateHelper() to use getString or getProperty: Object value; if (isInterpolateAllValues()) { value = getProperty(variable); } else { value = getString(variable); } Abstractconfiguration.java(977): setter and getter for flag: public void setInterpolateAllValues(boolean b) { this.interpolateAllValues = b; } public boolean isInterpolateAllValues() { return interpolateAllValues; } > -----Original Message----- > From: Emmanuel Bourg [mailto:ebourg@apache.org] > Sent: Tuesday, August 23, 2005 8:49 AM > To: Jakarta Commons Users List > Subject: Re: [configuration] Property Substitution Policy > > Hi Moran, > > Moran Ben-David wrote: > > > I was actually trying to get the substitution to only use the 1st value > in a > > multi-valued property. For example, a file like > > > > V1 = value1 > > V1 = value2 > > Myprop = ${V1} > > > > Would result in a configuration having > > > > Myprop = value1 > > > > This way, when I do config.getString("Myprop") I'd get "value1" and not > > "[value1, value2]". > > I tend to agree on this, the interpolated value should be a scalar, this > would be consistent with the getString() method called on a multi-valued > property. > > However regarding your use case I agree with Oliver, using a > CompositeConfiguration is the right solution. You can build it > programatically, or use the ConfigurationFactory with a configuration > descriptor. > > Emmanuel Bourg > > --------------------------------------------------------------------- > To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org > For additional commands, e-mail: commons-user-help@jakarta.apache.org ------=_NextPart_000_0088_01C5A8AF.C941E810 Content-Type: text/java; name="AbstractConfiguration.java" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="AbstractConfiguration.java" /* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed 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.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Properties; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.iterators.FilterIterator; import org.apache.commons.lang.BooleanUtils; /** * Abstract configuration class. Provide basic functionality but does = not store * any data. If you want to write your own Configuration class then you = should * implement only abstract methods from this class. *=20 * @author Konstantin Shaposhnikov * @author Oliver Heger * @author Henning P. Schmiedehausen = * @version $Id: AbstractConfiguration.java,v 1.29 2004/12/02 22:05:52 = ebourg * Exp $ */ public abstract class AbstractConfiguration implements Configuration { /** start token */ protected static final String START_TOKEN =3D "${"; /** end token */ protected static final String END_TOKEN =3D "}"; /** The property delimiter used while parsing (a comma). */ private static char DELIMITER =3D ','; /** * Whether the configuration should throw NoSuchElementExceptions or = simply * return null when a property does not exist. Defaults to return = null. */ private boolean throwExceptionOnMissing =3D false; =09 /** * Determines whether all values of a property are used or just the = first when=20 * substituting tokens. */ private boolean interpolateAllValues =3D true; /** * For configurations extending AbstractConfiguration, allow them to = change * the delimiter from the default comma (","). *=20 * @param delimiter The new delimiter */ public static void setDelimiter(char delimiter) { AbstractConfiguration.DELIMITER =3D delimiter; } /** * Retrieve the current delimiter. By default this is a comma (","). *=20 * @return The delimiter in use */ public static char getDelimiter() { return AbstractConfiguration.DELIMITER; } /** * If set to false, missing elements return null if possible (for = objects). *=20 * @param throwExceptionOnMissing The new value for the property */ public void setThrowExceptionOnMissing(boolean = throwExceptionOnMissing) { this.throwExceptionOnMissing =3D throwExceptionOnMissing; } /** * Returns true if missing values throw Exceptions. *=20 * @return true if missing values throw Exceptions */ public boolean isThrowExceptionOnMissing() { return throwExceptionOnMissing; } /** * {@inheritDoc} */ public void addProperty(String key, Object value) { Iterator it =3D PropertyConverter.toIterator(value, DELIMITER); while (it.hasNext()) { addPropertyDirect(key, it.next()); } } /** * Adds a key/value pair to the Configuration. Override this method = to * provide write acces to underlying Configuration store. *=20 * @param key key to use for mapping * @param obj object to store */ protected abstract void addPropertyDirect(String key, Object obj); /** * interpolate key names to handle ${key} stuff *=20 * @param base string to interpolate *=20 * @return returns the key name with the ${key} substituted */ protected String interpolate(String base) { return interpolateHelper(base, null); } /** * Recursive handler for multple levels of interpolation. *=20 * When called the first time, priorVariables should be null. *=20 * @param base string with the ${key} variables * @param priorVariables serves two purposes: to allow checking for = loops, * and creating a meaningful exception message should a loop occur. = It's * 0'th element will be set to the value of base from the first = call. All * subsequent interpolated variables are added afterward. *=20 * @return the string with the interpolation taken care of */ protected String interpolateHelper(String base, List priorVariables) { if (base =3D=3D null) { return null; } // on the first call initialize priorVariables // and add base as the first element if (priorVariables =3D=3D null) { priorVariables =3D new ArrayList(); priorVariables.add(base); } int begin =3D -1; int end =3D -1; int prec =3D 0 - END_TOKEN.length(); String variable =3D null; StringBuffer result =3D new StringBuffer(); // FIXME: we should probably allow the escaping of the start = token while (((begin =3D base.indexOf(START_TOKEN, prec + = END_TOKEN.length())) > -1) && ((end =3D base.indexOf(END_TOKEN, begin)) > -1)) { result.append(base.substring(prec + END_TOKEN.length(), = begin)); variable =3D base.substring(begin + START_TOKEN.length(), = end); // if we've got a loop, create a useful exception message = and throw if (priorVariables.contains(variable)) { String initialBase =3D = priorVariables.remove(0).toString(); priorVariables.add(variable); StringBuffer priorVariableSb =3D new StringBuffer(); // create a nice trace of interpolated variables like = so: // var1->var2->var3 for (Iterator it =3D priorVariables.iterator(); = it.hasNext();) { priorVariableSb.append(it.next()); if (it.hasNext()) { priorVariableSb.append("->"); } } throw new IllegalStateException("infinite loop in = property interpolation of " + initialBase + ": " + priorVariableSb.toString()); } // otherwise, add this variable to the interpolation list. else { priorVariables.add(variable); } =09 Object value; if (isInterpolateAllValues()) { value =3D getProperty(variable); } else { value =3D getString(variable); } =09 if (value !=3D null) { result.append(interpolateHelper(value.toString(), = priorVariables)); // pop the interpolated variable off the stack // this maintains priorVariables correctness for // properties with multiple interpolations, e.g. // = prop.name=3D${some.other.prop1}/blahblah/${some.other.prop2} priorVariables.remove(priorVariables.size() - 1); } else { //variable not defined - so put it back in the value = result.append(START_TOKEN).append(variable).append(END_TOKEN); } prec =3D end; } result.append(base.substring(prec + END_TOKEN.length(), = base.length())); return result.toString(); } /** * {@inheritDoc} */ public Configuration subset(String prefix) { return new SubsetConfiguration(this, prefix, "."); } /** * {@inheritDoc} */ public abstract boolean isEmpty(); /** * {@inheritDoc} */ public abstract boolean containsKey(String key); /** * {@inheritDoc} */ public void setProperty(String key, Object value) { clearProperty(key); addProperty(key, value); } /** * {@inheritDoc} */ public abstract void clearProperty(String key); /** * {@inheritDoc} */ public void clear() { Iterator it =3D getKeys(); while (it.hasNext()) { String key =3D (String) it.next(); it.remove(); if (containsKey(key)) { // workaround for Iterators that do not remove the = property on calling remove() clearProperty(key); } } } /** * {@inheritDoc} */ public abstract Iterator getKeys(); /** * {@inheritDoc} */ public Iterator getKeys(final String prefix) { return new FilterIterator(getKeys(), new Predicate() { public boolean evaluate(Object obj) { String key =3D (String) obj; return key.startsWith(prefix + ".") || = key.equals(prefix); } }); } /** * {@inheritDoc} */ public Properties getProperties(String key) { return getProperties(key, null); } /** * Get a list of properties associated with the given configuration = key. *=20 * @param key The configuration key. * @param defaults Any default values for the returned * Properties object. Ignored if null. *=20 * @return The associated properties if key is found. *=20 * @throws ConversionException is thrown if the key maps to an = object that * is not a String/List of Strings. *=20 * @throws IllegalArgumentException if one of the tokens is = malformed (does * not contain an equals sign). */ public Properties getProperties(String key, Properties defaults) { /* * Grab an array of the tokens for this key. */ String[] tokens =3D getStringArray(key); /* * Each token is of the form 'key=3Dvalue'. */ Properties props =3D defaults =3D=3D null ? new Properties() : = new Properties(defaults); for (int i =3D 0; i < tokens.length; i++) { String token =3D tokens[i]; int equalSign =3D token.indexOf('=3D'); if (equalSign > 0) { String pkey =3D token.substring(0, equalSign).trim(); String pvalue =3D token.substring(equalSign + 1).trim(); props.put(pkey, pvalue); } else if (tokens.length =3D=3D 1 && "".equals(token)) { // Semantically equivalent to an empty Properties // object. break; } else { throw new IllegalArgumentException('\'' + token + "' = does not contain an equals sign"); } } return props; } /** * {@inheritDoc} */ public boolean getBoolean(String key) { Boolean b =3D getBoolean(key, null); if (b !=3D null) { return b.booleanValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public boolean getBoolean(String key, boolean defaultValue) { return getBoolean(key, = BooleanUtils.toBooleanObject(defaultValue)).booleanValue(); } /** * {@inheritDoc} */ public Boolean getBoolean(String key, Boolean defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toBoolean(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Boolean object", e); } } } /** * {@inheritDoc} */ public byte getByte(String key) { Byte b =3D getByte(key, null); if (b !=3D null) { return b.byteValue(); } else { throw new NoSuchElementException('\'' + key + " doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public byte getByte(String key, byte defaultValue) { return getByte(key, new Byte(defaultValue)).byteValue(); } /** * {@inheritDoc} */ public Byte getByte(String key, Byte defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toByte(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Byte object", e); } } } /** * {@inheritDoc} */ public double getDouble(String key) { Double d =3D getDouble(key, null); if (d !=3D null) { return d.doubleValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public double getDouble(String key, double defaultValue) { return getDouble(key, new Double(defaultValue)).doubleValue(); } /** * {@inheritDoc} */ public Double getDouble(String key, Double defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toDouble(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Double object", e); } } } /** * {@inheritDoc} */ public float getFloat(String key) { Float f =3D getFloat(key, null); if (f !=3D null) { return f.floatValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public float getFloat(String key, float defaultValue) { return getFloat(key, new Float(defaultValue)).floatValue(); } /** * {@inheritDoc} */ public Float getFloat(String key, Float defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toFloat(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Float object", e); } } } /** * {@inheritDoc} */ public int getInt(String key) { Integer i =3D getInteger(key, null); if (i !=3D null) { return i.intValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public int getInt(String key, int defaultValue) { Integer i =3D getInteger(key, null); if (i =3D=3D null) { return defaultValue; } return i.intValue(); } /** * {@inheritDoc} */ public Integer getInteger(String key, Integer defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toInteger(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to an Integer object", e); } } } /** * {@inheritDoc} */ public long getLong(String key) { Long l =3D getLong(key, null); if (l !=3D null) { return l.longValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public long getLong(String key, long defaultValue) { return getLong(key, new Long(defaultValue)).longValue(); } /** * {@inheritDoc} */ public Long getLong(String key, Long defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toLong(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Long object", e); } } } /** * {@inheritDoc} */ public short getShort(String key) { Short s =3D getShort(key, null); if (s !=3D null) { return s.shortValue(); } else { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } } /** * {@inheritDoc} */ public short getShort(String key, short defaultValue) { return getShort(key, new Short(defaultValue)).shortValue(); } /** * {@inheritDoc} */ public Short getShort(String key, Short defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toShort(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a Short object", e); } } } /** * {@inheritDoc} */ public BigDecimal getBigDecimal(String key) { BigDecimal number =3D getBigDecimal(key, null); if (number !=3D null) { return number; } else if (isThrowExceptionOnMissing()) { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } else { return null; } } /** * {@inheritDoc} */ public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toBigDecimal(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a BigDecimal object", e); } } } /** * {@inheritDoc} */ public BigInteger getBigInteger(String key) { BigInteger number =3D getBigInteger(key, null); if (number !=3D null) { return number; } else if (isThrowExceptionOnMissing()) { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } else { return null; } } /** * {@inheritDoc} */ public BigInteger getBigInteger(String key, BigInteger defaultValue) { Object value =3D resolveContainerStore(key); if (value =3D=3D null) { return defaultValue; } else { try { return PropertyConverter.toBigInteger(value); } catch (ConversionException e) { throw new ConversionException('\'' + key + "' doesn't = map to a BigDecimal object", e); } } } /** * {@inheritDoc} */ public String getString(String key) { String s =3D getString(key, null); if (s !=3D null) { return s; } else if (isThrowExceptionOnMissing()) { throw new NoSuchElementException('\'' + key + "' doesn't map = to an existing object"); } else { return null; } } /** * {@inheritDoc} */ public String getString(String key, String defaultValue) { Object value =3D resolveContainerStore(key); if (value instanceof String) { return interpolate((String) value); } else if (value =3D=3D null) { return interpolate(defaultValue); } else { throw new ConversionException('\'' + key + "' doesn't map to = a String object"); } } /** * {@inheritDoc} */ public String[] getStringArray(String key) { Object value =3D getProperty(key); String[] array; if (value instanceof String) { array =3D new String[1]; array[0] =3D interpolate((String) value); } else if (value instanceof List) { List list =3D (List) value; array =3D new String[list.size()]; for (int i =3D 0; i < array.length; i++) { array[i] =3D interpolate((String) list.get(i)); } } else if (value =3D=3D null) { array =3D new String[0]; } else { throw new ConversionException('\'' + key + "' doesn't map to = a String/List object"); } return array; } /** * {@inheritDoc} */ public List getList(String key) { return getList(key, new ArrayList()); } /** * {@inheritDoc} */ public List getList(String key, List defaultValue) { Object value =3D getProperty(key); List list =3D null; if (value instanceof String) { list =3D new ArrayList(1); list.add(value); } else if (value instanceof List) { list =3D (List) value; } else if (value =3D=3D null) { list =3D defaultValue; } else { throw new ConversionException('\'' + key + "' doesn't map to = a List object: " + value + ", a " + value.getClass().getName()); } return list; } /** * Returns an object from the store described by the key. If the = value is a * List object, replace it with the first object in the list. *=20 * @param key The property key. *=20 * @return value Value, transparently resolving a possible List = dependency. */ protected Object resolveContainerStore(String key) { Object value =3D getProperty(key); if (value !=3D null) { if (value instanceof List) { List list =3D (List) value; value =3D list.isEmpty() ? null : list.get(0); } else if (value instanceof Object[]) { Object[] array =3D (Object[]) value; value =3D array.length =3D=3D 0 ? null : array[0]; } else if (value instanceof boolean[]) { boolean[] array =3D (boolean[]) value; value =3D array.length =3D=3D 0 ? null : new = Boolean(array[0]); } else if (value instanceof byte[]) { byte[] array =3D (byte[]) value; value =3D array.length =3D=3D 0 ? null : new = Byte(array[0]); } else if (value instanceof short[]) { short[] array =3D (short[]) value; value =3D array.length =3D=3D 0 ? null : new = Short(array[0]); } else if (value instanceof int[]) { int[] array =3D (int[]) value; value =3D array.length =3D=3D 0 ? null : new = Integer(array[0]); } else if (value instanceof long[]) { long[] array =3D (long[]) value; value =3D array.length =3D=3D 0 ? null : new = Long(array[0]); } else if (value instanceof float[]) { float[] array =3D (float[]) value; value =3D array.length =3D=3D 0 ? null : new = Float(array[0]); } else if (value instanceof double[]) { double[] array =3D (double[]) value; value =3D array.length =3D=3D 0 ? null : new = Double(array[0]); } } return value; } =09 public void setInterpolateAllValues(boolean b) { this.interpolateAllValues =3D b; } public boolean isInterpolateAllValues() { return interpolateAllValues; } } ------=_NextPart_000_0088_01C5A8AF.C941E810 Content-Type: text/java; name="TestBaseConfiguration.java" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="TestBaseConfiguration.java" /* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed 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.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Properties; import java.util.StringTokenizer; import junit.framework.TestCase; import junitx.framework.ObjectAssert; /** * Tests some basic functions of the BaseConfiguration class. Missing = keys will * throw Exceptions * * @version $Id: TestBaseConfiguration.java 158491 2005-03-21 17:58:56Z = ebourg $ */ public class TestBaseConfiguration extends TestCase { protected BaseConfiguration config =3D null; protected static Class missingElementException =3D = NoSuchElementException.class; protected static Class incompatibleElementException =3D = ConversionException.class; protected void setUp() throws Exception { config =3D new BaseConfiguration(); config.setThrowExceptionOnMissing(true); } public void testThrowExceptionOnMissing() { assertTrue("Throw Exception Property is not set!", = config.isThrowExceptionOnMissing()); } public void testGetProperty() { /* should be empty and return null */ assertEquals("This returns null", config.getProperty("foo"), = null); /* add a real value, and get it two different ways */ config.setProperty("number", "1"); assertEquals("This returns '1'", config.getProperty("number"), = "1"); assertEquals("This returns '1'", config.getString("number"), = "1"); } public void testGetByte() { config.setProperty("number", "1"); byte oneB =3D 1; byte twoB =3D 2; assertEquals("This returns 1(byte)", oneB, = config.getByte("number")); assertEquals("This returns 1(byte)", oneB, = config.getByte("number", twoB)); assertEquals("This returns 2(default byte)", twoB, = config.getByte("numberNotInConfig", twoB)); assertEquals("This returns 1(Byte)", new Byte(oneB), = config.getByte("number", new Byte("2"))); // missing key without default value Throwable t =3D null; try { config.getByte("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getByte("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetShort() { config.setProperty("numberS", "1"); short oneS =3D 1; short twoS =3D 2; assertEquals("This returns 1(short)", oneS, = config.getShort("numberS")); assertEquals("This returns 1(short)", oneS, = config.getShort("numberS", twoS)); assertEquals("This returns 2(default short)", twoS, = config.getShort("numberNotInConfig", twoS)); assertEquals("This returns 1(Short)", new Short(oneS), = config.getShort("numberS", new Short("2"))); // missing key without default value Throwable t =3D null; try { config.getShort("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getShort("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetLong() { config.setProperty("numberL", "1"); long oneL =3D 1; long twoL =3D 2; assertEquals("This returns 1(long)", oneL, = config.getLong("numberL")); assertEquals("This returns 1(long)", oneL, = config.getLong("numberL", twoL)); assertEquals("This returns 2(default long)", twoL, = config.getLong("numberNotInConfig", twoL)); assertEquals("This returns 1(Long)", new Long(oneL), = config.getLong("numberL", new Long("2"))); // missing key without default value Throwable t =3D null; try { config.getLong("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getLong("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetFloat() { config.setProperty("numberF", "1.0"); float oneF =3D 1; float twoF =3D 2; assertEquals("This returns 1(float)", oneF, = config.getFloat("numberF"), 0); assertEquals("This returns 1(float)", oneF, = config.getFloat("numberF", twoF), 0); assertEquals("This returns 2(default float)", twoF, = config.getFloat("numberNotInConfig", twoF), 0); assertEquals("This returns 1(Float)", new Float(oneF), = config.getFloat("numberF", new Float("2"))); // missing key without default value Throwable t =3D null; try { config.getFloat("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getFloat("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetDouble() { config.setProperty("numberD", "1.0"); double oneD =3D 1; double twoD =3D 2; assertEquals("This returns 1(double)", oneD, = config.getDouble("numberD"), 0); assertEquals("This returns 1(double)", oneD, = config.getDouble("numberD", twoD), 0); assertEquals("This returns 2(default double)", twoD, = config.getDouble("numberNotInConfig", twoD), 0); assertEquals("This returns 1(Double)", new Double(oneD), = config.getDouble("numberD", new Double("2"))); // missing key without default value Throwable t =3D null; try { config.getDouble("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getDouble("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetBigDecimal() { config.setProperty("numberBigD", "123.456"); BigDecimal number =3D new BigDecimal("123.456"); BigDecimal defaultValue =3D new BigDecimal("654.321"); assertEquals("Existing key", number, = config.getBigDecimal("numberBigD")); assertEquals("Existing key with default value", number, = config.getBigDecimal("numberBigD", defaultValue)); assertEquals("Missing key with default value", defaultValue, = config.getBigDecimal("numberNotInConfig", defaultValue)); // missing key without default value Throwable t =3D null; try { config.getBigDecimal("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getBigDecimal("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetBigInteger() { config.setProperty("numberBigI", "1234567890"); BigInteger number =3D new BigInteger("1234567890"); BigInteger defaultValue =3D new BigInteger("654321"); assertEquals("Existing key", number, = config.getBigInteger("numberBigI")); assertEquals("Existing key with default value", number, = config.getBigInteger("numberBigI", defaultValue)); assertEquals("Missing key with default value", defaultValue, = config.getBigInteger("numberNotInConfig", defaultValue)); // missing key without default value Throwable t =3D null; try { config.getBigInteger("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getBigInteger("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetString() { config.setProperty("testString", "The quick brown fox"); String string =3D new String("The quick brown fox"); String defaultValue =3D new String("jumps over the lazy dog"); assertEquals("Existing key", string, = config.getString("testString")); assertEquals("Existing key with default value", string, = config.getString("testString", defaultValue)); assertEquals("Missing key with default value", defaultValue, = config.getString("stringNotInConfig", defaultValue)); // missing key without default value Throwable t =3D null; try { config.getString("stringNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); } public void testGetBoolean() { config.setProperty("boolA", Boolean.TRUE); boolean boolT =3D true, boolF =3D false; assertEquals("This returns true", boolT, = config.getBoolean("boolA")); assertEquals("This returns true, not the default", boolT, = config.getBoolean("boolA", boolF)); assertEquals("This returns false(default)", boolF, = config.getBoolean("boolNotInConfig", boolF)); assertEquals("This returns true(Boolean)", new Boolean(boolT), = config.getBoolean("boolA", new Boolean(boolF))); // missing key without default value Throwable t =3D null; try { config.getBoolean("numberNotInConfig"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for missing keys", t); ObjectAssert.assertInstanceOf("Exception thrown for missing = keys", missingElementException, t); // existing key with an incompatible value config.setProperty("test.empty", ""); t =3D null; try { config.getBoolean("test.empty"); } catch (Throwable T) { t =3D T; } assertNotNull("No exception thrown for incompatible values", t); ObjectAssert.assertInstanceOf("Exception thrown for incompatible = values", incompatibleElementException, t); } public void testGetList() { config.addProperty("number", "1"); config.addProperty("number", "2"); List list =3D config.getList("number"); assertNotNull("The list is null", list); assertEquals("List size", 2, list.size()); assertTrue("The number 1 is missing from the list", = list.contains("1")); assertTrue("The number 2 is missing from the list", = list.contains("2")); /* * now test dan's new fix where we get the first scalar * when we access a list valued property */ try { config.getString("number"); } catch (NoSuchElementException nsse) { fail("Should return a string"); } } public void testCommaSeparatedString() { String prop =3D "hey, that's a test"; config.setProperty("prop.string", prop); try { config.getList("prop.string"); } catch (NoSuchElementException nsse) { fail("Should return a list"); } String prop2 =3D "hey\\, that's a test"; config.clearProperty("prop.string"); config.setProperty("prop.string", prop2); try { config.getString("prop.string"); } catch (NoSuchElementException nsse) { fail("Should return a list"); } } =20 public void testAddProperty() throws Exception { Collection props =3D new ArrayList(); props.add("one"); props.add("two,three,four"); props.add(new String[] { "5.1", "5.2", "5.3,5.4", "5.5" }); props.add("six"); config.addProperty("complex.property", props); =20 Object val =3D config.getProperty("complex.property"); assertTrue(val instanceof Collection); Collection col =3D (Collection) val; assertEquals(10, col.size()); =20 props =3D new ArrayList(); props.add("quick"); props.add("brown"); props.add("fox,jumps"); Object[] data =3D new Object[] { "The", props, "over,the", "lazy", "dog." }; config.setProperty("complex.property", data); val =3D config.getProperty("complex.property"); assertTrue(val instanceof Collection); col =3D (Collection) val; Iterator it =3D col.iterator(); StringTokenizer tok =3D new StringTokenizer("The quick brown fox = jumps over the lazy dog.", " "); while(tok.hasMoreTokens()) { assertTrue(it.hasNext()); assertEquals(tok.nextToken(), it.next()); } assertFalse(it.hasNext()); =20 config.setProperty("complex.property", null); assertFalse(config.containsKey("complex.property")); } public void testPropertyAccess() { config.clearProperty("prop.properties"); config.setProperty("prop.properties", ""); assertEquals( "This returns an empty Properties object", config.getProperties("prop.properties"), new Properties()); config.clearProperty("prop.properties"); config.setProperty("prop.properties", "foo=3Dbar, baz=3Dmoo, = seal=3Dclubber"); Properties p =3D new Properties(); p.setProperty("foo", "bar"); p.setProperty("baz", "moo"); p.setProperty("seal", "clubber"); assertEquals( "This returns a filled in Properties object", config.getProperties("prop.properties"), p); } public void testSubset() { /* * test subset : assure we don't reprocess the data elements * when generating the subset */ String prop =3D "hey, that's a test"; String prop2 =3D "hey\\, that's a test"; config.setProperty("prop.string", prop2); config.setProperty("property.string", "hello"); Configuration subEprop =3D config.subset("prop"); assertEquals( "Returns the full string", prop, subEprop.getString("string")); try { subEprop.getString("string"); } catch (NoSuchElementException nsse) { fail("Should return a string"); } try { subEprop.getList("string"); } catch (NoSuchElementException nsse) { fail("Should return a list"); } Iterator it =3D subEprop.getKeys(); it.next(); assertFalse(it.hasNext()); subEprop =3D config.subset("prop."); it =3D subEprop.getKeys(); assertFalse(it.hasNext()); } public void testInterpolation() throws Exception { config.setProperty("applicationRoot", "/home/applicationRoot"); config.setProperty("db", "${applicationRoot}/db/hypersonic"); String unInterpolatedValue =3D = "${applicationRoot2}/db/hypersonic"; config.setProperty("dbFailedInterpolate", unInterpolatedValue); String dbProp =3D "/home/applicationRoot/db/hypersonic"; //construct a new config, using config as the defaults config = for it. BaseConfiguration superProp =3D config; assertEquals( "Checking interpolated variable", dbProp, superProp.getString("db")); assertEquals( "lookup fails, leave variable as is", superProp.getString("dbFailedInterpolate"), unInterpolatedValue); superProp.setProperty("arrayInt", "${applicationRoot}/1"); String[] arrayInt =3D superProp.getStringArray("arrayInt"); assertEquals( "check first entry was interpolated", "/home/applicationRoot/1", arrayInt[0]); } public void testMultipleInterpolation() throws Exception { config.setProperty("test.base-level", "/base-level"); config.setProperty("test.first-level", = "${test.base-level}/first-level"); config.setProperty( "test.second-level", "${test.first-level}/second-level"); config.setProperty( "test.third-level", "${test.second-level}/third-level"); String expectedValue =3D "/base-level/first-level/second-level/third-level"; assertEquals(config.getString("test.third-level"), = expectedValue); } =09 public void testMultiValuedPropertyInterpolation() throws Exception { config.setProperty("multi", "value1"); config.addProperty("multi", "value2"); config.setProperty("interpolated", "${multi}"); =09 config.setInterpolateAllValues(false); String expectedValue =3D "value1";=09 assertEquals(config.getString("interpolated"), expectedValue); =09 config.setInterpolateAllValues(true); expectedValue =3D "" + config.getProperty("multi"); =09 assertEquals(config.getString("interpolated"), expectedValue); } public void testInterpolationLoop() throws Exception { config.setProperty("test.a", "${test.b}"); config.setProperty("test.b", "${test.a}"); try { config.getString("test.a"); } catch (IllegalStateException e) { return; } fail("IllegalStateException should have been thrown for looped = property references"); } public void testGetHexadecimalValue() { config.setProperty("number", "0xFF"); assertEquals("byte value", (byte) 0xFF, = config.getByte("number")); config.setProperty("number", "0xFFFF"); assertEquals("short value", (short) 0xFFFF, = config.getShort("number")); config.setProperty("number", "0xFFFFFFFF"); assertEquals("int value", 0xFFFFFFFF, config.getInt("number")); config.setProperty("number", "0xFFFFFFFFFFFFFFFF"); assertEquals("long value", 0xFFFFFFFFFFFFFFFFL, = config.getLong("number")); assertEquals("long value", 0xFFFFFFFFFFFFFFFFL, = config.getBigInteger("number").longValue()); } public void testResolveContainerStore() { AbstractConfiguration config =3D new BaseConfiguration(); // array of objects config.addPropertyDirect("array", new String[] { "foo", "bar" = }); assertEquals("first element of the 'array' property", "foo", = config.resolveContainerStore("array")); // list of objects List list =3D new ArrayList(); list.add("foo"); list.add("bar"); config.addPropertyDirect("list", list); assertEquals("first element of the 'list' property", "foo", = config.resolveContainerStore("list")); // arrays of primitives config.addPropertyDirect("array.boolean", new boolean[] { true, = false }); assertEquals("first element of the 'array.boolean' property", = true, config.getBoolean("array.boolean")); config.addPropertyDirect("array.byte", new byte[] { 1, 2 }); assertEquals("first element of the 'array.byte' property", 1, = config.getByte("array.byte")); config.addPropertyDirect("array.short", new short[] { 1, 2 }); assertEquals("first element of the 'array.short' property", 1, = config.getShort("array.short")); config.addPropertyDirect("array.int", new int[] { 1, 2 }); assertEquals("first element of the 'array.int' property", 1, = config.getInt("array.int")); config.addPropertyDirect("array.long", new long[] { 1, 2 }); assertEquals("first element of the 'array.long' property", 1, = config.getLong("array.long")); config.addPropertyDirect("array.float", new float[] { 1, 2 }); assertEquals("first element of the 'array.float' property", 1, = config.getFloat("array.float"), 0); config.addPropertyDirect("array.double", new double[] { 1, 2 }); assertEquals("first element of the 'array.double' property", 1, = config.getDouble("array.double"), 0); } } ------=_NextPart_000_0088_01C5A8AF.C941E810 Content-Type: text/plain; charset=us-ascii --------------------------------------------------------------------- To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-user-help@jakarta.apache.org ------=_NextPart_000_0088_01C5A8AF.C941E810--