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.db.print.value;
22  
23  import java.sql.ResultSet;
24  import java.sql.SQLException;
25  import java.sql.Statement;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Map.Entry;
32  import java.util.Set;
33  
34  import org.efaps.admin.datamodel.Attribute;
35  import org.efaps.admin.datamodel.Classification;
36  import org.efaps.admin.datamodel.Type;
37  import org.efaps.db.Context;
38  import org.efaps.db.Instance;
39  import org.efaps.db.print.OneSelect;
40  import org.efaps.db.transaction.ConnectionResource;
41  import org.efaps.db.wrapper.SQLPart;
42  import org.efaps.db.wrapper.SQLSelect;
43  import org.efaps.util.EFapsException;
44  import org.efaps.util.cache.CacheReloadException;
45  
46  /**
47   * TODO comment!
48   *
49   * @author The eFaps Team
50   * @version $Id$
51   */
52  public class ClassificationValueSelect
53      extends OIDValueSelect
54  {
55      /**
56       * Mapping of the instances to classIds. Used as temporary cache.
57       */
58      private final Map<Instance, List<Long>> instances2classId = new HashMap<Instance, List<Long>>();
59  
60      /**
61       * Have the values been retrieved allready.
62       */
63      private boolean retrieved = false;
64  
65      /**
66       * @param _oneSelect OneSelect
67       */
68      public ClassificationValueSelect(final OneSelect _oneSelect)
69      {
70          super(_oneSelect);
71      }
72  
73      /**
74       * {@inheritDoc}
75       */
76      @Override
77      public String getValueType()
78      {
79          return "classification";
80      }
81  
82      /**
83       * {@inheritDoc}
84       */
85      @Override
86      public Object getValue(final Object _object)
87          throws EFapsException
88      {
89          final ArrayList<Object> ret = new ArrayList<>();
90          final String oid = (String) super.getValue(_object);
91          // TODO the execution of getting the value should be done on the execute
92          // of the query!
93          if (!this.retrieved) {
94              getValues4Instances(oid);
95              this.retrieved = true;
96          }
97  
98          if (getChildValueSelect() == null) {
99              throw new EFapsException(ClassificationValueSelect.class, "notyet");
100         } else {
101             if (oid != null) {
102                 if ("type".equals(getChildValueSelect().getValueType())) {
103                     final Instance instance = Instance.get(oid);
104                     final Set<Classification> clazzes = getClassification(this.instances2classId.get(instance));
105                     for (final Classification clazz : clazzes) {
106                         final Object tmp = ((TypeValueSelect) getChildValueSelect())
107                                         .analyzeChildValue(getChildValueSelect(), clazz);
108                         ret.add(tmp);
109                     }
110                 }
111             }
112         }
113         return ret.size() > 0 ? ret : null;
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public Object getValue(final List<Object> _objectList)
121         throws EFapsException
122     {
123         final List<Object> ret = new ArrayList<Object>();
124         for (final Object object : _objectList) {
125             ret.add(getValue(object));
126         }
127         return _objectList.size() > 0 ? ret.size() > 1 ? ret : ret.get(0) : null;
128     }
129 
130     /**
131      * Method to get only the leaf of the classification tree.
132      *
133      * @param _classIds List of classids
134      * @return set
135      * @throws CacheReloadException on error
136      */
137     private Set<Classification> getClassification(final List<Long> _classIds)
138         throws CacheReloadException
139     {
140         final Set<Classification> noadd = new HashSet<Classification>();
141         final Set<Classification> add = new HashSet<Classification>();
142         if (_classIds != null) {
143             for (final Long id : _classIds) {
144                 Classification clazz = (Classification) Type.get(id);
145                 if (!noadd.contains(clazz)) {
146                     add.add(clazz);
147                     while (clazz.getParentClassification() != null) {
148                         clazz = clazz.getParentClassification();
149                         if (add.contains(clazz)) {
150                             add.remove(clazz);
151                         }
152                         noadd.add(clazz);
153                     }
154                 }
155             }
156         }
157         return add;
158     }
159 
160     /**
161      * Method to get the values for the instances.
162      *
163      * @param _oid oid of the current instance
164      * @throws EFapsException on error
165      */
166     private void getValues4Instances(final String _oid)
167         throws EFapsException
168     {
169         // group the instance by type
170         // check if the current instance is the list (happens if this
171         // value select is e.g. part of a linkto
172         final Map<Type, List<Instance>> type2instance = new HashMap<Type, List<Instance>>();
173         final Instance currentInst = Instance.get(_oid);
174         final List<Instance> instances = new ArrayList<Instance>();
175         if (getOneSelect().getQuery().getInstanceList().contains(currentInst)) {
176             instances.addAll(getOneSelect().getQuery().getInstanceList());
177         } else {
178             for (final Object object : getOneSelect().getObjectList()) {
179                 final String oid =  (String) super.getValue(object);
180                 instances.add(Instance.get(oid));
181             }
182         }
183 
184         for (final Instance instance : instances) {
185             List<Instance> tmpList;
186             if (type2instance.containsKey(instance.getType())) {
187                 tmpList = type2instance.get(instance.getType());
188             } else {
189                 tmpList = new ArrayList<Instance>();
190                 type2instance.put(instance.getType(), tmpList);
191             }
192             tmpList.add(instance);
193         }
194 
195         // make one union part for every type
196         for (final Entry<Type, List<Instance>> entry : type2instance.entrySet()) {
197 
198             boolean union = false;
199             final Set<Classification> classTypes = new HashSet<Classification>();
200             Type curr = entry.getKey();
201             while (curr.getParentType() != null) {
202                 classTypes.addAll(curr.getClassifiedByTypes());
203                 curr = curr.getParentType();
204             }
205             classTypes.addAll(curr.getClassifiedByTypes());
206             final SQLSelect unionSelect = new SQLSelect();
207             for (final Classification clazz : classTypes) {
208 
209                 final Attribute typeAttr = clazz.getClassifyRelationType()
210                                 .getAttribute(clazz.getRelTypeAttributeName());
211                 final Attribute linkAttr = clazz.getClassifyRelationType()
212                                 .getAttribute(clazz.getRelLinkAttributeName());
213                 final SQLSelect select;
214                 if (union) {
215                     unionSelect.addPart(SQLPart.UNION).addPart(SQLPart.ALL);
216                     select = new SQLSelect();
217                 } else {
218                     select = unionSelect;
219                     union = true;
220                 }
221                 select.column(0, "ID")
222                     .column(0, linkAttr.getSqlColNames().get(0))
223                     .column(0, typeAttr.getSqlColNames().get(0))
224                     .from(clazz.getClassifyRelationType().getMainTable().getSqlTable(), 0)
225                     .addPart(SQLPart.WHERE)
226                     .addColumnPart(0, linkAttr.getSqlColNames().get(0))
227                     .addPart(SQLPart.IN).addPart(SQLPart.PARENTHESIS_OPEN);
228 
229                 boolean first = true;
230                 for (final Instance instance : entry.getValue()) {
231                     if (first) {
232                         first = false;
233                     } else {
234                         select.addPart(SQLPart.COMMA);
235                     }
236                     select.addValuePart(instance.getId());
237                 }
238                 select.addPart(SQLPart.PARENTHESIS_CLOSE);
239                 if (clazz.getClassifyRelationType().getMainTable().getSqlColType() != null) {
240                     select.addPart(SQLPart.AND)
241                         .addColumnPart(0, clazz.getClassifyRelationType().getMainTable().getSqlColType())
242                         .addPart(SQLPart.EQUAL).addValuePart(clazz.getClassifyRelationType().getId());
243                 }
244                 if (union && !select.equals(unionSelect)) {
245                     unionSelect.addNestedSelectPart(select.getSQL());
246                 }
247             }
248             if (classTypes.size() > 0) {
249                 executeOneCompleteStmt(unionSelect.getSQL(), entry.getValue());
250             }
251         }
252     }
253 
254     /**
255      * Method to execute one statement against the eFaps-DataBase.
256      *
257      * @param _complStmt ststement
258      * @param _instances list of instances
259      * @return true it values where found
260      * @throws EFapsException on error
261      */
262     protected boolean executeOneCompleteStmt(final String _complStmt,
263                                              final List<Instance> _instances)
264         throws EFapsException
265     {
266         final boolean ret = false;
267         ConnectionResource con = null;
268         try {
269             con = Context.getThreadContext().getConnectionResource();
270 
271             final Statement stmt = con.getConnection().createStatement();
272 
273             final ResultSet rs = stmt.executeQuery(_complStmt.toString());
274             final Map<Long, List<Long>> link2clazz = new HashMap<Long, List<Long>>();
275             while (rs.next()) {
276                 final long linkId = rs.getLong(2);
277                 final long classificationID = rs.getLong(3);
278                 List<Long> templ;
279                 if (link2clazz.containsKey(linkId)) {
280                     templ = link2clazz.get(linkId);
281                 } else {
282                     templ = new ArrayList<Long>();
283                     link2clazz.put(linkId, templ);
284                 }
285                 templ.add(classificationID);
286             }
287             for (final Instance instance : _instances) {
288                 this.instances2classId.put(instance, link2clazz.get(instance.getId()));
289             }
290             rs.close();
291             stmt.close();
292             con.commit();
293         } catch (final SQLException e) {
294             throw new EFapsException(ClassificationValueSelect.class, "executeOneCompleteStmt", e);
295         } finally {
296             if (con != null && con.isOpened()) {
297                 con.abort();
298             }
299         }
300         return ret;
301     }
302 }