TransformedMultiValuedMap.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.multimap;

  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import java.util.Objects;

  21. import org.apache.commons.collections4.CollectionUtils;
  22. import org.apache.commons.collections4.FluentIterable;
  23. import org.apache.commons.collections4.MultiValuedMap;
  24. import org.apache.commons.collections4.Transformer;

  25. /**
  26.  * Decorates another {@code MultiValuedMap} to transform objects that are added.
  27.  * <p>
  28.  * This class affects the MultiValuedMap put methods. Thus objects must be
  29.  * removed or searched for using their transformed form. For example, if the
  30.  * transformation converts Strings to Integers, you must use the Integer form to
  31.  * remove objects.
  32.  * </p>
  33.  * <p>
  34.  * <strong>Note that TransformedMultiValuedMap is not synchronized and is not thread-safe.</strong>
  35.  * </p>
  36.  *
  37.  * @param <K> the type of the keys in this map
  38.  * @param <V> the type of the values in this map
  39.  * @since 4.1
  40.  */
  41. public class TransformedMultiValuedMap<K, V> extends AbstractMultiValuedMapDecorator<K, V> {

  42.     /** Serialization Version */
  43.     private static final long serialVersionUID = 20150612L;

  44.     /**
  45.      * Factory method to create a transforming MultiValuedMap that will
  46.      * transform existing contents of the specified map.
  47.      * <p>
  48.      * If there are any elements already in the map being decorated, they will
  49.      * be transformed by this method. Contrast this with
  50.      * {@link #transformingMap(MultiValuedMap, Transformer, Transformer)}.
  51.      * </p>
  52.      *
  53.      * @param <K> the key type
  54.      * @param <V> the value type
  55.      * @param map  the MultiValuedMap to decorate, may not be null
  56.      * @param keyTransformer  the transformer to use for key conversion, null means no conversion
  57.      * @param valueTransformer  the transformer to use for value conversion, null means no conversion
  58.      * @return a new transformed MultiValuedMap
  59.      * @throws NullPointerException if map is null
  60.      */
  61.     public static <K, V> TransformedMultiValuedMap<K, V> transformedMap(final MultiValuedMap<K, V> map,
  62.             final Transformer<? super K, ? extends K> keyTransformer,
  63.             final Transformer<? super V, ? extends V> valueTransformer) {
  64.         final TransformedMultiValuedMap<K, V> decorated =
  65.                 new TransformedMultiValuedMap<>(map, keyTransformer, valueTransformer);
  66.         if (!map.isEmpty()) {
  67.             final MultiValuedMap<K, V> mapCopy = new ArrayListValuedHashMap<>(map);
  68.             decorated.clear();
  69.             decorated.putAll(mapCopy);
  70.         }
  71.         return decorated;
  72.     }

  73.     /**
  74.      * Factory method to create a transforming MultiValuedMap.
  75.      * <p>
  76.      * If there are any elements already in the map being decorated, they are
  77.      * NOT transformed. Contrast this with
  78.      * {@link #transformedMap(MultiValuedMap, Transformer, Transformer)}.
  79.      * </p>
  80.      *
  81.      * @param <K> the key type
  82.      * @param <V> the value type
  83.      * @param map  the MultiValuedMap to decorate, may not be null
  84.      * @param keyTransformer  the transformer to use for key conversion, null means no conversion
  85.      * @param valueTransformer  the transformer to use for value conversion, null means no conversion
  86.      * @return a new transformed MultiValuedMap
  87.      * @throws NullPointerException if map is null
  88.      */
  89.     public static <K, V> TransformedMultiValuedMap<K, V> transformingMap(final MultiValuedMap<K, V> map,
  90.             final Transformer<? super K, ? extends K> keyTransformer,
  91.             final Transformer<? super V, ? extends V> valueTransformer) {
  92.         return new TransformedMultiValuedMap<>(map, keyTransformer, valueTransformer);
  93.     }

  94.     /** The key transformer */
  95.     private final Transformer<? super K, ? extends K> keyTransformer;

  96.     /** The value transformer */
  97.     private final Transformer<? super V, ? extends V> valueTransformer;

  98.     /**
  99.      * Constructor that wraps (not copies).
  100.      * <p>
  101.      * If there are any elements already in the collection being decorated, they
  102.      * are NOT transformed.
  103.      * </p>
  104.      *
  105.      * @param map  the MultiValuedMap to decorate, may not be null
  106.      * @param keyTransformer  the transformer to use for key conversion, null means no conversion
  107.      * @param valueTransformer  the transformer to use for value conversion, null means no conversion
  108.      * @throws NullPointerException if map is null
  109.      */
  110.     protected TransformedMultiValuedMap(final MultiValuedMap<K, V> map,
  111.             final Transformer<? super K, ? extends K> keyTransformer,
  112.             final Transformer<? super V, ? extends V> valueTransformer) {
  113.         super(map);
  114.         this.keyTransformer = keyTransformer;
  115.         this.valueTransformer = valueTransformer;
  116.     }

  117.     @Override
  118.     public boolean put(final K key, final V value) {
  119.         return decorated().put(transformKey(key), transformValue(value));
  120.     }

  121.     @Override
  122.     public boolean putAll(final K key, final Iterable<? extends V> values) {
  123.         Objects.requireNonNull(values, "values");

  124.         final Iterable<V> transformedValues = FluentIterable.of(values).transform(valueTransformer);
  125.         final Iterator<? extends V> it = transformedValues.iterator();
  126.         return it.hasNext() && CollectionUtils.addAll(decorated().get(transformKey(key)), it);
  127.     }

  128.     @Override
  129.     public boolean putAll(final Map<? extends K, ? extends V> map) {
  130.         Objects.requireNonNull(map, "map");
  131.         boolean changed = false;
  132.         for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
  133.             changed |= put(entry.getKey(), entry.getValue());
  134.         }
  135.         return changed;
  136.     }

  137.     @Override
  138.     public boolean putAll(final MultiValuedMap<? extends K, ? extends V> map) {
  139.         Objects.requireNonNull(map, "map");
  140.         boolean changed = false;
  141.         for (final Map.Entry<? extends K, ? extends V> entry : map.entries()) {
  142.             changed |= put(entry.getKey(), entry.getValue());
  143.         }
  144.         return changed;
  145.     }

  146.     /**
  147.      * Transforms a key.
  148.      * <p>
  149.      * The transformer itself may throw an exception if necessary.
  150.      * </p>
  151.      *
  152.      * @param object  the object to transform
  153.      * @return the transformed object
  154.      */
  155.     protected K transformKey(final K object) {
  156.         if (keyTransformer == null) {
  157.             return object;
  158.         }
  159.         return keyTransformer.apply(object);
  160.     }

  161.     /**
  162.      * Transforms a value.
  163.      * <p>
  164.      * The transformer itself may throw an exception if necessary.
  165.      * </p>
  166.      *
  167.      * @param object  the object to transform
  168.      * @return the transformed object
  169.      */
  170.     protected V transformValue(final V object) {
  171.         if (valueTransformer == null) {
  172.             return object;
  173.         }
  174.         return valueTransformer.apply(object);
  175.     }

  176. }