001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.multimap; 018 019import java.util.Iterator; 020import java.util.Map; 021 022import org.apache.commons.collections4.CollectionUtils; 023import org.apache.commons.collections4.FluentIterable; 024import org.apache.commons.collections4.MultiValuedMap; 025import org.apache.commons.collections4.Transformer; 026 027/** 028 * Decorates another <code>MultiValuedMap</code> to transform objects that are added. 029 * <p> 030 * This class affects the MultiValuedMap put methods. Thus objects must be 031 * removed or searched for using their transformed form. For example, if the 032 * transformation converts Strings to Integers, you must use the Integer form to 033 * remove objects. 034 * <p> 035 * <strong>Note that TransformedMultiValuedMap is not synchronized and is not thread-safe.</strong> 036 * 037 * @param <K> the type of the keys in this map 038 * @param <V> the type of the values in this map 039 * @since 4.1 040 */ 041public class TransformedMultiValuedMap<K, V> extends AbstractMultiValuedMapDecorator<K, V> { 042 043 /** Serialization Version */ 044 private static final long serialVersionUID = 20150612L; 045 046 /** The key transformer */ 047 private final Transformer<? super K, ? extends K> keyTransformer; 048 049 /** The value transformer */ 050 private final Transformer<? super V, ? extends V> valueTransformer; 051 052 /** 053 * Factory method to create a transforming MultiValuedMap. 054 * <p> 055 * If there are any elements already in the map being decorated, they are 056 * NOT transformed. Contrast this with 057 * {@link #transformedMap(MultiValuedMap, Transformer, Transformer)}. 058 * 059 * @param <K> the key type 060 * @param <V> the value type 061 * @param map the MultiValuedMap to decorate, may not be null 062 * @param keyTransformer the transformer to use for key conversion, null means no conversion 063 * @param valueTransformer the transformer to use for value conversion, null means no conversion 064 * @return a new transformed MultiValuedMap 065 * @throws NullPointerException if map is null 066 */ 067 public static <K, V> TransformedMultiValuedMap<K, V> transformingMap(final MultiValuedMap<K, V> map, 068 final Transformer<? super K, ? extends K> keyTransformer, 069 final Transformer<? super V, ? extends V> valueTransformer) { 070 return new TransformedMultiValuedMap<>(map, keyTransformer, valueTransformer); 071 } 072 073 /** 074 * Factory method to create a transforming MultiValuedMap that will 075 * transform existing contents of the specified map. 076 * <p> 077 * If there are any elements already in the map being decorated, they will 078 * be transformed by this method. Contrast this with 079 * {@link #transformingMap(MultiValuedMap, Transformer, Transformer)}. 080 * 081 * @param <K> the key type 082 * @param <V> the value type 083 * @param map the MultiValuedMap to decorate, may not be null 084 * @param keyTransformer the transformer to use for key conversion, null means no conversion 085 * @param valueTransformer the transformer to use for value conversion, null means no conversion 086 * @return a new transformed MultiValuedMap 087 * @throws NullPointerException if map is null 088 */ 089 public static <K, V> TransformedMultiValuedMap<K, V> transformedMap(final MultiValuedMap<K, V> map, 090 final Transformer<? super K, ? extends K> keyTransformer, 091 final Transformer<? super V, ? extends V> valueTransformer) { 092 final TransformedMultiValuedMap<K, V> decorated = 093 new TransformedMultiValuedMap<>(map, keyTransformer, valueTransformer); 094 if (!map.isEmpty()) { 095 final MultiValuedMap<K, V> mapCopy = new ArrayListValuedHashMap<>(map); 096 decorated.clear(); 097 decorated.putAll(mapCopy); 098 } 099 return decorated; 100 } 101 102 // ----------------------------------------------------------------------- 103 /** 104 * Constructor that wraps (not copies). 105 * <p> 106 * If there are any elements already in the collection being decorated, they 107 * are NOT transformed. 108 * 109 * @param map the MultiValuedMap to decorate, may not be null 110 * @param keyTransformer the transformer to use for key conversion, null means no conversion 111 * @param valueTransformer the transformer to use for value conversion, null means no conversion 112 * @throws NullPointerException if map is null 113 */ 114 protected TransformedMultiValuedMap(final MultiValuedMap<K, V> map, 115 final Transformer<? super K, ? extends K> keyTransformer, 116 final Transformer<? super V, ? extends V> valueTransformer) { 117 super(map); 118 this.keyTransformer = keyTransformer; 119 this.valueTransformer = valueTransformer; 120 } 121 122 /** 123 * Transforms a key. 124 * <p> 125 * The transformer itself may throw an exception if necessary. 126 * 127 * @param object the object to transform 128 * @return the transformed object 129 */ 130 protected K transformKey(final K object) { 131 if (keyTransformer == null) { 132 return object; 133 } 134 return keyTransformer.transform(object); 135 } 136 137 /** 138 * Transforms a value. 139 * <p> 140 * The transformer itself may throw an exception if necessary. 141 * 142 * @param object the object to transform 143 * @return the transformed object 144 */ 145 protected V transformValue(final V object) { 146 if (valueTransformer == null) { 147 return object; 148 } 149 return valueTransformer.transform(object); 150 } 151 152 @Override 153 public boolean put(final K key, final V value) { 154 return decorated().put(transformKey(key), transformValue(value)); 155 } 156 157 @Override 158 public boolean putAll(final K key, final Iterable<? extends V> values) { 159 if (values == null) { 160 throw new NullPointerException("Values must not be null."); 161 } 162 163 final Iterable<V> transformedValues = FluentIterable.of(values).transform(valueTransformer); 164 final Iterator<? extends V> it = transformedValues.iterator(); 165 return it.hasNext() && CollectionUtils.addAll(decorated().get(transformKey(key)), it); 166 } 167 168 @Override 169 public boolean putAll(final Map<? extends K, ? extends V> map) { 170 if (map == null) { 171 throw new NullPointerException("Map must not be null."); 172 } 173 boolean changed = false; 174 for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) { 175 changed |= put(entry.getKey(), entry.getValue()); 176 } 177 return changed; 178 } 179 180 @Override 181 public boolean putAll(final MultiValuedMap<? extends K, ? extends V> map) { 182 if (map == null) { 183 throw new NullPointerException("Map must not be null."); 184 } 185 boolean changed = false; 186 for (final Map.Entry<? extends K, ? extends V> entry : map.entries()) { 187 changed |= put(entry.getKey(), entry.getValue()); 188 } 189 return changed; 190 } 191 192}