1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.admin.user;
22  
23  import java.io.Serializable;
24  import java.util.HashMap;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  import java.util.Set;
28  
29  import org.efaps.admin.datamodel.Type;
30  import org.efaps.ci.CIAdminUser;
31  import org.efaps.db.Insert;
32  import org.efaps.db.InstanceQuery;
33  import org.efaps.db.MultiPrintQuery;
34  import org.efaps.db.QueryBuilder;
35  import org.efaps.db.Update;
36  import org.efaps.util.EFapsException;
37  
38  /**
39   * This class represents a set of UserAttribute related to a User.<br>
40   * For each User a set of UserAttribute can be made to store user-related
41   * content. e.g. Color of the UserInterface etc. The Class will read all Already
42   * in the eFpas-DataBase existing UserAttribute and store them in a map. A new
43   * or altered UserAttribute which is created/altered during the Session will
44   * only be stored in the map and not into the eFaps-DataBase. To store the
45   * UserAttribute into the eFapsDataBase the method {@link #storeInDb()} must be
46   * called explicitly.
47   *
48   * @author The eFasp Team
49   * @version $Id$
50   */
51  public class UserAttributesSet
52      implements Serializable
53  {
54  
55      /**
56       * This static variable is the Key used to store the UserAttribtues into the
57       * SessionContext {@link #org.efaps.db.Context.getUserAttribute()}.
58       */
59      public static final String CONTEXTMAPKEY = UserAttributesSet.class.getName() + ".UserAttributes";
60  
61      /**
62       * Needed for serialization.
63       */
64      private static final long serialVersionUID = 1L;
65  
66      /**
67       * this static Map contains the name-to=UserAttribute Relation used by the
68       * enumeration.
69       */
70      private static final Map<String, UserAttributesSet.UserAttributesDefinition> MAPPER
71          = new HashMap<String, UserAttributesSet.UserAttributesDefinition>();
72  
73      /**
74       * This enumeration is used to get a relation to the necessary types in the
75       * eFaps database for the attribute set.
76       */
77      public enum UserAttributesDefinition {
78          /**
79           *
80           */
81          ATTRIBUTE("Admin_User_Attribute", "Key", "Value");
82  
83          /**
84           * stores the name of the Type.
85           */
86          private final String name;
87  
88          /**
89           * Name of the attribute containing the Key.
90           */
91          private final String keyAttribute;
92  
93          /**
94           * Name of the attribute containing the Value.
95           */
96          private final String valueAttribute;
97  
98          /**
99           * Initializes the relationship definition for an user attribute set.
100          *
101          * @param _name name of type (relationship)
102          * @param _keyAttribute name of the key attribute
103          * @param _value name of the value attribute
104          */
105         private UserAttributesDefinition(final String _name,
106                                          final String _keyAttribute,
107                                          final String _value)
108         {
109             this.name = _name;
110             this.keyAttribute = _keyAttribute;
111             this.valueAttribute = _value;
112             UserAttributesSet.MAPPER.put(_name, this);
113         }
114 
115     }
116 
117     /**
118      * instance map to store a Key-to-UserAttribute Relation.
119      */
120     private final Map<String, UserAttribute> attributes = new HashMap<String, UserAttribute>();
121 
122     /**
123      * this instance variable stores the Id of the User this UserAttributeSet
124      * belongs to.
125      */
126     private final Long userId;
127 
128     /**
129      * Constructor using the constructor {@link #UserAttributesSet(long)}
130      * through searching the person id for the given name.
131      *
132      * @param _userName name of the user this attribute set will belong to
133      * @throws EFapsException if user attribute set could not be fetched from
134      *             eFaps database
135      */
136     public UserAttributesSet(final String _userName)
137         throws EFapsException
138     {
139         this(Person.get(_userName).getId());
140     }
141 
142     /**
143      * Constructor setting the {@link #userId} of the user this user attribute
144      * set belongs to and fetching the user attributes for this {@link #userId}
145      * from the eFaps database..
146      *
147      * @param _userId id of the user this user attribute set will belong to
148      * @throws EFapsException if attribute set could not be read from eFaps
149      * @see #readUserAttributes()
150      */
151     public UserAttributesSet(final long _userId)
152         throws EFapsException
153     {
154         this.userId = _userId;
155         readUserAttributes();
156     }
157 
158     /**
159      * Initialize this user attribute set.
160      *
161      * @throws EFapsException if this attribute set could not be read from the
162      *             eFaps database
163      * @see #readUserAttributes()
164      */
165     public void initialise()
166         throws EFapsException
167     {
168         readUserAttributes();
169     }
170 
171     /**
172      * Check if this user attribute set contains an attribute for given
173      * <code>_key</code>.
174      *
175      * @param _key key to check if this user attribute set contains it
176      * @return <i>true</i> if the key was found; otherwise <i>false</i>
177      */
178     public boolean containsKey(final String _key)
179     {
180         return this.attributes.containsKey(_key);
181     }
182 
183     /**
184      * Returns the value for a key as a String.
185      *
186      * @param _key key for the searched value
187      * @return string of the value if exist; otherwise <code>null</code>
188      */
189     public String getString(final String _key)
190     {
191         String ret = null;
192         if (this.attributes.containsKey(_key)) {
193             ret = this.attributes.get(_key).getValue();
194         }
195         return ret;
196     }
197 
198     /**
199      * Sets a key-value pair for the attribute set of this attribute set. It
200      * uses {@link #set(String, String, UserAttributesDefinition)} to set the
201      * the relationship information. It will search in the {@link #MAPPER} to
202      * retrieve the definition of the attribute. If found it will use the found
203      * one, else it will use the a default
204      * {@link UserAttributesDefinition#ATTRIBUTE}.
205      *
206      * @param _key key to be set
207      * @param _value value to be set
208      * @throws EFapsException if <code>_key</code> or <code>_value</code> is
209      *             <code>null</code>
210      */
211     public void set(final String _key,
212                     final String _value)
213         throws EFapsException
214     {
215         if (UserAttributesSet.MAPPER.containsKey(_key)) {
216             set(_key, _value, UserAttributesSet.MAPPER.get(_key));
217         } else {
218             set(_key, _value, UserAttributesSet.UserAttributesDefinition.ATTRIBUTE);
219         }
220     }
221 
222     /**
223      * Sets a key-value pair into the attribute set of an user. The method will
224      * search for the key and if the key already exists it will update the user
225      * attribute in this set. If the key does not exist a new user attribute
226      * will be added to this set.
227      *
228      * @param _key key to be set
229      * @param _value value to be set
230      * @param _definition type of the key-value pair
231      * @throws EFapsException if <code>_key</code> or <code>_value</code> is
232      *             <code>null</code>
233      */
234     public void set(final String _key,
235                     final String _value,
236                     final UserAttributesDefinition _definition)
237         throws EFapsException
238     {
239         if (_key == null || _value == null) {
240             throw new EFapsException(this.getClass(), "set", _key, _value, _definition);
241         } else {
242             final UserAttribute userattribute = this.attributes.get(_key);
243             if (userattribute == null) {
244                 this.attributes.put(_key, new UserAttribute(_definition.name, _value.trim(), true));
245             } else if (!userattribute.getValue().equals(_value.trim())) {
246                 userattribute.setUpdate(true);
247                 userattribute.setValue(_value.trim());
248             }
249         }
250     }
251 
252     /**
253      * This method stores all user attributes of this user attributes set into
254      * the eFaps database. Only user attributes which where added or updated in
255      * this session will be updated/inserted in the eFpas database.
256      *
257      * @throws EFapsException if update of the user attributes failed
258      */
259     public void storeInDb()
260         throws EFapsException
261     {
262         for (final Entry<String, UserAttribute> entry : this.attributes.entrySet()) {
263             if (entry.getValue().isUpdate()) {
264                 final QueryBuilder queryBldr = new QueryBuilder(Type.get(entry.getValue().getType()));
265                 queryBldr.addWhereAttrEqValue("UserLink", this.userId.toString());
266                 if (UserAttributesSet.MAPPER.get(entry.getValue().getType()).keyAttribute != null) {
267                     queryBldr.addWhereAttrEqValue(UserAttributesSet.MAPPER.get(entry.getValue().getType()).keyAttribute,
268                                               entry.getKey());
269                 }
270                 final InstanceQuery query = queryBldr.getQuery();
271                 query.execute();
272                 Update update;
273                 if (query.next()) {
274                     update = new Update(query.getCurrentValue());
275                 } else {
276                     update = new Insert(entry.getValue().getType());
277                     if (UserAttributesSet.MAPPER.get(entry.getValue().getType()).keyAttribute != null) {
278                         update.add(UserAttributesSet.MAPPER.get(entry.getValue().getType()).keyAttribute,
279                                         entry.getKey());
280                     }
281                     update.add("UserLink", this.userId.toString());
282                 }
283                 update.add("Value", entry.getValue().getValue());
284                 update.execute();
285                 update.close();
286             }
287         }
288         this.attributes.clear();
289     }
290 
291     /**
292      * Reads all {@link UserAttribute user attributes} from the eFaps database
293      * which belong to the user this user attribute set is read to and caches
294      * them in a map for fast access.
295      *
296      * @see #UserAttribute
297      * @throws EFapsException if attributes could not be read from eFaps
298      */
299     private void readUserAttributes()
300         throws EFapsException
301     {
302         // if the MAPPER is empty it must be initialized
303         if (UserAttributesSet.MAPPER.isEmpty()) {
304             UserAttributesSet.UserAttributesDefinition.values();
305         }
306 
307         final Set<Type> types = CIAdminUser.AttributeAbstract.getType().getChildTypes();
308         for (final Type type : types) {
309             if (UserAttributesSet.MAPPER.containsKey(type.getName())) {
310                 final UserAttributesDefinition definition = UserAttributesSet.MAPPER.get(type.getName());
311                 final QueryBuilder queryBldr = new QueryBuilder(Type.get(definition.name));
312                 queryBldr.addWhereAttrEqValue("UserLink", this.userId);
313                 final MultiPrintQuery multi = queryBldr.getPrint();
314                 multi.addAttribute(definition.valueAttribute);
315                 if (definition.keyAttribute != null) {
316                     multi.addAttribute(definition.keyAttribute);
317                 }
318                 multi.executeWithoutAccessCheck();
319                 while (multi.next()) {
320                     String key;
321                     if (definition.keyAttribute == null) {
322                         key = definition.name;
323                     } else {
324                         key = multi.getAttribute(definition.keyAttribute).toString().trim();
325                     }
326                     this.attributes.put(key,
327                                     new UserAttribute(definition.name, multi.getAttribute(definition.valueAttribute)
328                                                     .toString().trim(), false));
329                 }
330             }
331         }
332     }
333 
334     /**
335      * Each instance of this class represents one UserAttribute for this user
336      * attribute set.
337      */
338     private class UserAttribute
339         implements Serializable
340     {
341 
342         /**
343          * Needed for serialization.
344          */
345         private static final long serialVersionUID = 1L;
346 
347         /**
348          * Value of this user attribute.
349          */
350         private String value;
351 
352         /**
353          * Must this user attribute updated in the eFaps database?
354          */
355         private boolean update;
356 
357         /**
358          * Type of this user attribute.
359          */
360         private final String type;
361 
362         /**
363          *
364          * @param _type type of this user attribute set
365          * @param _value value of this user attribute set
366          * @param _update <i>true</i> if the attribute must be updated in eFaps
367          */
368         public UserAttribute(final String _type,
369                              final String _value,
370                              final boolean _update)
371         {
372             this.type = _type;
373             this.value = _value;
374             this.update = _update;
375         }
376 
377         /**
378          * This is the getter method for the instance variable {@link #value}.
379          *
380          * @return value of instance variable {@link #value}
381          */
382         public String getValue()
383         {
384             return this.value;
385         }
386 
387         /**
388          * This is the setter method for the instance variable {@link #value}.
389          *
390          * @param _value the value to set
391          */
392         public void setValue(final String _value)
393         {
394             this.value = _value;
395         }
396 
397         /**
398          * This is the getter method for the instance variable {@link #update}.
399          *
400          * @return value of instance variable {@link #update}
401          */
402         public boolean isUpdate()
403         {
404             return this.update;
405         }
406 
407         /**
408          * This is the setter method for the instance variable {@link #update}.
409          *
410          * @param _update the update to set
411          */
412         public void setUpdate(final boolean _update)
413         {
414             this.update = _update;
415         }
416 
417         /**
418          * This is the getter method for the instance variable {@link #type}.
419          *
420          * @return value of instance variable {@link #type}
421          */
422         public String getType()
423         {
424             return this.type;
425         }
426     }
427 }