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 * @see Get 036 * @see Put 037 */ 038public class SplitMapUtils { 039 040 private static final class WrappedGet<K, V> implements IterableMap<K, V>, Unmodifiable { 041 private final Get<K, V> get; 042 043 private WrappedGet(final Get<K, V> get) { 044 this.get = get; 045 } 046 047 @Override 048 public void clear() { 049 throw new UnsupportedOperationException(); 050 } 051 052 @Override 053 public boolean containsKey(final Object key) { 054 return get.containsKey(key); 055 } 056 057 @Override 058 public boolean containsValue(final Object value) { 059 return get.containsValue(value); 060 } 061 062 @Override 063 public Set<Map.Entry<K, V>> entrySet() { 064 return UnmodifiableEntrySet.unmodifiableEntrySet(get.entrySet()); 065 } 066 067 @Override 068 public boolean equals(final Object arg0) { 069 if (arg0 == this) { 070 return true; 071 } 072 return arg0 instanceof WrappedGet && ((WrappedGet<?, ?>) arg0).get.equals(get); 073 } 074 075 @Override 076 public V get(final Object key) { 077 return get.get(key); 078 } 079 080 @Override 081 public int hashCode() { 082 return "WrappedGet".hashCode() << 4 | get.hashCode(); 083 } 084 085 @Override 086 public boolean isEmpty() { 087 return get.isEmpty(); 088 } 089 090 @Override 091 public Set<K> keySet() { 092 return UnmodifiableSet.unmodifiableSet(get.keySet()); 093 } 094 095 @Override 096 public MapIterator<K, V> mapIterator() { 097 final MapIterator<K, V> it; 098 if (get instanceof IterableGet) { 099 it = ((IterableGet<K, V>) get).mapIterator(); 100 } else { 101 it = new EntrySetToMapIteratorAdapter<>(get.entrySet()); 102 } 103 return UnmodifiableMapIterator.unmodifiableMapIterator(it); 104 } 105 106 @Override 107 public V put(final K key, final V value) { 108 throw new UnsupportedOperationException(); 109 } 110 111 @Override 112 public void putAll(final Map<? extends K, ? extends V> t) { 113 throw new UnsupportedOperationException(); 114 } 115 116 @Override 117 public V remove(final Object key) { 118 return get.remove(key); 119 } 120 121 @Override 122 public int size() { 123 return get.size(); 124 } 125 126 @Override 127 public Collection<V> values() { 128 return UnmodifiableCollection.unmodifiableCollection(get.values()); 129 } 130 } 131 132 private static final class WrappedPut<K, V> implements Map<K, V>, Put<K, V> { 133 private final Put<K, V> put; 134 135 private WrappedPut(final Put<K, V> put) { 136 this.put = put; 137 } 138 139 @Override 140 public void clear() { 141 put.clear(); 142 } 143 144 @Override 145 public boolean containsKey(final Object key) { 146 throw new UnsupportedOperationException(); 147 } 148 149 @Override 150 public boolean containsValue(final Object value) { 151 throw new UnsupportedOperationException(); 152 } 153 154 @Override 155 public Set<Map.Entry<K, V>> entrySet() { 156 throw new UnsupportedOperationException(); 157 } 158 159 @Override 160 public boolean equals(final Object obj) { 161 if (obj == this) { 162 return true; 163 } 164 return obj instanceof WrappedPut && ((WrappedPut<?, ?>) obj).put.equals(put); 165 } 166 167 @Override 168 public V get(final Object key) { 169 throw new UnsupportedOperationException(); 170 } 171 172 @Override 173 public int hashCode() { 174 return "WrappedPut".hashCode() << 4 | put.hashCode(); 175 } 176 177 @Override 178 public boolean isEmpty() { 179 throw new UnsupportedOperationException(); 180 } 181 182 @Override 183 public Set<K> keySet() { 184 throw new UnsupportedOperationException(); 185 } 186 187 @Override 188 @SuppressWarnings("unchecked") 189 public V put(final K key, final V value) { 190 return (V) put.put(key, value); 191 } 192 193 @Override 194 public void putAll(final Map<? extends K, ? extends V> t) { 195 put.putAll(t); 196 } 197 198 @Override 199 public V remove(final Object key) { 200 throw new UnsupportedOperationException(); 201 } 202 203 @Override 204 public int size() { 205 throw new UnsupportedOperationException(); 206 } 207 208 @Override 209 public Collection<V> values() { 210 throw new UnsupportedOperationException(); 211 } 212 } 213 214 /** 215 * Gets the specified {@link Get} as an instance of {@link IterableMap}. 216 * If {@code get} implements {@link IterableMap} directly, no conversion will take place. 217 * If {@code get} implements {@link Map} but not {@link IterableMap} it will be decorated. 218 * Otherwise, an {@link Unmodifiable} {@link IterableMap} will be returned. 219 * 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}