View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.core.service;
20  
21  
22  import java.util.*;
23  import java.text.*;
24  
25  import java.sql.Connection;
26  import java.sql.PreparedStatement;
27  import java.sql.SQLException;
28  import java.sql.Timestamp;
29  
30  import org.caleigo.core.*;
31  import org.caleigo.core.exception.*;
32  
33  /*** This class constanis help methods for building valid SQL language
34   * statements based on CEL entities, descriptors and qualifiers.
35   *
36   * The class can be used by any IDataService implementation that connects
37   * to SQL based databases.
38   *
39   * @author  Dennis Zikovic
40   * @version 1.00
41   * 
42   *
43   * WHEN        WHO               WHY & WHAT
44   * -----------------------------------------------------------------------------
45   * 2001-07-24  Dennis Zikovic    Creation
46   */
47  public class SQLToolKitPS implements java.io.Serializable
48  {
49      // Constants ---------------------------------------------------------------
50      private static final String SELECT = "SELECT ";
51      private static final String INSERT = "INSERT INTO ";
52      private static final String UPDATE = "UPDATE ";
53      private static final String DELETE = "DELETE FROM ";
54      private static final String FROM = "FROM ";
55      private static final String WHERE = "WHERE ";
56      private static final String ORDER = "ORDER BY ";
57      
58      // Data members ------------------------------------------------------------
59      private Map mQualifierParserMap;
60      private Map mDataTypeConverterMap;
61      private boolean mIsQuotingIdentifiers = false;
62          
63      // Constructors ------------------------------------------------------------
64      /***
65       * Constructs a new SQLToolKitPS and does necessary initialization
66       */
67      public SQLToolKitPS()
68      {
69          // Create type concersion map and load it with default converters.
70          mDataTypeConverterMap = new HashMap();
71          mDataTypeConverterMap.put(DataType.STRING, new StringTypeConverter());
72          mDataTypeConverterMap.put(DataType.BOOLEAN, new BooleanTypeConverter()); 
73          mDataTypeConverterMap.put(DataType.DATE, new DateTypeConverter()); 
74          
75          // Create type parser map and load it with default parsers.
76          mQualifierParserMap = new HashMap();
77          mQualifierParserMap.put(RelationQualifier.class, new RelationQualifierParser());
78          mQualifierParserMap.put(CompositeQualifier.class, new CompositeQualifierParser());
79          mQualifierParserMap.put(NegateQualifier.class, new NegateQualifierParser());
80      }
81      
82  
83      // Access methods ----------------------------------------------------------
84      
85      // Access methods ----------------------------------------------------------
86      public void addDataTypeConverter(DataType dataType, IDataTypeConverter converter)
87      {
88          mDataTypeConverterMap.remove(dataType);
89          mDataTypeConverterMap.put(dataType, converter);
90      }    
91  
92      public IDataTypeConverter getDataTypeConverter(DataType dataType)
93      {
94          return (IDataTypeConverter)mDataTypeConverterMap.get(dataType);
95      }
96      
97      
98      /***
99       * Adds a qualifierParser for the specified qualifier class. The Parser is responsible for analysing the
100      * qualifier and constructing a SQL query 
101      * 
102      * @param qualifierClass The qualifier class that should be handled by the parser
103      * @param parser The parser that should handle the qualifier class
104      */    
105     public void addQualifierParser(Class qualifierClass, IQualifierParser parser)
106     {
107         mQualifierParserMap.remove(qualifierClass);
108         mQualifierParserMap.put(qualifierClass, parser);
109     }
110     
111     public IQualifierParser getQualifierParser(Class qualifierClass)
112     {
113         return (IQualifierParser)mQualifierParserMap.get(qualifierClass);
114     }
115     
116     public void setQuotingIdentifiers(boolean allwaysQuote)
117     {
118         mIsQuotingIdentifiers = allwaysQuote;
119     }
120     
121     public boolean isQuotingIdentifiers()
122     {
123         return mIsQuotingIdentifiers;
124     }    
125     
126     // Help methods ------------------------------------------------------------
127     public String buildSelectCommand(DataQuery query, ArrayList values)
128     {
129         // Build basic select command
130         StringBuffer command = new StringBuffer(1000);
131         command.append(this.buildSelectCommand(query.getEntityDescriptor(), query.getQualifier(), values));
132 
133         // Build the ORDER part. 
134         if(query.getEntityCollator()!=null)
135         {
136             EntityCollator collator = query.getEntityCollator();
137             
138             command.append(' ');
139             command.append(ORDER);
140             for(int j=0; j<collator.getFieldCount(); j++)
141             {
142                 if(j>0)
143                     command.append(", ");
144                 command.append(this.quoteIdentifier(collator.getFieldCollator(j).getFieldDescriptor().getEntityDescriptor().getSourceName()));
145                 command.append('.');
146                 command.append(this.quoteIdentifier(collator.getFieldCollator(j).getFieldDescriptor().getSourceName()));
147                 if(!collator.getFieldCollator(j).isAscending())
148                     command.append(" DESC");
149             }
150         }
151         
152         return command.toString();
153     }
154     
155     /***
156      * Builds the select query to be used by a PreparedStatement
157      * 
158      * @param descriptor The Entity type to be queried
159      * @param qualifier The qualifier that identies the wanted subset of entities
160      * @param values ArrayList that should be populated with the data to be set by the PreparedStatement
161      * @return String Returns a sql query used with a PreparedStatement
162      */
163     public String buildSelectCommand(IEntityDescriptor descriptor, Qualifier qualifier, ArrayList values)
164     {
165         // Validate descriptor and qualifier for select.
166         
167         // Build the SELECT part. 
168         StringBuffer command = new StringBuffer(1000);
169         command.append(SELECT);
170 
171         for(int j=0; j<descriptor.getFieldCount(); j++)
172         {
173             if(j>0)
174             {
175                 command.append(',');
176                 command.append(' ');
177             }
178             command.append(this.quoteIdentifier(descriptor.getFieldDescriptor(j).getEntityDescriptor().getSourceName()));
179             command.append('.');
180             command.append(this.quoteIdentifier(descriptor.getFieldDescriptor(j).getSourceName()));
181         }
182         command.append(' ');
183         
184         // Build the FROM part. 
185         command.append(FROM);
186         command.append(buildFromExpresion(descriptor, qualifier));
187         command.append(' ');
188         
189         // Build the optional WHERE part. 
190         if(qualifier!=null)
191         {
192             command.append(WHERE);
193             command.append(this.buildWhereExpresion(qualifier, values));
194         }
195         
196         return command.toString();
197     }
198    
199     /***
200      * Builds a Select PreparedStatement from a IEntityDescriptor and a Qualifier
201      * 
202      * @param descriptor IEntityDescriptor that identied the Type of entity wanted
203      * @param qualifier Qualifier that is used to limit the result to a wanted subset of entities
204      * @param connection SQL Connection that is the link to the database
205      * @return PreparedStatement Returns a fully populated Prepared Statement to be used for querying
206      * @throws SQLException throwmn if a database problem occurs
207      */
208     public PreparedStatement buildSelectStatement(IEntityDescriptor descriptor, Qualifier qualifier, Connection connection)
209         throws SQLException
210     {
211         ArrayList values = new ArrayList();
212         String sql = buildSelectCommand(descriptor, qualifier, values);
213         PreparedStatement preparedStatement = connection.prepareStatement(sql);
214         
215         Iterator iter = values.iterator(); 
216         
217         int count = 1;
218         while (iter.hasNext())
219         {
220             Object data = iter.next();
221             
222             preparedStatement.setObject(count++, data);
223         }
224 
225         return preparedStatement;
226     }
227     
228     /***
229      * Build the Prepared Statement SQL query for for an update of an entity
230      * 
231      * @param entity The entity that should be updated
232      * @param qualifier The qualifier that uniqely identifies the entity
233      * @param values Should be populated with all data to be set in the query and in the correct order.
234      * @return String Returns a SQL query representing the Update
235      */
236     public String buildUpdateCommand(IEntity entity, Qualifier qualifier, ArrayList values)
237     {
238         // Build the declaration part. 
239         StringBuffer command = new StringBuffer(1000);
240         command.append(UPDATE);
241         command.append(this.quoteIdentifier(entity.getEntityDescriptor().getSourceName()));
242         command.append(' ');
243 
244         // Build the value part.
245         command.append("SET ");
246         int addedCount = 0;
247         for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
248         {
249             if(!entity.getEntityDescriptor().getFieldDescriptor(j).isAutoGenerated() 
250                 && entity.isFieldDirty(entity.getEntityDescriptor().getFieldDescriptor(j)))
251             {
252                 if(addedCount>0)
253                 {
254                     command.append(',');
255                     command.append(' ');
256                 }
257                
258                 command.append(this.quoteIdentifier(entity.getEntityDescriptor().getFieldDescriptor(j).getSourceName()));
259                 command.append('=');
260                 //command.append(this.buildDataString(entity.getEntityDescriptor().getFieldDescriptor(j).getDataType(), entity.getData(entity.getEntityDescriptor().getFieldDescriptor(j))));
261                 values.add(entity.getData(entity.getEntityDescriptor().getFieldDescriptor(j)));
262                 addedCount++;
263             }
264         }
265         command.append(' ');
266         
267         // Build the WHERE part. 
268         if(qualifier==null)
269             qualifier = entity.getOriginQualifier();
270         command.append(WHERE);
271         command.append(this.buildWhereExpresion(qualifier, values));
272         
273         // Build the where clause.
274         return command.toString();    
275     }
276     
277     
278     /***
279      * Build a Update Prepared Statement from a Entity and a Qualifier
280      * 
281      * @param entity The entity containing the data to be updated     
282      * @param qualifier A qualifier that identifies the entity to be updated
283      * @param connection A reference to a SQL connection
284      * @return PreparedStatement Returns a fully runnable PreparedStatement
285      * @throws SQLException thrown if there was a problem with the sql connection
286      */
287     public PreparedStatement buildUpdateStatement(IEntity entity, Qualifier qualifier, Connection connection) 
288         throws SQLException
289     {
290         ArrayList values = new ArrayList();
291         String sql = buildUpdateCommand(entity, qualifier, values);
292 
293         PreparedStatement preparedStatement = connection.prepareStatement(sql);        
294 
295         Iterator iter = values.iterator();
296         int count = 1;
297         while (iter.hasNext())
298         {
299             Object data = iter.next();
300             
301             preparedStatement.setObject(count++, data);
302         }
303 
304         return preparedStatement;
305     }
306     
307     
308     /***
309      * Build the SQL Insert String for Prepared statements
310      * 
311      * @param entity The entity to be inserted
312      * @return String Returns the string that should be run by the prepared Statement
313      */
314     public String buildInsertCommand(IEntity entity)
315     {
316         // Validate descriptor and qualifier for insert.
317         
318         // Initialize declaration and value parts of the command. 
319         StringBuffer declarationPart = new StringBuffer(1000);
320         StringBuffer valuePart = new StringBuffer(1000);
321         int addedCount = 0;
322 
323         declarationPart.append(INSERT);
324         declarationPart.append(this.quoteIdentifier(entity.getEntityDescriptor().getSourceName()));
325         declarationPart.append(" (");
326         
327         valuePart.append(" VALUES (");
328                 
329         for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
330         {
331             // Do not include autogenerated fields.
332             if(!entity.getEntityDescriptor().getFieldDescriptor(j).isAutoGenerated())
333             {
334                 Object data = entity.getData(entity.getEntityDescriptor().getFieldDescriptor(j));
335                 
336                 // Do not include null data.
337                 if(data!=null)
338                 {
339                     if(addedCount>0)
340                     {
341                         declarationPart.append(", ");
342                         valuePart.append(", ");
343                     }
344                     addedCount++;
345 
346                     declarationPart.append(this.quoteIdentifier(entity.getEntityDescriptor().getFieldDescriptor(j).getSourceName()));
347                     valuePart.append("?");
348 //                  valuePart.append(this.buildDataString(entity.getEntityDescriptor().getFieldDescriptor(j).getDataType(), data));
349                 }
350             }
351         }
352         declarationPart.append(')');
353         valuePart.append(')');
354         
355         // Handle the case when no column data is specified
356         if (declarationPart.toString().endsWith("()") && valuePart.toString().endsWith("()"))
357         {
358             declarationPart.delete(declarationPart.indexOf("()"), declarationPart.length());
359             valuePart.replace(0, valuePart.length(), "DEFAULT VALUES");
360         }
361         
362         // Join and return command parts.
363         declarationPart.append(valuePart);
364         return declarationPart.toString();
365     }
366     
367     
368     /***
369      * Build a PreparedStatement for inserting the Entity
370      * 
371      * @param entity The entity to be inserted
372      * @param connection The SQL connections that should handle the Statement
373      * @param generateKeys Set to true if Generated Keys should be returned
374      * @return PreparedStatement A fully runnable Prepared Statement
375      * @exception SQLException Returned if an SQLException occured
376      */
377     public PreparedStatement buildInsertStatement(IEntity entity, Connection connection, boolean generateKeys) 
378         throws SQLException
379     {
380         String sql = buildInsertCommand(entity);
381         PreparedStatement preparedStatement = null;
382         if (generateKeys)
383             connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
384         else 
385             connection.prepareStatement(sql, PreparedStatement.NO_GENERATED_KEYS);
386         
387         int count=1;
388         
389         // Populate the statement
390         for(int j=0; j<entity.getEntityDescriptor().getFieldCount(); j++)
391         {
392             // Do not include autogenerated fields.
393             if(!entity.getEntityDescriptor().getFieldDescriptor(j).isAutoGenerated())
394             {
395                 Object data = entity.getData(entity.getEntityDescriptor().getFieldDescriptor(j));
396                 
397                 // Do not include null data.
398                 if(data!=null)
399                 {
400                     preparedStatement.setObject(count++, data);
401                 }
402             }
403         }
404         
405         return preparedStatement;
406     }
407     
408     
409     /***
410      * Build the delete SQL Prepared Statement Query
411      * 
412      * @param descriptor The descriptor that describes the entity/ies being removed
413      * @param qualifier The qualifier that identifies the entities to be removed
414      * @param values Should be updated with the data that should complement the PreparedStatement
415      * @return String Returns the Prepared Statement query string
416      */
417     public String buildDeleteCommand(IEntityDescriptor descriptor, Qualifier qualifier, ArrayList values)
418     {
419         // Validate descriptor and qualifier for delete.
420         if(descriptor.getEntityType()==IEntityDescriptor.CUSTOM_ENTITY)
421             throw new SQLToolKitException("Can't delete composite entities.");
422         if(qualifier==null || !qualifier.canUniquelyQualify(descriptor))
423             throw new SQLToolKitException("Delete command requires an identity qualifier.");
424 
425         // Build the declaration part.
426         StringBuffer command = new StringBuffer(1000);
427         command.append(DELETE);
428         command.append(this.quoteIdentifier(descriptor.getSourceName()));
429         command.append(' ');
430         
431         // Build the WHERE part. 
432         command.append(WHERE);
433         command.append(this.buildWhereExpresion(qualifier, values));
434         // Add the data to the PreparedStatement
435         
436 
437         return command.toString();
438     }   
439     
440     
441     /***
442      * Build a Delete Prepared Statement from a EntityDescriptor and a Qualifier
443      * 
444      * @param descriptor A IEntityDescriptor stating what descriptor type to be deleted
445      * @param qualifier A qualifier that identifies the subset of entities to be removed
446      * @param connection A reference to a SQL connection
447      * @return PreparedStatement Returns a fully runnable PreparedStatement
448      * @throws SQLException thrown if there was a problem with the sql connection
449      */
450     public PreparedStatement buildDeleteStatement(IEntityDescriptor descriptor, Qualifier qualifier, Connection connection) 
451         throws SQLException
452     {
453         ArrayList values = new ArrayList();
454         String sql = buildDeleteCommand(descriptor, qualifier, values);
455         
456         PreparedStatement preparedStatement = connection.prepareStatement(sql);        
457 
458         Iterator iter = values.iterator();
459         int count = 1;
460         while (iter.hasNext())
461         {
462             Object data = iter.next();
463             
464             preparedStatement.setObject(count++, data);
465         }
466         
467         return preparedStatement;    
468     
469     }
470    
471     /*** 
472      * This method creates a comma seperated string with the source name
473      * of all referenced entity descriptors.
474      * 
475      * @param descriptor The descriptor that describes the entity/ies
476      * @param qualifier The qualifier that identifies the entities
477      * @return Returns a String containing the FROM section of the query
478      */
479     public String buildFromExpresion(IEntityDescriptor descriptor, Qualifier qualifier)
480     {
481         // Scan the qualifier entity descriptors.
482         Set descriptorSet = this.getQualifierDescriptorSet(qualifier);
483         if(descriptorSet==null)
484             descriptorSet = new HashSet();
485         
486         // Scan the entity descriptor for fields of different origins.
487         for(int j=0; j<descriptor.getFieldCount(); j++)
488             descriptorSet.add(descriptor.getFieldDescriptor(j).getEntityDescriptor());
489 
490         // Build the from statement based on the descriptor set.
491         StringBuffer expresion = new StringBuffer(1000);
492         Iterator it = descriptorSet.iterator();
493         while(it.hasNext())
494         {
495             if(expresion.length()>0)
496             {
497                 expresion.append(',');
498                 expresion.append(' ');
499             }
500             expresion.append(this.quoteIdentifier(((IEntityDescriptor)it.next()).getSourceName()));            
501         }
502         
503         return expresion.toString();
504     }    
505     
506     
507     /***
508      * Builds a WHERE Prepared statement from a qualifier
509      * 
510      * @param qualifier The qualifier that should be parsed
511      * @param values The data stored should be stored in values in the same order it is being added to the generated query 
512      * @return String The prepared WHERE statement
513      */
514     public String buildWhereExpresion(Qualifier qualifier, ArrayList values)
515     {
516         if(qualifier==null)
517             return null;
518         else if(mQualifierParserMap.containsKey(qualifier.getClass())) 
519             return ((IQualifierParser)mQualifierParserMap.get(qualifier.getClass())).process(qualifier, this, values);
520         else
521             throw new SQLToolKitException("Failed to process unknown Qualifier class: "+qualifier.getClass().getName());
522     }    
523     
524     public String buildDataString(DataType type, Object data)
525     {
526         if(mDataTypeConverterMap.containsKey(type))
527             return (String)((IDataTypeConverter)mDataTypeConverterMap.get(type)).convertToDB(data);
528         else
529             return type.convertToString(data);
530     }    
531     
532     public String prepareWildcards(String dataString)
533     {
534         StringBuffer buf = new StringBuffer(dataString);
535         for(int j=0; j<dataString.length(); j++)
536         {
537             if(buf.charAt(j)=='*')
538                 buf.setCharAt(j, '%');
539             else if(buf.charAt(j)=='?')
540                 buf.setCharAt(j, '_');
541         }
542         return buf.toString();
543     }
544         
545     /*** Returns a Set containing the entity descriptors meaded for the WHERE
546      * statement built by the submited qualifier. Can be used to build the
547      * FROM statement of the SQL command. May return null
548      */
549     protected Set getQualifierDescriptorSet(Qualifier qualifier)
550     {
551         if(qualifier==null)
552             return null;
553         else if(mQualifierParserMap.containsKey(qualifier.getClass())) 
554             return ((IQualifierParser)mQualifierParserMap.get(qualifier.getClass())).stateDescriptorRequirement(qualifier, this);
555         else
556             throw new SQLToolKitException("Failed to process unknown Qualifier class: "+qualifier.getClass().getName());        
557     }
558     
559     public String quoteIdentifier(String indentifier)
560     {
561         if(mIsQuotingIdentifiers || indentifier.indexOf(' ')>=0)
562             return "\""+indentifier+"\"";
563         else
564             return indentifier;
565     }
566     
567   
568     
569     public static interface IQualifierParser
570     {
571         /*** Should produce a valid logic "term" to iclude in an sql where-
572          * statement.
573          */ 
574         public String process(Qualifier qualifier, SQLToolKitPS sql, ArrayList values);
575         
576         /*** Should produce a set of entity descriptors that are adressed by
577          * the qualifier.
578          */
579         public Set stateDescriptorRequirement(Qualifier qualifier, SQLToolKitPS sql);
580     }    
581     
582     
583     /***
584      * Class for parsing RelationQualifiers. 
585      */
586     public class RelationQualifierParser implements IQualifierParser
587     {
588         
589         /*** 
590          * Processes a qualifier and creates a Conditional query
591          * 
592          * @param qualifier the qualifier that should be parsed
593          * @param sql The Toolkit containing all data about the conversion
594          * @param values An ArrayList with all data needed to be set in the PreparedStatement
595          * @return Returns a string with the sql query
596          */
597         public String process(Qualifier qualifier, SQLToolKitPS sql, ArrayList values)
598         {
599             if(qualifier==null)
600                 return null;
601             
602             StringBuffer buf = new StringBuffer(100);
603             RelationQualifier qual = (RelationQualifier)qualifier;
604             
605             // Process the field descriptor.
606             buf.append(quoteIdentifier(qual.getFieldDescriptor().getEntityDescriptor().getSourceName()));
607             buf.append('.');
608             buf.append(quoteIdentifier(qual.getFieldDescriptor().getSourceName()));
609             
610             // Process the relation operator.
611             if(qual.getRelationType()==RelationType.EQUAL)
612             {
613                 if(qual.getRelationValue()==null)
614                     buf.append(" IS NULL");
615                 else
616                     buf.append('=');
617             }
618             else if(qual.getRelationType()==RelationType.NOT_EQUAL)
619             {
620                 if(qual.getRelationValue()==null)
621                     buf.append(" IS NOT NULL");
622                 else
623                     buf.append("<>");
624             }
625             else if(qual.getRelationType()==RelationType.LESS)
626                 buf.append('<');
627             else if(qual.getRelationType()==RelationType.LESS_EQUAL)
628                 buf.append("<=");
629             else if(qual.getRelationType()==RelationType.LARGER)
630                 buf.append('>');
631             else if(qual.getRelationType()==RelationType.LARGER_EQUAL)
632                 buf.append(">=");
633             else if(qual.getRelationType()==RelationType.SIMULAR)
634                 buf.append(" LIKE ");
635             else
636                 buf.append(qual.getRelationType().toString());
637             
638             // Process the relations data value.
639             if(qual.getRelationValue() instanceof IFieldDescriptor)
640             {
641                 buf.append(quoteIdentifier(((IFieldDescriptor)qual.getRelationValue()).getEntityDescriptor().getSourceName()));
642                 buf.append('.');
643                 buf.append(quoteIdentifier(((IFieldDescriptor)qual.getRelationValue()).getSourceName()));
644             }
645             else if(qual.getRelationValue()!=null)
646             {
647                 if(qual.getRelationType()==RelationType.SIMULAR)
648                     values.add(sql.prepareWildcards(sql.buildDataString(qual.getFieldDescriptor().getDataType(), qual.getRelationValue())));
649                 else
650                     values.add(qual.getRelationValue());
651                 
652                 buf.append("?");
653             }
654             else if(!(qual.getRelationType()==RelationType.EQUAL || qual.getRelationType()==RelationType.NOT_EQUAL))
655                 throw new SQLToolKitException("Relation using \""+qual.getRelationType()+"\" can not relate to NULL.");
656             
657             return buf.toString();
658         }
659         
660         /***
661          * States what descriptors are needed for the FROM expression
662          * 
663          * @param qualifier The qualifier constituting the condition
664          * @param sql The toolkit containinig the overall data for the expressionbuilding
665          * @return Returns a set of FieldDescriptor needed to be in the FROM sections of the sql expression.
666          */
667         public Set stateDescriptorRequirement(Qualifier qualifier, SQLToolKitPS sql)
668         {
669             Set descriptorSet = new HashSet();
670             descriptorSet.add(((RelationQualifier)qualifier).getFieldDescriptor().getEntityDescriptor());
671             if(((RelationQualifier)qualifier).getRelationValue() instanceof IFieldDescriptor)
672                 descriptorSet.add(((IFieldDescriptor)((RelationQualifier)qualifier).getRelationValue()).getEntityDescriptor());
673             return descriptorSet;
674         }
675     }    
676     
677     
678     /***
679      * Parser for CompositeQualifiers. Creates the necessary sql query
680      */
681     public static class CompositeQualifierParser implements IQualifierParser
682     {
683         
684         /***
685          * Processes the qualifier and creates the necessary SQL query
686          * 
687          * @param qualifier The qualifier constituting the condition
688          * @param sql The Toolkit containing all data about the conversion
689          * @param values An ArrayList with all data needed to be set in the PreparedStatement
690          * @return Returns a string with the sql query
691          */
692         public String process(Qualifier qualifier, SQLToolKitPS sql, ArrayList values)
693         {
694             if(qualifier==null || ((CompositeQualifier)qualifier).getQualifierCount()<=0)
695                 return null;
696             
697             StringBuffer buf = new StringBuffer(500);
698             buf.append('(');
699             buf.append(sql.buildWhereExpresion(((CompositeQualifier)qualifier).getQualifier(0), values));
700             for(int j=1; j<((CompositeQualifier)qualifier).getQualifierCount(); j++)
701             {
702                 if(((CompositeQualifier)qualifier).getUnionType()==CompositeQualifier.INTERSECTION)
703                     buf.append(" AND ");
704                 else
705                     buf.append(" OR ");
706                 buf.append(sql.buildWhereExpresion(((CompositeQualifier)qualifier).getQualifier(j), values));
707             }
708             buf.append(')');
709             
710             return buf.toString();
711         }
712        
713         /***
714          * States what descriptors are needed for the FROM expression
715          * 
716          * @param qualifier The qualifier constituting the condition
717          * @param sql The toolkit containinig the overall data for the expressionbuilding
718          * @return Returns a set of FieldDescriptor needed to be in the FROM sections of the sql expression.
719          */        
720         public Set stateDescriptorRequirement(Qualifier qualifier, SQLToolKitPS sql)
721         {
722             Set descriptorSet = new HashSet();
723             for(int j=0; j<((CompositeQualifier)qualifier).getQualifierCount(); j++)
724                 descriptorSet.addAll(sql.getQualifierDescriptorSet(((CompositeQualifier)qualifier).getQualifier(j)));
725             return descriptorSet;
726         }
727     }    
728     
729     public static class NegateQualifierParser
730     {
731         public String process(Qualifier qualifier, SQLToolKitPS sql, ArrayList values)
732         {
733             if(((NegateQualifier)qualifier).getNegatedQualifier()==null)
734                 return null;
735             else
736                 return "NOT "+sql.buildWhereExpresion(((NegateQualifier)qualifier).getNegatedQualifier(), values);
737         }
738         
739         public Set stateDescriptorRequirement(Qualifier qualifier, SQLToolKitPS sql)
740         {
741             if(((NegateQualifier)qualifier).getNegatedQualifier()!=null)
742                 return sql.getQualifierDescriptorSet(((NegateQualifier)qualifier).getNegatedQualifier());
743             else
744                 return null;
745         }
746     }
747     
748     public static class BooleanCharTypeConverter implements IDataTypeConverter
749     {
750         private static final Character DEFAULT_TRUE_CHARACTER   = new Character('t');
751         private static final Character DEFAULT_FALSE_CHARACTER  = new Character('f');
752         private static final String DEFAULT_ACCEPTED_TRUE_CHARS = "tT1yY";
753         
754         private Character mTrueCharacter;
755         private Character mFalseCharacter;
756         private String mAcceptedTrueChars;
757         
758         public BooleanCharTypeConverter()
759         {
760             mTrueCharacter  = DEFAULT_TRUE_CHARACTER;
761             mFalseCharacter = DEFAULT_FALSE_CHARACTER;
762             mAcceptedTrueChars = DEFAULT_ACCEPTED_TRUE_CHARS;
763         }
764         
765         public BooleanCharTypeConverter(char trueChar, char falseChar)
766         {
767             this(trueChar, falseChar, DEFAULT_ACCEPTED_TRUE_CHARS);
768         }
769             
770         public BooleanCharTypeConverter(char trueChar, char falseChar,
771                                             String acceptedTrueChars)
772         {
773             mTrueCharacter = new Character(trueChar);
774             mFalseCharacter = new Character(falseChar);
775             
776             if ( acceptedTrueChars != null )
777                 mAcceptedTrueChars = acceptedTrueChars;
778             else
779                 mAcceptedTrueChars = "" + mTrueCharacter;
780 
781             if ( acceptedTrueChars.indexOf(mTrueCharacter.charValue()) < 0 )
782                 acceptedTrueChars = mTrueCharacter + acceptedTrueChars;
783         }
784 
785         public Object convertToDB(Object data)
786         {
787             if(!(data instanceof Boolean))
788                 data = DataType.BOOLEAN.convertFrom(data);
789             if(data==null)
790                 return null;
791             else
792                 return ((Boolean)data).booleanValue() ? "\'" + mTrueCharacter + "\'": "\'" + mFalseCharacter + "\'";
793         }
794         
795         public Object convertFromDB(Object data)
796         {
797             if (data instanceof String && ((String) data).length()==1)
798                 data = new Character(((String) data).charAt(0));
799             if (data instanceof Character)
800             {
801                 Character characterData = (Character) data;
802                 if (characterData == mFalseCharacter)
803                 {
804                     data = new Boolean(false);
805                 }
806                 else
807                 {
808                     data = new Boolean(mAcceptedTrueChars.indexOf((characterData).charValue()) >= 0);
809                 }
810             }
811             else if(!(data instanceof Boolean))
812             {
813                 data = DataType.BOOLEAN.convertFrom(data);
814             }
815 
816             return data;
817         }
818     }
819     
820 
821     public static class BooleanTypeConverter implements IDataTypeConverter
822     {
823         public Object convertToDB(Object data)
824         {
825             if(!(data instanceof Boolean))
826                 data = DataType.BOOLEAN.convertFrom(data);
827             if(data==null)
828                 return null;
829             else
830                 return ((Boolean)data).booleanValue() ? "1" : "0";
831         }
832         
833         public Object convertFromDB(Object data)
834         {
835             if(!(data instanceof Boolean))
836                 data = DataType.BOOLEAN.convertFrom(data);
837             return data;
838         }
839     }
840     
841 
842     public static class DateTypeConverter implements IDataTypeConverter
843     {
844         private DateFormat mDateFormat = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''");
845         
846         public Object convertToDB(Object data)
847         {
848             if(!(data instanceof Date))
849                 data = DataType.DATE.convertFrom(data);
850             if(data==null)
851                 return null;
852             else
853                 return mDateFormat.format((Date)data);
854         }
855         
856         public Object convertFromDB(Object data)
857         {
858             if(data instanceof Timestamp)
859                 data = new Date(((Timestamp)data).getTime());
860             if(!(data instanceof Date))
861                 data = DataType.DATE.convertFrom(data);
862             return data;
863         }
864     }
865     
866 
867     // Nested classes ----------------------------------------------------------
868     public static interface IDataTypeConverter
869     {
870         public Object convertToDB(Object data);
871         public Object convertFromDB(Object data);
872     }    
873     
874 
875     public static class StringTypeConverter implements IDataTypeConverter
876     {
877         // Data members --------------------------------------------------------
878         private boolean mDoTrim;
879         
880         // Constructors --------------------------------------------------------
881         public StringTypeConverter()
882         {
883             this(true);
884         }
885         
886         public StringTypeConverter(boolean trim)
887         {
888             mDoTrim = trim;
889         }
890         
891         // IDataTypeConverter implementation -----------------------------------
892         public Object convertToDB(Object data)
893         {
894             String text = (String)data;
895             StringBuffer buf = new StringBuffer(text.length()+20);
896             buf.append('\'');
897             
898             int prevIndex = 0;
899             int index = text.indexOf('\'');
900             while(index>=0)
901             {
902                 buf.append(text.substring(prevIndex, index));
903                 buf.append('\'');
904                 prevIndex = index;
905                 index = text.indexOf('\'', index+1);
906             }
907             buf.append(text.substring(prevIndex));
908 
909             buf.append('\'');
910             return buf.toString();
911         }
912         
913         public Object convertFromDB(Object data)
914         {
915             if(data==null)
916                 return null;
917             else if(!(data instanceof String))
918                 data = data.toString();
919             
920             if(mDoTrim)
921                 data = ((String)data).trim();         
922             
923             return data;
924         }
925     }
926     
927 
928 
929     
930         
931 }