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.ui;
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.event.EventType;
32  import org.efaps.ci.CIAdminCommon;
33  import org.efaps.ci.CIAdminEvent;
34  import org.efaps.ci.CIAdminUserInterface;
35  import org.efaps.db.Delete;
36  import org.efaps.db.Insert;
37  import org.efaps.db.Instance;
38  import org.efaps.db.InstanceQuery;
39  import org.efaps.db.QueryBuilder;
40  import org.efaps.update.AbstractUpdate;
41  import org.efaps.update.LinkInstance;
42  import org.efaps.update.UpdateLifecycle;
43  import org.efaps.update.event.Event;
44  import org.efaps.update.util.InstallationException;
45  import org.efaps.util.EFapsException;
46  
47  /**
48   * This class imports/updates a Form or a Table using the
49   * <code>org.apache.commons.digester.Digester</code> to create objects and to
50   * execute methods.
51   *
52   * @author The eFaps Team
53   * @version $Id$
54   *
55   */
56  public abstract class AbstractCollectionUpdate
57      extends AbstractUpdate
58  {
59  
60      /** Link from field to icon. */
61      private static final Link LINKFIELD2ICON = new Link("Admin_UI_LinkIcon", "From", "Admin_UI_Image", "To");
62  
63      /** Link from field to table as target. */
64      private static final Link LINK2TARGETTABLE = new Link("Admin_UI_LinkTargetTable", "From", "Admin_UI_Table", "To");
65  
66      /** Link from field to command as picker. */
67      private static final Link LINK2PICKER = new Link("Admin_UI_LinkField2Command", "FromLink",
68                      "Admin_UI_Command", "ToLink");
69  
70      /**
71       * @param _url URL of the file
72       * @param _typeName name of the type
73       */
74      protected AbstractCollectionUpdate(final URL _url,
75                                         final String _typeName)
76      {
77          super(_url, _typeName);
78      }
79  
80      /**
81       * @param _url url of the file
82       * @param _typeName name of the type
83       * @param _allLinkTypes set of all links
84       */
85      protected AbstractCollectionUpdate(final URL _url,
86                                         final String _typeName,
87                                         final Set<Link> _allLinkTypes)
88      {
89          super(_url, _typeName, _allLinkTypes);
90      }
91  
92      /**
93       * Creates new instance of class {@link Definition}.
94       *
95       * @return new definition instance
96       * @see Definition
97       */
98      @Override
99      protected AbstractDefinition newDefinition()
100     {
101         return new Definition();
102     }
103 
104     /**
105      * Class for defining a field.
106      */
107     private final class FieldDefinition
108         extends AbstractDefinition
109     {
110 
111         /** Name of the field. */
112         private final String name;
113 
114         /** Icon of the field. */
115         private String icon = null;
116 
117         /** set the character of the field. */
118         private final String character;
119 
120         /**
121          *
122          * @param _name Name of the field
123          * @param _character charachter of the field
124          */
125         private FieldDefinition(final String _name,
126                                 final String _character)
127         {
128             this.name = _name;
129             this.character = _character;
130         }
131 
132         /**
133          * {@inheritDoc}
134          * @throws EFapsException
135          */
136         @Override
137         protected void readXML(final List<String> _tags,
138                                final Map<String, String> _attributes,
139                                final String _text)
140             throws EFapsException
141         {
142             final String value = _tags.get(0);
143             if ("evaluate".equals(value)) {
144                 if (_tags.size() == 1) {
145                     addEvent(new Event(_attributes.get("name"), EventType.UI_TABLE_EVALUATE, _attributes
146                                     .get("program"), _attributes.get("method"), _attributes.get("index")));
147                 } else if ((_tags.size() == 2) && "property".equals(_tags.get(1))) {
148                     getEvents().get(getEvents().size() - 1).addProperty(_attributes.get("name"), _text);
149                 } else {
150                     super.readXML(_tags, _attributes, _text);
151                 }
152             } else if ("icon".equals(value)) {
153                 this.icon = _text;
154             } else if ("table".equals(value)) {
155                 // assigns a table as target for this field definition
156                 addLink(AbstractCollectionUpdate.LINK2TARGETTABLE, new LinkInstance(_text));
157             } else if ("picker".equals(value)) {
158                 // assigns a picker as target for this field definition
159                 addLink(AbstractCollectionUpdate.LINK2PICKER, new LinkInstance(_attributes.get("name")));
160             } else if ("trigger".equals(value)) {
161                 if (_tags.size() == 1) {
162                     getEvents().add(new Event(_attributes.get("name"), EventType.valueOf(_attributes.get("event")),
163                                     _attributes.get("program"), _attributes.get("method"), _attributes.get("index")));
164                 } else if ((_tags.size() == 2) && "property".equals(_tags.get(1))) {
165                     getEvents().get(getEvents().size() - 1).addProperty(_attributes.get("name"), _text);
166                 } else {
167                     super.readXML(_tags, _attributes, _text);
168                 }
169             } else {
170                 super.readXML(_tags, _attributes, _text);
171             }
172         }
173 
174         /**
175          * Returns a string representation with values of all instance variables
176          * of a field.
177          *
178          * @return string representation of this definition of a column
179          */
180         @Override
181         public String toString()
182         {
183             return new ToStringBuilder(this).append("name", this.name).append("properties", getProperties()).toString();
184         }
185     }
186 
187     /**
188      * Class used to define a collection.
189      *
190      */
191     protected class Definition
192         extends AbstractDefinition
193     {
194         /** All fields for the collection are stored in this variable. */
195         private final List<AbstractCollectionUpdate.FieldDefinition> fields
196                                                             = new ArrayList<AbstractCollectionUpdate.FieldDefinition>();
197 
198         /**
199          * Current read field definition.
200          */
201         private FieldDefinition curField = null;
202 
203         /**
204          * @see org.efaps.update.AbstractUpdate.AbstractDefinition#readXML(java.util.List,
205          *      java.util.Map, java.lang.String)
206          * @param _tags tags
207          * @param _attributes attributes
208          * @param _text text
209          * @throws EFapsException on error
210          */
211         @Override
212         protected void readXML(final List<String> _tags,
213                                final Map<String, String> _attributes,
214                                final String _text)
215             throws EFapsException
216         {
217             final String value = _tags.get(0);
218             if ("field".equals(value)) {
219                 if (_tags.size() == 1) {
220                     this.curField = new FieldDefinition(_attributes.get("name"), _attributes.get("character"));
221                     this.fields.add(this.curField);
222                 } else {
223                     this.curField.readXML(_tags.subList(1, _tags.size()), _attributes, _text);
224                 }
225             } else {
226                 super.readXML(_tags, _attributes, _text);
227             }
228         }
229 
230         /**
231          * Updates / creates the instance in the database. Only the fields are
232          * also updated for collection defined through this definition.
233          *
234          * @param _step current update step
235          * @param _allLinkTypes set of all type of links
236          * @throws InstallationException on error
237          * @see #setFieldsInDB
238          */
239         @Override
240         public void updateInDB(final UpdateLifecycle _step,
241                                final Set<Link> _allLinkTypes)
242             throws InstallationException
243         {
244             super.updateInDB(_step, _allLinkTypes);
245             if (_step == UpdateLifecycle.EFAPS_UPDATE) {
246                 try {
247                     setFieldsInDB();
248                 } catch (final EFapsException e) {
249                     throw new InstallationException("error in setFieldsInDB", e);
250                 }
251             }
252         }
253 
254         /**
255          * The fields for this collection are created and / or updated in the
256          * database.
257          * TODO the deletion of existing fields is nested! Thats not
258          * the best idea.
259          *
260          * @throws EFapsException on error
261          */
262         protected void setFieldsInDB()
263             throws EFapsException
264         {
265             // cleanup fields (remove all fields from table)
266             final QueryBuilder queryBldr = new QueryBuilder(CIAdminUserInterface.Field);
267             queryBldr.addWhereAttrEqValue(CIAdminUserInterface.Field.Collection, getInstance().getId());
268             final InstanceQuery query = queryBldr.getQuery();
269             query.executeWithoutAccessCheck();
270             while (query.next()) {
271                 final Instance instField = query.getCurrentValue();
272                 setPropertiesInDb(instField, null);
273                 removeLinksInDB(instField, AbstractCollectionUpdate.LINKFIELD2ICON);
274                 removeLinksInDB(instField, AbstractCollectionUpdate.LINK2TARGETTABLE);
275                 removeLinksInDB(instField, AbstractCollectionUpdate.LINK2PICKER);
276                 // remove events
277                 final QueryBuilder eventQueryBldr = new QueryBuilder(CIAdminEvent.Definition);
278                 eventQueryBldr.addWhereAttrEqValue(CIAdminEvent.Definition.Abstract, instField.getId());
279                 final InstanceQuery eventQuery = eventQueryBldr.getQuery();
280                 eventQuery.execute();
281                 while (eventQuery.next()) {
282                     final Instance event = eventQuery.getCurrentValue();
283                     final QueryBuilder propQueryBldr = new QueryBuilder(CIAdminCommon.Property);
284                     propQueryBldr.addWhereAttrEqValue(CIAdminCommon.Property.Abstract, event.getId());
285                     final InstanceQuery propQuery = propQueryBldr.getQuery();
286                     propQuery.execute();
287                     while (propQuery.next()) {
288                         new Delete(propQuery.getCurrentValue()).executeWithoutAccessCheck();
289                     }
290                     new Delete(event).executeWithoutAccessCheck();
291                 }
292                 new Delete(instField).executeWithoutAccessCheck();
293             }
294 
295             // append new fields
296             for (final FieldDefinition field : this.fields) {
297                 Insert insert;
298                 if ("Command".equals(field.character)) {
299                     insert = new Insert(CIAdminUserInterface.FieldCommand);
300                 } else if ("Target".equals(field.character)) {
301                     insert = new Insert(CIAdminUserInterface.FieldTable);
302                 } else if ("Heading".equals(field.character)) {
303                     insert = new Insert(CIAdminUserInterface.FieldHeading);
304                 } else if ("Group".equals(field.character)) {
305                     insert = new Insert(CIAdminUserInterface.FieldGroup);
306                 } else if ("Set".equals(field.character)) {
307                     insert = new Insert(CIAdminUserInterface.FieldSet);
308                 } else if ("Classification".equals(field.character)) {
309                     insert = new Insert(CIAdminUserInterface.FieldClassification);
310                 } else if ("Picker".equals(field.character)) {
311                     insert = new Insert(CIAdminUserInterface.FieldPicker);
312                 } else if ("Chart".equals(field.character)) {
313                     insert = new Insert(CIAdminUserInterface.FieldChart);
314                 } else {
315                     insert = new Insert(CIAdminUserInterface.Field);
316                 }
317 
318                 insert.add("Collection", getInstance().getId());
319                 insert.add("Name", field.name);
320                 insert.executeWithoutAccessCheck();
321                 setPropertiesInDb(insert.getInstance(), field.getProperties());
322 
323                 if (field.icon != null) {
324                     final Set<LinkInstance> iconset = new HashSet<LinkInstance>();
325                     iconset.add(new LinkInstance(field.icon));
326                     setLinksInDB(insert.getInstance(), AbstractCollectionUpdate.LINKFIELD2ICON, iconset);
327                 }
328 
329                 // link to table
330                 setLinksInDB(insert.getInstance(), AbstractCollectionUpdate.LINK2TARGETTABLE,
331                                 field.getLinks(AbstractCollectionUpdate.LINK2TARGETTABLE));
332 
333                 // link to picker
334                 setLinksInDB(insert.getInstance(), AbstractCollectionUpdate.LINK2PICKER,
335                                 field.getLinks(AbstractCollectionUpdate.LINK2PICKER));
336 
337                 // append events
338                 for (final Event event : field.getEvents()) {
339                     final Instance newInstance = event.updateInDB(insert.getInstance(), field.name);
340                     setPropertiesInDb(newInstance, event.getProperties());
341                 }
342             }
343         }
344 
345         /**
346          * Adds a new field to this definition of the table.
347          *
348          * @param _field new field to add to this table
349          * @see #fields
350          */
351         public void addField(final FieldDefinition _field)
352         {
353             this.fields.add(_field);
354         }
355 
356         /**
357          * Returns a string representation with values of all instance variables
358          * of a field.
359          *
360          * @return string representation of this definition of a column
361          */
362         @Override
363         public String toString()
364         {
365             return new ToStringBuilder(this).appendSuper(super.toString()).append("fields", this.fields).toString();
366         }
367     }
368 }