1   /*
2    * Copyright 2003 - 2014 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.datamodel;
22  
23  import java.sql.PreparedStatement;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.StringTokenizer;
32  import java.util.TreeMap;
33  import java.util.UUID;
34  
35  import org.apache.commons.lang3.builder.ToStringBuilder;
36  import org.efaps.admin.event.EventDefinition;
37  import org.efaps.admin.event.EventType;
38  import org.efaps.admin.event.Parameter.ParameterValues;
39  import org.efaps.admin.event.Return;
40  import org.efaps.admin.event.Return.ReturnValues;
41  import org.efaps.ci.CIAdminDataModel;
42  import org.efaps.ci.CIAdminUser;
43  import org.efaps.db.Context;
44  import org.efaps.db.databases.information.ColumnInformation;
45  import org.efaps.db.transaction.ConnectionResource;
46  import org.efaps.db.wrapper.SQLInsert;
47  import org.efaps.db.wrapper.SQLPart;
48  import org.efaps.db.wrapper.SQLSelect;
49  import org.efaps.db.wrapper.SQLUpdate;
50  import org.efaps.util.EFapsException;
51  import org.efaps.util.cache.CacheLogListener;
52  import org.efaps.util.cache.CacheReloadException;
53  import org.efaps.util.cache.InfinispanCache;
54  import org.infinispan.Cache;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  /**
59   * This is the class for the attribute description. The type description holds
60   * information about creation of a new instance of a attribute with default
61   * values.
62   *
63   * @author The eFaps Team
64   * @version $Id$
65   */
66  public class Attribute
67      extends AbstractDataModelObject
68  {
69      /**
70       * ENUM used to access the different attribute types.
71       */
72      public enum AttributeTypeDef
73      {
74          /** Attribute type Link. */
75          ATTRTYPE_LINK("440f472f-7be2-41d3-baec-4a2f0e4e5b31"),
76          /** Attribute type Link with Ranges. */
77          ATTRTYPE_LINK_WITH_RANGES("9d6b2e3e-68ce-4509-a5f0-eae42323a696"),
78          /** Attribute type PersonLink. */
79          ATTRTYPE_GROUP_LINK("a48538dd-5d9b-468f-a84f-bf42791eed66"),
80          /** Attribute type PersonLink. */
81          ATTRTYPE_PERSON_LINK("7b8f98de-1967-44e0-b174-027349868a61"),
82          /** Attribute type Creator Link. */
83          ATTRTYPE_CREATOR_LINK("76122fe9-8fde-4dd4-a229-e48af0fb4083"),
84          /** Attribute type Modifier Link. */
85          ATTRTYPE_MODIFIER_LINK("447a7c87-8395-48c4-b2ed-d4e96d46332c"),
86          /** Attribute type Multi Line Array. */
87          ATTRTYPE_MULTILINEARRAY("adb13c3d-9506-4da2-8d75-b54c76779c6c"),
88          /** Attribute type Status. */
89          ATTRTYPE_STATUS("0161bcdb-45e9-4839-a709-3a1c56f8a76a"),
90          /** Attribute type Enum. */
91          ATTRTYPE_ENUM("b7c6a324-5dec-425f-b778-fa8fabf80202"),
92          /** Attribute type BitEnum. */
93          ATTRTYPE_BITENUM("a9b1abde-d58d-4aea-8cdc-f2870111f1cd"),
94          /** Attribute type BitEnum. */
95          ATTRTYPE_JAXB("58817bd8-db76-4b40-8acd-18112fe96170");
96  
97          /**
98           * Stored the UUID for the given type.
99           */
100         private final UUID uuid;
101 
102         /**
103          * Private Constructor.
104          *
105          * @param _uuid UUID to set
106          */
107         private AttributeTypeDef(final String _uuid)
108         {
109             this.uuid = UUID.fromString(_uuid);
110         }
111 
112         /**
113          * @return the uuid
114          */
115         public UUID getUuid()
116         {
117             return this.uuid;
118         }
119     }
120 
121     /**
122      * Needed for serialization.
123      */
124     private static final long serialVersionUID = 1L;
125 
126     /**
127      * Logging instance used in this class.
128      */
129     private static final Logger LOG = LoggerFactory.getLogger(Attribute.class);
130 
131     /**
132      * SQL Statement to get attributes for at type.
133      */
134     private static final String SQL_TYPE = new SQLSelect()
135                     .column("ID")
136                     .column("NAME")
137                     .column("TYPEID")
138                     .column("DMTABLE")
139                     .column("DMATTRIBUTETYPE")
140                     .column("DMTYPELINK")
141                     .column("PARENTSET")
142                     .column("SQLCOLUMN")
143                     .column("DEFAULTVAL")
144                     .column("DIMENSION")
145                     .column("CLASSNAME")
146                     .from("V_ADMINATTRIBUTE", 0)
147                     .addPart(SQLPart.WHERE).addColumnPart(0, "DMTYPE").addPart(SQLPart.EQUAL).addValuePart("?")
148                     .toString();
149 
150     /**
151      * SQL Statement to get an attribute.
152      */
153     private static final String SQL_ATTR = new SQLSelect()
154                     .column("DMTYPE")
155                     .from("V_ADMINATTRIBUTE", 0)
156                     .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString();
157 
158     /**
159      * Name of the Cache by Name.
160      */
161     private static String NAMECACHE = Attribute.class.getName() + ".Name";
162 
163     /**
164      * Name of the Cache by ID.
165      */
166     private static String IDCACHE = Attribute.class.getName() + ".ID";
167 
168     /**
169      * This is the instance variable for the table, where attribute is stored.
170      *
171      * @see #getTable
172      */
173     private final SQLTable sqlTable;
174 
175     /**
176      * Instance variable for the link to another type  id..
177      *
178      * @see #getLink
179      * @see #setLink
180      */
181     private Long link = null;
182 
183     /**
184      * Instance variable for the parent type id.
185      *
186      * @see #getParent
187      * @see #setParent
188      */
189     private final Long parent;
190 
191     /**
192      * This instance variable stores the sql column name.
193      *
194      * @see #getSqlColName
195      * @see #setSqlColName
196      */
197     private final ArrayList<String> sqlColNames = new ArrayList<String>();
198 
199     /**
200      * The instance variable stores the attribute type for this attribute.
201      *
202      * @see #getAttributeType
203      */
204     private final AttributeType attributeType;
205 
206     /**
207      * The String holds the default value as string for this Attribute.
208      *
209      * @see #getDefaultValue
210      */
211     private final String defaultValue;
212 
213     /**
214      * Is the attribute required? This means at minimum one part of the
215      * attribute is not allowed to be a null value.
216      *
217      * @see #isRequired
218      */
219     private final boolean required;
220 
221     /**
222      * The parent this attribute belongs to.
223      */
224     private AttributeSet parentSet;
225 
226     /**
227      * Size of the attribute (for string). Precision of the attribute (for
228      * decimal).
229      */
230     private final int size;
231 
232     /**
233      * Scale of the attribute (for decimal).
234      */
235     private final int scale;
236 
237     /**
238      * UUID of the dimension belonging to this attribute.
239      */
240     private final String dimensionUUID;
241 
242     /**
243      * Holds the Attributes this Attribute depend on. A TreeMap is used to have
244      * a fixed position of each attribute. (Needed e.g for printquery)
245      */
246     private Map<String, Attribute> dependencies;
247 
248     /**
249      * Key of this Attribute. Consist of name of the Parent Type and name of the Attribute itself
250      */
251     private String key;
252 
253     /**
254      * ClassName of this Attribute. Used only in case of
255      * {@link org.efaps.admin.datamodel.attributetype.EnumType}
256      * and
257      * {@link org.efaps.admin.datamodel.attributetype.BitEnumType}.
258      */
259     private String className;
260 
261     /**
262      * This is the constructor for class {@link Attribute}. Every instance of
263      * class {@link Attribute} must have a name (parameter <i>_name</i>) and an
264      * identifier (parameter <i>_id</i>).
265      *
266      * @param _id id of the attribute
267      * @param _parentId id of the parent type
268      * @param _name name of the instance
269      * @param _sqlColNames name of the SQL columns
270      * @param _sqlTable table of this attribute
271      * @param _attributeType type of this attribute
272      * @param _defaultValue default value for this attribute
273      * @param _dimensionUUID UUID of the Dimension
274      * @throws EFapsException on error while retrieving column information from
275      *             database
276      */
277     //CHECKSTYLE:OFF
278     protected Attribute(final long _id,
279                         final long _parentId,
280                         final String _name,
281                         final String _sqlColNames,
282                         final SQLTable _sqlTable,
283                         final AttributeType _attributeType,
284                         final String _defaultValue,
285                         final String _dimensionUUID)
286     //CHECKSTYLE:ON
287         throws EFapsException
288     {
289         super(_id, null, _name);
290         this.sqlTable = _sqlTable;
291         this.parent = _parentId;
292         this.attributeType = _attributeType;
293         this.defaultValue = (_defaultValue != null) ? _defaultValue.trim() : null;
294         this.dimensionUUID = (_dimensionUUID != null) ? _dimensionUUID.trim() : null;
295         // add SQL columns and evaluate if attribute is required
296         boolean req = false;
297         int sizeTemp = 0;
298         int scaleTemp = 0;
299         final StringTokenizer tok = new StringTokenizer(_sqlColNames.trim(), ",");
300         while (tok.hasMoreTokens()) {
301             final String colName = tok.nextToken().trim();
302             getSqlColNames().add(colName);
303             final ColumnInformation columInfo = this.sqlTable.getTableInformation().getColInfo(colName);
304             if (columInfo == null) {
305                 throw new EFapsException(Attribute.class, "Attribute", _id, _name, _sqlTable, colName);
306             }
307             req |= !columInfo.isNullable();
308             sizeTemp = columInfo.getSize();
309             scaleTemp = columInfo.getScale();
310         }
311         this.size = sizeTemp;
312         this.scale = scaleTemp;
313         this.required = req;
314     }
315 
316     /**
317      * This is the constructor for class {@link Attribute}. Every instance of
318      * class {@link Attribute} must have a name (parameter <i>_name</i>) and an
319      * identifier (parameter <i>_id</i>).<br/>
320      * This constructor is used for the copy method (clone of an attribute
321      * instance).
322      *
323      * @see #copy
324      * @param _id id of the attribute
325      * @param _name name of the instance
326      * @param _sqlTable table of this attribute
327      * @param _attributeType typer of this attribute
328      * @param _defaultValue default value for this attribute
329      * @param _dimensionUUID uuid of the dimension belnging to this attribute
330      * @param _required is it required
331      * @param _size Size
332      * @param _scale Scale
333      */
334     // CHECKSTYLE:OFF
335     private Attribute(final long _id,
336                       final long _parentId,
337                       final String _name,
338                       final SQLTable _sqlTable,
339                       final AttributeType _attributeType,
340                       final String _defaultValue,
341                       final String _dimensionUUID,
342                       final boolean _required,
343                       final int _size,
344                       final int _scale)
345     {
346         // CHECKSTYLE:ON
347         super(_id, null, _name);
348         this.parent = _parentId;
349         this.sqlTable = _sqlTable;
350         this.attributeType = _attributeType;
351         this.defaultValue = (_defaultValue != null) ? _defaultValue.trim() : null;
352         this.required = _required;
353         this.size = _size;
354         this.scale = _scale;
355         this.dimensionUUID = _dimensionUUID;
356     }
357 
358     /**
359      * This method returns <i>true</i> if a link exists. This is made with a
360      * test of the return value of method {@link #getLink} on null.
361      *
362      * @return <i>true</i> if this attribute has a link, otherwise <i>false</i>
363      */
364     public boolean hasLink()
365     {
366         return this.link != null;
367     }
368 
369     /**
370      * The method makes a clone of the current attribute instance.
371      * @param _parentId if of the parent type
372      * @return clone of current attribute instance
373      */
374     protected Attribute copy(final long _parentId)
375     {
376         final Attribute ret = new Attribute(getId(), _parentId, getName(), this.sqlTable, this.attributeType,
377                         this.defaultValue, this.dimensionUUID, this.required, this.size, this.scale);
378         ret.getSqlColNames().addAll(getSqlColNames());
379         ret.setLink(this.link);
380         ret.setClassName(getClassName());
381         ret.getProperties().putAll(getProperties());
382         return ret;
383     }
384 
385     /**
386      * {@inheritDoc}
387      */
388     @Override
389     public void addEvent(final EventType _eventtype,
390                          final EventDefinition _eventdef)
391         throws CacheReloadException
392     {
393         super.addEvent(_eventtype, _eventdef);
394         for (final Type child : getParent().getChildTypes()) {
395             final Attribute childAttr = child.getAttribute(getName());
396             if (childAttr != null) {
397                 childAttr.addEvent(_eventtype, _eventdef);
398             }
399         }
400     }
401 
402     /**
403      * This is the getter method for instance variable {@link #sqlTable}.
404      *
405      * @return value of instance variable {@link #sqlTable}
406      * @see #sqlTable
407      */
408     public SQLTable getTable()
409     {
410         return this.sqlTable;
411     }
412 
413     /**
414      * This is the setter method for instance variable {@link #link}.
415      *
416      * @param _link new instance of class {@link Long} to set for link
417      * @see #link
418      * @see #getLink
419      */
420     protected void setLink(final Long _link)
421     {
422         this.link = _link;
423     }
424 
425     /**
426      * This is the getter method for instance variable {@link #link}.
427      *
428      * @return value of instance variable {@link #link}
429      * @throws CacheReloadException on erorr
430      */
431     public Type getLink()
432         throws CacheReloadException
433     {
434         return Type.get(this.link);
435     }
436 
437     /**
438      * Getter method for the instance variable {@link #dependencies}.
439      *
440      * @return value of instance variable {@link #dependencies}
441      * @throws CacheReloadException on error
442      */
443     public Map<String, Attribute> getDependencies()
444         throws CacheReloadException
445     {
446         if (this.dependencies == null) {
447             this.dependencies = new TreeMap<String, Attribute>();
448             // in case of a rate attribute the dependencies to the currencies
449             // must be given
450             if (getProperties().containsKey("CurrencyAttribute4Rate")) {
451                 this.dependencies.put("CurrencyAttribute4Rate",
452                                 getParent().getAttribute(getProperties().get("CurrencyAttribute4Rate")));
453                 this.dependencies.put("TargetCurrencyAttribute4Rate",
454                                 getParent().getAttribute(getProperties().get("TargetCurrencyAttribute4Rate")));
455             }
456         }
457         return this.dependencies;
458     }
459 
460     /**
461      * This is the getter method for instance variable {@link #parent}.
462      *
463      * @return value of instance variable {@link #parent}
464      * @throws CacheReloadException on error
465      */
466     public Type getParent()
467         throws CacheReloadException
468     {
469         return Type.get(this.parent);
470     }
471 
472     /**
473      * @return the parent Type id
474      */
475     public Long getParentId()
476     {
477         return this.parent;
478     }
479 
480     /**
481      * This is the getter method for instance variable {@link #parentSet}.
482      *
483      * @return value of instance variable {@link #parentSet}
484      *
485      */
486     public AttributeSet getParentSet()
487     {
488         return this.parentSet;
489     }
490 
491     /**
492      * This is the setter method for instance variable {@link #parentSet}.
493      *
494      * @param _parentSet new instance of class {@link AttributeSet} to set
495      */
496     private void setParentSet(final AttributeSet _parentSet)
497     {
498         this.parentSet = _parentSet;
499     }
500 
501     /**
502      * This is the getter method for instance variable {@link #sqlColNames}.
503      *
504      * @return value of instance variable {@link #sqlColNames}
505      * @see #sqlColNames
506      */
507     public ArrayList<String> getSqlColNames()
508     {
509         return this.sqlColNames;
510     }
511 
512     /**
513      * This is the getter method for instance variable {@link #attributeType}.
514      *
515      * @return value of instance variable {@link #attributeType}
516      * @see #attributeType
517      */
518     public AttributeType getAttributeType()
519     {
520         return this.attributeType;
521     }
522 
523     /**
524      * This is the getter method for instance variable {@link #defaultValue}.
525      *
526      * @return value of instance variable {@link #defaultValue}
527      * @see #defaultValue
528      */
529     public String getDefaultValue()
530     {
531         return this.defaultValue;
532     }
533 
534     /**
535      * This is the getter method for instance variable {@link #required}.
536      *
537      * @return value of instance variable {@link #required}
538      * @see #required
539      */
540     public boolean isRequired()
541     {
542         return this.required;
543     }
544 
545     /**
546      * Getter method for instance variable {@link #size}.
547      *
548      * @return value of instance variable {@link #size}
549      */
550     public int getSize()
551     {
552         return this.size;
553     }
554 
555     /**
556      * Getter method for instance variable {@link #scale}.
557      *
558      * @return value of instance variable {@link #scale}
559      */
560     public int getScale()
561     {
562         return this.scale;
563     }
564 
565     /**
566      * Method to get the dimension related to this attribute.
567      *
568      * @return Dimension
569      */
570     public Dimension getDimension()
571 
572     {
573         Dimension ret = null;
574         try {
575             ret = Dimension.get(UUID.fromString(this.dimensionUUID));
576         } catch (final CacheReloadException e) {
577             // TODO Auto-generated catch block
578             e.printStackTrace();
579         }
580         return ret;
581     }
582 
583     /**
584      * Has this attribute an UoM.
585      *
586      * @return true id dimensionUUId!=null, else false
587      */
588     public boolean hasUoM()
589     {
590         return this.dimensionUUID != null;
591     }
592 
593     /**
594      * Prepares for given <code>_values</code> depending on this attribute the
595      * <code>_insert</code> into the database.
596      *
597      * @param _insert SQL insert statement for related {@link #sqlTable}
598      * @param _values values to insert
599      * @throws SQLException if values could not be inserted
600      */
601     public void prepareDBInsert(final SQLInsert _insert,
602                                 final Object... _values)
603         throws SQLException
604     {
605         Object[] tmp = _values;
606         try {
607             final List<Return> returns = executeEvents(EventType.UPDATE_VALUE, ParameterValues.CLASS, this,
608                             ParameterValues.OTHERS, _values);
609             for (final Return aRet : returns) {
610                 if (aRet.contains(ReturnValues.VALUES)) {
611                     tmp = (Object[]) aRet.get(ReturnValues.VALUES);
612                 }
613             }
614         } catch (final EFapsException e) {
615             throw new SQLException(e);
616         }
617         this.attributeType.getDbAttrType().prepareInsert(_insert, this, tmp);
618     }
619 
620     /**
621      * Prepares for given <code>_values</code> depending on this attribute the
622      * <code>_update</code> into the database.
623      *
624      * @param _update SQL update statement for related {@link #sqlTable}
625      * @param _values values to update
626      * @throws SQLException if values could not be inserted
627      */
628     public void prepareDBUpdate(final SQLUpdate _update,
629                                 final Object... _values)
630         throws SQLException
631     {
632         Object[] tmp = _values;
633         try {
634             final List<Return> returns = executeEvents(EventType.UPDATE_VALUE, ParameterValues.CLASS, this,
635                             ParameterValues.OTHERS, _values);
636             for (final Return aRet : returns) {
637                 if (aRet.contains(ReturnValues.VALUES)) {
638                     tmp = (Object[]) aRet.get(ReturnValues.VALUES);
639                 }
640             }
641         } catch (final EFapsException e) {
642             throw new SQLException(e);
643         }
644         this.attributeType.getDbAttrType().prepareUpdate(_update, this, tmp);
645     }
646 
647     /**
648      *
649      * @param _objectList object list from the database
650      * @return found value
651      * @throws EFapsException if values could not be read from the
652      *             <code>_objectList</code>
653      */
654     public Object readDBValue(final List<Object> _objectList)
655         throws EFapsException
656     {
657         Object ret = this.attributeType.getDbAttrType().readValue(this, _objectList);
658         final List<Return> returns = executeEvents(EventType.READ_VALUE, ParameterValues.CLASS, this,
659                         ParameterValues.OTHERS, ret);
660         for (final Return aRet : returns) {
661             if (aRet.contains(ReturnValues.VALUES)) {
662                 ret = aRet.get(ReturnValues.VALUES);
663             }
664         }
665         return ret;
666     }
667 
668     /**
669      * @return the key for the DBProperties value
670      */
671     public String getLabelKey()
672     {
673         return getKey() + ".Label";
674     }
675 
676     /**
677      * @return the key for the DBProperties value
678      */
679     public String getKey()
680     {
681         if (this.key == null) {
682             try {
683                 this.key = getParent().getName() + "/" + getName();
684             } catch (final CacheReloadException e) {
685                 Attribute.LOG.error("Problems during reading of key for Attribute: {}", this);
686             }
687         }
688         return this.key;
689     }
690 
691     /**
692      * Getter method for the instance variable {@link #className}.
693      *
694      * @return value of instance variable {@link #className}
695      */
696     public String getClassName()
697     {
698         return this.className;
699     }
700 
701     /**
702      * Setter method for instance variable {@link #className}.
703      *
704      * @param _className value for instance variable {@link #className}
705      */
706     protected void setClassName(final String _className)
707     {
708         this.className = _className;
709     }
710 
711     /**
712      * Method to initialize this Cache.
713      *
714      * @param _class clas that called this method
715      * @throws CacheReloadException on error
716      */
717     public static void initialize(final Class<?> _class)
718         throws CacheReloadException
719     {
720         if (InfinispanCache.get().exists(Attribute.NAMECACHE)) {
721             InfinispanCache.get().<String, Attribute>getCache(Attribute.NAMECACHE).clear();
722         } else {
723             InfinispanCache.get().<String, Attribute>getCache(Attribute.NAMECACHE)
724                             .addListener(new CacheLogListener(Attribute.LOG));
725         }
726 
727         if (InfinispanCache.get().exists(Attribute.IDCACHE)) {
728             InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE).clear();
729         } else {
730             InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE)
731                             .addListener(new CacheLogListener(Attribute.LOG));
732         }
733     }
734 
735     /**
736      * Method to initialize the Cache of this CacheObjectInterface.
737      *
738      * @throws CacheReloadException on error
739      */
740     public static void initialize()
741         throws CacheReloadException
742     {
743         Attribute.initialize(Attribute.class);
744     }
745 
746     /**
747      * Returns for given parameter <i>_id</i> the instance of class
748      * {@link Attribute}.
749      *
750      * @param _id id to search in the cache
751      * @return instance of class {@link Attribute}
752      * @throws CacheReloadException on error
753      * @see #getCache
754      */
755     public static Attribute get(final long _id)
756         throws CacheReloadException
757     {
758         final Cache<Long, Attribute> cache = InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE);
759         if (!cache.containsKey(_id)) {
760             Type.get(Attribute.getTypeID(_id));
761         }
762         return cache.get(_id);
763     }
764 
765     /**
766      * Returns for given parameter <i>_name</i> the instance of class
767      * {@link Attribute}.
768      *
769      * @param _name name to search in the cache
770      * @return instance of class {@link Attribute}
771      * @throws CacheReloadException on error
772      * @see #getCache
773      */
774     public static Attribute get(final String _name)
775         throws CacheReloadException
776     {
777         final Cache<String, Attribute> cache = InfinispanCache.get().<String, Attribute>getCache(Attribute.NAMECACHE);
778         if (!cache.containsKey(_name)) {
779             final String[] nameParts = _name.split("/");
780             if (nameParts != null && nameParts.length == 2) {
781                 Type.get(nameParts[0]);
782             }
783         }
784         return cache.get(_name);
785     }
786 
787     /**
788      * @param _attr Attribute to be cached
789      * @param _type Parent Type
790      */
791     private static void cacheAttribute(final Attribute _attr,
792                                        final Type _type)
793     {
794         final Cache<String, Attribute> nameCache = InfinispanCache.get().<String, Attribute>getIgnReCache(
795                         Attribute.NAMECACHE);
796         if (_type != null) {
797             nameCache.putIfAbsent(_type.getName() + "/" + _attr.getName(), _attr);
798         } else {
799             nameCache.putIfAbsent(_attr.getKey(), _attr);
800         }
801 
802         final Cache<Long, Attribute> idCache = InfinispanCache.get().<Long, Attribute>getIgnReCache(
803                         Attribute.IDCACHE);
804         idCache.putIfAbsent(_attr.getId(), _attr);
805     }
806 
807     /**
808      * The instance method returns the string representation of this attribute.
809      * The string representation of this attribute is the name of the type plus
810      * slash plus name of this attribute. (Must not contain getKey()!!)
811      *
812      * @return String representation
813      */
814     @Override
815     public String toString()
816     {
817         return new ToStringBuilder(this).appendSuper(super.toString())
818                         .append("attributetype", getAttributeType().toString())
819                         .append("required", this.required).toString();
820     }
821 
822     @Override
823     public boolean equals(final Object _obj)
824     {
825         boolean ret;
826         if (_obj instanceof Attribute) {
827             ret = ((Attribute) _obj).getId() == getId();
828         } else {
829             ret = super.equals(_obj);
830         }
831         return ret;
832     }
833 
834     @Override
835     public int hashCode()
836     {
837         return  Long.valueOf(getId()).intValue();
838     }
839 
840     /**
841      * @param _attrId   id of an attribute
842      * @return the id of a Type
843      * @throws CacheReloadException on error
844      */
845     protected static long getTypeID(final long _attrId)
846         throws CacheReloadException
847     {
848         long ret = 0;
849         ConnectionResource con = null;
850         try {
851             con = Context.getThreadContext().getConnectionResource();
852             PreparedStatement stmt = null;
853             try {
854                 stmt = con.getConnection().prepareStatement(Attribute.SQL_ATTR);
855                 stmt.setObject(1, _attrId);
856                 final ResultSet rs = stmt.executeQuery();
857                 while (rs.next()) {
858                     ret = rs.getLong(1);
859                 }
860                 rs.close();
861             } finally {
862                 if (stmt != null) {
863                     stmt.close();
864                 }
865             }
866             con.commit();
867         } catch (final SQLException e) {
868             throw new CacheReloadException("Cannot read a type for an attribute.", e);
869         } catch (final EFapsException e) {
870             throw new CacheReloadException("Cannot read a type for an attribute.", e);
871         } finally {
872             if ((con != null) && con.isOpened()) {
873                 try {
874                     con.abort();
875                 } catch (final EFapsException e) {
876                     throw new CacheReloadException("Cannot read a type for an attribute.", e);
877                 }
878             }
879         }
880         return ret;
881     }
882 
883     /**
884      * @param _type Type the attributes are wanted for
885      * @throws EFapsException on error
886      */
887     protected static void add4Type(final Type _type)
888         throws EFapsException
889     {
890         ConnectionResource con = null;
891         try {
892             con = Context.getThreadContext().getConnectionResource();
893             PreparedStatement stmt = null;
894             final List<Object[]> values = new ArrayList<Object[]>();
895             try {
896                 stmt = con.getConnection().prepareStatement(Attribute.SQL_TYPE);
897                 stmt.setObject(1, _type.getId());
898                 final ResultSet rs = stmt.executeQuery();
899                 while (rs.next()) {
900                     values.add(new Object[] {
901                                     rs.getLong(1),
902                                     rs.getString(2).trim(),
903                                     rs.getLong(3),
904                                     rs.getLong(4),
905                                     rs.getLong(5),
906                                     rs.getLong(6),
907                                     rs.getLong(7),
908                                     rs.getString(8),
909                                     rs.getString(9),
910                                     rs.getString(10),
911                                     rs.getString(11)
912                     });
913                 }
914                 rs.close();
915             } finally {
916                 if (stmt != null) {
917                     stmt.close();
918                 }
919             }
920             con.commit();
921 
922             final Map<Long, AttributeSet> id2Set = new HashMap<Long, AttributeSet>();
923             final Map<Attribute, Long> attribute2setId = new HashMap<Attribute, Long>();
924             final List<Attribute> attributes = new ArrayList<Attribute>();
925             for (final Object[] row : values) {
926                 final long id = (Long) row[0];
927                 final String name = (String) row[1];
928                 final long typeAttrId = (Long) row[2];
929                 final long tableId = (Long) row[3];
930                 final long attrTypeId = (Long) row[4];
931                 final long typeLinkId = (Long) row[5];
932                 final long parentSetId = (Long) row[6];
933                 final String sqlCol = (String) row[7];
934                 final String defaultval = (String) row[8];
935                 final String dimensionUUID = (String) row[9];
936                 final String className = (String) row[10];
937 
938                 Attribute.LOG.debug("read attribute '{}/{}' (id = {})", _type.getName(), name, id);
939 
940                 if (Type.check4Type(typeAttrId, CIAdminDataModel.AttributeSet.uuid)) {
941                     final AttributeSet set = new AttributeSet(id, _type, name, AttributeType.get(attrTypeId),
942                                     sqlCol, tableId, typeLinkId, dimensionUUID);
943                     id2Set.put(id, set);
944                 } else {
945                     final Attribute attr = new Attribute(id, _type.getId(), name, sqlCol, SQLTable.get(tableId),
946                                     AttributeType.get(attrTypeId), defaultval,
947                                     dimensionUUID);
948 
949                     final UUID uuid = attr.getAttributeType().getUUID();
950                     if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_LINK.getUuid())
951                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_LINK_WITH_RANGES.getUuid())
952                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_STATUS.getUuid())) {
953                         attr.setLink(typeLinkId);
954                         // in case of a PersonLink, CreatorLink or ModifierLink a link to Admin_User_Person
955                         // must be set
956                     } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_CREATOR_LINK.getUuid())
957                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_MODIFIER_LINK.getUuid())
958                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_PERSON_LINK.getUuid())) {
959                         attr.setLink(Type.getId4UUID(CIAdminUser.Person.uuid));
960                         // in case of a GroupLink, a link to Admin_User_Group must be set
961                     } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_GROUP_LINK.getUuid())) {
962                         attr.setLink(Type.getId4UUID(CIAdminUser.Group.uuid));
963                         // in case of a Enum and BitEnum the className must be set
964                     } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_ENUM.getUuid())
965                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_BITENUM.getUuid())
966                                     || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_JAXB.getUuid())) {
967                         if (className == null || (className != null && className.isEmpty())) {
968                             Attribute.LOG.error("An Attribute of Type Enum, BitEnum, Jaxb must have a className: {}",
969                                             attr);
970                         }
971                         attr.setClassName(className.trim());
972                     }
973                     attr.readFromDB4Properties();
974 
975                     if (Type.check4Type(typeAttrId, CIAdminDataModel.AttributeSetAttribute.uuid)) {
976                         attribute2setId.put(attr, parentSetId);
977                     } else {
978                         attributes.add(attr);
979                         Attribute.cacheAttribute(attr, _type);
980                     }
981                 }
982             }
983             // make connection between set and attributes
984             for (final Entry<Attribute, Long> entry : attribute2setId.entrySet()) {
985                 final AttributeSet parentset = id2Set.get(entry.getValue());
986                 final Attribute childAttr = entry.getKey();
987                 parentset.addAttributes(false, childAttr);
988                 childAttr.setParentSet(parentset);
989                 // needed due to cluster serialization that does not update automatically
990                 Attribute.cacheAttribute(childAttr, parentset);
991             }
992             for (final AttributeSet set : id2Set.values()) {
993                 Type.cacheType(set);
994             }
995 
996             _type.addAttributes(false, attributes.toArray(new Attribute[attributes.size()]));
997         } catch (final SQLException e) {
998             throw new CacheReloadException("Cannot read attributes.", e);
999         } finally {
1000             if ((con != null) && con.isOpened()) {
1001                 con.abort();
1002             }
1003         }
1004     }
1005 }