ImmutableConfigurationInvocationHandler.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.lang.reflect.InvocationHandler;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.util.Iterator;
  22. import java.util.Objects;

  23. /**
  24.  * <p>
  25.  * A specialized {@code InvocationHandler} implementation for supporting immutable configurations.
  26.  * </p>
  27.  * <p>
  28.  * An instance of this class is constructed with a reference to a {@code Configuration} object. All method invocations
  29.  * (which stem from the {@code ImmutableConfiguration} interface) are delegated to this object. That way all
  30.  * functionality is actually backed by the underlying {@code Configuration} implementation, but because the associated
  31.  * proxy only implements the {@code ImmutableConfiguration} interface manipulations are not possible.
  32.  * </p>
  33.  * <p>
  34.  * There is one caveat however: Some methods of the {@code ImmutableConfiguration} interface return an {@code Iterator}
  35.  * object. Using the iterator's {@code remove()} method it may be possible to remove keys from the underlying
  36.  * {@code Configuration} object. Therefore, in these cases a specialized {@code Iterator} is returned which does not
  37.  * support the remove operation.
  38.  * </p>
  39.  *
  40.  * @since 2.0
  41.  */
  42. final class ImmutableConfigurationInvocationHandler implements InvocationHandler {
  43.     /**
  44.      * A specialized {@code Iterator} implementation which delegates to an underlying iterator, but does not support the
  45.      * {@code remove()} method.
  46.      */
  47.     private static final class ImmutableIterator implements Iterator<Object> {
  48.         /** The underlying iterator. */
  49.         private final Iterator<?> wrappedIterator;

  50.         /**
  51.          * Creates a new instance of {@code ImmutableIterator} and sets the underlying iterator.
  52.          *
  53.          * @param it the underlying iterator
  54.          */
  55.         public ImmutableIterator(final Iterator<?> it) {
  56.             wrappedIterator = it;
  57.         }

  58.         /**
  59.          * {@inheritDoc} This implementation just delegates to the underlying iterator.
  60.          */
  61.         @Override
  62.         public boolean hasNext() {
  63.             return wrappedIterator.hasNext();
  64.         }

  65.         /**
  66.          * {@inheritDoc} This implementation just delegates to the underlying iterator.
  67.          */
  68.         @Override
  69.         public Object next() {
  70.             return wrappedIterator.next();
  71.         }

  72.         /**
  73.          * {@inheritDoc} This implementation just throws an exception: removing objects is not supported.
  74.          */
  75.         @Override
  76.         public void remove() {
  77.             throw new UnsupportedOperationException("remove() operation not supported!");
  78.         }
  79.     }

  80.     /**
  81.      * Handles the result from the method invocation on the wrapped configuration. This implementation wraps result objects
  82.      * if necessary so that the underlying configuration cannot be manipulated.
  83.      *
  84.      * @param result the result object
  85.      * @return the processed result object
  86.      */
  87.     private static Object handleResult(final Object result) {
  88.         if (result instanceof Iterator) {
  89.             return new ImmutableIterator((Iterator<?>) result);
  90.         }
  91.         return result;
  92.     }

  93.     /** The underlying configuration object. */
  94.     private final Configuration wrappedConfiguration;

  95.     /**
  96.      * Creates a new instance of {@code ImmutableConfigurationInvocationHandler} and initializes it with the wrapped
  97.      * configuration object.
  98.      *
  99.      * @param configuration the wrapped {@code Configuration} (must not be <strong>null</strong>)
  100.      * @throws NullPointerException if the {@code Configuration} is <strong>null</strong>
  101.      */
  102.     public ImmutableConfigurationInvocationHandler(final Configuration configuration) {
  103.         wrappedConfiguration = Objects.requireNonNull(configuration, "configuration");
  104.     }

  105.     /**
  106.      * {@inheritDoc} This implementation delegates to the wrapped configuration object. Result objects are wrapped if
  107.      * necessary.
  108.      */
  109.     @Override
  110.     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
  111.         try {
  112.             return handleResult(method.invoke(wrappedConfiguration, args));
  113.         } catch (final InvocationTargetException e) {
  114.             // unwrap
  115.             throw e.getCause();
  116.         }
  117.     }
  118. }