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;
20  
21  
22  import java.util.*;
23  
24  import org.caleigo.core.event.*;
25  
26  /*** The CompositeQualifier is a Qualifier that contains a collection of other 
27   * Qualifiers wrapping them as a single Qalifier effectively implementing the 
28   * "Composite" design pattern. A CompositeQualifier can form either a union
29   * or an intersection of the contained Qualifiers which is defined at the
30   * creation.
31   *
32   * @author  Dennis Zikovic
33   * @version 1.00
34   * 
35   *//* 
36   *
37   * WHEN        WHO               WHY & WHAT
38   * ------------------------------------------------------------------------------
39   * 2001-07-11  Dennis Zikovic    Creation
40   */
41  public class CompositeQualifier extends Qualifier 
42  {
43      // Constants ---------------------------------------------------------------
44      
45      /*** Used to state that all contained Qualifiers defines a union which means
46       * that they should be combined using using logical "OR". 
47       */
48      public final static int UNION = 0;
49      
50      /*** Used to state that all contained Qualifiers defines a intersection 
51       * which means that they should be combined using using logical "AND". 
52       */
53      public final static int INTERSECTION = 1;
54      
55      // Data members ------------------------------------------------------------
56      private int mUnionType; 
57      private List mQualifiers;
58      
59      private transient RelayListener mRelayListener;
60      
61      // Constructors ------------------------------------------------------------
62      
63      /*** Default constructor that creates a new CompositeQualifier forming an 
64       * intersection of any added Qualifiers.
65       */  
66      public CompositeQualifier() 
67      {
68          this(INTERSECTION);
69      }
70      
71      /*** Creates a new CompositeQualifier forming either an intersection or  
72       * a union depending on the provided of the union type constant.  
73       */  
74      public CompositeQualifier(int unionType) 
75      {
76          mUnionType = unionType;
77          mQualifiers = new ArrayList();
78      }
79      
80      /*** Creates a new CompositeQualifier forming an intersection of the two
81       * provided Qualifiers.
82       */
83      public CompositeQualifier(Qualifier qualifier1, Qualifier qualifier2) 
84      {
85          this(INTERSECTION);
86          this.add(qualifier1);
87          this.add(qualifier2);
88      }
89      
90      /*** Creates a new CompositeQualifier forming either an intersection or  
91       * a union depending on the defined of the unionType two provided 
92       * Qualifiers.
93       */
94      public CompositeQualifier(Qualifier qualifier1, Qualifier qualifier2, int unionType) 
95      {
96          this(unionType);
97          this.add(qualifier1);
98          this.add(qualifier2);
99      }
100     
101     // Superclass overrides ----------------------------------------------------
102 
103     /*** This method returns true if the qualifier does select the
104      * provided entity object.
105      */
106     public boolean doesQualify(IEntity entity)
107     {
108         // Count the number of qualifying child qualifiers.
109         int qualCount = 0;
110         for(int j=0; j<this.getQualifierCount(); j++)
111             if(this.getQualifier(j).doesQualify(entity))
112                 qualCount++;
113             
114         // Determine qualification based on count and union type.
115         if(mUnionType==INTERSECTION)
116             return qualCount==this.getQualifierCount();
117         else
118             return qualCount>=1;
119     }
120     
121     /*** This method returns true if the qualifier can select entities
122      * of the type defined by the provided entity descriptor.
123      */
124     public boolean canQualify(IEntityDescriptor entityDescriptor)
125     {
126         boolean selects = false;
127         for(int j=0; !selects && j<mQualifiers.size(); j++)
128             selects = ((Qualifier)mQualifiers.get(j)).canQualify(entityDescriptor);
129         return selects;
130 
131         // Count the number of qualifying child qualifiers.
132 //        int qualCount = 0;
133 //        for(int j=0; j<this.getQualifierCount(); j++)
134 //            if(this.getQualifier(j).canQualify(entityDescriptor))
135 //                qualCount++;
136             
137         // Determine qualification based on count and union type.
138 //        if(mUnionType==INTERSECTION)
139 //            return qualCount==this.getQualifierCount();
140 //        else
141 //            return qualCount>=1;
142     }
143     
144     /*** This abstract method must return true if the qualifier can select 
145      * entities of the type defined by the provided entity descriptor without
146      * the nead of any complementary data. No other information than can be
147      * accessed through the provided entity descriptor should be neaded if 
148      * this method returns true. This validates that the doesQualify method
149      * can be called for the called qualifier.
150      */
151     public boolean canDirectlyQualify(IEntityDescriptor entityDescriptor) 
152     {
153         boolean selects = true;
154         for(int j=0; selects && j<mQualifiers.size(); j++)
155             selects = ((Qualifier)mQualifiers.get(j)).canDirectlyQualify(entityDescriptor);
156         return selects;
157     }    
158 
159     /*** This method returns true if the qualifier can uniquely select
160      * entities of the type defined by the provided entity descriptor. 
161      * If that is the case then the qualifier is an identity qualifier and can 
162      * never qualify more then a single entity instance of the specified type.
163      */
164     public boolean canUniquelyQualify(IEntityDescriptor entityDescriptor)
165     {
166         UniqueQualifierVisitor visitor = new UniqueQualifierVisitor(entityDescriptor, this);
167         return visitor.canUniquelyQualify();
168     }
169     
170     /*** Adds an IQualifierListener to receive notifications of contents and 
171      * structure changes from the Qualifier object. The CompositeQualifier 
172      * overrides this method to know when to listen to the contained qualifiers.
173      */
174     public void addQualifierListener(IQualifierListener listener)
175     {
176         super.addQualifierListener(listener);
177         if(mRelayListener!=null)
178         {
179             mRelayListener = new RelayListener();
180             for(int j=0; j<this.getQualifierCount(); j++)
181                 this.getQualifier(j).addQualifierListener(mRelayListener);
182         }
183     }
184 
185     /*** The toString method returns an "abstract" expression of what the 
186      * qualifier selects. Note that this expresion is only intended for visual
187      * aid and is not a valid SQL expresion. 
188      */
189     public String toString()
190     {
191         if(mQualifiers.size()==0)
192             return "";
193         StringBuffer buf = new StringBuffer(500);
194         buf.append('(');
195         buf.append(mQualifiers.get(0));
196         for(int j=1; j<mQualifiers.size(); j++)
197         {
198             if(mUnionType==INTERSECTION)
199                 buf.append(" AND ");
200             else
201                 buf.append(" OR ");
202             buf.append(mQualifiers.get(j));
203         }
204         buf.append(')');
205         return buf.toString();
206     }
207         
208     // Action methods ----------------------------------------------------------
209     
210     /*** Adds a new Qulifier to contained collection of Qualifiers. The added 
211      * Qualifier will be joined as union or intersection depending of the set
212      * union type defined for the CompositeQualifier.
213      */ 
214     public void add(Qualifier qualifier)
215     {
216         mQualifiers.add(qualifier);
217         if(mRelayListener!=null)
218             qualifier.addQualifierListener(mRelayListener);
219         this.fireStructureChangedEvent();
220     }
221     
222     // Access methods ----------------------------------------------------------
223     
224     /*** Access method that returns the Qualifiers union defined by one of the 
225      * constants UNION or INTERSECTION.
226      */
227     public int getUnionType()
228     {
229         return mUnionType;
230     }
231     
232     /*** Access method that returns the count of the contained Qualifiers.
233      */
234     public int getQualifierCount()
235     {
236         return mQualifiers.size();
237     }
238     
239     /*** Access method that returns the indexed contained Qualifier.
240      * @throws java.lang.IndexOutOfBoundsException
241      */
242     public Qualifier getQualifier(int index)
243     {
244         return (Qualifier)mQualifiers.get(index);
245     }
246     
247     /*** Access method that returns an Iterator object for the cotained 
248      * collection of Qualifier objects.
249      */
250     public Iterator getQualifiers()
251     {
252         return mQualifiers.iterator();
253     }
254     
255     // Nested classes ----------------------------------------------------------
256     protected static class UniqueQualifierVisitor
257     {
258         private IEntityDescriptor mEntityDescriptor;
259         private boolean mIsUnique;
260         
261         private IFieldDescriptor[] identityFields;
262         private RelationQualifier[] foundFieldQualifiers;
263         
264         public UniqueQualifierVisitor(IEntityDescriptor entityDescriptor, CompositeQualifier compQualifier)
265         {
266             mEntityDescriptor = entityDescriptor;
267             mIsUnique = false;
268             
269             // Extract all identity fields to search for.
270             int fieldCount = 0;
271             for(int j=0; j<entityDescriptor.getFieldCount(); j++)
272                 if(entityDescriptor.getFieldDescriptor(j).isIdentityField())
273                     fieldCount++;
274                             
275             // Extract all identity fields to search for.
276             identityFields = new IFieldDescriptor[fieldCount];
277             foundFieldQualifiers = new RelationQualifier[fieldCount];
278             for(int j=0; j<entityDescriptor.getFieldCount(); j++)
279                 if(entityDescriptor.getFieldDescriptor(j).isIdentityField())
280                 {
281                     fieldCount--;     
282                     identityFields[fieldCount] = entityDescriptor.getFieldDescriptor(j);
283                     foundFieldQualifiers[fieldCount] = null;
284                 }
285             
286             // Visit all qualifiers.
287             this.visit(compQualifier);
288             
289             // Process result.
290             if(!mIsUnique)
291             {
292                 mIsUnique = true;
293                 
294                 // Check that identity qualifier has been found for all fields
295                 // and only exists once in the qualifier list.
296                 for(int j=0; mIsUnique && j<foundFieldQualifiers.length; j++)
297                 {
298                     mIsUnique = foundFieldQualifiers[j]!=null;
299                     
300                     if(mIsUnique)
301                     {
302                         // Check for duplicates.
303                         for(int k=j-1; mIsUnique && k>=0; k--)
304                             mIsUnique = foundFieldQualifiers[j]!=foundFieldQualifiers[k];
305                     }
306                 }
307             }
308         }
309         
310         protected void visit(Qualifier qual)
311         {
312             if(qual instanceof RelationQualifier)
313             {
314                 if(((RelationQualifier)qual).getRelationType()!=RelationType.EQUAL)
315                     return;
316                 
317                 // Check if identity field is qualified.
318                 for(int j=0; j<identityFields.length; j++)
319                 {
320                     if(identityFields[j]==((RelationQualifier)qual).getFieldDescriptor())
321                     {
322                         if(foundFieldQualifiers[j]==null || !foundFieldQualifiers[j].isFieldToFieldRelation())
323                             foundFieldQualifiers[j] = (RelationQualifier)qual; // Store if "better" = not field to field relation.
324                     }
325                     if(((RelationQualifier)qual).isFieldToFieldRelation() && (IFieldDescriptor)((RelationQualifier)qual).getRelationValue()==identityFields[j])
326                     {
327                         if(foundFieldQualifiers[j]==qual)
328                             foundFieldQualifiers[j] = null; // Ignore relation to self.
329                         else if(foundFieldQualifiers[j]!=null)
330                             foundFieldQualifiers[j] = (RelationQualifier)qual; // Only store if no other relation stored.
331                     }
332                 }
333             }
334             else if(qual instanceof CompositeQualifier)
335             {
336                 if(((CompositeQualifier)qual).getUnionType()!=CompositeQualifier.INTERSECTION)
337                     return;
338                     
339                 // Visit all child qualifiers.
340                 for(int j=0; j<((CompositeQualifier)qual).getQualifierCount(); j++)
341                     this.visit(((CompositeQualifier)qual).getQualifier(j));
342             }
343             else if(!mIsUnique && !(qual instanceof NegateQualifier))
344                 mIsUnique = qual.canUniquelyQualify(mEntityDescriptor);
345         }
346         
347         public boolean canUniquelyQualify()
348         {
349             return mIsUnique;
350         }
351     }
352 }