AbstractInputCheckedMapDecorator.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.collections4.map;

  18. import java.lang.reflect.Array;
  19. import java.util.Iterator;
  20. import java.util.Map;
  21. import java.util.Set;

  22. import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
  23. import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
  24. import org.apache.commons.collections4.set.AbstractSetDecorator;

  25. /**
  26.  * An abstract base class that simplifies the task of creating map decorators.
  27.  * <p>
  28.  * The Map API is very difficult to decorate correctly, and involves implementing
  29.  * lots of different classes. This class exists to provide a simpler API.
  30.  * </p>
  31.  * <p>
  32.  * Special hook methods are provided that are called when objects are added to
  33.  * the map. By overriding these methods, the input can be validated or manipulated.
  34.  * In addition to the main map methods, the entrySet is also affected, which is
  35.  * the hardest part of writing map implementations.
  36.  * </p>
  37.  * <p>
  38.  * This class is package-scoped, and may be withdrawn or replaced in future
  39.  * versions of Commons Collections.
  40.  * </p>
  41.  *
  42.  * @since 3.1
  43.  */
  44. abstract class AbstractInputCheckedMapDecorator<K, V>
  45.         extends AbstractMapDecorator<K, V> {

  46.     /**
  47.      * Implements an entry set that checks additions via setValue.
  48.      */
  49.     private final class EntrySet extends AbstractSetDecorator<Map.Entry<K, V>> {

  50.         /** Generated serial version ID. */
  51.         private static final long serialVersionUID = 4354731610923110264L;

  52.         /** The parent map */
  53.         private final AbstractInputCheckedMapDecorator<K, V> parent;

  54.         protected EntrySet(final Set<Map.Entry<K, V>> set, final AbstractInputCheckedMapDecorator<K, V> parent) {
  55.             super(set);
  56.             this.parent = parent;
  57.         }

  58.         @Override
  59.         public Iterator<Map.Entry<K, V>> iterator() {
  60.             return new EntrySetIterator(decorated().iterator(), parent);
  61.         }

  62.         @Override
  63.         @SuppressWarnings("unchecked")
  64.         public Object[] toArray() {
  65.             final Object[] array = decorated().toArray();
  66.             for (int i = 0; i < array.length; i++) {
  67.                 array[i] = new MapEntry((Map.Entry<K, V>) array[i], parent);
  68.             }
  69.             return array;
  70.         }

  71.         @Override
  72.         @SuppressWarnings("unchecked")
  73.         public <T> T[] toArray(final T[] array) {
  74.             Object[] result = array;
  75.             if (array.length > 0) {
  76.                 // we must create a new array to handle multithreaded situations
  77.                 // where another thread could access data before we decorate it
  78.                 result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0);
  79.             }
  80.             result = decorated().toArray(result);
  81.             for (int i = 0; i < result.length; i++) {
  82.                 result[i] = new MapEntry((Map.Entry<K, V>) result[i], parent);
  83.             }

  84.             // check to see if result should be returned straight
  85.             if (result.length > array.length) {
  86.                 return (T[]) result;
  87.             }

  88.             // copy back into input array to fulfil the method contract
  89.             System.arraycopy(result, 0, array, 0, result.length);
  90.             if (array.length > result.length) {
  91.                 array[result.length] = null;
  92.             }
  93.             return array;
  94.         }
  95.     }

  96.     /**
  97.      * Implements an entry set iterator that checks additions via setValue.
  98.      */
  99.     private final class EntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, V>> {

  100.         /** The parent map */
  101.         private final AbstractInputCheckedMapDecorator<K, V> parent;

  102.         protected EntrySetIterator(final Iterator<Map.Entry<K, V>> iterator,
  103.                                    final AbstractInputCheckedMapDecorator<K, V> parent) {
  104.             super(iterator);
  105.             this.parent = parent;
  106.         }

  107.         @Override
  108.         public Map.Entry<K, V> next() {
  109.             final Map.Entry<K, V> entry = getIterator().next();
  110.             return new MapEntry(entry, parent);
  111.         }
  112.     }

  113.     /**
  114.      * Implements a map entry that checks additions via setValue.
  115.      */
  116.     private final class MapEntry extends AbstractMapEntryDecorator<K, V> {

  117.         /** The parent map */
  118.         private final AbstractInputCheckedMapDecorator<K, V> parent;

  119.         protected MapEntry(final Map.Entry<K, V> entry, final AbstractInputCheckedMapDecorator<K, V> parent) {
  120.             super(entry);
  121.             this.parent = parent;
  122.         }

  123.         @Override
  124.         public V setValue(V value) {
  125.             value = parent.checkSetValue(value);
  126.             return getMapEntry().setValue(value);
  127.         }
  128.     }

  129.     /**
  130.      * Constructor only used in deserialization, do not use otherwise.
  131.      */
  132.     protected AbstractInputCheckedMapDecorator() {
  133.     }

  134.     /**
  135.      * Constructor that wraps (not copies).
  136.      *
  137.      * @param map  the map to decorate, must not be null
  138.      * @throws NullPointerException if map is null
  139.      */
  140.     protected AbstractInputCheckedMapDecorator(final Map<K, V> map) {
  141.         super(map);
  142.     }

  143.     /**
  144.      * Hook method called when a value is being set using {@code setValue}.
  145.      * <p>
  146.      * An implementation may validate the value and throw an exception
  147.      * or it may transform the value into another object.
  148.      * </p>
  149.      * <p>
  150.      * This implementation returns the input value.
  151.      * </p>
  152.      *
  153.      * @param value  the value to check
  154.      * @return the input value
  155.      * @throws UnsupportedOperationException if the map may not be changed by setValue
  156.      * @throws IllegalArgumentException if the specified value is invalid
  157.      * @throws ClassCastException if the class of the specified value is invalid
  158.      * @throws NullPointerException if the specified value is null and nulls are invalid
  159.      */
  160.     protected abstract V checkSetValue(V value);

  161.     @Override
  162.     public Set<Map.Entry<K, V>> entrySet() {
  163.         if (isSetValueChecking()) {
  164.             return new EntrySet(map.entrySet(), this);
  165.         }
  166.         return map.entrySet();
  167.     }

  168.     /**
  169.      * Hook method called to determine if {@code checkSetValue} has any effect.
  170.      * <p>
  171.      * An implementation should return false if the {@code checkSetValue} method
  172.      * has no effect as this optimizes the implementation.
  173.      * <p>
  174.      * This implementation returns {@code true}.
  175.      *
  176.      * @return true always
  177.      */
  178.     protected boolean isSetValueChecking() {
  179.         return true;
  180.     }

  181. }