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.update.schema.datamodel;
22  
23  import java.net.URL;
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.commons.lang3.builder.ToStringBuilder;
31  import org.efaps.admin.datamodel.AttributeSet;
32  import org.efaps.admin.datamodel.Classification;
33  import org.efaps.admin.datamodel.Type;
34  import org.efaps.admin.event.EventType;
35  import org.efaps.ci.CIAdminCommon;
36  import org.efaps.ci.CIAdminDataModel;
37  import org.efaps.ci.CIType;
38  import org.efaps.db.Delete;
39  import org.efaps.db.Insert;
40  import org.efaps.db.Instance;
41  import org.efaps.db.InstanceQuery;
42  import org.efaps.db.MultiPrintQuery;
43  import org.efaps.db.QueryBuilder;
44  import org.efaps.db.Update;
45  import org.efaps.update.AbstractUpdate;
46  import org.efaps.update.LinkInstance;
47  import org.efaps.update.UpdateLifecycle;
48  import org.efaps.update.event.Event;
49  import org.efaps.update.util.InstallationException;
50  import org.efaps.util.EFapsException;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  
54  /**
55   * Handles the import / update of types for eFaps read from a XML configuration
56   * item file.
57   *
58   * @author The eFaps Team
59   * @version $Id$
60   */
61  public class TypeUpdate
62      extends AbstractUpdate
63  {
64  
65      /**
66       * Logging instance used to give logging information of this class.
67       */
68      private static final Logger LOG = LoggerFactory.getLogger(TypeUpdate.class);
69  
70      /**
71       * Link the data model type to allowed event types.
72       */
73      private static final Link LINK2ALLOWEDEVENT = new Link("Admin_DataModel_TypeEventIsAllowedFor", "To",
74                                                             "Admin_DataModel_Type", "From");
75  
76      /**
77       * Link the data model type to a store.
78       */
79      private static final Link LINK2STORE = new Link("Admin_DataModel_Type2Store", "From", "DB_Store", "To");
80  
81      /**
82       * Link the data model type to a type that it classifies.
83       */
84      private static final Link LINK2CLASSIFIES = new Link("Admin_DataModel_TypeClassifies", "From",
85                                                           "Admin_DataModel_Type", "To");
86  
87      /**
88       * Link the data model type to a tyep used for classification.
89       */
90      private static final Link LINK2CLASSIFYREL = new Link("Admin_DataModel_TypeClassifyRelation", "From",
91                                                            "Admin_DataModel_Type", "To");
92  
93      /**
94       * Link the data model type to a tyep used for classification.
95       */
96      private static final Link LINK2CLASSIFYCOMPANY = new Link("Admin_DataModel_TypeClassifyCompany", "From",
97                                                            "Admin_User_Company", "To");
98  
99      /**
100      * List of all links for the type.
101      */
102     private static final Set<Link> ALLLINKS = new HashSet<Link>();
103     static {
104         TypeUpdate.ALLLINKS.add(TypeUpdate.LINK2ALLOWEDEVENT);
105         TypeUpdate.ALLLINKS.add(TypeUpdate.LINK2STORE);
106         TypeUpdate.ALLLINKS.add(TypeUpdate.LINK2CLASSIFIES);
107         TypeUpdate.ALLLINKS.add(TypeUpdate.LINK2CLASSIFYREL);
108         TypeUpdate.ALLLINKS.add(TypeUpdate.LINK2CLASSIFYCOMPANY);
109     }
110 
111     /**
112      * Default constructor to initialize this type instance for given
113      * <code>_url</code>.
114      *
115      * @param _url URL of the file
116      */
117     public TypeUpdate(final URL _url)
118     {
119         super(_url, "Admin_DataModel_Type", TypeUpdate.ALLLINKS);
120     }
121 
122     /**
123      * Creates new instance of class {@link TypeDefinition}.
124      *
125      * @return new definition instance
126      * @see TypeDefinition
127      */
128     @Override
129     protected AbstractDefinition newDefinition()
130     {
131         return new TypeDefinition();
132     }
133 
134     /**
135      * The class defines an attribute of a type.
136      */
137     public class AttributeDefinition
138         extends AbstractDefinition
139     {
140 
141         /** Name of the attribute. */
142         private String name = null;
143 
144         /** Name of the Attribute Type of the attribute. */
145         private String type = null;
146 
147         /** Name of the SQL Table of the attribute. */
148         private String sqlTable = null;
149 
150         /** SQL Column of the attribute. */
151         private String sqlColumn = null;
152 
153         /** Name of the Linked Type (used for links to another type). */
154         private String typeLink = null;
155 
156         /** Events for this Attribute. */
157         private final List<Event> events = new ArrayList<Event>();
158 
159         /** default value for this Attribute. */
160         private String defaultValue = null;
161 
162         /**
163          * UUID of the Dimension for this Attribute. (Used in conjunction with
164          * e.g. IntegerWithUoM).
165          */
166         private String dimensionUUID;
167         /**
168          * ClassName for this Attribute. (Used in conjunction with
169          * e.g. EnumType and BitEnumType).
170          */
171         private String className;
172 
173         /**
174          * @see org.efaps.update.AbstractUpdate.AbstractDefinition#readXML(java.util.List,
175          *      java.util.Map, java.lang.String)
176          * @param _tags list of the tags
177          * @param _attributes attributes
178          * @param _text text
179          * @throws EFapsException on error
180          */
181         @Override
182         protected void readXML(final List<String> _tags,
183                                final Map<String, String> _attributes,
184                                final String _text)
185             throws EFapsException
186         {
187 
188             final String value = _tags.get(0);
189             if ("className".equals(value)) {
190                 this.className = _text;
191             } else if ("defaultvalue".equals(value)) {
192                 this.defaultValue = _text;
193             } else if ("dimensionUUID".equals(value)) {
194                 this.dimensionUUID = _text;
195             } else if ("name".equals(value)) {
196                 this.name = _text;
197             } else if ("sqlcolumn".equals(value)) {
198                 this.sqlColumn = _text;
199             } else if ("sqltable".equals(value)) {
200                 this.sqlTable = _text;
201             } else if ("trigger".equals(value)) {
202                 if (_tags.size() == 1) {
203                     this.events.add(new Event(_attributes.get("name"), EventType.valueOf(_attributes.get("event")),
204                                     _attributes.get("program"), _attributes.get("method"), _attributes.get("index")));
205                 } else if ((_tags.size() == 2) && "property".equals(_tags.get(1))) {
206                     this.events.get(this.events.size() - 1).addProperty(_attributes.get("name"), _text);
207                 } else {
208                     super.readXML(_tags, _attributes, _text);
209                 }
210             } else if ("type".equals(value)) {
211                 this.type = _text;
212             } else if ("typelink".equals(value)) {
213                 this.typeLink = _text;
214             } else if ("validate".equals(value)) {
215                 if (_tags.size() == 1) {
216                     this.events.add(new Event(_attributes.get("name"), EventType.VALIDATE, _attributes.get("program"),
217                                     _attributes.get("method"), _attributes.get("index")));
218                 } else if ((_tags.size() == 2) && "property".equals(_tags.get(1))) {
219                     this.events.get(this.events.size() - 1).addProperty(_attributes.get("name"), _text);
220                 } else {
221                     super.readXML(_tags, _attributes, _text);
222                 }
223             } else {
224                 super.readXML(_tags, _attributes, _text);
225             }
226         }
227 
228         /**
229          * @see org.efaps.update.AbstractUpdate.AbstractDefinition#addEvent(org.efaps.update.event.Event)
230          * @param _event Event to add
231          */
232         @Override
233         public void addEvent(final Event _event)
234         {
235             this.events.add(_event);
236         }
237 
238         /**
239          * Getter method for instance variable {@link #name}.
240          *
241          * @return value of instance variable {@link #name}
242          */
243         public String getName()
244         {
245             return this.name;
246         }
247 
248         /**
249          * Setter method for instance variable {@link #name}.
250          *
251          * @param _name value for instance variable {@link #name}
252          */
253         @Override
254         protected void setName(final String _name)
255         {
256             this.name = _name;
257         }
258 
259         /**
260          * Getter method for instance variable {@link #type}.
261          *
262          * @return value of instance variable {@link #type}
263          */
264         public String getType()
265         {
266             return this.type;
267         }
268 
269         /**
270          * Setter method for instance variable {@link #type}.
271          *
272          * @param _type value for instance variable {@link #type}
273          */
274         public void setType(final String _type)
275         {
276             this.type = _type;
277         }
278 
279         /**
280          * Getter method for instance variable {@link #sqlTable}.
281          *
282          * @return value of instance variable {@link #sqlTable}
283          */
284         public String getSqlTable()
285         {
286             return this.sqlTable;
287         }
288 
289         /**
290          * Setter method for instance variable {@link #sqlTable}.
291          *
292          * @param _sqlTable value for instance variable {@link #sqlTable}
293          */
294         public void setSqlTable(final String _sqlTable)
295         {
296             this.sqlTable = _sqlTable;
297         }
298 
299         /**
300          * Getter method for instance variable {@link #sqlColumn}.
301          *
302          * @return value of instance variable {@link #sqlColumn}
303          */
304         public String getSqlColumn()
305         {
306             return this.sqlColumn;
307         }
308 
309         /**
310          * Setter method for instance variable {@link #sqlColumn}.
311          *
312          * @param _sqlColumn value for instance variable {@link #sqlColumn}
313          */
314         public void setSqlColumn(final String _sqlColumn)
315         {
316             this.sqlColumn = _sqlColumn;
317         }
318 
319         /**
320          * For given type defined with the instance parameter, this attribute is
321          * searched by name. If the attribute exists, the attribute is updated.
322          * Otherwise the attribute is created for this type.
323          *
324          * @param _instance type instance to update with this attribute
325          * @param _typeName name of the type to update
326          * @param _setID id to set
327          * @param _attrInstanceId
328          * @throws EFapsException on error
329          * @see #getAttrTypeId
330          * @see #getSqlTableId
331          * @see #getTypeLinkId TODO: throw Exception is not allowed
332          */
333         protected void updateInDB(final Instance _instance,
334                                   final String _typeName,
335                                   final long _setID)
336             throws EFapsException
337         {
338             final long attrTypeId = getAttrTypeId(_typeName);
339             final long sqlTableId = getSqlTableId(_typeName);
340             final long typeLinkId = getTypeLinkId(_typeName);
341 
342             final CIType typeTmp;
343             if (_setID > 0) {
344                 typeTmp = CIAdminDataModel.AttributeSetAttribute;
345             } else {
346                 typeTmp = CIAdminDataModel.Attribute;
347             }
348             final QueryBuilder queryBldr = new QueryBuilder(typeTmp);
349             queryBldr.addWhereAttrEqValue(CIAdminDataModel.Attribute.Name, this.name);
350             queryBldr.addWhereAttrEqValue(CIAdminDataModel.Attribute.ParentType, _instance.getId());
351             final InstanceQuery query = queryBldr.getQuery();
352             query.executeWithoutAccessCheck();
353             Update update;
354             if (query.next()) {
355                 update = new Update(query.getCurrentValue());
356             } else {
357                 update = new Insert(typeTmp);
358                 update.add(CIAdminDataModel.Attribute.ParentType, _instance.getId());
359                 update.add(CIAdminDataModel.Attribute.Name, this.name);
360             }
361             update.add(CIAdminDataModel.Attribute.AttributeType, attrTypeId);
362             update.add(CIAdminDataModel.Attribute.Table, sqlTableId);
363             update.add(CIAdminDataModel.Attribute.SQLColumn, this.sqlColumn);
364             if (typeLinkId == 0) {
365                 update.add(CIAdminDataModel.Attribute.TypeLink, (String) null);
366             } else {
367                 update.add(CIAdminDataModel.Attribute.TypeLink, typeLinkId);
368             }
369             if (this.defaultValue != null) {
370                 update.add(CIAdminDataModel.Attribute.DefaultValue, this.defaultValue);
371             }
372             if (this.dimensionUUID != null) {
373                 update.add(CIAdminDataModel.Attribute.DimensionUUID, this.dimensionUUID);
374             }
375             if (this.className != null) {
376                 update.add(CIAdminDataModel.Attribute.ClassName, this.className);
377             }
378             if (_setID != 0) {
379                 update.add(CIAdminDataModel.AttributeSetAttribute.ParentAttributeSet, _setID);
380             }
381 
382             update.executeWithoutAccessCheck();
383 
384             setPropertiesInDb(update.getInstance(), getProperties());
385 
386             for (final Event event : this.events) {
387                 final Instance newInstance = event.updateInDB(update.getInstance(), this.name);
388                 setPropertiesInDb(newInstance, event.getProperties());
389             }
390         }
391 
392         /**
393          * Makes a search query to return the id of the attribute type defined
394          * in {@link #type}.
395          *
396          * @param _typeName name of the type
397          * @return id of the attribute type
398          * @throws EFapsException on error
399          * @see #type
400          */
401         protected long getAttrTypeId(final String _typeName)
402             throws EFapsException
403         {
404             final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.AttributeType);
405             queryBldr.addWhereAttrEqValue(CIAdminDataModel.AttributeType.Name, this.type);
406             final InstanceQuery query = queryBldr.getQuery();
407             query.executeWithoutAccessCheck();
408             if (!query.next()) {
409                 TypeUpdate.LOG.error("type[" + _typeName + "]." + "attribute[" + this.name + "]: " + "attribute type '"
410                                 + this.type + "' not found");
411             }
412             return query.getCurrentValue().getId();
413         }
414 
415         /**
416          * Makes a search query to return the id of the SQL table defined in
417          * {@link #sqlTable}.
418          *
419          * @param _typeName name of the type
420          * @throws EFapsException on error
421          * @return id of the SQL table
422          * @see #sqlTable
423          */
424         protected long getSqlTableId(final String _typeName)
425             throws EFapsException
426         {
427             final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.SQLTable);
428             queryBldr.addWhereAttrEqValue(CIAdminDataModel.SQLTable.Name, this.sqlTable);
429             final InstanceQuery query = queryBldr.getQuery();
430             query.executeWithoutAccessCheck();
431             if (!query.next()) {
432                 TypeUpdate.LOG.error("type[" + _typeName + "]." + "attribute[" + this.name + "]: " + "SQL table '"
433                                 + this.sqlTable + "' not found");
434             }
435             return query.getCurrentValue().getId();
436         }
437 
438         /**
439          * Makes a search query to return the id of the SQL table defined in
440          * {@link #typeLink}.
441          *
442          * @param _typeName name of the type
443          * @throws EFapsException on error
444          * @return id of the linked type (or 0 if no type link is defined)
445          * @see #typeLink
446          */
447         private long getTypeLinkId(final String _typeName)
448             throws EFapsException
449         {
450             long typeLinkId = 0;
451             if ((this.typeLink != null) && (this.typeLink.length() > 0)) {
452                 final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.Type);
453                 queryBldr.addWhereAttrEqValue(CIAdminDataModel.Type.Name, this.typeLink);
454                 final InstanceQuery query = queryBldr.getQuery();
455                 query.executeWithoutAccessCheck();
456                 if (query.next()) {
457                     typeLinkId = query.getCurrentValue().getId();
458                 } else {
459                     TypeUpdate.LOG.error("type[" + _typeName + "]." + "attribute[" + this.name + "]: " + " Type '"
460                                     + this.typeLink + "' as link not found");
461                 }
462             }
463             return typeLinkId;
464         }
465 
466         /**
467          * Returns a string representation with values of all instance variables
468          * of an attribute.
469          *
470          * @return string representation of this definition of an attribute
471          */
472         @Override
473         public String toString()
474         {
475             return new ToStringBuilder(this).append("name", this.name).append("type", this.type).append("sqlTable",
476                             this.sqlTable).append("sqlColumn", this.sqlColumn).append("typeLink", this.typeLink)
477                             .toString();
478         }
479     }
480 
481     /**
482      * Class for the definition of a Attribute Set.
483      */
484     public class AttributeSetDefinition
485         extends TypeUpdate.AttributeDefinition
486     {
487 
488         /**
489          * Current read attribute definition instance.
490          *
491          * @see #readXML(List, Map, String)
492          */
493         private AttributeDefinition curAttr = null;
494 
495         /**
496          * List of the related attributes.
497          */
498         private final List<TypeUpdate.AttributeDefinition> attributes = new ArrayList<TypeUpdate.AttributeDefinition>();
499 
500         /**
501          * Name of the parent Type.
502          */
503         private String parentType;
504 
505         /**
506          * UUID of this attributeSet..
507          */
508         private String uuid;
509 
510         /**
511          * @param _tags current path as list of single tags
512          * @param _attributes attributes for current path
513          * @param _text content for current path
514          * @throws EFapsException on error
515          */
516         @Override
517         public void readXML(final List<String> _tags,
518                             final Map<String, String> _attributes,
519                             final String _text)
520             throws EFapsException
521         {
522             final String value = _tags.get(0);
523             if ("name".equals(value)) {
524                 setName(_text);
525             } else if ("type".equals(value)) {
526                 setType(_text);
527             } else if ("uuid".equals(value)) {
528                 this.uuid = _text;
529             } else if ("parent".equals(value)) {
530                 this.parentType = _text;
531             } else if ("sqltable".equals(value)) {
532                 setSqlTable(_text);
533             } else if ("sqlcolumn".equals(value)) {
534                 setSqlColumn(_text);
535             } else if ("attribute".equals(value)) {
536                 if (_tags.size() == 1) {
537                     this.curAttr = new AttributeDefinition();
538                     this.attributes.add(this.curAttr);
539                 } else {
540                     this.curAttr.readXML(_tags.subList(1, _tags.size()), _attributes, _text);
541                 }
542             } else {
543                 super.readXML(_tags, _attributes, _text);
544             }
545         }
546 
547         /**
548          * Method to get the id of the parent type.
549          *
550          * @param _typeName name of the type
551          * @return id of the parent type
552          * @throws EFapsException on error
553          */
554         protected long getParentTypeId(final String _typeName)
555             throws EFapsException
556         {
557             final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.Type);
558             queryBldr.addWhereAttrEqValue(CIAdminDataModel.Type.Name, _typeName);
559             final InstanceQuery query = queryBldr.getQuery();
560             query.executeWithoutAccessCheck();
561             if (!query.next()) {
562                 TypeUpdate.LOG.error("type[{}].attribute[{}]: Parent Type '{}' not found",
563                                 new Object[] {_typeName, getName(), this.parentType});
564             }
565             return query.getCurrentValue().getId();
566         }
567 
568         /**
569          * Set the values in the eFaps Database.
570          *
571          * @param _instance instance to be updated
572          * @param _typeName name of the type
573          * @throws EFapsException on error
574          */
575         public void updateInDB(final Instance _instance,
576                                final String _typeName)
577             throws EFapsException
578         {
579             final String name = AttributeSet.evaluateName(_typeName, getName());
580 
581             long parentTypeId = 0;
582             if (this.parentType != null) {
583                 parentTypeId = getParentTypeId(this.parentType);
584             }
585 
586             final long attrTypeId = getAttrTypeId(_typeName);
587             final long sqlTableId = getSqlTableId(_typeName);
588 
589             // create/update the attributSet
590             final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.AttributeSet);
591             queryBldr.addWhereAttrEqValue(CIAdminDataModel.AttributeSet.Name, getName());
592             queryBldr.addWhereAttrEqValue(CIAdminDataModel.AttributeSet.ParentType,  _instance.getId());
593             final InstanceQuery query = queryBldr.getQuery();
594             query.executeWithoutAccessCheck();
595             Update update = null;
596             if (query.next()) {
597                 update = new Update(query.getCurrentValue());
598             } else {
599                 update = new Insert(CIAdminDataModel.AttributeSet);
600                 update.add(CIAdminDataModel.AttributeSet.ParentType, "" + _instance.getId());
601                 update.add(CIAdminDataModel.AttributeSet.Name, getName());
602             }
603             update.add(CIAdminDataModel.AttributeSet.AttributeType, "" + attrTypeId);
604             update.add(CIAdminDataModel.AttributeSet.Table, "" + sqlTableId);
605             update.add(CIAdminDataModel.AttributeSet.SQLColumn, getSqlColumn());
606             update.add(CIAdminDataModel.AttributeSet.DimensionUUID, this.uuid);
607             if (parentTypeId > 0) {
608                 update.add(CIAdminDataModel.AttributeSet.TypeLink, "" + parentTypeId);
609             }
610 
611             update.executeWithoutAccessCheck();
612             final long setId = update.getInstance().getId();
613             update.close();
614 
615             // add the attributes to the new Type
616             for (final AttributeDefinition attr : this.attributes) {
617                 attr.updateInDB(_instance, name, setId);
618             }
619         }
620     }
621 
622     /**
623      * Class for the definition of the type.
624      */
625     public class TypeDefinition
626         extends AbstractDefinition
627     {
628 
629         /**
630          * Stores the name of the parent type. The parent type could not be
631          * evaluated because it could be that the type does not exists (and so
632          * the type id is evaluated before the insert / update from method
633          * {@link #updateInDB}).
634          *
635          * @see #setParent
636          * @see #updateInDB
637          */
638         private String parentType = null;
639 
640         /**
641          * Stores the name of the parent classification type.
642          */
643         private String parentClassType = null;
644 
645         /**
646          * All attributes of the type are stored in this list.
647          *
648          * @see #updateInDB
649          * @see #addAttribute
650          */
651         private final List<TypeUpdate.AttributeDefinition> attributes = new ArrayList<TypeUpdate.AttributeDefinition>();
652 
653         /**
654          * All attribute sets of the type are stored in this list.
655          *
656          * @see #updateInDB
657          * @see #addAttribute
658          */
659         private final List<TypeUpdate.AttributeSetDefinition> attributeSets
660             = new ArrayList<TypeUpdate.AttributeSetDefinition>();
661 
662         /**
663          * Current read attribute definition instance.
664          *
665          * @see #readXML(List, Map, String)
666          */
667         private AttributeDefinition curAttr = null;
668 
669         /**
670          * Current read attribute set definition instance.
671          *
672          * @see #readXML(List, Map, String)
673          */
674         private AttributeSetDefinition curAttrSet = null;
675 
676         /**
677          * @see org.efaps.update.AbstractUpdate.AbstractDefinition#readXML(java.util.List,
678          *      java.util.Map, java.lang.String)
679          * @param _tags tags
680          * @param _attributes attributes
681          * @param _text text
682          * @throws EFapsException on error
683          */
684         @Override
685         protected void readXML(final List<String> _tags,
686                                final Map<String, String> _attributes,
687                                final String _text)
688             throws EFapsException
689         {
690             final String value = _tags.get(0);
691             if ("purpose".equals(value)) {
692                 if (_tags.size() == 1) {
693                     Integer valueTmp = 0;
694                     if ("true".equalsIgnoreCase(_attributes.get("abstract"))) {
695                         valueTmp = valueTmp + Type.Purpose.ABSTRACT.getInt();
696                     }
697                     if ("true".equalsIgnoreCase(_attributes.get("classification"))) {
698                         valueTmp = valueTmp + Type.Purpose.CLASSIFICATION.getInt();
699                     }
700                     if ("true".equalsIgnoreCase(_attributes.get("GeneralInstance"))) {
701                         valueTmp = valueTmp + Type.Purpose.GENERALINSTANCE.getInt();
702                     }
703                     if ("false".equalsIgnoreCase(_attributes.get("GeneralInstance"))) {
704                         valueTmp = valueTmp + Type.Purpose.NOGENERALINSTANCE.getInt();
705                     }
706                     addValue("Purpose", valueTmp.toString());
707                 } else if (_tags.size() == 2) {
708                     if ("LinkColumn".equals(_tags.get(1))) {
709                         getProperties().put(Classification.Keys.LINKATTR.getValue(), _text);
710                     } else if ("parent".equals(_tags.get(1))) {
711                         this.parentClassType = _text;
712                     }
713                 }
714             } else if ("attribute".equals(value)) {
715                 if (_tags.size() == 1) {
716                     this.curAttr = new AttributeDefinition();
717                     this.attributes.add(this.curAttr);
718                 } else {
719                     this.curAttr.readXML(_tags.subList(1, _tags.size()), _attributes, _text);
720                 }
721             } else if ("attributeset".equals(value)) {
722                 if (_tags.size() == 1) {
723                     this.curAttrSet = new AttributeSetDefinition();
724                     this.attributeSets.add(this.curAttrSet);
725                 } else {
726                     this.curAttrSet.readXML(_tags.subList(1, _tags.size()), _attributes, _text);
727                 }
728             } else if ("event-for".equals(value)) {
729                 // Adds the name of a allowed event type
730                 addLink(TypeUpdate.LINK2ALLOWEDEVENT, new LinkInstance(_attributes.get("type")));
731             } else if ("classifies".equals(value)) {
732                 if (_tags.size() == 1) {
733                     addLink(TypeUpdate.LINK2CLASSIFIES,
734                                     new LinkInstance(_attributes.get(Classification.Keys.TYPE.getValue())));
735                     addLink(TypeUpdate.LINK2CLASSIFYREL,
736                                     new LinkInstance(_attributes.get(Classification.Keys.RELTYPE.getValue())));
737                     getProperties().put(Classification.Keys.RELTYPEATTR.getValue(),
738                                     _attributes.get(Classification.Keys.RELTYPEATTR.getValue()));
739                     getProperties().put(Classification.Keys.RELLINKATTR.getValue(),
740                                     _attributes.get(Classification.Keys.RELLINKATTR.getValue()));
741                     getProperties().put(Classification.Keys.MULTI.getValue(),
742                                     _attributes.get(Classification.Keys.MULTI.getValue()));
743                 } else if ((_tags.size() == 2) && "company".equals(_tags.get(1))) {
744                     addLink(TypeUpdate.LINK2CLASSIFYCOMPANY, new LinkInstance(_text));
745                 } else {
746                     super.readXML(_tags, _attributes, _text);
747                 }
748             } else if ("parent".equals(value)) {
749                 this.parentType = _text;
750             } else if ("store".equals(value)) {
751                 addLink(TypeUpdate.LINK2STORE, new LinkInstance(_attributes.get("name")));
752             } else if ("trigger".equals(value)) {
753                 if (_tags.size() == 1) {
754                     addEvent(new Event(_attributes.get("name"), EventType.valueOf(_attributes.get("event")),
755                                     _attributes.get("program"), _attributes.get("method"), _attributes.get("index")));
756                 } else if ((_tags.size() == 2) && "property".equals(_tags.get(1))) {
757                     getEvents().get(getEvents().size() - 1).addProperty(_attributes.get("name"), _text);
758                 } else {
759                     super.readXML(_tags, _attributes, _text);
760                 }
761             } else {
762                 super.readXML(_tags, _attributes, _text);
763             }
764         }
765 
766         /**
767          * If a parent type in {@link #parentType} is defined, the type id is
768          * evaluated and added to attributes to update (if no parent type is
769          * defined, the parent type id is set to <code>null</code>). After the
770          * type is updated (or inserted if needed), all attributes must be
771          * updated.
772          *
773          * @param _step lifecycle step
774          * @param _allLinkTypes set of all links
775          * @throws InstallationException on error
776          *
777          * @see #parentType
778          * @see #attributes
779          */
780         @Override
781         public void updateInDB(final UpdateLifecycle _step,
782                                final Set<Link> _allLinkTypes)
783             throws InstallationException
784         {
785             try {
786                 if (_step == UpdateLifecycle.EFAPS_UPDATE) {
787                     // set the id of the parent type (if defined)
788                     if ((this.parentType != null) && (this.parentType.length() > 0)) {
789                         final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.Type);
790                         queryBldr.addWhereAttrEqValue(CIAdminDataModel.Type.Name, this.parentType);
791                         final InstanceQuery query = queryBldr.getQuery();
792                         query.executeWithoutAccessCheck();
793                         if (query.next()) {
794                             final Instance instance = query.getCurrentValue();
795                             addValue(CIAdminDataModel.Type.ParentType.name, "" + instance.getId());
796                         } else {
797                             addValue(CIAdminDataModel.Type.ParentType.name, null);
798                         }
799                     } else {
800                         addValue(CIAdminDataModel.Type.ParentType.name, null);
801                     }
802                     if ((this.parentClassType != null) && (this.parentClassType.length() > 0)) {
803                         final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.Type);
804                         queryBldr.addWhereAttrEqValue(CIAdminDataModel.Type.Name, this.parentClassType);
805                         final InstanceQuery query = queryBldr.getQuery();
806                         query.executeWithoutAccessCheck();
807                         if (query.next()) {
808                             final Instance instance = query.getCurrentValue();
809                             addValue(CIAdminDataModel.Type.ParentClassType.name, "" + instance.getId());
810                         } else {
811                             addValue(CIAdminDataModel.Type.ParentClassType.name, null);
812                         }
813                     } else {
814                         addValue(CIAdminDataModel.Type.ParentClassType.name, null);
815                     }
816                 }
817 
818                 super.updateInDB(_step, _allLinkTypes);
819 
820                 if (_step == UpdateLifecycle.EFAPS_UPDATE) {
821                     for (final AttributeDefinition attr : this.attributes) {
822                         attr.updateInDB(getInstance(), getValue("Name"), 0);
823                     }
824 
825                     for (final AttributeSetDefinition attrSet : this.attributeSets) {
826                         attrSet.updateInDB(getInstance(), getValue("Name"));
827                     }
828 
829                     removeObsoleteAttributes();
830                 }
831             } catch (final EFapsException e) {
832                 throw new InstallationException(" Type can not be updated", e);
833             }
834         }
835 
836         /**
837          * @throws EFapsException on error
838          */
839         private void removeObsoleteAttributes()
840             throws EFapsException
841         {
842             final Set<String> attrNames = new HashSet<String>();
843             for (final AttributeDefinition attr : this.attributes) {
844                 attrNames.add(attr.name);
845             }
846             for (final AttributeSetDefinition attr : this.attributeSets) {
847                 attrNames.add(attr.getName());
848                 for (final AttributeDefinition subAttr : attr.attributes) {
849                     attrNames.add(subAttr.name);
850                 }
851             }
852             final QueryBuilder queryBldr = new QueryBuilder(CIAdminDataModel.Attribute);
853             queryBldr.addWhereAttrEqValue(CIAdminDataModel.Attribute.ParentType, getInstance().getId());
854             final MultiPrintQuery multi = queryBldr.getPrint();
855             multi.addAttribute(CIAdminDataModel.Attribute.Name);
856             multi.executeWithoutAccessCheck();
857             while (multi.next()) {
858                 if (!attrNames.contains(multi.getAttribute(CIAdminDataModel.Attribute.Name))) {
859                     // check if the attribute is used as type (attributeset)
860                     final QueryBuilder queryBldr2 = new QueryBuilder(CIAdminCommon.GeneralInstance);
861                     queryBldr2.addWhereAttrEqValue(CIAdminCommon.GeneralInstance.InstanceTypeID,
862                                     multi.getCurrentInstance().getId());
863                     final InstanceQuery query = queryBldr2.getQuery();
864                     query.execute();
865                     if (!query.next()) {
866                         // Delete the related Properties first
867                         setPropertiesInDb(multi.getCurrentInstance(), null);
868                         final Delete delete = new Delete(multi.getCurrentInstance());
869                         delete.executeWithoutAccessCheck();
870                     }
871                 }
872             }
873         }
874     }
875 }