KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > ejbql > parser > Compiler


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19 package org.apache.cayenne.ejbql.parser;
20
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
28 import org.apache.cayenne.ejbql.EJBQLCompiledExpression;
29 import org.apache.cayenne.ejbql.EJBQLException;
30 import org.apache.cayenne.ejbql.EJBQLExpression;
31 import org.apache.cayenne.ejbql.EJBQLExpressionVisitor;
32 import org.apache.cayenne.map.EntityResolver;
33 import org.apache.cayenne.map.ObjRelationship;
34 import org.apache.cayenne.query.SQLResultSetMapping;
35 import org.apache.cayenne.reflect.ArcProperty;
36 import org.apache.cayenne.reflect.ClassDescriptor;
37 import org.apache.cayenne.reflect.Property;
38
39 /**
40  * Produces an {@link EJBQLCompiledExpression} out of an EJBQL expression tree.
41  *
42  * @since 3.0
43  * @author Andrus Adamchik
44  */

45 class Compiler {
46
47     private String JavaDoc rootId;
48     private EntityResolver resolver;
49     private Map JavaDoc descriptorsById;
50     private Map JavaDoc incomingById;
51     private Collection JavaDoc paths;
52     private EJBQLExpressionVisitor fromItemVisitor;
53     private EJBQLExpressionVisitor joinVisitor;
54     private EJBQLExpressionVisitor pathVisitor;
55     private EJBQLExpressionVisitor rootDescriptorVisitor;
56     private SQLResultSetMapping resultSetMapping;
57
58     Compiler(EntityResolver resolver) {
59         this.resolver = resolver;
60         this.descriptorsById = new HashMap JavaDoc();
61         this.incomingById = new HashMap JavaDoc();
62
63         this.rootDescriptorVisitor = new SelectExpressionVisitor();
64         this.fromItemVisitor = new FromItemVisitor();
65         this.joinVisitor = new JoinVisitor();
66         this.pathVisitor = new PathVisitor();
67     }
68
69     CompiledExpression compile(String JavaDoc source, EJBQLExpression parsed) {
70         parsed.visit(new CompilationVisitor());
71
72         // postprocess paths, now that all id vars are resolved
73
if (paths != null) {
74             Iterator JavaDoc it = paths.iterator();
75             while (it.hasNext()) {
76                 EJBQLPath path = (EJBQLPath) it.next();
77                 String JavaDoc id = normalizeIdPath(path.getId());
78
79                 ClassDescriptor descriptor = (ClassDescriptor) descriptorsById.get(id);
80                 if (descriptor == null) {
81                     throw new EJBQLException("Unmapped id variable: " + id);
82                 }
83
84                 StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(id);
85
86                 for (int i = 1; i < path.getChildrenCount(); i++) {
87
88                     String JavaDoc pathChunk = path.getChild(i).getText();
89                     buffer.append('.').append(pathChunk);
90
91                     Property property = descriptor.getProperty(pathChunk);
92                     if (property instanceof ArcProperty) {
93                         ObjRelationship incoming = ((ArcProperty) property)
94                                 .getRelationship();
95                         descriptor = ((ArcProperty) property).getTargetDescriptor();
96                         String JavaDoc pathString = buffer.substring(0, buffer.length());
97
98                         descriptorsById.put(pathString, descriptor);
99                         incomingById.put(pathString, incoming);
100                     }
101                 }
102             }
103         }
104
105         CompiledExpression compiled = new CompiledExpression();
106         compiled.setExpression(parsed);
107         compiled.setSource(source);
108
109         compiled.setRootId(rootId);
110         compiled.setDescriptorsById(descriptorsById);
111         compiled.setIncomingById(incomingById);
112         compiled.setResultSetMapping(resultSetMapping);
113
114         return compiled;
115     }
116
117     private void addPath(EJBQLPath path) {
118         if (paths == null) {
119             paths = new ArrayList JavaDoc();
120         }
121
122         paths.add(path);
123     }
124
125     static String JavaDoc normalizeIdPath(String JavaDoc idPath) {
126
127         // per JPA spec, 4.4.2, "Identification variables are case insensitive."
128

129         int pathSeparator = idPath.indexOf('.');
130         return pathSeparator < 0 ? idPath.toLowerCase() : idPath.substring(
131                 0,
132                 pathSeparator).toLowerCase()
133                 + idPath.substring(pathSeparator);
134     }
135
136     class CompilationVisitor extends EJBQLBaseVisitor {
137
138         public boolean visitSelectExpression(EJBQLExpression expression) {
139             expression.visit(rootDescriptorVisitor);
140             return false;
141         }
142
143         public boolean visitFromItem(EJBQLFromItem expression) {
144             expression.visit(fromItemVisitor);
145             return false;
146         }
147
148         public boolean visitInnerFetchJoin(EJBQLJoin join) {
149             join.visit(joinVisitor);
150             return false;
151         }
152
153         public boolean visitInnerJoin(EJBQLJoin join) {
154             join.visit(joinVisitor);
155             return false;
156         }
157
158         public boolean visitOuterFetchJoin(EJBQLJoin join) {
159             join.visit(joinVisitor);
160             return false;
161         }
162
163         public boolean visitOuterJoin(EJBQLJoin join) {
164             join.visit(joinVisitor);
165             return false;
166         }
167
168         public boolean visitWhere(EJBQLExpression expression) {
169             expression.visit(pathVisitor);
170             return false;
171         }
172
173         public boolean visitOrderBy(EJBQLExpression expression) {
174             expression.visit(pathVisitor);
175             return false;
176         }
177     }
178
179     class FromItemVisitor extends EJBQLBaseVisitor {
180
181         private String JavaDoc entityName;
182
183         public boolean visitIdentificationVariable(EJBQLExpression expression) {
184             entityName = expression.getText();
185             return true;
186         }
187
188         public boolean visitIdentifier(EJBQLExpression expression) {
189
190             // per JPA spec, 4.4.2, "Identification variables are case insensitive."
191
String JavaDoc rootId = normalizeIdPath(expression.getText());
192
193             // resolve class descriptor
194
ClassDescriptor descriptor = resolver.getClassDescriptor(entityName);
195             if (descriptor == null) {
196                 throw new EJBQLException("Unmapped abstract schema name: " + entityName);
197             }
198
199             ClassDescriptor old = (ClassDescriptor) descriptorsById.put(
200                     rootId,
201                     descriptor);
202             if (old != null && old != descriptor) {
203                 throw new EJBQLException("Duplicate identification variable definition: "
204                         + rootId
205                         + ", it is already used for "
206                         + old.getEntity().getName());
207             }
208
209             // if root wasn't detected in the Select Clause, use the first id var as root
210
if (Compiler.this.rootId == null) {
211                 Compiler.this.rootId = rootId;
212             }
213
214             return true;
215         }
216     }
217
218     class JoinVisitor extends EJBQLBaseVisitor {
219
220         private String JavaDoc id;
221         private ObjRelationship incoming;
222         private ClassDescriptor descriptor;
223
224         public boolean visitPath(EJBQLPath expression, int finishedChildIndex) {
225             if (finishedChildIndex + 1 < expression.getChildrenCount()) {
226                 this.id = expression.getId();
227                 this.descriptor = (ClassDescriptor) descriptorsById.get(id);
228
229                 if (descriptor == null) {
230                     throw new EJBQLException("Unmapped id variable: " + id);
231                 }
232             }
233
234             return true;
235         }
236
237         public boolean visitIdentificationVariable(EJBQLExpression expression) {
238             Property property = descriptor.getProperty(expression.getText());
239             if (property instanceof ArcProperty) {
240                 incoming = ((ArcProperty) property).getRelationship();
241                 descriptor = ((ArcProperty) property).getTargetDescriptor();
242             }
243             else {
244                 throw new EJBQLException("Incorrect relationship path: "
245                         + expression.getText());
246             }
247
248             return true;
249         }
250
251         public boolean visitIdentifier(EJBQLExpression expression) {
252             if (incoming != null) {
253
254                 String JavaDoc aliasId = expression.getText();
255
256                 // map id variable to class descriptor
257
ClassDescriptor old = (ClassDescriptor) descriptorsById.put(
258                         aliasId,
259                         descriptor);
260                 if (old != null && old != descriptor) {
261                     throw new EJBQLException(
262                             "Duplicate identification variable definition: "
263                                     + aliasId
264                                     + ", it is already used for "
265                                     + old.getEntity().getName());
266                 }
267
268                 incomingById.put(aliasId, incoming);
269
270                 id = null;
271                 descriptor = null;
272                 incoming = null;
273             }
274
275             return true;
276         }
277     }
278
279     class PathVisitor extends EJBQLBaseVisitor {
280
281         public boolean visitPath(EJBQLPath expression, int finishedChildIndex) {
282             addPath(expression);
283             return false;
284         }
285     }
286
287     class SelectExpressionVisitor extends EJBQLBaseVisitor {
288
289         public boolean visitIdentifier(EJBQLExpression expression) {
290             rootId = normalizeIdPath(expression.getText());
291             return false;
292         }
293
294         public boolean visitAggregate(EJBQLExpression expression) {
295             addResultSetColumn();
296             return false;
297         }
298
299         public boolean visitPath(EJBQLPath expression, int finishedChildIndex) {
300             addPath(expression);
301             addResultSetColumn();
302             return false;
303         }
304
305         private void addResultSetColumn() {
306             if (resultSetMapping == null) {
307                 resultSetMapping = new SQLResultSetMapping();
308             }
309
310             String JavaDoc column = "sc" + resultSetMapping.getColumnResults().size();
311             resultSetMapping.addColumnResult(column);
312         }
313     }
314 }
Popular Tags