    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.configuration;
    019
    020    import java.util.Iterator;
    021
    022    import org.apache.commons.collections.Transformer;
    023    import org.apache.commons.collections.iterators.TransformIterator;
    024    import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
    025
    026    /**
    027     * <p>A subset of another configuration. The new Configuration object contains
    028     * every key from the parent Configuration that starts with prefix. The prefix
    029     * is removed from the keys in the subset.</p>
    030     * <p>It is usually not necessary to use this class directly. Instead the
    031     * <code>{@link Configuration#subset(String)}</code> method should be used,
    032     * which will return a correctly initialized instance.</p>
    033     *
    034     * @author Emmanuel Bourg
    035     * @version $Revision: 930693 $, $Date: 2010-04-04 16:02:59 +0200 (So, 04. Apr 2010) $
    036     */
    037    public class SubsetConfiguration extends AbstractConfiguration
    038    {
    039        /** The parent configuration. */
    040        protected Configuration parent;
    041
    042        /** The prefix used to select the properties. */
    043        protected String prefix;
    044
    045        /** The prefix delimiter */
    046        protected String delimiter;
    047
    048        /**
    049         * Create a subset of the specified configuration
    050         *
    051         * @param parent The parent configuration
    052         * @param prefix The prefix used to select the properties
    053         */
    054        public SubsetConfiguration(Configuration parent, String prefix)
    055        {
    056            this.parent = parent;
    057            this.prefix = prefix;
    058        }
    059
    060        /**
    061         * Create a subset of the specified configuration
    062         *
    063         * @param parent    The parent configuration
    064         * @param prefix    The prefix used to select the properties
    065         * @param delimiter The prefix delimiter
    066         */
    067        public SubsetConfiguration(Configuration parent, String prefix, String delimiter)
    068        {
    069            this.parent = parent;
    070            this.prefix = prefix;
    071            this.delimiter = delimiter;
    072        }
    073
    074        /**
    075         * Return the key in the parent configuration associated to the specified
    076         * key in this subset.
    077         *
    078         * @param key The key in the subset.
    079         * @return the key as to be used by the parent
    080         */
    081        protected String getParentKey(String key)
    082        {
    083            if ("".equals(key) || key == null)
    084            {
    085                return prefix;
    086            }
    087            else
    088            {
    089                return delimiter == null ? prefix + key : prefix + delimiter + key;
    090            }
    091        }
    092
    093        /**
    094         * Return the key in the subset configuration associated to the specified
    095         * key in the parent configuration.
    096         *
    097         * @param key The key in the parent configuration.
    098         * @return the key in the context of this subset configuration
    099         */
    100        protected String getChildKey(String key)
    101        {
    102            if (!key.startsWith(prefix))
    103            {
    104                throw new IllegalArgumentException("The parent key '" + key + "' is not in the subset.");
    105            }
    106            else
    107            {
    108                String modifiedKey = null;
    109                if (key.length() == prefix.length())
    110                {
    111                    modifiedKey = "";
    112                }
    113                else
    114                {
    115                    int i = prefix.length() + (delimiter != null ? delimiter.length() : 0);
    116                    modifiedKey = key.substring(i);
    117                }
    118
    119                return modifiedKey;
    120            }
    121        }
    122
    123        /**
    124         * Return the parent configuration for this subset.
    125         *
    126         * @return the parent configuration
    127         */
    128        public Configuration getParent()
    129        {
    130            return parent;
    131        }
    132
    133        /**
    134         * Return the prefix used to select the properties in the parent configuration.
    135         *
    136         * @return the prefix used by this subset
    137         */
    138        public String getPrefix()
    139        {
    140            return prefix;
    141        }
    142
    143        /**
    144         * Set the prefix used to select the properties in the parent configuration.
    145         *
    146         * @param prefix the prefix
    147         */
    148        public void setPrefix(String prefix)
    149        {
    150            this.prefix = prefix;
    151        }
    152
    153        public Configuration subset(String prefix)
    154        {
    155            return parent.subset(getParentKey(prefix));
    156        }
    157
    158        public boolean isEmpty()
    159        {
    160            return !getKeys().hasNext();
    161        }
    162
    163        public boolean containsKey(String key)
    164        {
    165            return parent.containsKey(getParentKey(key));
    166        }
    167
    168        public void addPropertyDirect(String key, Object value)
    169        {
    170            parent.addProperty(getParentKey(key), value);
    171        }
    172
    173        protected void clearPropertyDirect(String key)
    174        {
    175            parent.clearProperty(getParentKey(key));
    176        }
    177
    178        public Object getProperty(String key)
    179        {
    180            return parent.getProperty(getParentKey(key));
    181        }
    182
    183        public Iterator getKeys(String prefix)
    184        {
    185            return new TransformIterator(parent.getKeys(getParentKey(prefix)), new Transformer()
    186            {
    187                public Object transform(Object obj)
    188                {
    189                    return getChildKey((String) obj);
    190                }
    191            });
    192        }
    193
    194        public Iterator getKeys()
    195        {
    196            return new TransformIterator(parent.getKeys(prefix), new Transformer()
    197            {
    198                public Object transform(Object obj)
    199                {
    200                    return getChildKey((String) obj);
    201                }
    202            });
    203        }
    204
    205        protected Object interpolate(Object base)
    206        {
    207            if (delimiter == null && "".equals(prefix))
    208            {
    209                return super.interpolate(base);
    210            }
    211            else
    212            {
    213                SubsetConfiguration config = new SubsetConfiguration(parent, "");
    214                ConfigurationInterpolator interpolator = config.getInterpolator();
    215                getInterpolator().registerLocalLookups(interpolator);
    216                if (parent instanceof AbstractConfiguration)
    217                {
    218                    interpolator.setParentInterpolator(((AbstractConfiguration) parent).getInterpolator());
    219                }
    220                return config.interpolate(base);
    221            }
    222        }
    223
    224        protected String interpolate(String base)
    225        {
    226            return super.interpolate(base);
    227        }
    228
    229        /**
    230         * {@inheritDoc}
    231         *
    232         * Change the behaviour of the parent configuration if it supports this feature.
    233         */
    234        public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing)
    235        {
    236            if (parent instanceof AbstractConfiguration)
    237            {
    238                ((AbstractConfiguration) parent).setThrowExceptionOnMissing(throwExceptionOnMissing);
    239            }
    240            else
    241            {
    242                super.setThrowExceptionOnMissing(throwExceptionOnMissing);
    243            }
    244        }
    245
    246        /**
    247         * {@inheritDoc}
    248         *
    249         * The subset inherits this feature from its parent if it supports this feature.
    250         */
    251        public boolean isThrowExceptionOnMissing()
    252        {
    253            if (parent instanceof AbstractConfiguration)
    254            {
    255                return ((AbstractConfiguration) parent).isThrowExceptionOnMissing();
    256            }
    257            else
    258            {
    259                return super.isThrowExceptionOnMissing();
    260            }
    261        }
    262
    263        /**
    264         * Returns the list delimiter. This property will be fetched from the parent
    265         * configuration if supported.
    266         *
    267         * @return the list delimiter
    268         * @since 1.4
    269         */
    270        public char getListDelimiter()
    271        {
    272            return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
    273                    .getListDelimiter()
    274                    : super.getListDelimiter();
    275        }
    276
    277        /**
    278         * Sets the list delimiter. If the parent configuration supports this
    279         * feature, the delimiter will be set at the parent.
    280         *
    281         * @param delim the new list delimiter
    282         * @since 1.4
    283         */
    284        public void setListDelimiter(char delim)
    285        {
    286            if (parent instanceof AbstractConfiguration)
    287            {
    288                ((AbstractConfiguration) parent).setListDelimiter(delim);
    289            }
    290            else
    291            {
    292                super.setListDelimiter(delim);
    293            }
    294        }
    295
    296        /**
    297         * Returns a flag whether string properties should be checked for list
    298         * delimiter characters. This implementation ensures that this flag is kept
    299         * in sync with the parent configuration if this object supports this
    300         * feature.
    301         *
    302         * @return the delimiter parsing disabled flag
    303         * @since 1.4
    304         */
    305        public boolean isDelimiterParsingDisabled()
    306        {
    307            return (parent instanceof AbstractConfiguration) ? ((AbstractConfiguration) parent)
    308                    .isDelimiterParsingDisabled()
    309                    : super.isDelimiterParsingDisabled();
    310        }
    311
    312        /**
    313         * Sets a flag whether list parsing is disabled. This implementation will
    314         * also set the flag at the parent configuration if this object supports
    315         * this feature.
    316         *
    317         * @param delimiterParsingDisabled the delimiter parsing disabled flag
    318         * @since 1.4
    319         */
    320        public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
    321        {
    322            if (parent instanceof AbstractConfiguration)
    323            {
    324                ((AbstractConfiguration) parent)
    325                        .setDelimiterParsingDisabled(delimiterParsingDisabled);
    326            }
    327            else
    328            {
    329                super.setDelimiterParsingDisabled(delimiterParsingDisabled);
    330            }
    331        }
    332    }