KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > loader > custom > SQLQueryParser


1 //$Id: SQLQueryParser.java,v 1.7 2005/07/03 19:42:43 maxcsaucdk Exp $
2
package org.hibernate.loader.custom;
3
4 import org.hibernate.QueryException;
5 import org.hibernate.hql.classic.ParserHelper;
6 import org.hibernate.persister.collection.SQLLoadableCollection;
7 import org.hibernate.persister.entity.SQLLoadable;
8 import org.hibernate.util.StringHelper;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.Arrays JavaDoc;
12 import java.util.HashMap JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Map JavaDoc;
15
16 /**
17  * @author Gavin King, Max Andersen
18  */

19 public class SQLQueryParser {
20
21     private final String JavaDoc sqlQuery;
22
23     private final Map JavaDoc entityPersisterByAlias;
24     private final String JavaDoc[] aliases;
25     private final String JavaDoc[] suffixes;
26     
27     private final SQLLoadableCollection[] collectionPersisters;
28     private final String JavaDoc[] collectionAliases;
29     private final String JavaDoc[] collectionSuffixes;
30
31     private int parameterCount = 0;
32     private final Map JavaDoc namedParameters = new HashMap JavaDoc();
33     private final Map JavaDoc returnByAlias;
34     
35     private long aliasesFound = 0;
36     public SQLQueryParser(String JavaDoc sqlQuery,
37                           Map JavaDoc alias2Persister,
38                           Map JavaDoc alias2Return,
39                           String JavaDoc[] aliases,
40                           String JavaDoc[] collectionAliases,
41                           SQLLoadableCollection[] collectionPersisters,
42                           String JavaDoc[] suffixes,
43                           String JavaDoc[] collectionSuffixes) {
44         this.sqlQuery = sqlQuery;
45         this.entityPersisterByAlias = alias2Persister;
46         this.returnByAlias = alias2Return; // TODO: maybe just fieldMaps ?
47
this.collectionAliases = collectionAliases;
48         this.collectionPersisters = collectionPersisters;
49         this.suffixes = suffixes;
50         this.aliases = aliases;
51         this.collectionSuffixes = collectionSuffixes;
52     }
53
54     private SQLLoadable getPersisterByResultAlias(String JavaDoc aliasName) {
55         return (SQLLoadable) entityPersisterByAlias.get(aliasName);
56     }
57     
58     private Map JavaDoc getPropertyResultByResultAlias(String JavaDoc aliasName) {
59         SQLQueryReturn sqr = (SQLQueryReturn) returnByAlias.get(aliasName);
60         return sqr.getPropertyResultsMap();
61     }
62     
63     private boolean isEntityAlias(String JavaDoc aliasName) {
64         return entityPersisterByAlias.containsKey(aliasName);
65     }
66
67     private int getPersisterIndex(String JavaDoc aliasName) {
68         for ( int i = 0; i < aliases.length; i++ ) {
69             if ( aliasName.equals( aliases[i] ) ) {
70                 return i;
71             }
72         }
73         return -1;
74     }
75
76     public String JavaDoc process() {
77         return substituteParams( substituteBrackets() );
78     }
79
80     // TODO: should "record" how many properties we have reffered to - and if we
81
// don't get'em'all we throw an exception! Way better than trial and error ;)
82
private String JavaDoc substituteBrackets() throws QueryException {
83
84         StringBuffer JavaDoc result = new StringBuffer JavaDoc( sqlQuery.length() + 20 );
85         int left, right;
86
87         // replace {....} with corresponding column aliases
88
for ( int curr = 0; curr < sqlQuery.length(); curr = right + 1 ) {
89             if ( ( left = sqlQuery.indexOf( '{', curr ) ) < 0 ) {
90                 // No additional open braces found in the string, append the
91
// rest of the string in its entirty and quit this loop
92
result.append( sqlQuery.substring( curr ) );
93                 break;
94             }
95
96             // apend everything up until the next encountered open brace
97
result.append( sqlQuery.substring( curr, left ) );
98
99             if ( ( right = sqlQuery.indexOf( '}', left + 1 ) ) < 0 ) {
100                 throw new QueryException( "Unmatched braces for alias path", sqlQuery );
101             }
102
103             String JavaDoc aliasPath = sqlQuery.substring( left + 1, right );
104             int firstDot = aliasPath.indexOf( '.' );
105             if ( firstDot == -1 ) {
106                 if ( isEntityAlias(aliasPath) ) {
107                     // it is a simple table alias {foo}
108
result.append(aliasPath);
109                     aliasesFound++;
110                 }
111                 else {
112                     // passing through anything we do not know to support jdbc escape sequences HB-898
113
result.append( '{' ).append(aliasPath).append( '}' );
114                 }
115             }
116             else {
117                 String JavaDoc aliasName = aliasPath.substring(0, firstDot);
118                 int collectionIndex = Arrays.binarySearch(collectionAliases, aliasName);
119                 boolean isCollection = collectionIndex>-1;
120                 boolean isEntity = isEntityAlias(aliasName);
121                 
122                 if (isCollection) {
123                     // The current alias is referencing the collection to be eagerly fetched
124
String JavaDoc propertyName = aliasPath.substring( firstDot + 1 );
125                     result.append(resolveCollectionProperties(aliasName,
126                                                 propertyName,
127                                                 getPropertyResultByResultAlias(aliasName),
128                                                 getPersisterByResultAlias(aliasName),
129                                                 collectionPersisters[collectionIndex],
130                                                 collectionSuffixes[collectionIndex],
131                                                 suffixes[getPersisterIndex(aliasName)]) );
132                     aliasesFound++;
133                 }
134                 else if (isEntity) {
135                     // it is a property reference {foo.bar}
136
String JavaDoc propertyName = aliasPath.substring( firstDot + 1 );
137                     result.append(resolveProperties(
138                             aliasName,
139                             propertyName,
140                             getPropertyResultByResultAlias(aliasName),
141                             getPersisterByResultAlias(aliasName), suffixes[getPersisterIndex(aliasName)] // TODO: guard getPersisterIndex
142
) );
143                     aliasesFound++;
144                 }
145                 
146                 if ( !isEntity && !isCollection ) {
147                     // passing through anything we do not know to support jdbc escape sequences HB-898
148
result.append( '{' ).append(aliasPath).append( '}' );
149                 }
150     
151             }
152         }
153
154         // Possibly handle :something parameters for the query ?
155

156         return result.toString();
157     }
158
159     private String JavaDoc resolveCollectionProperties(String JavaDoc aliasName,
160             String JavaDoc propertyName,
161             Map JavaDoc fieldResults, SQLLoadable elementPersister, SQLLoadableCollection currentPersister, String JavaDoc suffix, String JavaDoc persisterSuffix) {
162         
163         if ( "*".equals( propertyName ) ) {
164             if( !fieldResults.isEmpty() ) {
165                 throw new QueryException("Using return-propertys together with * syntax is not supported.");
166             }
167             
168             String JavaDoc selectFragment = currentPersister.selectFragment( aliasName, suffix );
169             aliasesFound++;
170             return new String JavaDoc(selectFragment
171                         + ", "
172                         + resolveProperties(aliasName, propertyName, fieldResults, elementPersister, persisterSuffix ) );
173         }
174         else if ( "element.*".equals( propertyName ) ) {
175             return resolveProperties(aliasName, "*", fieldResults, elementPersister, persisterSuffix);
176             
177         }
178         else {
179                     
180             String JavaDoc[] columnAliases;
181             
182 // Let return-propertys override whatever the persister has for aliases.
183
columnAliases = (String JavaDoc[]) fieldResults.get(propertyName);
184             if(columnAliases==null) {
185                 columnAliases = currentPersister.getCollectionPropertyColumnAliases( propertyName, suffix );
186             }
187             
188             if ( columnAliases == null || columnAliases.length == 0 ) {
189                 throw new QueryException( "No column name found for property [" +
190                         propertyName +
191                         "] for alias [" + aliasName + "]",
192                         sqlQuery );
193             }
194             if ( columnAliases.length != 1 ) {
195 // TODO: better error message since we actually support composites if names are explicitly listed.
196
throw new QueryException( "SQL queries only support properties mapped to a single column - property [" +
197                         propertyName +
198                         "] is mapped to " +
199                         columnAliases.length +
200                         " columns.",
201                         sqlQuery );
202             }
203             aliasesFound++;
204             return columnAliases[0];
205         
206         }
207     }
208     private String JavaDoc resolveProperties(String JavaDoc aliasName,
209                                    String JavaDoc propertyName,
210                                    Map JavaDoc fieldResults, SQLLoadable currentPersister, String JavaDoc suffix) {
211         /*int currentPersisterIndex = getPersisterIndex( aliasName );
212
213         if ( !aliasName.equals( aliases[currentPersisterIndex] ) ) {
214             throw new QueryException( "Alias [" +
215                     aliasName +
216                     "] does not correspond to return alias " +
217                     aliases[currentPersisterIndex],
218                     sqlQuery );
219         }*/

220
221         if ( "*".equals( propertyName ) ) {
222             if( !fieldResults.isEmpty() ) {
223                 throw new QueryException("Using return-propertys together with * syntax is not supported.");
224             }
225             aliasesFound++;
226             return currentPersister.selectFragment( aliasName, suffix ) ;
227         }
228         else {
229
230             String JavaDoc[] columnAliases;
231
232             // Let return-propertys override whatever the persister has for aliases.
233
columnAliases = (String JavaDoc[]) fieldResults.get(propertyName);
234             if(columnAliases==null) {
235                 columnAliases = currentPersister.getSubclassPropertyColumnAliases( propertyName, suffix );
236             }
237
238             if ( columnAliases == null || columnAliases.length == 0 ) {
239                 throw new QueryException( "No column name found for property [" +
240                         propertyName +
241                         "] for alias [" + aliasName + "]",
242                         sqlQuery );
243             }
244             if ( columnAliases.length != 1 ) {
245                 // TODO: better error message since we actually support composites if names are explicitly listed.
246
throw new QueryException( "SQL queries only support properties mapped to a single column - property [" +
247                         propertyName +
248                         "] is mapped to " +
249                         columnAliases.length +
250                         " columns.",
251                         sqlQuery );
252             }
253             aliasesFound++;
254             return columnAliases[0];
255         }
256     }
257
258
259     private String JavaDoc substituteParams(String JavaDoc sqlString) {
260
261         StringBuffer JavaDoc result = new StringBuffer JavaDoc( sqlString.length() );
262         int left, right;
263
264         // Replace :... with ? and record the parameter. Naively just replaces ALL occurences of :...
265
// including whatever is BEFORE FROM. Do not "fast-forward" to the first or last FROM as
266
// "weird" sql might have parameters in places we do not know of, right ? ;)
267
for ( int curr = 0; curr < sqlString.length(); curr = right + 1 ) {
268             if ( ( left = sqlString.indexOf( ParserHelper.HQL_VARIABLE_PREFIX, curr ) ) < 0 ) {
269                 result.append( sqlString.substring( curr ) );
270                 break;
271             }
272
273             result.append( sqlString.substring( curr, left ) );
274
275             // Find first place of a HQL_SEPERATOR char
276
right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, left + 1 );
277
278             // did we find a HQL_SEPERATOR ?
279
boolean foundSeperator = right > 0;
280             int chopLocation = -1;
281             if ( right < 0 ) {
282                 chopLocation = sqlString.length();
283             }
284             else {
285                 chopLocation = right;
286             }
287
288             String JavaDoc param = sqlString.substring( left + 1, chopLocation );
289             addNamedParameter( param );
290             result.append( "?" );
291             if ( foundSeperator ) {
292                 result.append( sqlString.charAt( right ) );
293             }
294             else {
295                 break;
296             }
297         }
298         return result.toString();
299     }
300
301     // NAMED PARAMETER SUPPORT, copy/pasted from QueryTranslator!
302
private void addNamedParameter(String JavaDoc name) {
303         Integer JavaDoc loc = new Integer JavaDoc( parameterCount++ );
304         Object JavaDoc o = namedParameters.get( name );
305         if ( o == null ) {
306             namedParameters.put( name, loc );
307         }
308         else if ( o instanceof Integer JavaDoc ) {
309             ArrayList JavaDoc list = new ArrayList JavaDoc( 4 );
310             list.add( o );
311             list.add( loc );
312             namedParameters.put( name, list );
313         }
314         else {
315             ( ( List JavaDoc ) o ).add( loc );
316         }
317     }
318
319     public Map JavaDoc getNamedParameters() {
320         return namedParameters;
321     }
322
323     public boolean queryHasAliases() {
324         return aliasesFound>0;
325     }
326
327 }
328
Popular Tags