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;
22  
23  import java.io.StringReader;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.sql.Statement;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.UUID;
32  
33  import org.apache.commons.dbutils.handlers.ArrayListHandler;
34  import org.efaps.admin.common.MsgPhrase;
35  import org.efaps.admin.datamodel.Attribute;
36  import org.efaps.admin.datamodel.AttributeSet;
37  import org.efaps.admin.datamodel.Type;
38  import org.efaps.beans.ValueList;
39  import org.efaps.beans.valueparser.ParseException;
40  import org.efaps.beans.valueparser.ValueParser;
41  import org.efaps.ci.CIAttribute;
42  import org.efaps.db.print.OneSelect;
43  import org.efaps.db.print.Phrase;
44  import org.efaps.db.transaction.ConnectionResource;
45  import org.efaps.db.wrapper.SQLPart;
46  import org.efaps.db.wrapper.SQLSelect;
47  import org.efaps.util.EFapsException;
48  import org.infinispan.Cache;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  /**
53   * Abstract class all print queries are based on.
54   *
55   * @author The eFaps Team
56   * @version $Id: AbstractPrintQuery.java 14276 2014-10-21 15:40:19Z
57   *          jan@moxter.net $
58   */
59  public abstract class AbstractPrintQuery
60  {
61  
62      /**
63       * Logging instance used in this class.
64       */
65      private static final Logger LOG = LoggerFactory.getLogger(PrintQuery.class);
66  
67      /**
68       * Mapping of Select statements to OneSelect.
69       */
70      private final Map<String, OneSelect> selectStmt2OneSelect = new HashMap<String, OneSelect>();
71  
72      /**
73       * Mapping of attributes to OneSelect.
74       */
75      private final Map<String, OneSelect> attr2OneSelect = new HashMap<String, OneSelect>();
76  
77      /**
78       * Mapping of sql tables to table index.
79       *
80       * @see #tableIndex
81       */
82      private final Map<String, Integer> sqlTable2Index = new HashMap<String, Integer>();
83  
84      /**
85       * Mapping of key to Phrase.
86       */
87      private final Map<String, Phrase> key2Phrase = new HashMap<String, Phrase>();
88  
89      /**
90       * List of all OneSelect belonging to this PrintQuery.
91       */
92      private final List<OneSelect> allSelects = new ArrayList<OneSelect>();
93  
94      /**
95       * Index of an sqltable.
96       */
97      private int tableIndex = 0;
98  
99      /**
100      * Must the list of instance be in the same order as given. (There are some
101      * cases the sequence might be different returned from the database. To
102      * enforce the exact sequence this flag can be set. But sorting takes time
103      * and should not be used by default.)
104      */
105     private boolean enforceSorted;
106 
107     /**
108      * Index of the type column.
109      */
110     private int typeColumnIndex = 0;
111 
112     /**
113      * Add an attribute to the PrintQuery. It is used to get editable values
114      * from the eFaps DataBase.
115      *
116      * @param _attributes Attribute to add
117      * @return this PrintQuery
118      * @throws EFapsException on error
119      */
120     public AbstractPrintQuery addAttribute(final CIAttribute... _attributes)
121         throws EFapsException
122     {
123         if (isMarked4execute()) {
124             for (final CIAttribute attr : _attributes) {
125                 addAttribute(attr.name);
126             }
127         }
128         return this;
129     }
130 
131     /**
132      * Add an attribute to the PrintQuery. It is used to get editable values
133      * from the eFaps DataBase.
134      *
135      * @param _attributes Attribute to add
136      * @return this PrintQuery
137      */
138     public AbstractPrintQuery addAttribute(final Attribute... _attributes)
139     {
140         if (isMarked4execute()) {
141             for (final Attribute attr : _attributes) {
142                 final OneSelect oneselect = new OneSelect(this, attr);
143                 this.allSelects.add(oneselect);
144                 this.attr2OneSelect.put(attr.getName(), oneselect);
145             }
146         }
147         return this;
148     }
149 
150     /**
151      * Add a oneselect to this print query.
152      *
153      * @param _oneSelect select to be added
154      */
155     protected void addOneSelect(final OneSelect _oneSelect)
156     {
157         this.allSelects.add(_oneSelect);
158     }
159 
160     /**
161      * Getter method for instance variable {@link #allSelects}.
162      *
163      * @return value of instance variable {@link #allSelects}
164      */
165     protected List<OneSelect> getAllSelects()
166     {
167         return this.allSelects;
168     }
169 
170     /**
171      * Method to get the attribute for an <code>_attributeName</code>.
172      *
173      * @param _attributeName name of the attribute
174      * @return Attribute
175      */
176     public Attribute getAttribute4Attribute(final String _attributeName)
177     {
178         final OneSelect oneselect = this.attr2OneSelect.get(_attributeName);
179         return oneselect == null ? null : oneselect.getAttribute();
180     }
181 
182     /**
183      * Method to get the instance for an <code>_attributeName</code>.
184      *
185      * @param _attributeName name of the attribute
186      * @return list of instance
187      * @throws EFapsException on error
188      */
189     public List<Instance> getInstances4Attribute(final String _attributeName)
190         throws EFapsException
191     {
192         final OneSelect oneselect = this.attr2OneSelect.get(_attributeName);
193         return oneselect == null ? null : oneselect.getInstances();
194     }
195 
196     /**
197      * Get the object returned by the given name of an attribute.
198      *
199      * @param <T> class the return value will be casted to
200      * @param _attribute attribute the object is wanted for
201      * @return object for the select statement
202      * @throws EFapsException on error
203      */
204     public <T> T getAttribute(final CIAttribute _attribute)
205         throws EFapsException
206     {
207         return this.<T>getAttribute(_attribute.name);
208     }
209 
210     /**
211      * Get the object returned by the given name of an attribute.
212      *
213      * @param <T> class the return value will be casted to
214      * @param _attributeName name of the attribute the object is wanted for
215      * @return object for the select statement
216      * @throws EFapsException on error
217      */
218     @SuppressWarnings("unchecked")
219     public <T> T getAttribute(final String _attributeName)
220         throws EFapsException
221     {
222         final OneSelect oneselect = this.attr2OneSelect.get(_attributeName);
223         return oneselect == null ? null : (T) oneselect.getObject();
224     }
225 
226     /**
227      * Get the object returned by the given Attribute.
228      *
229      * @param <T> class the return value will be casted to
230      * @param _attribute the object is wanted for
231      * @return object for the select statement
232      * @throws EFapsException on error
233      */
234     @SuppressWarnings("unchecked")
235     public <T> T getAttribute(final Attribute _attribute)
236         throws EFapsException
237     {
238         return (T) getAttribute(_attribute.getName());
239     }
240 
241     /**
242      * Method returns the Main type of the query. In case that the query is
243      * based on only one type, this Type is returned. In case that the query
244      * contains different Types, the type returned must be the type, all other
245      * types are derived from.
246      *
247      * @return Type
248      */
249     public abstract Type getMainType();
250 
251     /**
252      * Method to get the instances this PrintQuery is executed on.
253      *
254      * @return List of instances
255      */
256     public abstract List<Instance> getInstanceList();
257 
258     /**
259      * Method to get the current Instance.
260      *
261      * @return current Instance
262      */
263     public abstract Instance getCurrentInstance();
264 
265     /**
266      * Add an AttributeSet to the PrintQuery. It is used to get editable values
267      * from the eFaps DataBase.
268      *
269      * @param _setName Name of the AttributeSet to add
270      * @return this PrintQuery
271      * @throws EFapsException on error
272      */
273     public AbstractPrintQuery addAttributeSet(final String _setName)
274         throws EFapsException
275     {
276         final Type type = getMainType();
277         if (type != null) {
278             final AttributeSet set = AttributeSet.find(type.getName(), _setName);
279             addAttributeSet(set);
280         }
281         return this;
282     }
283 
284     /**
285      * Add an AttributeSet to the PrintQuery. It is used to get editable values
286      * from the eFaps DataBase. The AttributeSet is internally transformed into
287      * an linkfrom query.
288      *
289      * @param _set AttributeSet to add
290      * @return this PrintQuery
291      * @throws EFapsException on error
292      */
293     public AbstractPrintQuery addAttributeSet(final AttributeSet _set)
294         throws EFapsException
295     {
296         final String key = "linkfrom[" + _set.getName() + "#" + _set.getAttributeName() + "]";
297         final OneSelect oneselect = new OneSelect(this, key);
298         this.allSelects.add(oneselect);
299         this.attr2OneSelect.put(_set.getAttributeName(), oneselect);
300         oneselect.analyzeSelectStmt();
301         for (final String setAttrName : _set.getSetAttributes()) {
302             if (!setAttrName.equals(_set.getAttributeName())) {
303                 oneselect.getFromSelect().addOneSelect(new OneSelect(this, _set.getAttribute(setAttrName)));
304             }
305         }
306         oneselect.getFromSelect().getMainOneSelect().setAttribute(_set.getAttribute(_set.getAttributeName()));
307         return this;
308     }
309 
310     /**
311      * Get the object returned by the given name of an AttributeSet.
312      *
313      * @param <T> class the return value will be casted to
314      * @param _setName name of the AttributeSet the object is wanted for
315      * @return object for the select statement
316      * @throws EFapsException on error
317      */
318     @SuppressWarnings("unchecked")
319     public <T> T getAttributeSet(final String _setName)
320         throws EFapsException
321     {
322         final OneSelect oneselect = this.attr2OneSelect.get(_setName);
323         Map<String, Object> ret = null;
324         if (oneselect == null || oneselect.getFromSelect() == null) {
325             LOG.error("Could not get an AttributeSet for the name: '{}' in PrintQuery '{]'", _setName, this);
326         } else if (oneselect.getFromSelect().hasResult()) {
327             ret = new HashMap<String, Object>();
328             // in an attributset the first one is fake
329             boolean first = true;
330             for (final OneSelect onsel : oneselect.getFromSelect().getAllSelects()) {
331                 if (first) {
332                     first = false;
333                 } else {
334                     final ArrayList<Object> list = new ArrayList<Object>();
335                     final Object object = onsel.getObject();
336                     if (object instanceof List<?>) {
337                         list.addAll((List<?>) object);
338                     } else {
339                         list.add(object);
340                     }
341                     ret.put(onsel.getAttribute().getName(), list);
342                 }
343             }
344         }
345         return (T) ret;
346     }
347 
348     /**
349      * Add an attribute to the PrintQuery. It is used to get editable values
350      * from the eFaps DataBase.
351      *
352      * @param _attrNames Name of the Attribute to add
353      * @return this PrintQuery
354      * @throws EFapsException on error
355      */
356     public AbstractPrintQuery addAttribute(final String... _attrNames)
357         throws EFapsException
358     {
359         final Type type = getMainType();
360         if (type != null) {
361             for (final String attrName : _attrNames) {
362                 final Attribute attr = type.getAttribute(attrName);
363                 if (attr == null) {
364                     final AttributeSet set = AttributeSet.find(type.getName(), attrName);
365                     if (set != null) {
366                         addAttributeSet(set);
367                     }
368                 } else {
369                     addAttribute(attr);
370                 }
371             }
372         }
373         return this;
374     }
375 
376     /**
377      * Add a Phrase to this PrintQuery. A Phrase is something like:
378      * <code>"$&lt;attribute[LastName]&gt; - $&lt;attribute[FirstName]&gt;"</code>
379      * This would return " John - Doe". One Phrase can contain various selects
380      * as defined for {@link #addSelect(String...)} and string to connect them.
381      *
382      * @param _key key the phrase can be accessed
383      * @param _phraseStmt phrase to add
384      * @throws EFapsException on error
385      * @return this PrintQuery
386      */
387     public AbstractPrintQuery addPhrase(final String _key,
388                                         final String _phraseStmt)
389         throws EFapsException
390     {
391         ValueList list = null;
392 
393         final ValueParser parser = new ValueParser(new StringReader(_phraseStmt));
394         try {
395             list = parser.ExpressionString();
396         } catch (final ParseException e) {
397             throw new EFapsException(PrintQuery.class.toString(), e);
398         }
399         final Phrase phrase = new Phrase(_key, _phraseStmt, list);
400         this.key2Phrase.put(_key, phrase);
401 
402         for (final String selectStmt : list.getExpressions()) {
403             final OneSelect oneselect = new OneSelect(this, selectStmt);
404             this.allSelects.add(oneselect);
405             phrase.addSelect(oneselect);
406             oneselect.analyzeSelectStmt();
407         }
408         return this;
409     }
410 
411     /**
412      * Get the String representation of a phrase.
413      *
414      * @param _key key to the phrase
415      * @return String representation of the phrase
416      * @throws EFapsException on error
417      */
418     public String getPhrase(final String _key)
419         throws EFapsException
420     {
421         final Phrase phrase = this.key2Phrase.get(_key);
422         return phrase == null ? null : phrase.getPhraseValue(getCurrentInstance());
423     }
424 
425     /**
426      * @param _msgPhrase phrase to add
427      * @throws EFapsException on error
428      * @return this PrintQuery
429      */
430     public AbstractPrintQuery addMsgPhrase(final MsgPhrase... _msgPhrase)
431         throws EFapsException
432     {
433         return addMsgPhrase(null, _msgPhrase);
434     }
435 
436     /**
437      * @param _msgPhrase phrase to add
438      * @throws EFapsException on error
439      * @return this PrintQuery
440      */
441     public AbstractPrintQuery addMsgPhrase(final SelectBuilder _selectBldr,
442                                            final MsgPhrase... _msgPhrase)
443         throws EFapsException
444     {
445         String baseSel;
446         if (_selectBldr == null) {
447             baseSel = "";
448         } else {
449             baseSel = _selectBldr.toString() + ".";
450         }
451         for (final MsgPhrase phrase : _msgPhrase) {
452             for (final String selectStmt : phrase.getArguments()) {
453                 addSelect(baseSel + selectStmt);
454             }
455         }
456         return this;
457     }
458 
459     public AbstractPrintQuery addMsgPhrase(final String... _msgPhrase)
460         throws EFapsException
461     {
462         final List<MsgPhrase> msgphrases = new ArrayList<>();
463         for (final String phraseStr : _msgPhrase) {
464             msgphrases.add(MsgPhrase.get(phraseStr));
465         }
466         return addMsgPhrase(msgphrases.toArray(new MsgPhrase[msgphrases.size()]));
467     }
468 
469     public AbstractPrintQuery addMsgPhrase(final UUID... _msgPhrase)
470         throws EFapsException
471     {
472         return addMsgPhrase(null, _msgPhrase);
473     }
474 
475     public AbstractPrintQuery addMsgPhrase(final SelectBuilder _selectBldr,
476                                            final UUID... _msgPhrase)
477         throws EFapsException
478     {
479         final List<MsgPhrase> msgphrases = new ArrayList<>();
480         for (final UUID phraseUUID : _msgPhrase) {
481             msgphrases.add(MsgPhrase.get(phraseUUID));
482         }
483         return addMsgPhrase(_selectBldr, msgphrases.toArray(new MsgPhrase[msgphrases.size()]));
484     }
485 
486     /**
487      * Get the String representation of a phrase.
488      *
489      * @param _key key to the phrase
490      * @return String representation of the phrase
491      * @throws EFapsException on error
492      */
493     public String getMsgPhrase(final String _msgPhrase)
494         throws EFapsException
495     {
496         return getMsgPhrase(MsgPhrase.get(_msgPhrase));
497     }
498 
499     /**
500      * Get the String representation of a phrase.
501      *
502      * @param _key key to the phrase
503      * @return String representation of the phrase
504      * @throws EFapsException on error
505      */
506     public String getMsgPhrase(final UUID _msgPhrase)
507         throws EFapsException
508     {
509         return getMsgPhrase(null, MsgPhrase.get(_msgPhrase));
510     }
511 
512     /**
513      * Get the String representation of a phrase.
514      *
515      * @param _key key to the phrase
516      * @return String representation of the phrase
517      * @throws EFapsException on error
518      */
519     public String getMsgPhrase(final SelectBuilder _selectBldr,
520                                final UUID _msgPhrase)
521         throws EFapsException
522     {
523         return getMsgPhrase(_selectBldr, MsgPhrase.get(_msgPhrase));
524     }
525 
526     /**
527      * Get the String representation of a phrase.
528      *
529      * @param _key key to the phrase
530      * @return String representation of the phrase
531      * @throws EFapsException on error
532      */
533     public String getMsgPhrase(final MsgPhrase _msgPhrase)
534         throws EFapsException
535     {
536         return getMsgPhrase(null, _msgPhrase);
537     }
538 
539     /**
540      * Get the String representation of a phrase.
541      *
542      * @param _key key to the phrase
543      * @return String representation of the phrase
544      * @throws EFapsException on error
545      */
546     public String getMsgPhrase(final SelectBuilder _selectBldr,
547                                final MsgPhrase _msgPhrase)
548         throws EFapsException
549     {
550         final List<Object> objects = new ArrayList<>();
551         String baseSel;
552         if (_selectBldr == null) {
553             baseSel = "";
554         } else {
555             baseSel = _selectBldr.toString() + ".";
556         }
557         boolean isNotNull = true;
558         for (final String select : _msgPhrase.getArguments()) {
559             final Object tmpObj = getSelect(baseSel + select);
560             isNotNull = isNotNull && tmpObj != null;
561             objects.add(tmpObj);
562         }
563         return isNotNull ? _msgPhrase.format(objects.toArray()) : null;
564     }
565 
566     /**
567      * Add an select to the PrintQuery. A select is something like:
568      * <code>class[Emperador_Products_ClassFloorLaminate].linkto[SurfaceAttrId].attribute[Value]</code>
569      * <br>
570      * The use of the key words like "class" etc is mandatory. Contrary to
571      * {@link #addPhrase(String, String)} the values will not be parsed! The
572      * values will not be editable.
573      *
574      * @param _selectBldrs selectBuilder to be added
575      * @return this PrintQuery
576      * @throws EFapsException on error
577      */
578     public AbstractPrintQuery addSelect(final SelectBuilder... _selectBldrs)
579         throws EFapsException
580     {
581         if (isMarked4execute()) {
582             for (final SelectBuilder selectBldr : _selectBldrs) {
583                 addSelect(selectBldr.toString());
584             }
585         }
586         return this;
587     }
588 
589     /**
590      * Add an select to the PrintQuery. A select is something like:
591      * <code>class[Emperador_Products_ClassFloorLaminate].linkto[SurfaceAttrId].attribute[Value]</code>
592      * <br>
593      * The use of the key words like "class" etc is mandatory. Contrary to
594      * {@link #addPhrase(String, String)} the values will not be parsed! The
595      * values will not be editable.
596      *
597      * @param _selectStmts selectStatments to be added
598      * @return this PrintQuery
599      * @throws EFapsException on error
600      */
601     public AbstractPrintQuery addSelect(final String... _selectStmts)
602         throws EFapsException
603     {
604         if (isMarked4execute()) {
605             for (final String selectStmt : _selectStmts) {
606                 final OneSelect oneselect = new OneSelect(this, selectStmt);
607                 this.allSelects.add(oneselect);
608                 this.selectStmt2OneSelect.put(selectStmt, oneselect);
609                 oneselect.analyzeSelectStmt();
610             }
611         }
612         return this;
613     }
614 
615     /**
616      * Get the object returned by the given select statement.
617      *
618      * @param <T> class the return value will be casted to
619      * @param _selectStmt select statement the object is wanted for
620      * @return object for the select statement
621      * @throws EFapsException on error
622      */
623     @SuppressWarnings("unchecked")
624     public <T> T getSelect(final String _selectStmt)
625         throws EFapsException
626     {
627         final OneSelect oneselect = this.selectStmt2OneSelect.get(_selectStmt);
628         return oneselect == null ? null : (T) oneselect.getObject();
629     }
630 
631     /**
632      * Get the object returned by the given select statement.
633      *
634      * @param <T> class the return value will be casted to
635      * @param _selectBldr select bldr the object is wanted for
636      * @return object for the select statement
637      * @throws EFapsException on error
638      */
639     @SuppressWarnings("unchecked")
640     public <T> T getSelect(final SelectBuilder _selectBldr)
641         throws EFapsException
642     {
643         final OneSelect oneselect = this.selectStmt2OneSelect.get(_selectBldr.toString());
644         return oneselect == null ? null : (T) oneselect.getObject();
645     }
646 
647     /**
648      * Method to get the Attribute used for an select.
649      *
650      * @param _selectStmt selectstatement the attribute is wanted for
651      * @return Attribute for the selectstatement
652      */
653     public Attribute getAttribute4Select(final String _selectStmt)
654     {
655         final OneSelect oneselect = this.selectStmt2OneSelect.get(_selectStmt);
656         return oneselect == null ? null : oneselect.getAttribute();
657     }
658 
659     /**
660      * Method to get the instances used for an select.
661      *
662      * @param _selectStmt selectstatement the attribute is wanted for
663      * @return List of instances for the select or an empty list in case that
664      *         the onselect is not found
665      * @throws EFapsException on error
666      */
667     public List<Instance> getInstances4Select(final String _selectStmt)
668         throws EFapsException
669     {
670         final OneSelect oneselect = this.selectStmt2OneSelect.get(_selectStmt);
671         return oneselect == null ? new ArrayList<Instance>() : oneselect.getInstances();
672     }
673 
674     /**
675      * Method to determine it the select statement returns more than one value.
676      *
677      * @param _selectStmt selectstatement the attribute is wanted for
678      * @return true it the oneselect is muliple, else false
679      * @throws EFapsException on error
680      */
681     public boolean isList4Select(final String _selectStmt)
682         throws EFapsException
683     {
684         final OneSelect oneselect = this.selectStmt2OneSelect.get(_selectStmt);
685         return oneselect == null ? false : oneselect.isMultiple();
686     }
687 
688     /**
689      * Getter method for the instance variable {@link #enforceSorted}.
690      *
691      * @return value of instance variable {@link #enforceSorted}
692      */
693     public boolean isEnforceSorted()
694     {
695         return this.enforceSorted;
696     }
697 
698     /**
699      * Setter method for instance variable {@link #enforceSorted}.
700      *
701      * @param _enforceSorted value for instance variable {@link #enforceSorted}
702      */
703 
704     public void setEnforceSorted(final boolean _enforceSorted)
705     {
706         this.enforceSorted = _enforceSorted;
707     }
708 
709     /**
710      * Dryrun the Print. "Analyze the selects etc. without running against the
711      * database".
712      *
713      * @throws EFapsException on error
714      */
715     public void dryRun()
716         throws EFapsException
717     {
718         final String sql = createSQLStatement();
719         LOG.debug("DryRun SQL", sql);
720     }
721 
722     /**
723      * The instance method executes the query.
724      *
725      * @return true if the query contains values, else false
726      * @throws EFapsException on error
727      */
728     public boolean execute()
729         throws EFapsException
730     {
731         return executeWithoutAccessCheck();
732     }
733 
734     /**
735      * The instance method executes the query without an access check.
736      *
737      * @return true if the query contains values, else false
738      * @throws EFapsException on error
739      */
740     public boolean executeWithoutAccessCheck()
741         throws EFapsException
742     {
743         boolean ret = false;
744         if (isMarked4execute()) {
745             if (getInstanceList().size() > 0) {
746                 ret = executeOneCompleteStmt(createSQLStatement(), this.allSelects);
747             }
748             if (ret) {
749                 for (final OneSelect onesel : this.allSelects) {
750                     if (onesel.getFromSelect() != null) {
751                         onesel.getFromSelect().execute(onesel);
752                     }
753                 }
754             }
755         }
756         return ret;
757     }
758 
759     /**
760      * Method to create on Statement out of the different parts.
761      *
762      * @return StringBuilder containing the SQL statement
763      * @throws EFapsException on error
764      */
765     protected String createSQLStatement()
766         throws EFapsException
767     {
768 
769         final SQLSelect select = new SQLSelect()
770                         .column(0, "ID")
771                         .from(getMainType().getMainTable().getSqlTable(), 0);
772         for (final OneSelect oneSel : this.allSelects) {
773             oneSel.append2SQLFrom(select);
774         }
775 
776         int colIndex = select.getColumns().size() + 1;
777         // if the main table has a column for the type it is selected also
778         if (getMainType().getMainTable().getSqlColType() != null) {
779             select.column(0, getMainType().getMainTable().getSqlColType());
780             this.typeColumnIndex = colIndex;
781             colIndex++;
782         }
783 
784         for (final OneSelect onesel : this.allSelects) {
785             if (onesel.getValueSelect() != null) {
786                 colIndex += onesel.append2SQLSelect(select, colIndex);
787             }
788         }
789 
790         select.addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.IN).addPart(SQLPart.PARENTHESIS_OPEN);
791 
792         int i = 0;
793         for (final Instance instance : getInstanceList()) {
794             if (Context.getDbType().getMaxExpressions() > -1 && i > Context.getDbType().getMaxExpressions()) {
795                 select.addPart(SQLPart.PARENTHESIS_CLOSE)
796                                 .addPart(SQLPart.OR)
797                                 .addColumnPart(0, "ID").addPart(SQLPart.IN).addPart(SQLPart.PARENTHESIS_OPEN);
798                 i = 0;
799             }
800             if (i > 0) {
801                 select.addPart(SQLPart.COMMA);
802             }
803             select.addValuePart(instance.getId());
804             i++;
805         }
806         select.addPart(SQLPart.PARENTHESIS_CLOSE);
807 
808         for (final OneSelect oneSel : this.allSelects) {
809             oneSel.append2SQLWhere(select);
810         }
811 
812         return select.getSQL();
813     }
814 
815     /**
816      * The instance method executes exact one complete statement and populates
817      * the result in the cached result {@link #cachedResult}.
818      *
819      * @param _complStmt complete statement instance to execute
820      * @param _oneSelects lsit of OneSelects the statement is executed for
821      * @return true if the query contains values, else false
822      * @throws EFapsException on error
823      */
824 
825     @SuppressWarnings("unchecked")
826     protected boolean executeOneCompleteStmt(final String _complStmt,
827                                              final List<OneSelect> _oneSelects)
828         throws EFapsException
829     {
830         boolean ret = false;
831         ConnectionResource con = null;
832         try {
833             AbstractPrintQuery.LOG.debug("Executing SQL: {}", _complStmt);
834 
835             List<Object[]> rows = null;
836             boolean cached = false;
837             if (isCacheEnabled()) {
838                 final QueryKey querykey = QueryKey.get(getKey(), _complStmt);
839                 final Cache<QueryKey, Object> cache = QueryCache.getSqlCache();
840                 if (cache.containsKey(querykey)) {
841                     final Object object = cache.get(querykey);
842                     if (object instanceof List) {
843                         rows = (List<Object[]>) object;
844                     }
845                     cached = true;
846                 }
847             }
848 
849             if (!cached) {
850                 con = Context.getThreadContext().getConnectionResource();
851                 final Statement stmt = con.getConnection().createStatement();
852                 final ResultSet rs = stmt.executeQuery(_complStmt);
853                 final ArrayListHandler handler = new ArrayListHandler(Context.getDbType().getRowProcessor());
854                 rows = handler.handle(rs);
855                 rs.close();
856                 stmt.close();
857                 con.commit();
858 
859                 if (isCacheEnabled()) {
860                     QueryCache.put((ICacheDefinition) this, QueryKey.get(getKey(), _complStmt), rows);
861                 }
862             }
863 
864             for (final Object[] row : rows) {
865                 for (final OneSelect onesel : _oneSelects) {
866                     onesel.addObject(row);
867                 }
868                 ret = true;
869             }
870 
871             final List<Instance> tmpList = new ArrayList<Instance>();
872             final Map<Instance, Integer> sortMap = new HashMap<Instance, Integer>();
873             int i = 0;
874             for (final Object[] row : rows) {
875                 final Instance instance;
876                 if (getMainType().getMainTable().getSqlColType() != null) {
877                     instance = Instance.get(Type.get((Long) row[this.typeColumnIndex - 1]), (Long) row[0]);
878                 } else {
879                     instance = Instance.get(getMainType(), (Long) row[0]);
880                 }
881                 sortMap.put(instance, i);
882                 tmpList.add(instance);
883                 i++;
884             }
885 
886             if (this.enforceSorted) {
887                 for (final OneSelect onesel : _oneSelects) {
888                     onesel.sortByInstanceList(getInstanceList(), sortMap);
889                 }
890             } else {
891                 getInstanceList().clear();
892                 getInstanceList().addAll(tmpList);
893             }
894         } catch (final SQLException e) {
895             throw new EFapsException(InstanceQuery.class, "executeOneCompleteStmt", e);
896         } finally {
897             if (con != null && con.isOpened()) {
898                 con.abort();
899             }
900         }
901         return ret;
902     }
903 
904     /**
905      * Method to get an table index from {@link #sqlTable2Index}.
906      *
907      * @param _tableName tablename the index is wanted for
908      * @param _column name of the column, used for the relation
909      * @param _relIndex relation the table is used in
910      * @param _clazzId optional id of the classification
911      * @return index of the table or null if not found
912      */
913     public Integer getTableIndex(final String _tableName,
914                                  final String _column,
915                                  final int _relIndex,
916                                  final Long _clazzId)
917     {
918         return this.sqlTable2Index.get(_relIndex + "__" + _tableName + "__" + _column
919                         + (_clazzId == null ? "" : "__" + _clazzId));
920     }
921 
922     /**
923      * Get a new table index and add the table to the map of existing table
924      * indexes.
925      *
926      * @param _tableName tablename the index is wanted for
927      * @param _column name of the column, used for the relation
928      * @param _relIndex relation the table is used in
929      * @param _clazzId optional id of the classification
930      * @return new index for the table
931      */
932     public Integer getNewTableIndex(final String _tableName,
933                                     final String _column,
934                                     final Integer _relIndex,
935                                     final Long _clazzId)
936     {
937         this.tableIndex++;
938         this.sqlTable2Index.put(_relIndex + "__" + _tableName + "__" + _column
939                         + (_clazzId == null ? "" : "__" + _clazzId), this.tableIndex);
940         return this.tableIndex;
941     }
942 
943     /**
944      * A PrintQuery will only be executed if at least one Instance is given to
945      * be executed on.
946      *
947      * @return true if this PrintQuery will be executed.
948      */
949     public boolean isMarked4execute()
950     {
951         return !getInstanceList().isEmpty();
952     }
953 
954     /**
955      * @return true if for this query caching is enabled, else false
956      */
957     public abstract boolean isCacheEnabled();
958 
959     /**
960      * @return key used in caching
961      */
962     public String getKey()
963     {
964         return QueryCache.DEFAULTKEY;
965     }
966 
967 }