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