2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.util;
10 import com.google.common.base.Preconditions;
11 import com.romix.scala.collection.concurrent.TrieMap;
12 import java.util.Collections;
13 import java.util.HashMap;
15 import java.util.Map.Entry;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
20 * A simple layer on top of maps, which performs snapshot mediation and optimization of
21 * what the underlying implementation is.
23 public final class MapAdaptor {
24 public static final int DEFAULT_COPY_MAX_ITEMS = 100;
25 public static final String COPY_MAX_ITEMS_MAX_PROP = "org.opendaylight.yangtools.util.mapadaptor.maxcopy";
27 public static final int DEFAULT_PERSIST_MIN_ITEMS = 50;
28 public static final String PERSIST_MIN_ITEMS_PROP = "org.opendaylight.yangtools.util.mapadaptor.minpersist";
30 private static final Logger LOG = LoggerFactory.getLogger(MapAdaptor.class);
31 private static final MapAdaptor DEFAULT_INSTANCE;
33 private final boolean useSingleton;
34 private final int persistMinItems;
35 private final int copyMaxItems;
38 DEFAULT_INSTANCE = new MapAdaptor(true,
39 getProperty(COPY_MAX_ITEMS_MAX_PROP, DEFAULT_COPY_MAX_ITEMS),
40 getProperty(PERSIST_MIN_ITEMS_PROP, DEFAULT_PERSIST_MIN_ITEMS));
41 LOG.debug("Configured HashMap/TrieMap cutoff at {}/{} entries",
42 DEFAULT_INSTANCE.persistMinItems, DEFAULT_INSTANCE.copyMaxItems);
45 private static final int getProperty(final String name, final int defaultValue) {
47 final String p = System.getProperty(name);
50 int pi = Integer.valueOf(p);
52 LOG.warn("Ignoring illegal value of {}: has to be a positive number", name);
56 } catch (NumberFormatException e) {
57 LOG.warn("Ignoring non-numerical value of {}", name, e);
60 } catch (Exception e) {
61 LOG.debug("Failed to get {}", name, e);
66 private MapAdaptor(final boolean useSingleton, final int copyMaxItems, final int persistMinItems) {
67 this.useSingleton = useSingleton;
68 this.copyMaxItems = copyMaxItems;
69 this.persistMinItems = persistMinItems;
73 * Return the default-configured instance.
75 * @return the singleton global instance
77 public static MapAdaptor getDefaultInstance() {
78 return DEFAULT_INSTANCE;
81 public static MapAdaptor getInstance(final boolean useSingleton, final int copyMaxItems, final int persistMinItems) {
82 Preconditions.checkArgument(copyMaxItems >= 0, "copyMaxItems has to be a non-negative integer");
83 Preconditions.checkArgument(persistMinItems >= 0, "persistMinItems has to be a positive integer");
84 Preconditions.checkArgument(persistMinItems <= copyMaxItems, "persistMinItems must be less than or equal to copyMaxItems");
85 return new MapAdaptor(useSingleton, copyMaxItems, persistMinItems);
89 * Input is treated is supposed to be left unmodified, result must be mutable.
94 public <K, V> Map<K, V> takeSnapshot(final Map<K, V> input) {
95 if (input instanceof ReadOnlyTrieMap) {
96 return ((ReadOnlyTrieMap<K, V>)input).toReadWrite();
99 LOG.trace("Converting input {} to a HashMap", input);
101 // FIXME: be a bit smart about allocation based on observed size
103 final Map<K, V> ret = new HashMap<>(input);
104 LOG.trace("Read-write HashMap is {}", ret);
109 * Input will be thrown away, result will be retained for read-only access or
110 * {@link #takeSnapshot(Map)} purposes.
115 public <K, V> Map<K, V> optimize(final Map<K, V> input) {
116 if (input instanceof ReadOnlyTrieMap) {
117 LOG.warn("Optimizing read-only map {}", input);
120 final int size = input.size();
126 LOG.trace("Reducing input {} to an empty map", input);
127 return Collections.emptyMap();
131 * We retain the persistent map as long as it holds at least
134 if (input instanceof ReadWriteTrieMap && size >= persistMinItems) {
135 return ((ReadWriteTrieMap<K, V>)input).toReadOnly();
139 * If the user opted to use singleton maps, use them. Except for the case
140 * when persistMinItems dictates we should not move off of the persistent
143 if (useSingleton && size == 1) {
144 final Entry<K, V> e = input.entrySet().iterator().next();
145 final Map<K, V> ret = Collections.singletonMap(e.getKey(), e.getValue());
146 LOG.trace("Reducing input {} to singleton map {}", input, ret);
150 if (size <= copyMaxItems) {
152 * Favor access speed: use a HashMap and copy it on modification.
154 if (input instanceof HashMap) {
158 LOG.trace("Copying input {} to a HashMap ({} entries)", input, size);
159 final Map<K, V> ret = new HashMap<>(input);
160 LOG.trace("Read-only HashMap is {}", ret);
165 * Favor isolation speed: use a TrieMap and perform snapshots
167 * This one is a bit tricky, as the TrieMap is concurrent and does not
168 * keep an uptodate size. Updating it requires a full walk -- which is
169 * O(N) and we want to avoid that. So we wrap it in an interceptor,
170 * which will maintain the size for us.
172 LOG.trace("Copying input {} to a TrieMap ({} entries)", input, size);
173 final TrieMap<K, V> map = TrieMap.empty();
175 final Map<K, V> ret = new ReadOnlyTrieMap<>(map, size);
176 LOG.trace("Read-only TrieMap is {}", ret);