PatternSubtreeConfigurationWrapper.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2;

  18. import java.io.IOException;
  19. import java.io.Reader;
  20. import java.io.Writer;
  21. import java.math.BigDecimal;
  22. import java.math.BigInteger;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Objects;
  28. import java.util.Properties;

  29. import org.apache.commons.configuration2.event.Event;
  30. import org.apache.commons.configuration2.event.EventListener;
  31. import org.apache.commons.configuration2.event.EventType;
  32. import org.apache.commons.configuration2.ex.ConfigurationException;
  33. import org.apache.commons.configuration2.io.FileBased;
  34. import org.apache.commons.configuration2.tree.ExpressionEngine;
  35. import org.apache.commons.configuration2.tree.ImmutableNode;

  36. /**
  37.  * Wraps a BaseHierarchicalConfiguration and allows subtrees to be accessed via a configured path with replaceable
  38.  * tokens derived from the ConfigurationInterpolator. When used with injection frameworks such as Spring it allows
  39.  * components to be injected with subtrees of the configuration.
  40.  *
  41.  * @since 1.6
  42.  */
  43. public class PatternSubtreeConfigurationWrapper extends BaseHierarchicalConfiguration implements FileBasedConfiguration {
  44.     /** The wrapped configuration */
  45.     private final HierarchicalConfiguration<ImmutableNode> config;

  46.     /** The path to the subtree */
  47.     private final String path;

  48.     /** True if the path ends with '/', false otherwise */
  49.     private final boolean trailing;

  50.     /** True if the constructor has finished */
  51.     private final boolean init;

  52.     /**
  53.      * Constructor
  54.      *
  55.      * @param config The Configuration to be wrapped.
  56.      * @param path The base path pattern.
  57.      */
  58.     public PatternSubtreeConfigurationWrapper(final HierarchicalConfiguration<ImmutableNode> config, final String path) {
  59.         this.config = Objects.requireNonNull(config, "config");
  60.         this.path = path;
  61.         this.trailing = path.endsWith("/");
  62.         this.init = true;
  63.     }

  64.     @Override
  65.     public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  66.         getConfig().addEventListener(eventType, listener);
  67.     }

  68.     @Override
  69.     protected void addNodesInternal(final String key, final Collection<? extends ImmutableNode> nodes) {
  70.         getConfig().addNodes(key, nodes);
  71.     }

  72.     @Override
  73.     protected void addPropertyInternal(final String key, final Object value) {
  74.         config.addProperty(makePath(key), value);
  75.     }

  76.     @Override
  77.     public void clearErrorListeners() {
  78.         getConfig().clearErrorListeners();
  79.     }

  80.     @Override
  81.     public void clearEventListeners() {
  82.         getConfig().clearEventListeners();
  83.     }

  84.     @Override
  85.     protected void clearInternal() {
  86.         getConfig().clear();
  87.     }

  88.     @Override
  89.     protected void clearPropertyDirect(final String key) {
  90.         config.clearProperty(makePath(key));
  91.     }

  92.     @Override
  93.     protected Object clearTreeInternal(final String key) {
  94.         config.clearTree(makePath(key));
  95.         return Collections.emptyList();
  96.     }

  97.     @Override
  98.     public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key) {
  99.         return config.configurationAt(makePath(key));
  100.     }

  101.     @Override
  102.     public HierarchicalConfiguration<ImmutableNode> configurationAt(final String key, final boolean supportUpdates) {
  103.         return config.configurationAt(makePath(key), supportUpdates);
  104.     }

  105.     @Override
  106.     public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(final String key) {
  107.         return config.configurationsAt(makePath(key));
  108.     }

  109.     @Override
  110.     protected boolean containsKeyInternal(final String key) {
  111.         return config.containsKey(makePath(key));
  112.     }

  113.     /**
  114.      * Tests whether this configuration contains one or more matches to this value. This operation stops at first
  115.      * match but may be more expensive than the containsKey method.
  116.      * @since 2.11.0
  117.      */
  118.     @Override
  119.     protected boolean containsValueInternal(final Object value) {
  120.         return config.containsValue(value);
  121.     }

  122.     /**
  123.      * Returns the wrapped configuration as a {@code FileBased} object. If this cast is not possible, an exception is
  124.      * thrown.
  125.      *
  126.      * @return the wrapped configuration as {@code FileBased}
  127.      * @throws ConfigurationException if the wrapped configuration does not implement {@code FileBased}
  128.      */
  129.     private FileBased fetchFileBased() throws ConfigurationException {
  130.         if (!(config instanceof FileBased)) {
  131.             throw new ConfigurationException("Wrapped configuration does not implement FileBased!" + " No I/O operations are supported.");
  132.         }
  133.         return (FileBased) config;
  134.     }

  135.     @Override
  136.     public BigDecimal getBigDecimal(final String key) {
  137.         return config.getBigDecimal(makePath(key));
  138.     }

  139.     @Override
  140.     public BigDecimal getBigDecimal(final String key, final BigDecimal defaultValue) {
  141.         return config.getBigDecimal(makePath(key), defaultValue);
  142.     }

  143.     @Override
  144.     public BigInteger getBigInteger(final String key) {
  145.         return config.getBigInteger(makePath(key));
  146.     }

  147.     @Override
  148.     public BigInteger getBigInteger(final String key, final BigInteger defaultValue) {
  149.         return config.getBigInteger(makePath(key), defaultValue);
  150.     }

  151.     @Override
  152.     public boolean getBoolean(final String key) {
  153.         return config.getBoolean(makePath(key));
  154.     }

  155.     @Override
  156.     public boolean getBoolean(final String key, final boolean defaultValue) {
  157.         return config.getBoolean(makePath(key), defaultValue);
  158.     }

  159.     @Override
  160.     public Boolean getBoolean(final String key, final Boolean defaultValue) {
  161.         return config.getBoolean(makePath(key), defaultValue);
  162.     }

  163.     @Override
  164.     public byte getByte(final String key) {
  165.         return config.getByte(makePath(key));
  166.     }

  167.     @Override
  168.     public byte getByte(final String key, final byte defaultValue) {
  169.         return config.getByte(makePath(key), defaultValue);
  170.     }

  171.     @Override
  172.     public Byte getByte(final String key, final Byte defaultValue) {
  173.         return config.getByte(makePath(key), defaultValue);
  174.     }

  175.     private BaseHierarchicalConfiguration getConfig() {
  176.         return (BaseHierarchicalConfiguration) config.configurationAt(makePath());
  177.     }

  178.     @Override
  179.     public double getDouble(final String key) {
  180.         return config.getDouble(makePath(key));
  181.     }

  182.     @Override
  183.     public double getDouble(final String key, final double defaultValue) {
  184.         return config.getDouble(makePath(key), defaultValue);
  185.     }

  186.     @Override
  187.     public Double getDouble(final String key, final Double defaultValue) {
  188.         return config.getDouble(makePath(key), defaultValue);
  189.     }

  190.     @Override
  191.     public <T extends Event> Collection<EventListener<? super T>> getEventListeners(final EventType<T> eventType) {
  192.         return getConfig().getEventListeners(eventType);
  193.     }

  194.     @Override
  195.     public ExpressionEngine getExpressionEngine() {
  196.         return config.getExpressionEngine();
  197.     }

  198.     @Override
  199.     public float getFloat(final String key) {
  200.         return config.getFloat(makePath(key));
  201.     }

  202.     @Override
  203.     public float getFloat(final String key, final float defaultValue) {
  204.         return config.getFloat(makePath(key), defaultValue);
  205.     }

  206.     @Override
  207.     public Float getFloat(final String key, final Float defaultValue) {
  208.         return config.getFloat(makePath(key), defaultValue);
  209.     }

  210.     @Override
  211.     public int getInt(final String key) {
  212.         return config.getInt(makePath(key));
  213.     }

  214.     @Override
  215.     public int getInt(final String key, final int defaultValue) {
  216.         return config.getInt(makePath(key), defaultValue);
  217.     }

  218.     @Override
  219.     public Integer getInteger(final String key, final Integer defaultValue) {
  220.         return config.getInteger(makePath(key), defaultValue);
  221.     }

  222.     @Override
  223.     protected Iterator<String> getKeysInternal() {
  224.         return config.getKeys(makePath());
  225.     }

  226.     @Override
  227.     protected Iterator<String> getKeysInternal(final String prefix) {
  228.         return config.getKeys(makePath(prefix));
  229.     }

  230.     @Override
  231.     public List<Object> getList(final String key) {
  232.         return config.getList(makePath(key));
  233.     }

  234.     @Override
  235.     public List<Object> getList(final String key, final List<?> defaultValue) {
  236.         return config.getList(makePath(key), defaultValue);
  237.     }

  238.     @Override
  239.     public long getLong(final String key) {
  240.         return config.getLong(makePath(key));
  241.     }

  242.     @Override
  243.     public long getLong(final String key, final long defaultValue) {
  244.         return config.getLong(makePath(key), defaultValue);
  245.     }

  246.     @Override
  247.     public Long getLong(final String key, final Long defaultValue) {
  248.         return config.getLong(makePath(key), defaultValue);
  249.     }

  250.     @Override
  251.     protected int getMaxIndexInternal(final String key) {
  252.         return config.getMaxIndex(makePath(key));
  253.     }

  254.     @Override
  255.     public Properties getProperties(final String key) {
  256.         return config.getProperties(makePath(key));
  257.     }

  258.     @Override
  259.     protected Object getPropertyInternal(final String key) {
  260.         return config.getProperty(makePath(key));
  261.     }

  262.     @Override
  263.     public short getShort(final String key) {
  264.         return config.getShort(makePath(key));
  265.     }

  266.     @Override
  267.     public short getShort(final String key, final short defaultValue) {
  268.         return config.getShort(makePath(key), defaultValue);
  269.     }

  270.     @Override
  271.     public Short getShort(final String key, final Short defaultValue) {
  272.         return config.getShort(makePath(key), defaultValue);
  273.     }

  274.     @Override
  275.     public String getString(final String key) {
  276.         return config.getString(makePath(key));
  277.     }

  278.     @Override
  279.     public String getString(final String key, final String defaultValue) {
  280.         return config.getString(makePath(key), defaultValue);
  281.     }

  282.     @Override
  283.     public String[] getStringArray(final String key) {
  284.         return config.getStringArray(makePath(key));
  285.     }

  286.     @Override
  287.     public Configuration interpolatedConfiguration() {
  288.         return getConfig().interpolatedConfiguration();
  289.     }

  290.     @Override
  291.     protected boolean isEmptyInternal() {
  292.         return getConfig().isEmpty();
  293.     }

  294.     private String makePath() {
  295.         final String pathPattern = trailing ? path.substring(0, path.length() - 1) : path;
  296.         return substitute(pathPattern);
  297.     }

  298.     /*
  299.      * Resolve the root expression and then add the item being retrieved. Insert a separator character as required.
  300.      */
  301.     private String makePath(final String item) {
  302.         final String pathPattern;
  303.         if ((item.isEmpty() || item.startsWith("/")) && trailing) {
  304.             pathPattern = path.substring(0, path.length() - 1);
  305.         } else if (!item.startsWith("/") || !trailing) {
  306.             pathPattern = path + "/";
  307.         } else {
  308.             pathPattern = path;
  309.         }
  310.         return substitute(pathPattern) + item;
  311.     }

  312.     @Override
  313.     public void read(final Reader reader) throws ConfigurationException, IOException {
  314.         fetchFileBased().read(reader);
  315.     }

  316.     @Override
  317.     public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
  318.         return getConfig().removeEventListener(eventType, listener);
  319.     }

  320.     @Override
  321.     public void setExpressionEngine(final ExpressionEngine expressionEngine) {
  322.         if (init) {
  323.             config.setExpressionEngine(expressionEngine);
  324.         } else {
  325.             super.setExpressionEngine(expressionEngine);
  326.         }
  327.     }

  328.     @Override
  329.     protected void setPropertyInternal(final String key, final Object value) {
  330.         getConfig().setProperty(key, value);
  331.     }

  332.     @Override
  333.     public Configuration subset(final String prefix) {
  334.         return getConfig().subset(prefix);
  335.     }

  336.     /**
  337.      * Uses this configuration's {@code ConfigurationInterpolator} to perform variable substitution on the given pattern
  338.      * string.
  339.      *
  340.      * @param pattern the pattern string
  341.      * @return the string with variables replaced
  342.      */
  343.     private String substitute(final String pattern) {
  344.         return Objects.toString(getInterpolator().interpolate(pattern), null);
  345.     }

  346.     @Override
  347.     public void write(final Writer writer) throws ConfigurationException, IOException {
  348.         fetchFileBased().write(writer);
  349.     }
  350. }