001    package org.apache.ojb.broker.query;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.Collection;
019    import java.util.HashSet;
020    import java.util.Vector;
021    
022    import org.apache.ojb.broker.metadata.ClassDescriptor;
023    import org.apache.ojb.broker.metadata.DescriptorRepository;
024    import org.apache.ojb.broker.metadata.FieldDescriptor;
025    import org.apache.ojb.broker.metadata.MetadataManager;
026    
027    /**
028     * Insert the type's description here.
029     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
030     * @version $Id: QueryFactory.java,v 1.1 2007-08-24 22:17:36 ewestfal Exp $
031     */
032    public final class QueryFactory
033    {
034        private static DescriptorRepository getRepository()
035        {
036            return MetadataManager.getInstance().getRepository();
037        }
038    
039        /**
040         * create a new ReportQueryByCriteria
041         * @param classToSearchFrom
042         * @param criteria
043         * @param distinct
044         * @return ReportQueryByCriteria
045         */
046        public static ReportQueryByCriteria newReportQuery(Class classToSearchFrom, String[] columns, Criteria criteria, boolean distinct)
047        {
048            criteria = addCriteriaForOjbConcreteClasses(getRepository().getDescriptorFor(classToSearchFrom), criteria);
049            return new ReportQueryByCriteria(classToSearchFrom, columns, criteria, distinct);
050        }
051    
052        /**
053         * create a new ReportQueryByCriteria
054         * @param classToSearchFrom
055         * @param criteria
056         * @param distinct
057         * @return ReportQueryByCriteria
058         */
059        public static ReportQueryByCriteria newReportQuery(Class classToSearchFrom, Criteria criteria, boolean distinct)
060        {
061            criteria = addCriteriaForOjbConcreteClasses(getRepository().getDescriptorFor(classToSearchFrom), criteria);
062            return newReportQuery(classToSearchFrom, null, criteria, distinct);
063        }
064    
065        /**
066         * create a new ReportQueryByCriteria
067         * @param classToSearchFrom
068         * @param criteria
069         * @return ReportQueryByCriteria
070         */
071        public static ReportQueryByCriteria newReportQuery(Class classToSearchFrom, Criteria criteria)
072        {
073            return newReportQuery(classToSearchFrom, criteria, false);
074        }
075    
076        /**
077         * Method declaration
078         * @param classToSearchFrom
079         * @param criteria
080         * @param distinct
081         * @return QueryByCriteria
082         */
083        public static QueryByCriteria newQuery(Class classToSearchFrom, Criteria criteria, boolean distinct)
084        {
085            criteria = addCriteriaForOjbConcreteClasses(getRepository().getDescriptorFor(classToSearchFrom), criteria);
086            return new QueryByCriteria(classToSearchFrom, criteria, distinct);
087        }
088    
089        /**
090         * Method declaration
091         * @param classToSearchFrom
092         * @param criteria
093         * @return QueryByCriteria
094         */
095        public static QueryByCriteria newQuery(Class classToSearchFrom, Criteria criteria)
096        {
097            return newQuery(classToSearchFrom, criteria, false);
098        }
099    
100        /**
101         * Return a QueryByIdentity for example_or_identity
102         * @param example_or_identity
103         * @return QueryByIdentity
104         */
105        public static QueryByIdentity newQuery(Object example_or_identity)
106        {
107            return newQueryByIdentity(example_or_identity);
108        }
109    
110        /**
111         * Return a QueryByIdentity for example_or_identity
112         * @param example_or_identity
113         * @return QueryByIdentity
114         */
115        public static QueryByIdentity newQueryByIdentity(Object example_or_identity)
116        {
117            return new QueryByIdentity(example_or_identity);
118        }
119    
120        /**
121         * Return a QueryByCriteria for example
122         * <br>Use with care because building of Query is not foolproof !!!
123         * @param example
124         * @return QueryByCriteria
125         */
126        public static QueryByCriteria newQueryByExample(Object example)
127        {
128            return new QueryByCriteria(example);
129        }
130    
131        /**
132         * @param classToSearchFrom
133         * @param indirectionTable
134         * @param criteria
135         * @param distinct
136         * @return QueryByMtoNCriteria
137         */
138        public static QueryByMtoNCriteria newQuery(Class classToSearchFrom, String indirectionTable, Criteria criteria, boolean distinct)
139        {
140            criteria = addCriteriaForOjbConcreteClasses(getRepository().getDescriptorFor(classToSearchFrom), criteria);
141            return new QueryByMtoNCriteria(classToSearchFrom, indirectionTable, criteria, distinct);
142        }
143    
144        /**
145         * @param classToSearchFrom
146         * @param indirectionTable
147         * @param criteria
148         * @return QueryByCriteria
149         */
150        public static QueryByCriteria newQuery(Class classToSearchFrom, String indirectionTable, Criteria criteria)
151        {
152            criteria = addCriteriaForOjbConcreteClasses(getRepository().getDescriptorFor(classToSearchFrom), criteria);
153            return new QueryByMtoNCriteria(classToSearchFrom, indirectionTable, criteria);
154        }
155    
156        /**
157         * Factory method for QueryBySQL
158         * @param classToSearchFrom
159         * @param anSqlStatement
160         * @return QueryBySQL
161         */
162        public static QueryBySQL newQuery(Class classToSearchFrom, String anSqlStatement)
163        {
164            return new QueryBySQL(classToSearchFrom, anSqlStatement);
165        }
166    
167        /**
168         * Searches the class descriptor for the ojbConcrete class attribute
169         * if it finds the concrete class attribute, append a where clause which
170         * specifies we can load all classes that are this type or extents of this type.
171         * @param cld
172         * @return the extent classes
173         */
174        private static Collection getExtentClasses(ClassDescriptor cld)
175        {
176            /**
177             * 1. check if this class has a ojbConcreteClass attribute
178             */
179            FieldDescriptor fd = cld.getFieldDescriptorByName(ClassDescriptor.OJB_CONCRETE_CLASS);
180            Collection classes = new HashSet();  // use same class only once
181            if (fd != null)
182            {
183                classes.add(cld.getClassOfObject().getName());
184            }
185    
186            /**
187             * 2. if this class has extents/is an extent search for all extents
188             */
189            if (cld.isExtent())
190            {
191                Vector extentClasses = cld.getExtentClasses();
192    
193                /**
194                 * 3. get all extents for this class
195                 */
196                for (int i = 0; i < extentClasses.size(); i++)
197                {
198                    Class ec = (Class) extentClasses.get(i);
199                    ClassDescriptor extCld = cld.getRepository().getDescriptorFor(ec);
200                    classes.addAll(getExtentClasses(extCld));
201                }
202            }
203    
204            return classes;
205        }
206    
207        /**
208         * Searches the class descriptor for the ojbConcrete class attribute
209         * if it finds the concrete class attribute, append a where clause which
210         * specifies we can load all classes that are this type or extents of this type.
211         * @param cld
212         * @param crit
213         * @return the passed in Criteria object + optionally and'ed criteria with OR'd class
214         * type discriminators.
215         */
216        private static Criteria addCriteriaForOjbConcreteClasses(ClassDescriptor cld, Criteria crit)
217        {
218            /**
219             * 1. check if this class has a ojbConcreteClass attribute
220             */
221            Criteria concreteClassDiscriminator = null;
222            Collection classes = getExtentClasses(cld);
223    
224            /**
225             * 1. create a new Criteria for objConcreteClass
226             */
227            if (!classes.isEmpty())
228            {
229                concreteClassDiscriminator = new Criteria();
230                if (classes.size() > 1)
231                {
232                    concreteClassDiscriminator = new Criteria();
233                    concreteClassDiscriminator.addIn(ClassDescriptor.OJB_CONCRETE_CLASS, classes);
234                }
235                else
236                {
237                    concreteClassDiscriminator.addEqualTo(ClassDescriptor.OJB_CONCRETE_CLASS, classes.toArray()[0]);
238                }
239            }
240    
241            /**
242             * 2. only add the AND (objConcreteClass = "some.class" OR....) if we've actually found concrete
243             * classes.
244             */
245            if (concreteClassDiscriminator != null)
246            {
247                /**
248                 * it's possible there is no criteria attached to the query, and in this
249                 * case we still have to add the IN/EqualTo criteria for the concrete class type
250                 * so check if the crit is null and then create a blank one if needed.
251                 */
252                if (crit == null)
253                {
254                    crit = new Criteria();
255                }
256     
257                crit.addAndCriteria(concreteClassDiscriminator);
258            }
259            /**
260             * will just return the passed in criteria if no OJB concrete class is attribute is found.
261             */
262            return crit;
263        }
264    
265    }