001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015-2016 ForgeRock AS.
015 */
016
017package org.forgerock.util;
018
019// Java SE
020import java.util.Collection;
021import java.util.Map;
022import java.util.Set;
023
024/**
025 * A map with lazy initialization. The factory is called to initialize the map
026 * on the first call to one of this object's methods.
027 *
028 * @param <K>
029 *            The type of key.
030 * @param <V>
031 *            The type of value.
032 */
033public class LazyMap<K, V> implements Map<K, V> {
034
035    /** The map that this lazy map exposes, once initialized. */
036    private Map<K, V> map;
037
038    /** Factory to create the instance of the map to expose. */
039    protected Factory<Map<K, V>> factory;
040
041    /**
042     * Constructs a new lazy map. Allows factory to be set in subclass
043     * constructor.
044     */
045    protected LazyMap() {
046    }
047
048    /**
049     * Constructs a new lazy map.
050     *
051     * @param factory
052     *            factory to create the map instance to expose.
053     */
054    public LazyMap(Factory<Map<K, V>> factory) {
055        this.factory = factory;
056    }
057
058    /**
059     * Performs lazy initialization of the map if not already performed, and
060     * returns the initialized map.
061     */
062    private Map<K, V> lazy() {
063        if (map == null) {
064            synchronized (this) {
065                if (map == null) {
066                    map = factory.newInstance();
067                }
068            }
069        }
070        return map;
071    }
072
073    /**
074     * Returns the number of key-value mappings in this map.
075     */
076    @Override
077    public int size() {
078        return lazy().size();
079    }
080
081    /**
082     * Returns {@code true} if the map contains no key-value mappings.
083     */
084    @Override
085    public boolean isEmpty() {
086        return lazy().isEmpty();
087    }
088
089    /**
090     * Returns {@code true} if this map contains a mapping for the specified
091     * key.
092     *
093     * @param key
094     *            the key whose presence in this map is to be tested.
095     * @return {@code true} if this map contains a mapping for the specified
096     *         key.
097     */
098    @Override
099    public boolean containsKey(Object key) {
100        return lazy().containsKey(key);
101    }
102
103    /**
104     * Returns {@code true} if the map maps one or more keys to the specified
105     * value.
106     *
107     * @param value
108     *            the value whose presence in the map is to be tested.
109     * @return {@code true} if the map maps one or more keys to the specified
110     *         value.
111     */
112    @Override
113    public boolean containsValue(Object value) {
114        return lazy().containsValue(value);
115    }
116
117    /**
118     * Returns the value to which the specified key is mapped, or {@code null}
119     * if the map contains no mapping for the key.
120     *
121     * @param key
122     *            the key whose associated value is to be returned.
123     * @return the value to which the specified key is mapped, or {@code null}
124     *         if no mapping.
125     */
126    @Override
127    public V get(Object key) {
128        return lazy().get(key);
129    }
130
131    /**
132     * Associates the specified value with the specified key in the map.
133     *
134     * @param key
135     *            key with which the specified value is to be associated.
136     * @param value
137     *            value to be associated with the specified key.
138     * @return the previous value associated with key, or {@code null} if no
139     *         mapping.
140     */
141    @Override
142    public V put(K key, V value) {
143        return lazy().put(key, value);
144    }
145
146    /**
147     * Removes the mapping for a key from the map if it is present.
148     *
149     * @param key
150     *            key whose mapping is to be removed from the map.
151     * @return the previous value associated with key, or {@code null} if no
152     *         mapping.
153     */
154    @Override
155    public V remove(Object key) {
156        return lazy().remove(key);
157    }
158
159    /**
160     * Copies all of the mappings from the specified map to the map.
161     *
162     * @param m
163     *            mappings to be stored in the map.
164     */
165    @Override
166    public void putAll(Map<? extends K, ? extends V> m) {
167        lazy().putAll(m);
168    }
169
170    /**
171     * Removes all of the mappings from the map.
172     */
173    @Override
174    public void clear() {
175        lazy().clear();
176    }
177
178    /**
179     * Returns a {@link Set} view of the keys contained in the map.
180     */
181    @Override
182    public Set<K> keySet() {
183        return lazy().keySet();
184    }
185
186    /**
187     * Returns a {@link Collection} view of the values contained in the map.
188     */
189    @Override
190    public Collection<V> values() {
191        return lazy().values();
192    }
193
194    /**
195     * Returns a {@link Set} view of the mappings contained in the map.
196     */
197    @Override
198    public Set<Map.Entry<K, V>> entrySet() {
199        return lazy().entrySet();
200    }
201
202    /**
203     * Returns the hash code value for the map.
204     */
205    @Override
206    public int hashCode() {
207        return lazy().hashCode();
208    }
209
210    /**
211     * Compares the specified object with the map for equality.
212     *
213     * @param o
214     *            object to be compared for equality with the map.
215     * @return {@code true} if the specified object is equal to the map.
216     */
217    @Override
218    public boolean equals(Object o) {
219        return lazy().equals(o);
220    }
221}