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.*;
024
025/**
026 * Default implementation of the {@link PrincipalMap} interface.
027 *
028 * *EXPERIMENTAL for Shiro 1.2 - DO NOT USE YET*
029 *
030 * @author Les Hazlewood
031 * @since 1.2
032 */
033public class SimplePrincipalMap implements PrincipalMap {
034
035    //Key: realm name, Value: map of principals specific to that realm
036    //                        internal map - key: principal name, value: principal
037    private Map<String, Map<String, Object>> realmPrincipals;
038
039    //maintains the principals from all realms plus any that are modified via the Map modification methods
040    //this ensures a fast lookup of any named principal instead of needing to iterate over
041    //the realmPrincipals for each lookup.
042    private Map<String, Object> combinedPrincipals;
043
044    public SimplePrincipalMap() {
045        this(null);
046    }
047
048    public SimplePrincipalMap(Map<String, Map<String, Object>> backingMap) {
049        if (!CollectionUtils.isEmpty(backingMap)) {
050            this.realmPrincipals = backingMap;
051            for (Map<String, Object> principals : this.realmPrincipals.values()) {
052                if (!CollectionUtils.isEmpty(principals) ) {
053                    ensureCombinedPrincipals().putAll(principals);
054                }
055            }
056        }
057    }
058
059    public int size() {
060        return CollectionUtils.size(this.combinedPrincipals);
061    }
062
063    protected Map<String, Object> ensureCombinedPrincipals() {
064        if (this.combinedPrincipals == null) {
065            this.combinedPrincipals = new HashMap<String, Object>();
066        }
067        return this.combinedPrincipals;
068    }
069
070    public boolean containsKey(Object o) {
071        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
072    }
073
074    public boolean containsValue(Object o) {
075        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
076    }
077
078    public Object get(Object o) {
079        return this.combinedPrincipals != null && this.combinedPrincipals.containsKey(o);
080    }
081
082    public Object put(String s, Object o) {
083        return ensureCombinedPrincipals().put(s, o);
084    }
085
086    public Object remove(Object o) {
087        return this.combinedPrincipals != null ? this.combinedPrincipals.remove(o) : null;
088    }
089
090    public void putAll(Map<? extends String, ?> map) {
091        if (!CollectionUtils.isEmpty(map)) {
092            ensureCombinedPrincipals().putAll(map);
093        }
094    }
095
096    public Set<String> keySet() {
097        return CollectionUtils.isEmpty(this.combinedPrincipals) ?
098                Collections.<String>emptySet() :
099                Collections.unmodifiableSet(this.combinedPrincipals.keySet());
100    }
101
102    public Collection<Object> values() {
103        return CollectionUtils.isEmpty(this.combinedPrincipals) ?
104                Collections.emptySet() :
105                Collections.unmodifiableCollection(this.combinedPrincipals.values());
106    }
107
108    public Set<Entry<String, Object>> entrySet() {
109        return CollectionUtils.isEmpty(this.combinedPrincipals) ?
110                Collections.<Entry<String,Object>>emptySet() :
111                Collections.unmodifiableSet(this.combinedPrincipals.entrySet());
112    }
113
114    public void clear() {
115        this.realmPrincipals = null;
116        this.combinedPrincipals = null;
117    }
118
119    public Object getPrimaryPrincipal() {
120        //heuristic - just use the first one we come across:
121        return !CollectionUtils.isEmpty(this.combinedPrincipals) ?
122                this.combinedPrincipals.values().iterator().next() :
123                null;
124    }
125
126    public <T> T oneByType(Class<T> type) {
127        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
128            return null;
129        }
130        for( Object value : this.combinedPrincipals.values()) {
131            if (type.isInstance(value) ) {
132                return type.cast(value);
133            }
134        }
135        return null;
136    }
137
138    public <T> Collection<T> byType(Class<T> type) {
139        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
140            return Collections.emptySet();
141        }
142        Collection<T> instances = null;
143        for( Object value : this.combinedPrincipals.values()) {
144            if (type.isInstance(value) ) {
145                if (instances == null) {
146                    instances = new ArrayList<T>();
147                }
148                instances.add(type.cast(value));
149            }
150        }
151        return instances != null ? instances : Collections.<T>emptyList();
152    }
153
154    public List asList() {
155        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
156            return Collections.emptyList();
157        }
158        List<Object> list = new ArrayList<Object>(this.combinedPrincipals.size());
159        list.addAll(this.combinedPrincipals.values());
160        return list;
161    }
162
163    public Set asSet() {
164        if (CollectionUtils.isEmpty(this.combinedPrincipals)) {
165            return Collections.emptySet();
166        }
167        Set<Object> set = new HashSet<Object>(this.combinedPrincipals.size());
168        set.addAll(this.combinedPrincipals.values());
169        return set;
170    }
171
172    public Collection fromRealm(String realmName) {
173        if (CollectionUtils.isEmpty(this.realmPrincipals)) {
174            return Collections.emptySet();
175        }
176        Map<String,Object> principals = this.realmPrincipals.get(realmName);
177        if (CollectionUtils.isEmpty(principals)) {
178            return Collections.emptySet();
179        }
180        return Collections.unmodifiableCollection(principals.values());
181    }
182
183    public Set<String> getRealmNames() {
184        if (CollectionUtils.isEmpty(this.realmPrincipals)) {
185            return Collections.emptySet();
186        }
187        return Collections.unmodifiableSet(this.realmPrincipals.keySet());
188    }
189
190    public boolean isEmpty() {
191        return CollectionUtils.isEmpty(this.combinedPrincipals);
192    }
193
194    public Iterator iterator() {
195        return asList().iterator();
196    }
197
198    public Map<String, Object> getRealmPrincipals(String name) {
199        if (this.realmPrincipals == null) {
200            return null;
201        }
202        Map<String,Object> principals = this.realmPrincipals.get(name);
203        if (principals == null) {
204            return null;
205        }
206        return Collections.unmodifiableMap(principals);
207    }
208
209    public Map<String,Object> setRealmPrincipals(String realmName, Map<String, Object> principals) {
210        if (realmName == null) {
211            throw new NullPointerException("realmName argument cannot be null.");
212        }
213        if (this.realmPrincipals == null) {
214            if (!CollectionUtils.isEmpty(principals)) {
215                this.realmPrincipals = new HashMap<String,Map<String,Object>>();
216                return this.realmPrincipals.put(realmName, new HashMap<String,Object>(principals));
217            } else {
218                return null;
219            }
220        } else {
221            Map<String,Object> existingPrincipals = this.realmPrincipals.remove(realmName);
222            if (!CollectionUtils.isEmpty(principals)) {
223                this.realmPrincipals.put(realmName, new HashMap<String,Object>(principals));
224            }
225            return existingPrincipals;
226        }
227    }
228
229    public Object setRealmPrincipal(String realmName, String principalName, Object principal) {
230        if (realmName == null) {
231            throw new NullPointerException("realmName argument cannot be null.");
232        }
233        if (principalName == null) {
234            throw new NullPointerException(("principalName argument cannot be null."));
235        }
236        if (principal == null) {
237            return removeRealmPrincipal(realmName, principalName);
238        }
239        if (this.realmPrincipals == null) {
240            this.realmPrincipals = new HashMap<String,Map<String,Object>>();
241        }
242        Map<String,Object> principals = this.realmPrincipals.get(realmName);
243        if (principals == null) {
244            principals = new HashMap<String,Object>();
245            this.realmPrincipals.put(realmName, principals);
246        }
247        return principals.put(principalName, principal);
248    }
249
250    public Object getRealmPrincipal(String realmName, String principalName) {
251        if (realmName == null) {
252            throw new NullPointerException("realmName argument cannot be null.");
253        }
254        if (principalName == null) {
255            throw new NullPointerException(("principalName argument cannot be null."));
256        }
257        if (this.realmPrincipals == null) {
258            return null;
259        }
260        Map<String,Object> principals = this.realmPrincipals.get(realmName);
261        if (principals != null) {
262            return principals.get(principalName);
263        }
264        return null;
265    }
266
267    public Object removeRealmPrincipal(String realmName, String principalName) {
268        if (realmName == null) {
269            throw new NullPointerException("realmName argument cannot be null.");
270        }
271        if (principalName == null) {
272            throw new NullPointerException(("principalName argument cannot be null."));
273        }
274        if (this.realmPrincipals == null) {
275            return null;
276        }
277        Map<String,Object> principals = this.realmPrincipals.get(realmName);
278        if (principals != null) {
279            return principals.remove(principalName);
280        }
281        return null;
282    }
283}