MultiWrapDynaBean.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.builder.combined;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.HashMap;
  21. import java.util.Map;

  22. import org.apache.commons.beanutils.DynaBean;
  23. import org.apache.commons.beanutils.DynaClass;
  24. import org.apache.commons.beanutils.DynaProperty;
  25. import org.apache.commons.configuration2.beanutils.BeanHelper;

  26. /**
  27.  * <p>
  28.  * An implementation of the {@code DynaBean} interfaces which wraps multiple other beans.
  29.  * </p>
  30.  * <p>
  31.  * An instance of this class is constructed with a collection of beans to be wrapped. When reading or writing a property
  32.  * the wrapped bean which defines this property is determined, and the operation is executed on this bean.
  33.  * </p>
  34.  * <p>
  35.  * The wrapped beans should have disjunct properties. Otherwise, it is undefined which bean property is read or written.
  36.  * </p>
  37.  *
  38.  * @since 2.0
  39.  */
  40. final class MultiWrapDynaBean implements DynaBean {
  41.     /**
  42.      * Creates a {@code DynaBean} object for the given bean.
  43.      *
  44.      * @param bean the bean
  45.      * @return the {@code DynaBean} for this bean
  46.      */
  47.     private static DynaBean createDynaBean(final Object bean) {
  48.         if (bean instanceof DynaBean) {
  49.             return (DynaBean) bean;
  50.         }
  51.         return BeanHelper.createWrapDynaBean(bean);
  52.     }

  53.     /** Stores the class of this DynaBean. */
  54.     private final DynaClass dynaClass;

  55.     /** A map which associates property names with their defining beans. */
  56.     private final Map<String, DynaBean> propsToBeans;

  57.     /**
  58.      * Creates a new instance of {@code MultiWrapDynaBean} and initializes it with the given collections of beans to be
  59.      * wrapped.
  60.      *
  61.      * @param beans the wrapped beans
  62.      */
  63.     public MultiWrapDynaBean(final Collection<?> beans) {
  64.         propsToBeans = new HashMap<>();
  65.         final Collection<DynaClass> beanClasses = new ArrayList<>(beans.size());

  66.         beans.forEach(bean -> {
  67.             final DynaBean dynaBean = createDynaBean(bean);
  68.             final DynaClass beanClass = dynaBean.getDynaClass();
  69.             for (final DynaProperty prop : beanClass.getDynaProperties()) {
  70.                 // ensure an order of properties
  71.                 propsToBeans.putIfAbsent(prop.getName(), dynaBean);
  72.             }
  73.             beanClasses.add(beanClass);
  74.         });

  75.         dynaClass = new MultiWrapDynaClass(beanClasses);
  76.     }

  77.     /**
  78.      * {@inheritDoc} This operation is not supported by the {@code WrapDynaBean} objects used internally by this class.
  79.      * Therefore, just an exception is thrown.
  80.      */
  81.     @Override
  82.     public boolean contains(final String name, final String key) {
  83.         throw new UnsupportedOperationException("contains() operation not supported!");
  84.     }

  85.     /**
  86.      * Returns the bean instance to which the given property belongs. If no such bean is found, an arbitrary bean is
  87.      * returned. (This causes the operation on this bean to fail with a meaningful error message.)
  88.      *
  89.      * @param property the property name
  90.      * @return the bean defining this property
  91.      */
  92.     private DynaBean fetchBean(final String property) {
  93.         DynaBean dynaBean = propsToBeans.get(property);
  94.         if (dynaBean == null) {
  95.             dynaBean = propsToBeans.values().iterator().next();
  96.         }
  97.         return dynaBean;
  98.     }

  99.     @Override
  100.     public Object get(final String name) {
  101.         return fetchBean(name).get(name);
  102.     }

  103.     @Override
  104.     public Object get(final String name, final int index) {
  105.         return fetchBean(name).get(name, index);
  106.     }

  107.     @Override
  108.     public Object get(final String name, final String key) {
  109.         return fetchBean(name).get(name, key);
  110.     }

  111.     /**
  112.      * {@inheritDoc} This implementation returns an instance of {@code MultiWrapDynaClass}.
  113.      */
  114.     @Override
  115.     public DynaClass getDynaClass() {
  116.         return dynaClass;
  117.     }

  118.     /**
  119.      * {@inheritDoc} This operation is not supported by the {@code WrapDynaBean} objects used internally by this class.
  120.      * Therefore, just an exception is thrown.
  121.      */
  122.     @Override
  123.     public void remove(final String name, final String key) {
  124.         throw new UnsupportedOperationException("remove() operation not supported!");
  125.     }

  126.     @Override
  127.     public void set(final String name, final int index, final Object value) {
  128.         fetchBean(name).set(name, index, value);
  129.     }

  130.     @Override
  131.     public void set(final String name, final Object value) {
  132.         fetchBean(name).set(name, value);
  133.     }

  134.     @Override
  135.     public void set(final String name, final String key, final Object value) {
  136.         fetchBean(name).set(name, key, value);
  137.     }
  138. }