Author: oheger Date: Mon Jan 21 21:15:01 2013 New Revision: 1436623 URL: http://svn.apache.org/viewvc?rev=1436623&view=rev Log: Added a new InterpolatorSpecification class.
Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java (with props) commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java (with props) Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java?rev=1436623&view=auto ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java (added) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java Mon Jan 21 21:15:01 2013 @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.configuration.interpol; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * <p> + * A simple value class defining a {@link ConfigurationInterpolator}. + * </p> + * <p> + * Objects of this class can be used for creating new + * {@code ConfigurationInterpolator} instances; they contain all required + * properties. It is either possible to set a fully initialized + * {@code ConfigurationInterpolator} directly which can be used as is. + * Alternatively, some or all properties of an instance to be newly created can + * be set. These properties include + * <ul> + * <li>a map with {@code Lookup} objects associated with a specific prefix</li> + * <li>a collection with default {@code Lookup} objects (without a prefix)</li> + * <li>a parent {@code ConfigurationInterpolator}</li> + * </ul> + * </p> + * <p> + * When setting up a configuration it is possible to define the + * {@code ConfigurationInterpolator} in terms of this class. The configuration + * will then either use the {@code ConfigurationInterpolator} instance + * explicitly defined in the {@code InterpolatorSpecification} instance or + * create a new one. + * </p> + * <p> + * Instances are not created directly, but using the nested {@code Builder} + * class. They are then immutable. + * </p> + * + * @version $Id$ + * @since 2.0 + */ +public final class InterpolatorSpecification +{ + /** The {@code ConfigurationInterpolator} instance to be used directly. */ + private final ConfigurationInterpolator interpolator; + + /** The parent {@code ConfigurationInterpolator}. */ + private final ConfigurationInterpolator parentInterpolator; + + /** The map with prefix lookups. */ + private final Map<String, Lookup> prefixLookups; + + /** The collection with default lookups. */ + private final Collection<Lookup> defaultLookups; + + /** + * Creates a new instance of {@code InterpolatorSpecification} with the + * properties defined by the given builder object. + * + * @param builder the builder + */ + private InterpolatorSpecification(Builder builder) + { + interpolator = builder.interpolator; + parentInterpolator = builder.parentInterpolator; + prefixLookups = + Collections.unmodifiableMap(new HashMap<String, Lookup>( + builder.prefixLookups)); + defaultLookups = + Collections.unmodifiableCollection(new ArrayList<Lookup>( + builder.defLookups)); + } + + /** + * Returns the {@code ConfigurationInterpolator} instance to be used + * directly. + * + * @return the {@code ConfigurationInterpolator} (can be <b>null</b>) + */ + public ConfigurationInterpolator getInterpolator() + { + return interpolator; + } + + /** + * Returns the parent {@code ConfigurationInterpolator} object. + * + * @return the parent {@code ConfigurationInterpolator} (can be <b>null</b>) + */ + public ConfigurationInterpolator getParentInterpolator() + { + return parentInterpolator; + } + + /** + * Returns a map with prefix lookups. The keys of the map are the prefix + * strings, its values are the corresponding {@code Lookup} objects. + * + * @return the prefix lookups for a new {@code ConfigurationInterpolator} + * instance (never <b>null</b>) + */ + public Map<String, Lookup> getPrefixLookups() + { + return prefixLookups; + } + + /** + * Returns a collection with the default lookups. + * + * @return the default lookups for a new {@code ConfigurationInterpolator} + * instance (never <b>null</b>) + */ + public Collection<Lookup> getDefaultLookups() + { + return defaultLookups; + } + + /** + * <p<A <em>builder</em> class for creating instances of + * {@code InterpolatorSpecification}.</p> + * <p> + * This class provides a fluent API for defining the various properties of + * an {@code InterpolatorSpecification} object. <em>Note:</em> This builder + * class is not thread-safe. + * </p> + */ + public static class Builder + { + /** A map with prefix lookups. */ + private final Map<String, Lookup> prefixLookups; + + /** A collection with default lookups. */ + private final Collection<Lookup> defLookups; + + /** The {@code ConfigurationInterpolator}. */ + private ConfigurationInterpolator interpolator; + + /** The parent {@code ConfigurationInterpolator}. */ + private ConfigurationInterpolator parentInterpolator; + + public Builder() + { + prefixLookups = new HashMap<String, Lookup>(); + defLookups = new LinkedList<Lookup>(); + } + + /** + * Adds a {@code Lookup} object for a given prefix. + * + * @param prefix the prefix (must not be <b>null</b>) + * @param lookup the {@code Lookup} (must not be <b>null</b>) + * @return a reference to this builder for method chaining + * @throws IllegalArgumentException if a required parameter is missing + */ + public Builder withPrefixLookup(String prefix, Lookup lookup) + { + if (prefix == null) + { + throw new IllegalArgumentException("Prefix must not be null!"); + } + checkLookup(lookup); + prefixLookups.put(prefix, lookup); + return this; + } + + /** + * Adds the content of the given map to the prefix lookups managed by + * this builder. The map can be <b>null</b>, then this method has no + * effect. + * + * @param lookups the map with prefix lookups to be added + * @return a reference to this builder for method chaining + * @throws IllegalArgumentException if the map contains <b>null</b> + * values + */ + public Builder withPrefixLookups(Map<String, ? extends Lookup> lookups) + { + if (lookups != null) + { + for (Map.Entry<String, ? extends Lookup> e : lookups.entrySet()) + { + withPrefixLookup(e.getKey(), e.getValue()); + } + } + return this; + } + + /** + * Adds the given {@code Lookup} object to the list of default lookups. + * + * @param lookup the {@code Lookup} (must not be <b>null</b>) + * @return a reference to this builder for method chaining + * @throws IllegalArgumentException if the {@code Lookup} is <b>null</b> + */ + public Builder withDefaultLookup(Lookup lookup) + { + checkLookup(lookup); + defLookups.add(lookup); + return this; + } + + /** + * Adds the content of the given collection to the default lookups + * managed by this builder. The collection can be <b>null</b>, then this + * method has no effect. + * + * @param lookups the collection with lookups to be added + * @return a reference to this builder for method chaining + * @throws IllegalArgumentException if the collection contains + * <b>null</b> entries + */ + public Builder withDefaultLookups(Collection<? extends Lookup> lookups) + { + if (lookups != null) + { + for (Lookup l : lookups) + { + withDefaultLookup(l); + } + } + return this; + } + + /** + * Sets the {@code ConfigurationInterpolator} instance for the + * {@code InterpolatorSpecification}. This means that a + * {@code ConfigurationInterpolator} has been created and set up + * externally and can be used directly. + * + * @param ci the {@code ConfigurationInterpolator} (can be <b>null</b>) + * @return a reference to this builder for method chaining + */ + public Builder withInterpolator(ConfigurationInterpolator ci) + { + interpolator = ci; + return this; + } + + /** + * Sets an optional parent {@code ConfigurationInterpolator}. If + * defined, this object is set as parent of a newly created + * {@code ConfigurationInterpolator} instance. + * + * @param parent the parent {@code ConfigurationInterpolator} (can be + * <b>null</b>) + * @return a reference to this builder for method chaining + */ + public Builder withParentInterpolator(ConfigurationInterpolator parent) + { + parentInterpolator = parent; + return this; + } + + /** + * Creates a new {@code InterpolatorSpecification} instance with the + * properties set so far. After that this builder instance is reset so + * that it can be reused for creating further specification objects. + * + * @return the newly created {@code InterpolatorSpecification} + */ + public InterpolatorSpecification create() + { + InterpolatorSpecification spec = + new InterpolatorSpecification(this); + reset(); + return spec; + } + + /** + * Removes all data from this builder. Afterwards it can be used to + * define a brand new {@code InterpolatorSpecification} object. + */ + public void reset() + { + interpolator = null; + parentInterpolator = null; + prefixLookups.clear(); + defLookups.clear(); + } + + /** + * Helper method for checking a lookup. Throws an exception if the + * lookup is <b>null</b>. + * + * @param lookup the lookup to be checked + * @throws IllegalArgumentException if the lookup is <b>null</b> + */ + private static void checkLookup(Lookup lookup) + { + if (lookup == null) + { + throw new IllegalArgumentException("Lookup must not be null!"); + } + } + } +} Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/InterpolatorSpecification.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java?rev=1436623&view=auto ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java (added) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java Mon Jan 21 21:15:01 2013 @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.configuration.interpol; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@code InterpolatorSpecification}. + * + * @version $Id$ + */ +public class TestInterpolatorSpecification +{ + /** Constant for a prefix for a prefix lookup. */ + private static final String PREFIX1 = "p1"; + + /** Constant for another prefix for a prefix lookup. */ + private static final String PREFIX2 = "p2"; + + /** The builder for creating new instances. */ + private InterpolatorSpecification.Builder builder; + + @Before + public void setUp() throws Exception + { + builder = new InterpolatorSpecification.Builder(); + } + + /** + * Convenience method for creating a mock object. + * + * @param cls the class of the mock + * @param <T> the type of the mock + * @return the mock + */ + private static <T> T createMock(Class<T> cls) + { + T mock = EasyMock.createMock(cls); + EasyMock.replay(mock); + return mock; + } + + /** + * Convenience method for creating a mock lookup. + * + * @return the mock lookup + */ + private static Lookup createLookup() + { + return createMock(Lookup.class); + } + + /** + * Checks whether the given test object contains the expected prefix + * lookups. + * + * @param spec the object to be tested + * @param prefLook1 prefix lookup 1 + * @param prefLook2 prefix lookup 2 + */ + private static void checkPrefixLookups(InterpolatorSpecification spec, + Lookup prefLook1, Lookup prefLook2) + { + assertEquals("Wrong number of prefix lookups", 2, spec + .getPrefixLookups().size()); + assertSame("Wrong prefix lookup 1", prefLook1, spec.getPrefixLookups() + .get(PREFIX1)); + assertSame("Wrong prefix lookup 2", prefLook2, spec.getPrefixLookups() + .get(PREFIX2)); + } + + /** + * Checks whether the given test object contains the expected default + * lookups. + * + * @param spec the object to be tested + * @param defLook1 default lookup 1 + * @param defLook2 default lookup 2 + */ + private static void checkDefaultLookups(InterpolatorSpecification spec, + Lookup defLook1, Lookup defLook2) + { + assertEquals("Wrong number of default lookups", 2, spec + .getDefaultLookups().size()); + assertTrue("Wrong default lookups", spec.getDefaultLookups() + .containsAll(Arrays.asList(defLook1, defLook2))); + } + + /** + * Tests whether an instance with all possible properties can be set. + */ + @Test + public void testCreateInstance() + { + Lookup prefLook1 = createLookup(); + Lookup prefLook2 = createLookup(); + Lookup defLook1 = createLookup(); + Lookup defLook2 = createLookup(); + ConfigurationInterpolator interpolator = + createMock(ConfigurationInterpolator.class); + ConfigurationInterpolator parent = + createMock(ConfigurationInterpolator.class); + InterpolatorSpecification spec = + builder.withPrefixLookup(PREFIX1, prefLook1) + .withDefaultLookup(defLook1) + .withPrefixLookup(PREFIX2, prefLook2) + .withParentInterpolator(parent) + .withDefaultLookup(defLook2) + .withInterpolator(interpolator).create(); + assertSame("Wrong interpolator", interpolator, spec.getInterpolator()); + assertSame("Wrong parent interpolator", parent, + spec.getParentInterpolator()); + checkPrefixLookups(spec, prefLook1, prefLook2); + checkDefaultLookups(spec, defLook1, defLook2); + } + + /** + * Tests whether lookups can be set passing in full collections. + */ + @Test + public void testCreateInstanceCollections() + { + Lookup prefLook1 = createLookup(); + Lookup prefLook2 = createLookup(); + Lookup defLook1 = createLookup(); + Lookup defLook2 = createLookup(); + Map<String, Lookup> prefixLookups = new HashMap<String, Lookup>(); + prefixLookups.put(PREFIX1, prefLook1); + prefixLookups.put(PREFIX2, prefLook2); + InterpolatorSpecification spec = + builder.withPrefixLookups(prefixLookups) + .withDefaultLookups(Arrays.asList(defLook1, defLook2)) + .create(); + checkPrefixLookups(spec, prefLook1, prefLook2); + checkDefaultLookups(spec, defLook1, defLook2); + } + + /** + * Tests whether a null map with prefix lookups is accepted. + */ + @Test + public void testWithPrefixLookupsNull() + { + InterpolatorSpecification spec = + builder.withPrefixLookups(null).create(); + assertTrue("No empty map with prefix lookups", spec.getPrefixLookups() + .isEmpty()); + } + + /** + * Tests whether a null collection with default lookups is accepted. + */ + @Test + public void testWithDefaultLookupsNull() + { + InterpolatorSpecification spec = + builder.withDefaultLookups(null).create(); + assertTrue("No empty default lookups collection", spec + .getDefaultLookups().isEmpty()); + } + + /** + * Tests whether a null prefix causes an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testWithPrefixLookupNoPrefix() + { + builder.withPrefixLookup(null, createLookup()); + } + + /** + * Tests whether a null prefix lookup causes an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testWithPrefixLookupNoLookup() + { + builder.withPrefixLookup(PREFIX1, null); + } + + /** + * Tests whether a null default lookup causes an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testWithDefaultLookupNull() + { + builder.withDefaultLookup(null); + } + + /** + * Tests that the map with prefix lookups cannot be modified. + */ + @Test(expected = UnsupportedOperationException.class) + public void testGetPrefixLookupsModify() + { + InterpolatorSpecification spec = + builder.withPrefixLookup(PREFIX1, createLookup()).create(); + spec.getPrefixLookups().put(PREFIX1, createLookup()); + } + + /** + * Tests that the collection with default lookups cannot be modified. + */ + @Test(expected = UnsupportedOperationException.class) + public void testGetDefaultLookupsModify() + { + InterpolatorSpecification spec = + builder.withDefaultLookup(createLookup()).create(); + spec.getDefaultLookups().add(createLookup()); + } + + /** + * Tests whether a builder can be reused. + */ + @Test + public void testBuilderReuse() + { + builder.withDefaultLookup(createLookup()) + .withInterpolator(createMock(ConfigurationInterpolator.class)) + .withPrefixLookup("test", createLookup()) + .withParentInterpolator( + createMock(ConfigurationInterpolator.class)).create(); + Lookup prefLook1 = createLookup(); + Lookup prefLook2 = createLookup(); + Lookup defLook1 = createLookup(); + Lookup defLook2 = createLookup(); + ConfigurationInterpolator parent = + createMock(ConfigurationInterpolator.class); + InterpolatorSpecification spec = + builder.withPrefixLookup(PREFIX1, prefLook1) + .withPrefixLookup(PREFIX2, prefLook2) + .withDefaultLookups(Arrays.asList(defLook1, defLook2)) + .withParentInterpolator(parent).create(); + assertNull("Got an interpolator", spec.getInterpolator()); + assertSame("Wrong parent interpolator", parent, + spec.getParentInterpolator()); + checkPrefixLookups(spec, prefLook1, prefLook2); + checkDefaultLookups(spec, defLook1, defLook2); + } +} Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestInterpolatorSpecification.java ------------------------------------------------------------------------------ svn:mime-type = text/plain