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; 018 019import java.util.Collection; 020import java.util.Map; 021import java.util.Set; 022 023import org.apache.commons.collections4.collection.UnmodifiableCollection; 024import org.apache.commons.collections4.iterators.UnmodifiableMapIterator; 025import org.apache.commons.collections4.map.EntrySetToMapIteratorAdapter; 026import org.apache.commons.collections4.map.UnmodifiableEntrySet; 027import org.apache.commons.collections4.set.UnmodifiableSet; 028 029/** 030 * Utilities for working with "split maps:" objects that implement {@link Put} 031 * and/or {@link Get} but not {@link Map}. 032 * 033 * @since 4.0 034 * 035 * @see Get 036 * @see Put 037 */ 038public class SplitMapUtils { 039 040 /** 041 * <code>SplitMapUtils</code> should not normally be instantiated. 042 */ 043 private SplitMapUtils() {} 044 045 //----------------------------------------------------------------------- 046 047 private static class WrappedGet<K, V> implements IterableMap<K, V>, Unmodifiable { 048 private final Get<K, V> get; 049 050 private WrappedGet(final Get<K, V> get) { 051 this.get = get; 052 } 053 054 @Override 055 public void clear() { 056 throw new UnsupportedOperationException(); 057 } 058 059 @Override 060 public boolean containsKey(final Object key) { 061 return get.containsKey(key); 062 } 063 064 @Override 065 public boolean containsValue(final Object value) { 066 return get.containsValue(value); 067 } 068 069 @Override 070 public Set<Map.Entry<K, V>> entrySet() { 071 return UnmodifiableEntrySet.unmodifiableEntrySet(get.entrySet()); 072 } 073 074 @Override 075 public boolean equals(final Object arg0) { 076 if (arg0 == this) { 077 return true; 078 } 079 return arg0 instanceof WrappedGet && ((WrappedGet<?, ?>) arg0).get.equals(this.get); 080 } 081 082 @Override 083 public V get(final Object key) { 084 return get.get(key); 085 } 086 087 @Override 088 public int hashCode() { 089 return ("WrappedGet".hashCode() << 4) | get.hashCode(); 090 } 091 092 @Override 093 public boolean isEmpty() { 094 return get.isEmpty(); 095 } 096 097 @Override 098 public Set<K> keySet() { 099 return UnmodifiableSet.unmodifiableSet(get.keySet()); 100 } 101 102 @Override 103 public V put(final K key, final V value) { 104 throw new UnsupportedOperationException(); 105 } 106 107 @Override 108 public void putAll(final Map<? extends K, ? extends V> t) { 109 throw new UnsupportedOperationException(); 110 } 111 112 @Override 113 public V remove(final Object key) { 114 return get.remove(key); 115 } 116 117 @Override 118 public int size() { 119 return get.size(); 120 } 121 122 @Override 123 public Collection<V> values() { 124 return UnmodifiableCollection.unmodifiableCollection(get.values()); 125 } 126 127 @Override 128 public MapIterator<K, V> mapIterator() { 129 MapIterator<K, V> it; 130 if (get instanceof IterableGet) { 131 it = ((IterableGet<K, V>) get).mapIterator(); 132 } else { 133 it = new EntrySetToMapIteratorAdapter<>(get.entrySet()); 134 } 135 return UnmodifiableMapIterator.unmodifiableMapIterator(it); 136 } 137 } 138 139 private static class WrappedPut<K, V> implements Map<K, V>, Put<K, V> { 140 private final Put<K, V> put; 141 142 private WrappedPut(final Put<K, V> put) { 143 this.put = put; 144 } 145 146 @Override 147 public void clear() { 148 put.clear(); 149 } 150 151 @Override 152 public boolean containsKey(final Object key) { 153 throw new UnsupportedOperationException(); 154 } 155 156 @Override 157 public boolean containsValue(final Object value) { 158 throw new UnsupportedOperationException(); 159 } 160 161 @Override 162 public Set<Map.Entry<K, V>> entrySet() { 163 throw new UnsupportedOperationException(); 164 } 165 166 @Override 167 public boolean equals(final Object obj) { 168 if (obj == this) { 169 return true; 170 } 171 return obj instanceof WrappedPut && ((WrappedPut<?, ?>) obj).put.equals(this.put); 172 } 173 174 @Override 175 public V get(final Object key) { 176 throw new UnsupportedOperationException(); 177 } 178 179 @Override 180 public int hashCode() { 181 return ("WrappedPut".hashCode() << 4) | put.hashCode(); 182 } 183 184 @Override 185 public boolean isEmpty() { 186 throw new UnsupportedOperationException(); 187 } 188 189 @Override 190 public Set<K> keySet() { 191 throw new UnsupportedOperationException(); 192 } 193 194 @Override 195 @SuppressWarnings("unchecked") 196 public V put(final K key, final V value) { 197 return (V) put.put(key, value); 198 } 199 200 @Override 201 public void putAll(final Map<? extends K, ? extends V> t) { 202 put.putAll(t); 203 } 204 205 @Override 206 public V remove(final Object key) { 207 throw new UnsupportedOperationException(); 208 } 209 210 @Override 211 public int size() { 212 throw new UnsupportedOperationException(); 213 } 214 215 @Override 216 public Collection<V> values() { 217 throw new UnsupportedOperationException(); 218 } 219 } 220 221 //----------------------------------------------------------------------- 222 223 /** 224 * Get the specified {@link Get} as an instance of {@link IterableMap}. 225 * If <code>get</code> implements {@link IterableMap} directly, no conversion will take place. 226 * If <code>get</code> implements {@link Map} but not {@link IterableMap} it will be decorated. 227 * Otherwise an {@link Unmodifiable} {@link IterableMap} will be returned. 228 * @param <K> the key type 229 * @param <V> the value type 230 * @param get to wrap, must not be null 231 * @return {@link IterableMap} 232 * @throws NullPointerException if the argument is null 233 */ 234 @SuppressWarnings("unchecked") 235 public static <K, V> IterableMap<K, V> readableMap(final Get<K, V> get) { 236 if (get == null) { 237 throw new NullPointerException("Get must not be null"); 238 } 239 if (get instanceof Map) { 240 return get instanceof IterableMap ? 241 ((IterableMap<K, V>) get) : 242 MapUtils.iterableMap((Map<K, V>) get); 243 } 244 return new WrappedGet<>(get); 245 } 246 247 /** 248 * Get the specified {@link Put} as an instanceof {@link Map}. 249 * If <code>put</code> implements {@link Map} directly, no conversion will take place. 250 * Otherwise a <em>write-only</em> {@link Map} will be returned. On such a {@link Map} 251 * it is recommended that the result of #put(K, V) be discarded as it likely will not 252 * match <code>V</code> at runtime. 253 * 254 * @param <K> the key type 255 * @param <V> the element type 256 * @param put to wrap, must not be null 257 * @return {@link Map} 258 * @throws NullPointerException if the argument is null 259 */ 260 @SuppressWarnings("unchecked") 261 public static <K, V> Map<K, V> writableMap(final Put<K, V> put) { 262 if (put == null) { 263 throw new NullPointerException("Put must not be null"); 264 } 265 if (put instanceof Map) { 266 return (Map<K, V>) put; 267 } 268 return new WrappedPut<>(put); 269 } 270 271}