001package org.apache.fulcrum.security.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.Serializable;
023import java.util.Collection;
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.Map;
027import java.util.Set;
028import java.util.TreeMap;
029
030import org.apache.commons.lang3.StringUtils;
031import org.apache.fulcrum.security.entity.SecurityEntity;
032
033/**
034 * This class represents a set of Security Entities. It makes it easy to build a
035 * UI. It wraps a TreeSet object to enforce that only relevant methods are
036 * available. TreeSet's contain only unique Objects (no duplicates) based on the
037 * ID. They may or may not have a name, that depends on the implementation. Want
038 * to get away from requiring an ID and a name... Nothing should force Name to
039 * be unique in the basic architecture of Fulcrum Security.
040 *
041 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
042 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
043 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
044 * @author <a href="mailto:marco@intermeta.de">Marco Kn&uuml;ttel</a>
045 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
046 * @version $Id$
047 */
048public abstract class SecuritySet<T extends SecurityEntity> implements Serializable, Set<T>, Iterable<T> {
049        /** Serial version */
050        private static final long serialVersionUID = 2251987059226422569L;
051
052        /** Map for "name" is "security object" */
053        protected Map<String, T> nameMap = null;
054
055        /** Map for "id" is "security object" */
056        protected Map<Object, T> idMap = null;
057
058        /**
059         * Constructs an empty Set
060         */
061        public SecuritySet() 
062        {
063                nameMap = new TreeMap<String, T>(String.CASE_INSENSITIVE_ORDER);
064                idMap = new TreeMap<Object, T>();
065        }
066
067        /**
068         * Returns a set of security objects in this object.
069         *
070         * @return A Set Object
071         *
072         */
073        public Set<T> getSet() 
074        {
075                return new HashSet<T>(idMap.values());
076        }
077
078        /**
079         * Returns a set of Names in this Object.
080         *
081         * @return The Set of Names in this Object, backed by the actual data.
082         */
083        public Set<String> getNames() 
084        {
085                return nameMap.keySet();
086        }
087
088        /**
089         * Returns a set of Id values in this Object.
090         *
091         * @return The Set of Ids in this Object, backed by the actual data.
092         */
093        public Set<Object> getIds() 
094        {
095                return idMap.keySet();
096        }
097
098        /**
099         * Removes all Objects from this Set.
100         */
101        @Override
102        public void clear() 
103        {
104                nameMap.clear();
105                idMap.clear();
106        }
107
108        /**
109         * Searches if an Object with a given name is in the Set
110         *
111         * @param name Name of the Security Object.
112         * @return True if argument matched an Object in this Set; false if no match.
113         */
114        public boolean containsName(String name) 
115        {
116                return StringUtils.isNotEmpty(name) ? nameMap.containsKey(name) : false;
117        }
118
119        /**
120         * Searches if an Object with a given Id is in the Set
121         *
122         * @param id Id of the Security Object.
123         * @return True if argument matched an Object in this Set; false if no match.
124         */
125        public boolean containsId(Object id) 
126        {
127                return (id == null) ? false : idMap.containsKey(id);
128        }
129
130        /**
131         * Returns an Iterator for Objects in this Set.
132         *
133         * @return An iterator for the Set
134         */
135        @Override
136        public Iterator<T> iterator() 
137        {
138                return idMap.values().iterator();
139        }
140
141        /**
142         * Returns size (cardinality) of this set.
143         *
144         * @return The cardinality of this Set.
145         */
146        @Override
147        public int size() 
148        {
149                return idMap.size();
150        }
151
152        /**
153         * list of role names in this set
154         *
155         * @return The string representation of this Set.
156         */
157        @Override
158        public String toString() 
159        {
160                StringBuilder sbuf = new StringBuilder(12 * size());
161
162                for (Iterator<T> it = iterator(); it.hasNext();) {
163                        T se = it.next();
164                        sbuf.append('[');
165                        sbuf.append(se.getName());
166                        sbuf.append(" -> ");
167                        sbuf.append(se.getId());
168                        sbuf.append(']');
169                        if (it.hasNext()) {
170                                sbuf.append(", ");
171                        }
172                }
173
174                return sbuf.toString();
175        }
176
177        // methods from Set
178        /**
179         * @see java.util.Collection#add(java.lang.Object)
180         */
181        @Override
182        public boolean add(T o) 
183        {
184                if (contains(o)) 
185                {
186                        return false;
187                }
188
189                if (o.getId() != null) 
190                {
191                        idMap.put(o.getId(), o);
192                }
193                
194                if (o.getName() != null) 
195                {
196                        nameMap.put(o.getName(), o);
197                }
198
199                return true;
200        }
201
202        /**
203         * Adds the entities in a Collection to this SecuritySet.
204         *
205         * @param collection A Collection of entities.
206         * @return True if this Set changed as a result; false if no change to this Set
207         *         occurred (this Set already contained all members of the added Set).
208         */
209        public boolean add(Collection<? extends T> collection) 
210        {
211                return addAll(collection);
212        }
213
214        @Override
215        public boolean addAll(Collection<? extends T> collection) 
216        {
217                boolean res = false;
218
219                for (T o : collection) 
220                        res |= add(o);
221
222                return res;
223        }
224
225        @Override
226        public boolean isEmpty() 
227        {
228                return idMap.isEmpty();
229        }
230
231        @Override
232        public boolean containsAll(Collection<?> collection) 
233        {
234                for (Object object : collection) 
235                {
236                        if (!contains(object)) 
237                        {
238                                return false;
239                        }
240                }
241                return true;
242        }
243
244        @Override
245        public boolean removeAll(Collection<?> collection) 
246        {
247                boolean changed = false;
248                for (Object object : collection) 
249                {
250                        boolean result = remove(object);
251                        if (result) 
252                        {
253                                changed = true;
254                        }
255                }
256
257                return changed;
258        }
259
260        @Override
261        public boolean retainAll(Collection<?> collection) 
262        {
263                throw new RuntimeException("not implemented");
264        }
265
266        /*
267         * (non-Javadoc)
268         *
269         * @see java.util.Collection#toArray()
270         */
271        @Override
272        public Object[] toArray() 
273        {
274                return getSet().toArray();
275        }
276
277        /**
278         * Checks whether this SecuritySet contains an entity.
279         *
280         * @param o An entity.
281         * @return True if this Set contains the entity, false otherwise.
282         */
283        @Override
284        public boolean contains(Object o) 
285        {
286                if (o == null || !(o instanceof SecurityEntity)) 
287                {
288                        return false;
289                } 
290                else 
291                {
292                        return containsId(((SecurityEntity) o).getId());
293                }
294        }
295
296        /**
297         * Removes an entity from this SecuritySet.
298         *
299         * @param o An entity.
300         * @return True if this Set contained the entity before it was removed.
301         */
302        @Override
303        public boolean remove(Object o) 
304        {
305                if (o instanceof SecurityEntity) 
306                {
307                        boolean res = contains(o);
308                        idMap.remove(((SecurityEntity) o).getId());
309                        nameMap.remove(((SecurityEntity) o).getName());
310                        return res;
311                }
312
313                return false;
314        }
315
316        /* (non-Javadoc)
317         * @see java.util.Set#toArray(java.lang.Object[])
318         */
319        @Override
320        public <A> A[] toArray(A[] a) 
321        {
322                return getSet().toArray(a);
323        }
324
325        /**
326         * Returns an entity with the given name, if it is contained in this
327         * SecuritySet.
328         *
329         * @param name Name of entity.
330         * @return entity if argument matched an entity in this Set; null if no match.
331         */
332        public T getByName(String name) 
333        {
334                return nameMap.get(name);
335        }
336
337        /**
338         * Returns an entity with the given id, if it is contained in this SecuritySet.
339         *
340         * @param id ID of entity.
341         * @return entity if argument matched an entity in this Set; null if no match.
342         */
343        public T getById(Object id) 
344        {
345                return idMap.get(id);
346        }
347}