1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.util.MapContext; 20 21 import hunt.shiro.util.CollectionUtils; 22 import hunt.shiro.util.Common; 23 24 import hunt.collection.HashMap; 25 import hunt.collection.Map; 26 import hunt.Exceptions; 27 import hunt.logging.Logger; 28 import hunt.Object; 29 import hunt.util.Common; 30 import hunt.util.ObjectUtils; 31 32 import std.array; 33 import std.range; 34 35 36 /** 37 * A {@code MapContext} provides a common base for context-based data storage in a {@link Map}. Type-safe attribute 38 * retrieval is provided for subclasses with the {@link #getTypedValue(string, Class)} method. 39 * 40 * @see hunt.shiro.subject.SubjectContext SubjectContext 41 * @see hunt.shiro.session.mgt.SessionContext SessionContext 42 */ 43 class MapContext : Map!(string, Object) { 44 45 private Map!(string, Object) backingMap; 46 47 this() { 48 this.backingMap = new HashMap!(string, Object)(); 49 } 50 51 this(Map!(string, Object) map) { 52 this(); 53 if (!CollectionUtils.isEmpty(map)) { 54 this.backingMap.putAll(map); 55 } 56 } 57 58 /** 59 * Performs a {@link #get get} operation but additionally ensures that the value returned is of the specified 60 * {@code type}. If there is no value, {@code null} is returned. 61 * 62 * @param key the attribute key to look up a value 63 * @param type the expected type of the value 64 * @param <E> the expected type of the value 65 * @return the typed value or {@code null} if the attribute does not exist. 66 */ 67 //@SuppressWarnings({"unchecked"}) 68 protected E getTypedValue(E)(string key) { 69 // tracef("object: %s, key %s", cast(void*)this, key); 70 71 // tracef(toString()); 72 73 E found = null; 74 Object o = backingMap.get(key); 75 if (o is null) { 76 // warningf("No value found for %s", key); 77 // tracef(toString()); 78 } else { 79 found = cast(E) o; 80 if (found is null) { 81 string msg = "Invalid object found in SubjectContext Map under key [" ~ key ~ "]. Expected type " ~ 82 "was [" ~ typeid(E).toString() ~ "], but the object under that key is of type " ~ 83 "[" ~ typeid(o).name ~ "]."; 84 throw new IllegalArgumentException(msg); 85 } 86 } 87 // warningf("object: %s, key %s, null: %s", cast(void*)this, key, found is null); 88 return found; 89 } 90 91 /** 92 * Places a value in this context map under the given key only if the given {@code value} argument is not null. 93 * 94 * @param key the attribute key under which the non-null value will be stored 95 * @param value the non-null value to store. If {@code null}, this method does nothing and returns immediately. 96 */ 97 protected void nullSafePut(string key, Object value) { 98 // warningf("object: %s, puting %s, null: %s", cast(void*)this, key, value is null); 99 // warning(toString()); 100 if (value !is null) { 101 put(key, value); 102 } 103 } 104 105 int size() { 106 return backingMap.size(); 107 } 108 109 bool isEmpty() { 110 return backingMap.isEmpty(); 111 } 112 113 bool containsKey(string o) { 114 return backingMap.containsKey(o); 115 } 116 117 bool containsValue(Object o) { 118 return backingMap.containsValue(o); 119 } 120 121 Object get(string o) { 122 return backingMap.get(o); 123 } 124 125 Object put(string s, Object o) { 126 return backingMap.put(s, o); 127 } 128 129 Object remove(string o) { 130 return backingMap.remove(o); 131 } 132 133 bool remove(string key, Object value) { 134 Object curValue = get(key); 135 if (curValue != value || !containsKey(key)) 136 return false; 137 remove(key); 138 return true; 139 } 140 141 void putAll(Map!(string, Object) map) { 142 backingMap.putAll(map); 143 } 144 145 void clear() { 146 backingMap.clear(); 147 } 148 149 bool replace(string key, Object oldValue, Object newValue) { 150 Object curValue = get(key); 151 if (curValue != oldValue || !containsKey(key)) { 152 return false; 153 } 154 put(key, newValue); 155 return true; 156 } 157 158 Object replace(string key, Object value) { 159 Object curValue = Object.init; 160 if (containsKey(key)) { 161 curValue = put(key, value); 162 } 163 return curValue; 164 } 165 166 override string toString() { 167 if (isEmpty()) 168 return "{}"; 169 170 Appender!string sb; 171 sb.put("{\n"); 172 bool isFirst = true; 173 foreach (string key, Object value; this) { 174 if (!isFirst) { 175 sb.put(";\n"); 176 } 177 sb.put(key ~ "=" ~ value.toString()); 178 isFirst = false; 179 } 180 sb.put("\n}"); 181 182 return sb.data; 183 } 184 185 Object putIfAbsent(string key, Object value) { 186 Object v = Object.init; 187 188 if (!containsKey(key)) 189 v = put(key, value); 190 191 return v; 192 } 193 194 Object[] values() { 195 return byValue().array(); 196 } 197 198 Object opIndex(string key) { 199 return get(key); 200 } 201 202 int opApply(scope int delegate(ref string, ref Object) dg) { 203 int result = 0; 204 205 foreach(string key, Object value; backingMap) { 206 result = dg(key, value); 207 } 208 209 return result; 210 } 211 212 int opApply(scope int delegate(MapEntry!(string, Object) entry) dg) { 213 int result = 0; 214 215 foreach(MapEntry!(string, Object) entry; backingMap) { 216 result = dg(entry); 217 } 218 219 return result; 220 } 221 222 InputRange!string byKey() { 223 return backingMap.byKey(); 224 } 225 226 InputRange!Object byValue() { 227 return backingMap.byValue(); 228 } 229 230 override bool opEquals(Object o) { 231 throw new UnsupportedOperationException(); 232 } 233 234 bool opEquals(IObject o) { 235 return opEquals(cast(Object) o); 236 } 237 238 override size_t toHash() @trusted nothrow { 239 size_t h = 0; 240 try { 241 foreach (MapEntry!(string, Object) i; this) { 242 h += i.toHash(); 243 } 244 } catch (Exception ex) { 245 } 246 return h; 247 } 248 249 mixin CloneMemberTemplate!(typeof(this)); 250 }