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.wicket.util.value;
018
019import java.io.Serializable;
020import java.sql.Time;
021import java.time.Duration;
022import java.time.Instant;
023import java.util.Collection;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.wicket.util.string.StringValue;
028import org.apache.wicket.util.string.StringValueConversionException;
029
030
031/**
032 * An implementation of <code>IValueMap</code> that makes a copy when a caller tries to change an
033 * immutable <code>Map</code>. That is, the <code>Map</code> may or may not be immutable, but if it
034 * is, a copy is made.
035 * 
036 * @author Johan Compagner
037 * @author Doug Donohoe
038 * @since 1.2.6
039 */
040public class CopyOnWriteValueMap implements IValueMap, Serializable
041{
042        private static final long serialVersionUID = 1L;
043
044        /** the wrapped <code>IValueMap</code> */
045        private IValueMap wrapped;
046
047        /**
048         * Constructor.
049         * 
050         * @param wrapped
051         *            the wrapped <code>IValueMap</code>
052         */
053        public CopyOnWriteValueMap(final IValueMap wrapped)
054        {
055                this.wrapped = wrapped;
056        }
057
058        @Override
059        public void clear()
060        {
061                checkAndCopy();
062                wrapped.clear();
063        }
064
065        /**
066         * Checks if this <code>IValueMap</code> is immutable. If it is, this method makes a new
067         * <code>IValueMap</code> using the <code>ValueMap</code> copy constructor, and sets it to be
068         * this <code>CopyOnWriteValueMap</code>.
069         */
070        private void checkAndCopy()
071        {
072                if (wrapped.isImmutable())
073                {
074                        wrapped = new ValueMap(wrapped);
075                }
076        }
077
078        @Override
079        public boolean containsKey(final Object key)
080        {
081                return wrapped.containsKey(key);
082        }
083
084        @Override
085        public boolean containsValue(final Object value)
086        {
087                return wrapped.containsValue(value);
088        }
089
090        @Override
091        public Set<Entry<String, Object>> entrySet()
092        {
093                checkAndCopy();
094                return wrapped.entrySet();
095        }
096
097        @Override
098        public boolean equals(final Object o)
099        {
100                return wrapped.equals(o);
101        }
102
103        @Override
104        public int hashCode()
105        {
106                return wrapped.hashCode();
107        }
108
109        @Override
110        public Object get(final Object key)
111        {
112                return wrapped.get(key);
113        }
114
115        @Override
116        public boolean getBoolean(final String key) throws StringValueConversionException
117        {
118                return wrapped.getBoolean(key);
119        }
120
121        @Override
122        public CharSequence getCharSequence(final String key)
123        {
124                return wrapped.getCharSequence(key);
125        }
126
127        @Override
128        public double getDouble(final String key) throws StringValueConversionException
129        {
130                return wrapped.getDouble(key);
131        }
132
133        @Override
134        public double getDouble(final String key, final double defaultValue)
135        {
136                return wrapped.getDouble(key, defaultValue);
137        }
138
139        @Override
140        public Duration getDuration(final String key) throws StringValueConversionException
141        {
142                return wrapped.getDuration(key);
143        }
144
145        @Override
146        public int getInt(final String key, final int defaultValue)
147        {
148                return wrapped.getInt(key, defaultValue);
149        }
150
151        @Override
152        public int getInt(final String key) throws StringValueConversionException
153        {
154                return wrapped.getInt(key);
155        }
156
157        @Override
158        public String getKey(final String key)
159        {
160                return wrapped.getKey(key);
161        }
162
163        @Override
164        public long getLong(final String key, final long defaultValue)
165        {
166                return wrapped.getLong(key, defaultValue);
167        }
168
169        @Override
170        public long getLong(final String key) throws StringValueConversionException
171        {
172                return wrapped.getLong(key);
173        }
174
175        @Override
176        public String getString(final String key, final String defaultValue)
177        {
178                return wrapped.getString(key, defaultValue);
179        }
180
181        @Override
182        public String getString(final String key)
183        {
184                return wrapped.getString(key);
185        }
186
187        @Override
188        public String[] getStringArray(final String key)
189        {
190                return wrapped.getStringArray(key);
191        }
192
193        @Override
194        public StringValue getStringValue(final String key)
195        {
196                return wrapped.getStringValue(key);
197        }
198
199        @Override
200        public Instant getInstant(final String key) throws StringValueConversionException
201        {
202                return wrapped.getInstant(key);
203        }
204
205        @Override
206        public boolean isEmpty()
207        {
208                return wrapped.isEmpty();
209        }
210
211        @Override
212        public boolean isImmutable()
213        {
214                return false;
215        }
216
217        @Override
218        public Set<String> keySet()
219        {
220                checkAndCopy();
221                return wrapped.keySet();
222        }
223
224        @Override
225        public IValueMap makeImmutable()
226        {
227                return wrapped.makeImmutable();
228        }
229
230        @Override
231        public Object put(final String key, final Object value)
232        {
233                checkAndCopy();
234                return wrapped.put(key, value);
235        }
236
237        @Override
238        public void putAll(final Map<? extends String, ?> map)
239        {
240                checkAndCopy();
241                wrapped.putAll(map);
242        }
243
244        @Override
245        public Object remove(final Object key)
246        {
247                checkAndCopy();
248                return wrapped.remove(key);
249        }
250
251        @Override
252        public int size()
253        {
254                return wrapped.size();
255        }
256
257        @Override
258        public Collection<Object> values()
259        {
260                return wrapped.values();
261        }
262
263        // //
264        // // getAs convenience methods
265        // //
266
267        @Override
268        public Boolean getAsBoolean(final String key)
269        {
270                return wrapped.getAsBoolean(key);
271        }
272
273        @Override
274        public boolean getAsBoolean(final String key, final boolean defaultValue)
275        {
276                return wrapped.getAsBoolean(key, defaultValue);
277        }
278
279        @Override
280        public Integer getAsInteger(final String key)
281        {
282                return wrapped.getAsInteger(key);
283        }
284
285        @Override
286        public int getAsInteger(final String key, final int defaultValue)
287        {
288                return wrapped.getAsInteger(key, defaultValue);
289        }
290
291        @Override
292        public Long getAsLong(final String key)
293        {
294                return wrapped.getAsLong(key);
295        }
296
297        @Override
298        public long getAsLong(final String key, final long defaultValue)
299        {
300                return wrapped.getAsLong(key, defaultValue);
301        }
302
303        @Override
304        public Double getAsDouble(final String key)
305        {
306                return wrapped.getAsDouble(key);
307        }
308
309        @Override
310        public double getAsDouble(final String key, final double defaultValue)
311        {
312                return wrapped.getAsDouble(key, defaultValue);
313        }
314
315        @Override
316        public Duration getAsDuration(final String key)
317        {
318                return wrapped.getAsDuration(key);
319        }
320
321        @Override
322        public Duration getAsDuration(final String key, final Duration defaultValue)
323        {
324                return wrapped.getAsDuration(key, defaultValue);
325        }
326
327        @Override
328        public Instant getAsInstant(final String key)
329        {
330                return wrapped.getAsInstant(key);
331        }
332
333        @Override
334        public Instant getAsTime(final String key, final Instant defaultValue)
335        {
336                return wrapped.getAsTime(key, defaultValue);
337        }
338
339        @Override
340        public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass)
341        {
342                return wrapped.getAsEnum(key, eClass);
343        }
344
345        @Override
346        public <T extends Enum<T>> T getAsEnum(final String key, final T defaultValue)
347        {
348                return wrapped.getAsEnum(key, defaultValue);
349        }
350
351        @Override
352        public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass,
353                final T defaultValue)
354        {
355                return wrapped.getAsEnum(key, eClass, defaultValue);
356        }
357}