001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.subject;
020
021import org.apache.shiro.util.CollectionUtils;
022
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032
033/**
034 * Default implementation of the {@link PrincipalMap} interface.
035 * <p>
036 * *EXPERIMENTAL for Shiro 1.2 - DO NOT USE YET*
037 *
038 * @since 1.2
039 */
040public class SimplePrincipalMap implements PrincipalMap {
041
042    //Key: realm name, Value: map of principals specific to that realm
043    //                        internal map - key: principal name, value: principal
044    private Map<String, Map<String, Object>> realmPrincipals;
045
046    //maintains the principals from all realms plus any that are modified via the Map modification methods
047    //this ensures a fast lookup of any named principal instead of needing to iterate over
048    //the realmPrincipals for each lookup.
049    private Map<String, Object> combinedPrincipals;
050
051    public SimplePrincipalMap() {
052        this(null);
053    }
054
055    public SimplePrincipalMap(Map<String, Map<String, Object>> backingMap) {
056        if (!CollectionUtils.isEmpty(backingMap)) {
057            this.realmPrincipals = backingMap;
058            for (Map<String, Object> principals : this.realmPrincipals.values()) {
059                if (!CollectionUtils.isEmpty(principals)) {
060                    ensureCombinedPrincipals().putAll(principals);
061                }
062            }
063        }
064    }
065
066    public int size() {
067        return CollectionUtils.size(this.combinedPrincipals);
068    }
069
070    protected Map<String, Object> ensureCombinedPrincipals() {
071        if (this.combinedPrincipals == null) {
072            this.combinedPrincipals = new HashMap<String, Object>();
073        }
074        return this.combinedPrincipals;
075    }
076
077    public boolean containsKey(Object o) {
078        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
079    }
080
081    public boolean containsValue(Object o) {
082        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
083    }
084
085    public Object get(Object o) {
086        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
087    }
088
089    public Object put(String s, Object o) {
090        return ensureCombinedPrincipals().put(s, o);
091    }
092
093    public Object remove(Object o) {
094        return this.combinedPrincipals != null ? this.combinedPrincipals.remove(o) : null;
095    }
096
097    public void putAll(Map<? extends String, ?> map) {
098        if (!CollectionUtils.isEmpty(map)) {
099            ensureCombinedPrincipals().putAll(map);
100        }
101    }
102
103    public Set<String> keySet() {
104        return CollectionUtils.isEmpty(this.combinedPrincipals) ? Collections.<String>emptySet()
105                : Collections.unmodifiableSet(this.combinedPrincipals.keySet());
106    }
107
108    public Collection<Object> values() {
109        return CollectionUtils.isEmpty(this.combinedPrincipals) ? Collections.emptySet()
110                : Collections.unmodifiableCollection(this.combinedPrincipals.values());
111    }
112
113    public Set<Entry<String, Object>> entrySet() {
114        return CollectionUtils.isEmpty(this.combinedPrincipals)
115                ? Collections.<Entry<String, Object>>emptySet() : Collections.unmodifiableSet(this.combinedPrincipals.entrySet());
116    }
117
118    public void clear() {
119        this.realmPrincipals = null;
120        this.combinedPrincipals = null;
121    }
122
123    public Object getPrimaryPrincipal() {
124        //heuristic - just use the first one we come across:
125        return !CollectionUtils.isEmpty(this.combinedPrincipals) ? this.combinedPrincipals.values().iterator().next() : null;
126    }
127
128    public <T> T oneByType(Class<T> type) {
129        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
130            return null;
131        }
132        for (Object value : this.combinedPrincipals.values()) {
133            if (type.isInstance(value)) {
134                return type.cast(value);
135            }
136        }
137        return null;
138    }
139
140    public <T> Collection<T> byType(Class<T> type) {
141        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
142            return Collections.emptySet();
143        }
144        Collection<T> instances = null;
145        for (Object value : this.combinedPrincipals.values()) {
146            if (type.isInstance(value)) {
147                if (instances == null) {
148                    instances = new ArrayList<T>();
149                }
150                instances.add(type.cast(value));
151            }
152        }
153        return instances != null ? instances : Collections.<T>emptyList();
154    }
155
156    public List asList() {
157        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
158            return Collections.emptyList();
159        }
160        List<Object> list = new ArrayList<Object>(this.combinedPrincipals.size());
161        list.addAll(this.combinedPrincipals.values());
162        return list;
163    }
164
165    public Set asSet() {
166        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
167            return Collections.emptySet();
168        }
169        Set<Object> set = new HashSet<Object>(this.combinedPrincipals.size());
170        set.addAll(this.combinedPrincipals.values());
171        return set;
172    }
173
174    public Collection fromRealm(String realmName) {
175        if (CollectionUtils.isEmpty(this.realmPrincipals)) {
176            return Collections.emptySet();
177        }
178        Map<String, Object> principals = this.realmPrincipals.get(realmName);
179        if (CollectionUtils.isEmpty(principals)) {
180            return Collections.emptySet();
181        }
182        return Collections.unmodifiableCollection(principals.values());
183    }
184
185    public Set<String> getRealmNames() {
186        if (CollectionUtils.isEmpty(this.realmPrincipals)) {
187            return Collections.emptySet();
188        }
189        return Collections.unmodifiableSet(this.realmPrincipals.keySet());
190    }
191
192    public boolean isEmpty() {
193        return CollectionUtils.isEmpty(this.combinedPrincipals);
194    }
195
196    public Iterator iterator() {
197        return asList().iterator();
198    }
199
200    public Map<String, Object> getRealmPrincipals(String name) {
201        if (this.realmPrincipals == null) {
202            return null;
203        }
204        Map<String, Object> principals = this.realmPrincipals.get(name);
205        if (principals == null) {
206            return null;
207        }
208        return Collections.unmodifiableMap(principals);
209    }
210
211    public Map<String, Object> setRealmPrincipals(String realmName, Map<String, Object> principals) {
212        if (realmName == null) {
213            throw new NullPointerException("realmName argument cannot be null.");
214        }
215        if (this.realmPrincipals == null) {
216            if (!CollectionUtils.isEmpty(principals)) {
217                this.realmPrincipals = new HashMap<String, Map<String, Object>>();
218                return this.realmPrincipals.put(realmName, new HashMap<String, Object>(principals));
219            } else {
220                return null;
221            }
222        } else {
223            Map<String, Object> existingPrincipals = this.realmPrincipals.remove(realmName);
224            if (!CollectionUtils.isEmpty(principals)) {
225                this.realmPrincipals.put(realmName, new HashMap<String, Object>(principals));
226            }
227            return existingPrincipals;
228        }
229    }
230
231    public Object setRealmPrincipal(String realmName, String principalName, Object principal) {
232        if (realmName == null) {
233            throw new NullPointerException("realmName argument cannot be null.");
234        }
235        if (principalName == null) {
236            throw new NullPointerException(("principalName argument cannot be null."));
237        }
238        if (principal == null) {
239            return removeRealmPrincipal(realmName, principalName);
240        }
241        if (this.realmPrincipals == null) {
242            this.realmPrincipals = new HashMap<String, Map<String, Object>>();
243        }
244        Map<String, Object> principals = this.realmPrincipals.get(realmName);
245        if (principals == null) {
246            principals = new HashMap<String, Object>();
247            this.realmPrincipals.put(realmName, principals);
248        }
249        return principals.put(principalName, principal);
250    }
251
252    public Object getRealmPrincipal(String realmName, String principalName) {
253        if (realmName == null) {
254            throw new NullPointerException("realmName argument cannot be null.");
255        }
256        if (principalName == null) {
257            throw new NullPointerException(("principalName argument cannot be null."));
258        }
259        if (this.realmPrincipals == null) {
260            return null;
261        }
262        Map<String, Object> principals = this.realmPrincipals.get(realmName);
263        if (principals != null) {
264            return principals.get(principalName);
265        }
266        return null;
267    }
268
269    public Object removeRealmPrincipal(String realmName, String principalName) {
270        if (realmName == null) {
271            throw new NullPointerException("realmName argument cannot be null.");
272        }
273        if (principalName == null) {
274            throw new NullPointerException(("principalName argument cannot be null."));
275        }
276        if (this.realmPrincipals == null) {
277            return null;
278        }
279        Map<String, Object> principals = this.realmPrincipals.get(realmName);
280        if (principals != null) {
281            return principals.remove(principalName);
282        }
283        return null;
284    }
285}