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 19 import java.lang.reflect.InvocationHandler; 20 import java.lang.reflect.InvocationTargetException; 21 import java.lang.reflect.Method; 22 import java.util.Iterator; 23 import java.util.Objects; 24 25 /** 26 * <p> 27 * A specialized {@code InvocationHandler} implementation for supporting immutable configurations. 28 * </p> 29 * <p> 30 * An instance of this class is constructed with a reference to a {@code Configuration} object. All method invocations 31 * (which stem from the {@code ImmutableConfiguration} interface) are delegated to this object. That way all 32 * functionality is actually backed by the underlying {@code Configuration} implementation, but because the associated 33 * proxy only implements the {@code ImmutableConfiguration} interface manipulations are not possible. 34 * </p> 35 * <p> 36 * There is one caveat however: Some methods of the {@code ImmutableConfiguration} interface return an {@code Iterator} 37 * object. Using the iterator's {@code remove()} method it may be possible to remove keys from the underlying 38 * {@code Configuration} object. Therefore, in these cases a specialized {@code Iterator} is returned which does not 39 * support the remove operation. 40 * </p> 41 * 42 * @since 2.0 43 */ 44 final class ImmutableConfigurationInvocationHandler implements InvocationHandler { 45 /** The underlying configuration object. */ 46 private final Configuration wrappedConfiguration; 47 48 /** 49 * Creates a new instance of {@code ImmutableConfigurationInvocationHandler} and initializes it with the wrapped 50 * configuration object. 51 * 52 * @param configuration the wrapped {@code Configuration} (must not be <b>null</b>) 53 * @throws NullPointerException if the {@code Configuration} is <b>null</b> 54 */ 55 public ImmutableConfigurationInvocationHandler(final Configuration configuration) { 56 wrappedConfiguration = Objects.requireNonNull(configuration, "configuration"); 57 } 58 59 /** 60 * {@inheritDoc} This implementation delegates to the wrapped configuration object. Result objects are wrapped if 61 * necessary. 62 */ 63 @Override 64 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 65 try { 66 return handleResult(method.invoke(wrappedConfiguration, args)); 67 } catch (final InvocationTargetException e) { 68 // unwrap 69 throw e.getCause(); 70 } 71 } 72 73 /** 74 * Handles the result from the method invocation on the wrapped configuration. This implementation wraps result objects 75 * if necessary so that the underlying configuration cannot be manipulated. 76 * 77 * @param result the result object 78 * @return the processed result object 79 */ 80 private static Object handleResult(final Object result) { 81 if (result instanceof Iterator) { 82 return new ImmutableIterator((Iterator<?>) result); 83 } 84 return result; 85 } 86 87 /** 88 * A specialized {@code Iterator} implementation which delegates to an underlying iterator, but does not support the 89 * {@code remove()} method. 90 */ 91 private static final class ImmutableIterator implements Iterator<Object> { 92 /** The underlying iterator. */ 93 private final Iterator<?> wrappedIterator; 94 95 /** 96 * Creates a new instance of {@code ImmutableIterator} and sets the underlying iterator. 97 * 98 * @param it the underlying iterator 99 */ 100 public ImmutableIterator(final Iterator<?> it) { 101 wrappedIterator = it; 102 } 103 104 /** 105 * {@inheritDoc} This implementation just delegates to the underlying iterator. 106 */ 107 @Override 108 public boolean hasNext() { 109 return wrappedIterator.hasNext(); 110 } 111 112 /** 113 * {@inheritDoc} This implementation just delegates to the underlying iterator. 114 */ 115 @Override 116 public Object next() { 117 return wrappedIterator.next(); 118 } 119 120 /** 121 * {@inheritDoc} This implementation just throws an exception: removing objects is not supported. 122 */ 123 @Override 124 public void remove() { 125 throw new UnsupportedOperationException("remove() operation not supported!"); 126 } 127 } 128 }