001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 ninja.leaping.configurate.util; 018 019import com.google.common.collect.ImmutableSet; 020import org.checkerframework.checker.nullness.qual.NonNull; 021 022import java.util.Collection; 023import java.util.Comparator; 024import java.util.LinkedHashMap; 025import java.util.Map; 026import java.util.Objects; 027import java.util.Set; 028import java.util.concurrent.ConcurrentHashMap; 029import java.util.concurrent.ConcurrentMap; 030import java.util.concurrent.ConcurrentSkipListMap; 031 032import static java.util.Objects.requireNonNull; 033 034/** 035 * Default implementations of {@link MapFactory}. 036 */ 037public final class MapFactories { 038 private MapFactories() {} 039 040 /** 041 * Returns a {@link MapFactory} which creates maps without an order. 042 * 043 * @return A map factory which produces unordered maps 044 */ 045 public static MapFactory unordered() { 046 return DefaultFactory.UNORDERED; 047 } 048 049 /** 050 * Returns a {@link MapFactory} which creates maps which are sorted using the given comparator. 051 * 052 * @param comparator The comparator used to sort the map keys 053 * @return A map factory which produces sorted maps 054 */ 055 public static MapFactory sorted(Comparator<Object> comparator) { 056 return new SortedMapFactory(requireNonNull(comparator, "comparator")); 057 } 058 059 /** 060 * Returns a {@link MapFactory} which creates maps which are naturally sorted. 061 * 062 * @return A map factory which produces naturally sorted maps 063 * @see Comparator#naturalOrder() 064 */ 065 public static MapFactory sortedNatural() { 066 return DefaultFactory.SORTED_NATURAL; 067 } 068 069 /** 070 * Returns a {@link MapFactory} which creates maps which are sorted by insertion order. 071 * 072 * @return A map factory which produces maps sorted by insertion order 073 */ 074 public static MapFactory insertionOrdered() { 075 return DefaultFactory.INSERTION_ORDERED; 076 } 077 078 private enum DefaultFactory implements MapFactory { 079 UNORDERED { 080 @NonNull 081 @Override 082 public <K, V> ConcurrentMap<K, V> create() { 083 return new ConcurrentHashMap<>(); 084 } 085 }, 086 SORTED_NATURAL { 087 @NonNull 088 @Override 089 public <K, V> ConcurrentMap<K, V> create() { 090 return new ConcurrentSkipListMap<>(); 091 } 092 }, 093 INSERTION_ORDERED { 094 @NonNull 095 @Override 096 public <K, V> ConcurrentMap<K, V> create() { 097 return new SynchronizedWrapper<>(new LinkedHashMap<>()); 098 } 099 } 100 } 101 102 private static final class SortedMapFactory implements MapFactory { 103 private final Comparator<Object> comparator; 104 105 private SortedMapFactory(Comparator<Object> comparator) { 106 this.comparator = comparator; 107 } 108 109 @NonNull 110 @Override 111 public <K, V> ConcurrentMap<K, V> create() { 112 return new ConcurrentSkipListMap<>(comparator); 113 } 114 115 @Override 116 public boolean equals(Object obj) { 117 return obj instanceof SortedMapFactory && comparator.equals(((SortedMapFactory) obj).comparator); 118 } 119 120 @Override 121 public int hashCode() { 122 return comparator.hashCode(); 123 } 124 125 @Override 126 public String toString() { 127 return "SortedMapFactory{comparator=" + comparator + '}'; 128 } 129 } 130 131 private static class SynchronizedWrapper<K, V> implements ConcurrentMap<K, V> { 132 private final Map<K, V> wrapped; 133 134 private SynchronizedWrapper(Map<K, V> wrapped) { 135 this.wrapped = wrapped; 136 } 137 138 @Override 139 public V putIfAbsent(K k, V v) { 140 synchronized (wrapped) { 141 if (!wrapped.containsKey(k)) { 142 wrapped.put(k, v); 143 } else { 144 return wrapped.get(k); 145 } 146 } 147 return null; 148 } 149 150 @Override 151 public boolean remove(Object key, Object expected) { 152 synchronized (wrapped) { 153 if (Objects.equals(expected, wrapped.get(key))) { 154 return wrapped.remove(key) != null; 155 } 156 } 157 return false; 158 } 159 160 @Override 161 public boolean replace(K key, V old, V replace) { 162 synchronized (wrapped) { 163 if (Objects.equals(old, wrapped.get(key))) { 164 wrapped.put(key, replace); 165 return true; 166 } 167 } 168 return false; 169 } 170 171 @Override 172 public V replace(K k, V v) { 173 synchronized (wrapped) { 174 if (wrapped.containsKey(k)) { 175 return wrapped.put(k, v); 176 } 177 } 178 return null; 179 } 180 181 @Override 182 public int size() { 183 synchronized (wrapped) { 184 return wrapped.size(); 185 } 186 } 187 188 @Override 189 public boolean isEmpty() { 190 synchronized (wrapped) { 191 return wrapped.isEmpty(); 192 } 193 } 194 195 @Override 196 public boolean containsKey(Object o) { 197 synchronized (wrapped) { 198 return wrapped.containsKey(o); 199 } 200 } 201 202 @Override 203 public boolean containsValue(Object o) { 204 synchronized (wrapped) { 205 return wrapped.containsKey(o); 206 } 207 } 208 209 @Override 210 public V get(Object o) { 211 synchronized (wrapped) { 212 return wrapped.get(o); 213 } 214 } 215 216 @Override 217 public V put(K k, V v) { 218 synchronized (wrapped) { 219 return wrapped.put(k, v); 220 } 221 } 222 223 @Override 224 public V remove(Object o) { 225 synchronized (wrapped) { 226 return wrapped.remove(o); 227 } 228 } 229 230 @Override 231 public void putAll(Map<? extends K, ? extends V> map) { 232 synchronized (wrapped) { 233 wrapped.putAll(map); 234 } 235 } 236 237 @Override 238 public void clear() { 239 synchronized (wrapped) { 240 wrapped.clear(); 241 } 242 } 243 244 @Override 245 public Set<K> keySet() { 246 synchronized (wrapped) { 247 return ImmutableSet.copyOf(wrapped.keySet()); 248 } 249 } 250 251 @Override 252 public Collection<V> values() { 253 synchronized (wrapped) { 254 return ImmutableSet.copyOf(wrapped.values()); 255 } 256 } 257 258 @Override 259 public Set<Entry<K, V>> entrySet() { 260 synchronized (wrapped) { 261 return ImmutableSet.copyOf(wrapped.entrySet()); 262 } 263 } 264 265 @Override 266 public boolean equals(Object obj) { 267 if (obj == this) { 268 return true; 269 } 270 synchronized (wrapped) { 271 return wrapped.equals(obj); 272 } 273 } 274 275 @Override 276 public int hashCode() { 277 synchronized (wrapped) { 278 return wrapped.hashCode(); 279 } 280 } 281 282 @Override 283 public String toString() { 284 synchronized (wrapped) { 285 return "SynchronizedWrapper{backing=" + wrapped.toString() + '}'; 286 } 287 } 288 } 289 290}