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.commons.chain.web; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024 025/** 026 * Implementation of {@code Map} for session attributes with a 027 * parameter-provider. 028 * 029 * @param <S> the type of the session-class 030 * @param <R> the type of the request-class 031 * 032 * @author Graff Stefan 033 * @since Chain 1.3 034 */ 035public abstract class AbstractSessionScopeMap<S, R> implements Map<String, Object> { 036 037 /** 038 * The Session-class. 039 */ 040 private S session = null; 041 042 /** 043 * The Request-class. 044 */ 045 private R request; 046 047 /** 048 * Mutable-Parameter-Map with the session-attributes. 049 */ 050 private MutableParameterMap<S, Object> parameterMap = null; 051 052 /** 053 * The constructor for the session attributes. 054 * 055 * @param request the request-class 056 */ 057 public AbstractSessionScopeMap(final R request) { 058 this.request = request; 059 sessionExists(); 060 } 061 062 /** 063 * Removes all of the mappings from this session-map. 064 * The session-map will be empty after this call returns. 065 */ 066 @Override 067 public void clear() { 068 if (sessionExists()) { 069 parameterMap.clear(); 070 } 071 } 072 073 /** 074 * Returns {@code true} if this session-map contains a mapping 075 * for the specified key. 076 * 077 * @param key The key whose presence in this session-map is to 078 * be tested 079 * 080 * @return {@code true} if this session-map contains a mapping 081 * for the specified key. 082 */ 083 @Override 084 public boolean containsKey(Object key) { 085 return sessionExists() && parameterMap.containsKey(key); 086 } 087 088 /** 089 * Returns {@code true} if this session-map maps one or more keys 090 * to the specified value. 091 * 092 * @param value value whose presence in this session-map is to be 093 * tested 094 * 095 * @return {@code true} if this session-map maps one or more keys 096 * to the specified value 097 */ 098 @Override 099 public boolean containsValue(Object value) { 100 return value != null && sessionExists() && parameterMap.containsValue(value); 101 } 102 103 /** 104 * Returns a {@link Set} view of the mappings contained in this 105 * session-map. The set is not backed by the session-map, so 106 * changes to the session-map are not reflected in the set, 107 * and vice-versa. 108 * 109 * @return a set view of the mappings contained in this 110 * session-map 111 */ 112 @Override 113 public Set<Map.Entry<String, Object>> entrySet() { 114 return sessionExists() ? parameterMap.entrySet() : new HashSet<>(); 115 } 116 117 /** 118 * Returns the value to which the specified key is mapped, 119 * or {@code null} if this session-map contains no mapping 120 * for the key. 121 * 122 * <p>A return value of {@code null} does not <i>necessarily</i> 123 * indicate that the session-map contains no mapping for the key; 124 * it's also possible that the session-map explicitly maps the 125 * key to {@code null}. The {@link #containsKey containsKey} 126 * operation may be used to distinguish these two cases.</p> 127 * 128 * @param key the key whose associated value is to be returned 129 * 130 * @return the value to which the specified key is mapped, or 131 * {@code null} if this session-map contains no mapping for 132 * the key 133 * 134 * @see #put(Object, Object) 135 */ 136 @Override 137 public Object get(Object key) { 138 return sessionExists() ? parameterMap.get(key) : null; 139 } 140 141 /** 142 * Returns {@code true} if this session-map contains no 143 * key-value mappings. 144 * 145 * @return {@code true} if this session-map contains no 146 * key-value mappings 147 */ 148 @Override 149 public boolean isEmpty() { 150 return !sessionExists() || parameterMap.isEmpty(); 151 } 152 153 /** 154 * Returns a {@link Set} view of the keys contained in this 155 * session-map. The set is not backed by the session-map, so 156 * changes to the session-map are not reflected in the set, and 157 * vice-versa. 158 * 159 * @return a set view of the keys contained in this session-map 160 */ 161 @Override 162 public Set<String> keySet() { 163 return sessionExists() ? parameterMap.keySet() : new HashSet<>(); 164 } 165 166 /** 167 * Associates the specified value with the specified key in this 168 * session-map. If the session-map previously contained a 169 * mapping for the key, the old value is replaced. 170 * 171 * @param key key with which the specified value is to be associated 172 * @param value value to be associated with the specified key 173 * 174 * @return the previous value associated with {@code key}, or 175 * {@code null} if there was no mapping for {@code key}. 176 * (A {@code null} return can also indicate that the 177 * session-map previously associated {@code null} with 178 * {@code key}.) 179 */ 180 @Override 181 public Object put(String key, Object value) { 182 if (value == null) { 183 return remove(key); 184 } 185 186 // Ensure the Session is created, if it 187 // doesn't exist 188 return sessionExists(true) ? parameterMap.put(key, value) : null; 189 } 190 191 /** 192 * Copies all of the mappings from the specified map to this 193 * session-map. These mappings will replace any mappings that 194 * this session-map had for any of the keys currently in the 195 * specified map. 196 * 197 * @param map mappings to be stored in this session-map 198 * 199 * @throws NullPointerException if the specified map is null 200 */ 201 @Override 202 public void putAll(Map<? extends String, ? extends Object> map) { 203 map.forEach(this::put); 204 } 205 206 /** 207 * Removes the mapping for the specified key from this 208 * session-map if present. 209 * 210 * @param key key whose mapping is to be removed from the 211 * session-map 212 * 213 * @return the previous value associated with {@code key}, or 214 * {@code null} if there was no mapping for {@code key}. 215 * (A {@code null} return can also indicate that the 216 * session-map previously associated {@code null} with 217 * {@code key}.) 218 */ 219 @Override 220 public Object remove(Object key) { 221 return sessionExists() ? parameterMap.remove(key) : null; 222 } 223 224 /** 225 * Returns the number of key-value mappings in this session-map. 226 * 227 * @return the number of key-value mappings in this session-map 228 */ 229 @Override 230 public int size() { 231 return sessionExists() ? parameterMap.size() : 0; 232 } 233 234 /** 235 * Returns a {@link Collection} view of the values contained in 236 * this session-map. The collection is not backed by the 237 * session-map, so changes to the session-map are not 238 * reflected in the collection, and vice-versa. 239 * 240 * @return a view of the values contained in this session-map 241 */ 242 @Override 243 public Collection<Object> values() { 244 return sessionExists() ? parameterMap.values() : new ArrayList<>(); 245 } 246 247 /** 248 * Returns the hash code value for this session-map. The 249 * hash code of a session-map is defined to be the sum of 250 * the hash codes of each entry in the session-map's 251 * {@code entrySet()} view. This ensures that {@code m1.equals(m2)} 252 * implies that {@code m1.hashCode()==m2.hashCode()} for any two 253 * session-maps {@code m1} and {@code m2}, as required by the 254 * general contract of {@link Object#hashCode}. 255 * 256 * @return the hash code value for this session-map 257 */ 258 @Override 259 public int hashCode() { 260 return sessionExists() ? parameterMap.hashCode() : 0; 261 } 262 263 /** 264 * Compares the specified object with this session-map for equality. 265 * Returns {@code true} if the given object is also a session-map 266 * and the two session-maps represent the same mappings. More formally, 267 * two session-maps {@code m1} and {@code m2} represent the same 268 * mappings if {@code m1.entrySet().equals(m2.entrySet())}. 269 * 270 * @param obj object to be compared for equality with this 271 * session-map 272 * 273 * @return {@code true} if the specified object is equal to this 274 * session-map 275 */ 276 @Override 277 public boolean equals(Object obj) { 278 return sessionExists() && parameterMap.equals(obj); 279 } 280 281 /** 282 * Returns a string representation of this session-map. 283 * 284 * @return a string representation of this session-map 285 */ 286 public String toString() { 287 if (request != null) { 288 return "{request: " + request + '}'; 289 } 290 291 if (session != null) { 292 final StringBuilder sb = new StringBuilder(); 293 sb 294 .append("{session: ") 295 .append(session); 296 297 if (parameterMap != null) { 298 sb 299 .append(", parameters: ") 300 .append(parameterMap); 301 } 302 return sb.append('}').toString(); 303 } 304 return "{}"; 305 } 306 307 /** 308 * Returns the session-class. 309 * 310 * @return the session-class 311 */ 312 protected S getSession() { 313 return session; 314 } 315 316 /** 317 * Returns the request-class. 318 * 319 * @return the request-class 320 */ 321 protected R getRequest() { 322 return request; 323 } 324 325 /** 326 * Returns {@code true} if a session exists. 327 * 328 * @return {@code true} if a session exists 329 */ 330 protected boolean sessionExists() { 331 return sessionExists(false); 332 } 333 334 /** 335 * Returns {@code true} if a session exists or creates a new 336 * session if the parameter {@code create} is set to {@code true}. 337 * 338 * @param create {@code true} to create a new session if no 339 * session exists 340 * 341 * @return {@code true} if a session exists 342 */ 343 protected boolean sessionExists(boolean create) { 344 if (session == null && request != null) { 345 session = getSession(create); 346 if (session != null) { 347 request = null; 348 parameterMap = createParameterMap(); 349 } 350 } 351 352 if (session == null) { 353 parameterMap = null; 354 return false; 355 } else { 356 return parameterMap != null; 357 } 358 } 359 360 /** 361 * Returns the current session or, if there is no current session and 362 * the given flag is {@code true}, creates one and returns the new session. 363 * 364 * <p>If the given flag is {@code false} and there is no current 365 * session, this method returns {@code null}.</p> 366 * 367 * @param create {@code true} to create a new session, {@code false} to return 368 * {@code null} if there is no current session 369 * 370 * @return the session 371 */ 372 protected abstract S getSession(boolean create); 373 374 /** 375 * Creates a new mutable-parameter-map to access the session-attributes. 376 * 377 * @return a new mutable-parameter-map to access the session-attributes 378 */ 379 protected abstract MutableParameterMap<S, Object> createParameterMap(); 380}