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