KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > jdbc > EJBQLTranslationContext


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.access.jdbc;
20
21 import java.util.HashMap JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.cayenne.ejbql.EJBQLCompiledExpression;
25 import org.apache.cayenne.ejbql.EJBQLException;
26 import org.apache.cayenne.query.SQLResultSetMapping;
27 import org.apache.cayenne.query.SQLTemplate;
28
29 /**
30  * A context used for translating of EJBQL to SQL.
31  *
32  * @since 3.0
33  * @author Andrus Adamchik
34  */

35 class EJBQLTranslationContext {
36
37     private Map JavaDoc tableAliases;
38     private Map JavaDoc boundParameters;
39     private StringBuffer JavaDoc mainBuffer;
40     private StringBuffer JavaDoc currentBuffer;
41     private EJBQLCompiledExpression compiledExpression;
42     private Map JavaDoc attributes;
43     private Map JavaDoc reusableJoins;
44     private Map JavaDoc parameters;
45     private int columnAliasPosition;
46
47     EJBQLTranslationContext(EJBQLCompiledExpression compiledExpression, Map JavaDoc parameters) {
48         this.compiledExpression = compiledExpression;
49         this.mainBuffer = new StringBuffer JavaDoc();
50         this.currentBuffer = mainBuffer;
51         this.parameters = parameters;
52     }
53
54     SQLTemplate getQuery() {
55         String JavaDoc sql = mainBuffer.length() > 0 ? mainBuffer.toString() : null;
56         SQLTemplate query = new SQLTemplate(compiledExpression
57                 .getRootDescriptor()
58                 .getObjectClass(), sql);
59         query.setParameters(boundParameters);
60         return query;
61     }
62
63     /**
64      * Inserts a marker in the SQL, mapped to a StringBuffer that can be later filled with
65      * content.
66      */

67     void markCurrentPosition(String JavaDoc marker) {
68         // ensure buffer is created for the marker
69
findOrCreateMarkedBuffer(marker);
70
71         String JavaDoc internalMarker = (String JavaDoc) getAttribute(marker);
72
73         // make sure we mark the main buffer
74
StringBuffer JavaDoc current = this.currentBuffer;
75
76         try {
77             switchToMainBuffer();
78             append("${").append(internalMarker).append("}");
79         }
80         finally {
81             this.currentBuffer = current;
82         }
83     }
84
85     /**
86      * Switches the current buffer to a marked buffer. Note that this can be done even
87      * before the marker is inserted in the main buffer.
88      */

89     void switchToMarker(String JavaDoc marker) {
90         this.currentBuffer = (StringBuffer JavaDoc) findOrCreateMarkedBuffer(marker);
91     }
92
93     void switchToMainBuffer() {
94         this.currentBuffer = this.mainBuffer;
95     }
96
97     private StringBuffer JavaDoc findOrCreateMarkedBuffer(String JavaDoc marker) {
98         StringBuffer JavaDoc buffer;
99
100         String JavaDoc internalMarker = (String JavaDoc) getAttribute(marker);
101         if (internalMarker == null) {
102             buffer = new StringBuffer JavaDoc();
103             internalMarker = bindParameter(buffer, "marker");
104
105             // register mapping of internal to external marker
106
setAttribute(marker, internalMarker);
107         }
108         else {
109             Object JavaDoc object = boundParameters.get(internalMarker);
110             if (!(object instanceof StringBuffer JavaDoc)) {
111                 throw new IllegalArgumentException JavaDoc(
112                         "Invalid or missing buffer for marker: " + marker);
113             }
114
115             buffer = (StringBuffer JavaDoc) object;
116         }
117
118         return buffer;
119     }
120
121     /**
122      * Returns a context "attribute" stored for the given name. Attributes is a state
123      * preservation mechanism used by translators and have the same scope as the context.
124      */

125     Object JavaDoc getAttribute(String JavaDoc name) {
126         return attributes != null ? attributes.get(name) : null;
127     }
128
129     /**
130      * Sets a context "attribute". Attributes is a state preservation mechanism used by
131      * translators and have the same scope as the context.
132      */

133     void setAttribute(String JavaDoc var, Object JavaDoc value) {
134         if (attributes == null) {
135             attributes = new HashMap JavaDoc();
136         }
137
138         attributes.put(var, value);
139     }
140
141     /**
142      * Appends a piece of SQL to the internal buffer.
143      */

144     EJBQLTranslationContext append(String JavaDoc chunk) {
145         currentBuffer.append(chunk);
146         return this;
147     }
148
149     /**
150      * Appends a piece of SQL to the internal buffer.
151      */

152     EJBQLTranslationContext append(char chunk) {
153         currentBuffer.append(chunk);
154         return this;
155     }
156
157     /**
158      * Deletes a specified number of characters from the end of the current buffer.
159      */

160     EJBQLTranslationContext trim(int n) {
161         int len = currentBuffer.length();
162
163         if (len >= n) {
164             currentBuffer.delete(len - n, len);
165         }
166         return this;
167     }
168
169     EJBQLCompiledExpression getCompiledExpression() {
170         return compiledExpression;
171     }
172
173     String JavaDoc bindPositionalParameter(int position) {
174         return bindParameter(parameters.get(new Integer JavaDoc(position)));
175     }
176
177     String JavaDoc bindNamedParameter(String JavaDoc name) {
178         return bindParameter(parameters.get(name));
179     }
180
181     /**
182      * Creates a new parameter variable, binding provided value to it.
183      */

184     String JavaDoc bindParameter(Object JavaDoc value) {
185         return bindParameter(value, "id");
186     }
187
188     void rebindParameter(String JavaDoc boundName, Object JavaDoc newValue) {
189         boundParameters.put(boundName, newValue);
190     }
191
192     /**
193      * Creates a new parameter variable with the specified prefix, binding provided value
194      * to it.
195      */

196     String JavaDoc bindParameter(Object JavaDoc value, String JavaDoc prefix) {
197         if (boundParameters == null) {
198             boundParameters = new HashMap JavaDoc();
199         }
200
201         String JavaDoc var = prefix + boundParameters.size();
202         boundParameters.put(var, value);
203         return var;
204     }
205
206     Object JavaDoc getBoundParameter(String JavaDoc name) {
207         return boundParameters != null ? boundParameters.get(name) : null;
208     }
209
210     /**
211      * Registers a "reusable" join, returning a preexisting ID if the join is already
212      * registered. Reusable joins are the implicit inner joins that are added as a result
213      * of processing of path expressions in SELECT or WHERE clauses. Note that if an
214      * implicit INNER join overlaps with an explicit INNER join, both joins are added to
215      * the query.
216      */

217     String JavaDoc registerReusableJoin(String JavaDoc sourceIdPath, String JavaDoc relationship, String JavaDoc targetId) {
218         if (reusableJoins == null) {
219             reusableJoins = new HashMap JavaDoc();
220         }
221
222         String JavaDoc key = sourceIdPath + ":" + relationship;
223
224         String JavaDoc oldId = (String JavaDoc) reusableJoins.put(key, targetId);
225         if (oldId != null) {
226             // revert back to old id
227
reusableJoins.put(key, oldId);
228             return oldId;
229         }
230
231         return null;
232     }
233
234     /**
235      * Retrieves a SQL alias for the combination of EJBQL id variable and a table name. If
236      * such alias hasn't been used, it is created on the fly.
237      */

238     String JavaDoc getTableAlias(String JavaDoc idPath, String JavaDoc tableName) {
239
240         StringBuffer JavaDoc keyBuffer = new StringBuffer JavaDoc();
241
242         // per JPA spec, 4.4.2, "Identification variables are case insensitive.", while
243
// relationship path is case-sensitive
244

245         int dot = idPath.indexOf('.');
246         if (dot > 0) {
247             keyBuffer.append(idPath.substring(0, dot).toLowerCase()).append(
248                     idPath.substring(dot));
249         }
250         else {
251             keyBuffer.append(idPath.toLowerCase());
252         }
253
254         String JavaDoc key = keyBuffer.append(':').append(tableName).toString();
255
256         String JavaDoc alias;
257
258         if (tableAliases != null) {
259             alias = (String JavaDoc) tableAliases.get(key);
260         }
261         else {
262             tableAliases = new HashMap JavaDoc();
263             alias = null;
264         }
265
266         if (alias == null) {
267             alias = "t" + tableAliases.size();
268             tableAliases.put(key, alias);
269         }
270
271         return alias;
272     }
273
274     /**
275      * Returns a positional column alias, incrementing position index on each call.
276      */

277     String JavaDoc nextColumnAlias() {
278
279         SQLResultSetMapping resultSetMapping = compiledExpression.getResultSetMapping();
280         if (resultSetMapping == null) {
281             throw new EJBQLException(
282                     "No result set mapping exists for expression, can't map column aliases");
283         }
284
285         return (String JavaDoc) resultSetMapping.getColumnResults().get(columnAliasPosition++);
286     }
287 }
288
Popular Tags