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}