KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > query > SqlStruct


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.query;
13
14 import com.versant.core.util.CharBuf;
15 import com.versant.core.common.Debug;
16 import com.versant.core.common.OID;
17 import com.versant.core.common.Utils;
18 import com.versant.core.metadata.ModelMetaData;
19 import com.versant.core.metadata.ClassMetaData;
20 import com.versant.core.metadata.MDStaticUtils;
21 import com.versant.core.jdbc.sql.SqlDriver;
22 import com.versant.core.jdbc.metadata.JdbcColumn;
23 import com.versant.core.jdbc.metadata.JdbcField;
24 import com.versant.core.jdbc.metadata.JdbcClass;
25 import com.versant.core.jdbc.JdbcUtils;
26 import com.versant.core.jdbc.JdbcOID;
27
28 import java.io.Serializable JavaDoc;
29 import java.sql.PreparedStatement JavaDoc;
30 import java.sql.SQLException JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Iterator JavaDoc;
33
34 import com.versant.core.common.BindingSupportImpl;
35
36 /**
37  * A struct representing sql for a query.
38  */

39 public class SqlStruct {
40
41     private static final char[] COUNT_STAR_PRE = "COUNT(".toCharArray();
42     private static final char[] COUNT_STAR_POST = ")".toCharArray();
43     private static final char[] COUNT_STAR_PRE_DISTINCT = "COUNT(DISTINCT(".toCharArray();
44     private static final char[] COUNT_STAR_POST_DISTINCT = "))".toCharArray();
45     private static final char[] IS_NULL = "is null".toCharArray();
46     private static final char[] IS_NOT_NULL = "is not null".toCharArray();
47
48     public String JavaDoc jdoqlFilter;
49
50     /**
51      * The SQL to execute the query. It is kept in a CharBuf as
52      * it may need to be modified before each execution of the query depending
53      * on which parameters are null.
54      */

55     private CharBuf sqlbuf;
56     /**
57      * Is the query a 'select distinct'?
58      */

59     private boolean distinct;
60     /**
61      * Index of the first character in the select list.
62      */

63     private int selectListStartIndex;
64     /**
65      * Number of characters in the select list.
66      */

67     private int selectListLength;
68     /**
69      * Index of the first column in the select list.
70      */

71     private int selectFirstColStart;
72     /**
73      * Index of the first column in the select list.
74      */

75     private int selectFirstColLength;
76     /**
77      * Index of the start of the 'order by ..' clause or 0 if none.
78      */

79     private int orderByStartIndex;
80     /**
81      * Number of characters in the 'order by ..' clause.
82      */

83     private int orderByLength;
84     /**
85      * The first table or alias in the from clause for the query. This is
86      * required for some databases if the query needs to be converted into a
87      * 'select for update' query (e.g. postgres).
88      */

89     private String JavaDoc firstTableOrAlias;
90     /**
91      * The sql is cached here in string form.
92      */

93     private transient String JavaDoc sql;
94     /**
95      * Is the current sqlbuf 'select for update'?
96      */

97     private boolean sqlForUpdate;
98     /**
99      * What was the original size of the sqlbuf before any modifications for
100      * 'select for update'?
101      */

102     private int sqlbufNotForUpdateSize;
103     /**
104      * Is the current sqlbuf 'select count(*)'?
105      */

106     private boolean sqlForCount;
107     /**
108      * Store for the select list when query is converted to count(*).
109      */

110     private char[] originalSelectList;
111     /**
112      * Store for the first column of the select list when query is converted to
113      * count(*).
114      */

115     private char[] originalSelectListFirstColumn;
116     /**
117      * Store for the order by clause when query is converted to count(*).
118      */

119     private char[] originalOrderByClause;
120     /**
121      * Params in the order that they appear in the SQL string. Each declared
122      * parameter may have several entries in this array if it was used more
123      * than once in the original query.
124      */

125     private Param paramList;
126     /**
127      * Characters used to convert a param into 'is null'.
128      */

129     private char[] isNullChars;
130     /**
131      * Characters used to convert a param into 'is not null'.
132      */

133     private char[] isNotNullChars;
134     /**
135      * Is this an aggregate query.
136      */

137     private boolean aggregate;
138
139     public SqlStruct() {
140         sqlbuf = new CharBuf(256);
141     }
142
143     public synchronized SqlStruct getClone() {
144         SqlStruct clone = new SqlStruct();
145         clone.jdoqlFilter = jdoqlFilter;
146         clone.sqlbuf = new CharBuf(sqlbuf);
147         clone.distinct = distinct;
148         clone.selectListStartIndex = selectListStartIndex;
149         clone.selectListLength = selectListLength;
150         clone.orderByStartIndex = orderByStartIndex;
151         clone.orderByLength = orderByLength;
152         clone.firstTableOrAlias = firstTableOrAlias;
153         clone.sql = sql;
154         clone.sqlForUpdate = sqlForUpdate;
155         clone.sqlbufNotForUpdateSize = sqlbufNotForUpdateSize;
156         clone.sqlForCount = sqlForCount;
157         clone.originalSelectList = originalSelectList;
158         clone.originalOrderByClause = originalOrderByClause;
159         clone.paramList = (paramList == null ? null : paramList.getClone());
160
161         if (isNullChars != null) {
162             clone.isNullChars = new char[isNullChars.length];
163             for (int i = 0; i < isNullChars.length; i++) {
164                 clone.isNullChars[i] = isNullChars[i];
165             }
166         }
167
168         if (isNotNullChars != null) {
169             clone.isNotNullChars = new char[isNotNullChars.length];
170             for (int i = 0; i < isNotNullChars.length; i++) {
171                 clone.isNotNullChars[i] = isNotNullChars[i];
172             }
173         }
174         return clone;
175     }
176
177     public boolean isAggregate() {
178         return aggregate;
179     }
180
181     public void setAggregate(boolean aggregate) {
182         this.aggregate = aggregate;
183     }
184
185     /**
186      * Set the range of characters in our buffer that contain all the columns
187      * in the select list.
188      * @param start Index of the first character in the select list (after
189      * 'SELECT ' or 'SELECT DISTINCT ')
190      * @param firstColEnd Index of the last character in the first column + 1
191      * @param end Index of the last character in the list + 1
192      */

193     public void setSelectListRange(boolean distinct, int start, int firstColEnd,
194             int end) {
195         this.distinct = distinct;
196         if (distinct) {
197             selectListStartIndex = start - 9; // "DISTINCT ".length()
198
} else {
199             selectListStartIndex = start;
200         }
201         selectFirstColStart = start;
202         selectListLength = end - selectListStartIndex;
203         selectFirstColLength = firstColEnd - selectFirstColStart;
204     }
205
206     /**
207      * Set the range of characters in our buffer that contain order by clause
208      * including the 'order by' keywords. The end index is exclusive.
209      */

210     public void setOrderByRange(int start, int end) {
211         orderByStartIndex = start;
212         orderByLength = end - start;
213     }
214
215     public CharBuf getSqlbuf() {
216         return sqlbuf;
217     }
218
219     public boolean isDistinct() {
220         return distinct;
221     }
222
223     public String JavaDoc getFirstTableOrAlias() {
224         return firstTableOrAlias;
225     }
226
227     public void setFirstTableOrAlias(String JavaDoc firstTableOrAlias) {
228         this.firstTableOrAlias = firstTableOrAlias;
229     }
230
231     public String JavaDoc getSql() {
232         if (sql == null) sql = sqlbuf.toString();
233         return sql;
234     }
235
236     public boolean isSqlForUpdate() {
237         return sqlForUpdate;
238     }
239
240     public int getSqlbufNotForUpdateSize() {
241         return sqlbufNotForUpdateSize;
242     }
243
244     public boolean isSqlForCount() {
245         return sqlForCount;
246     }
247
248     public char[] getOriginalSelectList() {
249         return originalSelectList;
250     }
251
252     public char[] getOriginalOrderByClause() {
253         return originalOrderByClause;
254     }
255
256     public Param getParamList() {
257         return paramList;
258     }
259
260     public void setParamList(Param paramList) {
261         this.paramList = paramList;
262         if (paramList != null) analyzeCharSpans();
263     }
264
265     private void analyzeCharSpans() {
266         int max = IS_NOT_NULL.length;
267         for (Param p = paramList; p != null; p = p.next) {
268             for (CharSpan s = p.charSpanList; s != null; s = s.next) {
269                 if (s.type == CharSpan.TYPE_REMOVE) continue;
270                 int len = s.lastCharIndex - s.firstCharIndex;
271                 if (len > max) max = len;
272             }
273         }
274         if (max > 0) {
275             isNullChars = new char[max];
276             copyAndPad(IS_NULL, isNullChars, max);
277             isNotNullChars = new char[max];
278             copyAndPad(IS_NOT_NULL, isNotNullChars, max);
279         }
280     }
281
282     /**
283      * This must create space in the charBuf at the index.
284      */

285     public void createSpace(CharBuf charBuf, int index, int amount) {
286         sql = null;
287         for (SqlStruct.Param p = paramList; p != null; p = p.next) {
288             if (p.firstCharIndex > index) {
289                 //must update
290
p.firstCharIndex += amount;
291                 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) {
292                     cs.firstCharIndex += amount;
293                     cs.lastCharIndex += amount;
294                 }
295             }
296         }
297
298         if (orderByStartIndex > index) {
299             orderByStartIndex += amount;
300         }
301     }
302
303     /**
304      * This must create space in the charBuf at the index.
305      */

306     public void removeSpace(CharBuf charBuf, int index, int amount) {
307         sql = null;
308         for (SqlStruct.Param p = paramList; p != null; p = p.next) {
309             if (p.firstCharIndex > index) {
310                 //must update
311
p.firstCharIndex -= amount;
312                 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) {
313                     cs.firstCharIndex -= amount;
314                     cs.lastCharIndex -= amount;
315                 }
316             }
317         }
318
319         if (orderByStartIndex > index) {
320             orderByStartIndex -= amount;
321         }
322     }
323
324     /**
325      * Update all our Param's for the null/not null state of their parameters
326      * and for 'select for update' or not. This may change the SQL query
327      * string.
328      */

329     public synchronized void updateSql(SqlDriver driver, Object JavaDoc[] params, boolean forUpdate,
330                           boolean forCount) {
331         boolean changed = false;
332         if (params != null) {
333             int paramIndex = 0;
334             for (SqlStruct.Param p = paramList; p != null; p = p.next, paramIndex++) {
335                 if (params[p.declaredParamIndex] instanceof Collection JavaDoc) {
336                     Collection JavaDoc col = (Collection JavaDoc) params[p.declaredParamIndex];
337                     int n = col.size();
338                     if (n == 0) {
339                         throw BindingSupportImpl.getInstance().invalidOperation(
340                                 "The supplied collection param at index "
341                                 + paramIndex + " may not be empty");
342                     }
343                     if (p.inListParamCount == 0) {
344                         //this is a readOnly char[]. it may not be modified
345
final char[] charsToInsert = driver.getSqlParamStringChars(p.jdbcType);
346                         int toInsert = (n == 1 ? charsToInsert.length : (n * charsToInsert.length) + (n - 1));
347                         char[] chars = new char[toInsert];
348
349                         int offset = 0;
350                         for (int i = 0; i < n; i++) {
351                             if (offset > 0) {
352                                 chars[offset++] = ',';
353                             }
354                             for (int j = 0; j < charsToInsert.length; j++) {
355                                 chars[j + offset] = charsToInsert[j];
356                             }
357                             offset += charsToInsert.length;
358                         }
359                         createSpace(sqlbuf, p.firstCharIndex, toInsert);
360                         sqlbuf.insert(p.firstCharIndex, chars);
361                         p.charLength = chars.length;
362                         p.inListParamCount = n;
363                         changed = true;
364                     } else if (p.inListParamCount < n) {
365                         //must insert more param's
366
int insertPoint = p.charLength + p.firstCharIndex;
367                         //the diff between the required number and what is already there
368
int paramsToInsert = n - p.inListParamCount;
369                         final char[] charStamp = driver.getSqlParamStringChars(p.jdbcType);
370                         int charsToInsert = (charStamp.length * paramsToInsert) + paramsToInsert;
371                         char[] chars = new char[charsToInsert];
372                         int offset = 0;
373                         for (int i = 0; i < paramsToInsert; i++) {
374                             chars[offset++] = ',';
375                             for (int j = 0; j < charStamp.length; j++) {
376                                 chars[offset++] = charStamp[j];
377                             }
378                         }
379                         if (Debug.DEBUG) {
380                             if (offset != chars.length) {
381                                 throw BindingSupportImpl.getInstance().internal("");
382                             }
383                         }
384                         createSpace(sqlbuf, insertPoint, charsToInsert);
385                         sqlbuf.insert(insertPoint, chars);
386                         p.charLength += chars.length;
387                         p.inListParamCount = n;
388                         changed = true;
389                     } else if (p.inListParamCount > n) {
390                         //must remove some
391
changed = true;
392                         int removeStart = p.firstCharIndex;
393                         int paramToRemove = p.inListParamCount - n;
394
395                         int charsToRemove = driver.getSqlParamStringChars(p.jdbcType).length * paramToRemove + paramToRemove;
396                         int removeTo = charsToRemove + removeStart;
397                         sqlbuf.remove(removeStart, removeTo);
398                         removeSpace(sqlbuf, p.firstCharIndex, removeTo - removeStart);
399
400                         p.charLength -= (removeTo - removeStart);
401                         p.inListParamCount = n;
402                         changed = true;
403                     }
404
405                 }
406             }
407         }
408
409         if (params != null) {
410             for (SqlStruct.Param p = paramList; p != null; p = p.next) {
411                 if (p.update(this, params[p.declaredParamIndex] == null)) {
412                     changed = true;
413                 }
414             }
415         }
416         if (forUpdate != sqlForUpdate
417                 && (!distinct || driver.isSelectForUpdateWithDistinctOk())
418                 && (!aggregate || driver.isSelectForUpdateWithAggregateOk())) {
419             char[] a = driver.getSelectForUpdate();
420             if (a != null) {
421                 if (forUpdate) {
422                     sqlbufNotForUpdateSize = sqlbuf.size();
423                     sqlbuf.append(a);
424                     if (driver.isSelectForUpdateAppendTable()) {
425                         sqlbuf.append(firstTableOrAlias);
426                     }
427                 } else {
428                     sqlbuf.setSize(sqlbufNotForUpdateSize);
429                 }
430                 sqlForUpdate = forUpdate;
431                 changed = true;
432             }
433         }
434         if (forCount != sqlForCount) {
435             if (forCount) {
436                 if (originalSelectList == null) {
437                     originalSelectList = sqlbuf.toArray(selectListStartIndex,
438                             selectListLength);
439                     originalSelectListFirstColumn = sqlbuf.toArray(
440                             selectFirstColStart, selectFirstColLength);
441                 }
442                 int start;
443                 if (distinct) {
444                     sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE_DISTINCT);
445                     start = selectListStartIndex + COUNT_STAR_PRE_DISTINCT.length;
446                 } else {
447                     sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE);
448                     start = selectListStartIndex + COUNT_STAR_PRE.length;
449                 }
450                 sqlbuf.replace(start, originalSelectListFirstColumn);
451                 start += originalSelectListFirstColumn.length;
452                 if (distinct) {
453                     sqlbuf.replace(start, COUNT_STAR_POST_DISTINCT);
454                     start += COUNT_STAR_POST_DISTINCT.length;
455                 } else {
456                     sqlbuf.replace(start, COUNT_STAR_POST);
457                     start += COUNT_STAR_POST.length;
458                 }
459                 int n = (selectListStartIndex + selectListLength) - start;
460                 if (n > 0) sqlbuf.replace(start, start + n, ' ');
461                 if (orderByStartIndex > 0) {
462                     if (originalOrderByClause == null) {
463                         originalOrderByClause = sqlbuf.toArray(orderByStartIndex,
464                                 orderByLength);
465                     }
466                     sqlbuf.replace(orderByStartIndex,
467                             orderByStartIndex + orderByLength, ' ');
468                 }
469             } else {
470                 sqlbuf.replace(selectListStartIndex, originalSelectList);
471                 if (orderByStartIndex > 0) {
472                     sqlbuf.replace(orderByStartIndex, originalOrderByClause);
473                 }
474             }
475             sqlForCount = forCount;
476             changed = true;
477         }
478
479         if (changed) sql = null;
480     }
481
482     /**
483      * Set all the parameters for this query on ps. This is a NOP if params
484      * is null.
485      */

486     public synchronized void setParamsOnPS(ModelMetaData jmd, SqlDriver driver,
487             PreparedStatement JavaDoc ps, Object JavaDoc[] params, String JavaDoc sql) throws SQLException JavaDoc {
488         if (params == null) return;
489         int pos = 1;
490         SqlStruct.Param p = paramList;
491         Object JavaDoc value = null;
492         try {
493             for (; p != null; p = p.next) {
494                 value = params[p.declaredParamIndex];
495                 switch (p.mod) {
496                     case Param.MOD_NONE:
497                         break;
498                     case Param.MOD_APPEND_PERCENT:
499                         if (value != null) value = value + "%";
500                         break;
501                     case Param.MOD_PREPEND_PERCENT:
502                         if (value != null) value = "%" + value;
503                         break;
504                     default:
505                         throw BindingSupportImpl.getInstance().internal("Invalid mod: "
506                                 + p.mod);
507                 }
508                 if (value == null && p.requiresUpdate()) continue;
509                 int pci = p.classIndex;
510                 if (pci >= 0) {
511                     ClassMetaData pcmd = jmd.classes[pci];
512                     int pfno = p.fieldNo;
513                     if (pfno >= 0) {
514                         JdbcField f = ((JdbcClass)pcmd.storeClass).stateFields[pfno];
515                         if (value instanceof Collection JavaDoc) {
516                             Collection JavaDoc col = (Collection JavaDoc) value;
517                             for (Iterator JavaDoc iterator = col.iterator(); iterator.hasNext();) {
518                                 Object JavaDoc o = iterator.next();
519                                 if (o instanceof OID) {
520                                     pos = ((JdbcOID)o).setParams(ps, pos);
521                                 } else {
522                                     pos = f.setQueryParam(ps, pos, o);
523                                 }
524                             }
525                         } else {
526                             pos = f.setQueryParam(ps, pos, value);
527                         }
528                     } else { // this is an OID param for a link table
529
if (value != null) {
530                             pos = ((JdbcOID)value).setParams(ps, pos);
531                         } else {
532                             JdbcColumn[] pkcols = ((JdbcClass)pcmd.storeClass).table.pkSimpleCols;
533                             int nc = pkcols.length;
534                             for (int i = 0; i < nc; i++) {
535                                 ps.setNull(pos++, pkcols[i].jdbcType);
536                             }
537                         }
538                     }
539                 } else {
540                     if (p.col != null) {
541                         p.col.set(ps, pos++, value);
542                     } else {
543                         int javaTypeCode = p.javaTypeCode;
544                         if (javaTypeCode == 0 && value != null) {
545                             javaTypeCode = MDStaticUtils.toTypeCode(value.getClass());
546                         }
547                         JdbcUtils.set(ps, pos++, value, javaTypeCode, p.jdbcType);
548                     }
549                 }
550             }
551         } catch (Exception JavaDoc e) {
552             throw driver.mapException(e, "Error setting query parameter " +
553                 p.getIdentifier() + " = '" + Utils.toString(value) +
554                 "' at PreparedStatement index " + pos + " in\n" +
555                 JdbcUtils.getPreparedStatementInfo(sql, ps) + "\n" +
556                JdbcUtils.toString(e), false);
557         }
558     }
559
560     private static void copyAndPad(char[] src, char[] dest, int len) {
561         int n = src.length;
562         System.arraycopy(src, 0, dest, 0, n);
563         for (; n < len;) dest[n++] = ' ';
564     }
565
566     public char[] getNullChars() {
567         return isNullChars;
568     }
569
570     public void setNullChars(char[] nullChars) {
571         isNullChars = nullChars;
572     }
573
574     public char[] getNotNullChars() {
575         return isNotNullChars;
576     }
577
578     public void setNotNullChars(char[] notNullChars) {
579         isNotNullChars = notNullChars;
580     }
581
582     /**
583      * A parameter. This tells us the indexes of the first and last characters
584      * of this parameter and its index within the original list of declared
585      * parameters.
586      */

587     public final static class Param implements Serializable JavaDoc {
588
589         public static final int MOD_NONE = 0;
590         public static final int MOD_PREPEND_PERCENT = 1;
591         public static final int MOD_APPEND_PERCENT = 2;
592
593         /**
594          * An identifier for this parameter for error messages.
595          */

596         private String JavaDoc identifier;
597         /**
598          * The next Param in the list.
599          */

600         public Param next;
601         /**
602          * The index of this parameter in the original list of declared
603          * parameters for the query.
604          */

605         public int declaredParamIndex;
606         /**
607          * The index of the first character in sql for this Param.
608          * If charSpanList is not null then this is the same as there. It
609          * is used for sorting.
610          */

611         public transient int firstCharIndex;
612         /**
613          * The current amount of chars that the param occupies when used a in list
614          * for collection params
615          */

616         public transient int charLength;
617         /**
618          * The current amount params that this param can take is used as a collection
619          * param.
620          */

621         public transient int inListParamCount;
622         /**
623          * List of character ranges used by this Param. This info is used
624          * to turn '= ?' into 'is null' and so on. It is null if there is
625          * no need for any replacement (e.g. using Sybase).
626          */

627         public CharSpan charSpanList;
628         /**
629          * The classIndex of the class associated with this parameter or
630          * -1 if there is none (e.g. a parameter used in an expression).
631          * @see #fieldNo
632          */

633         public int classIndex;
634         /**
635          * The field number associated with this parameter or -1 if none.
636          * This is used with cls to locate its column(s). It is not used if
637          * the classIndex is -1.
638          * @see #classIndex
639          */

640         public int fieldNo;
641         /**
642          * The java type code of this parameter. This is only set if
643          * classIndex is -1 i.e. this parameter is not for a field.
644          */

645         public int javaTypeCode;
646         /**
647          * The JDBC type (from java.sql.Types) for this parameter. This is
648          * only set if classIndex is -1.
649          * @see java.sql.Types
650          */

651         public int jdbcType;
652         /**
653          * How must the parameter value be modified before being set? This is
654          * used for startsWith and endsWith for databases that do not allow
655          * expressions on the right hand side of a LIKE (e.g. Informix).
656          * @see #MOD_APPEND_PERCENT
657          */

658         public int mod;
659
660         public transient JdbcColumn col;
661
662         public Param(String JavaDoc identifier) {
663             this.identifier = identifier;
664         }
665
666         public String JavaDoc getIdentifier() {
667             return identifier;
668         }
669
670         /**
671          * Update all our CharSpan's for the null/not null state of the
672          * parameter. This is a NOP if the parameter does not require update.
673          * @return True if changes were made else false
674          * @see #requiresUpdate
675          */

676         public boolean update(SqlStruct q, boolean newParamIsNull) {
677             boolean ans = false;
678             for (CharSpan cs = charSpanList; cs != null; cs = cs.next) {
679                 if (cs.update(q, newParamIsNull)) ans = true;
680             }
681             return ans;
682         }
683
684         /**
685          * Does this need to be updated for null/not null parameter values?
686          * @see #update
687          */

688         public boolean requiresUpdate() {
689             return charSpanList != null;
690         }
691
692         public Param getClone() {
693             Param clone = new Param(identifier);
694             clone.next = (next == null ? null : next.getClone());
695             clone.declaredParamIndex = declaredParamIndex;
696             clone.firstCharIndex = firstCharIndex;
697             clone.charSpanList = (charSpanList == null ? null : charSpanList.getClone());
698             clone.classIndex = classIndex;
699             clone.fieldNo = fieldNo;
700             clone.javaTypeCode = javaTypeCode;
701             clone.jdbcType = jdbcType;
702             clone.mod = mod;
703             clone.col = col;
704
705             return clone;
706         }
707     }
708
709     /**
710      * This specifies a range of characters for a Param in our sql buffer. The
711      * lastCharIndex is the index of the character after the last character
712      * in the range.
713      */

714     public final static class CharSpan implements Serializable JavaDoc {
715
716         public static final int TYPE_NULL = 1;
717         public static final int TYPE_NOT_NULL = 2;
718         public static final int TYPE_REMOVE = 3;
719
720         /**
721          * What must be done to this span?
722          */

723         public int type;
724         /**
725          * The index of the first character in sql.
726          */

727         public int firstCharIndex;
728         /**
729          * The index of the character after the last character in the span.
730          */

731         public int lastCharIndex;
732         /**
733          * The next span in the list.
734          */

735         public CharSpan next;
736
737         /**
738          * The current state of the parameter in sql. If this is true then
739          * the parameter has been replaced with an 'is null' or 'is not null'.
740          */

741         private boolean paramIsNull;
742         /**
743          * The original text from the sql query. This is filled the first
744          * time the param is null and is used to restore the query when it
745          * is not null in future.
746          */

747         private char[] originalSql;
748
749         public CharSpan getClone() {
750             CharSpan clone = new CharSpan();
751             clone.type = type;
752             clone.firstCharIndex = firstCharIndex;
753             clone.lastCharIndex = lastCharIndex;
754             clone.next = (next == null ? null : next.getClone());
755             clone.paramIsNull = paramIsNull;
756             if (originalSql != null) {
757                 clone.originalSql = new char[originalSql.length];
758                 for (int i = 0; i < originalSql.length; i++) {
759                     clone.originalSql[i] = originalSql[i];
760                 }
761             }
762             return clone;
763         }
764
765         /**
766          * Update the query and our state if the newParamIsNull value differs
767          * from our paramIsNull field.
768          * @return True if changes were made else false
769          */

770         public boolean update(SqlStruct q, boolean newParamIsNull) {
771             if (newParamIsNull == paramIsNull) return false;
772             CharBuf sql = q.sqlbuf;
773             if (newParamIsNull) {
774                 if (originalSql == null) {
775                     originalSql = sql.toArray(firstCharIndex,
776                             lastCharIndex - firstCharIndex);
777                 }
778                 if (Debug.DEBUG) {
779                     System.out.println("*** CharSpan.update replacing '" +
780                             new String JavaDoc(originalSql) + "' " + originalSql.length +
781                             " " + q.isNullChars.length);
782                 }
783                 switch (type) {
784                     case TYPE_NULL:
785                         sql.replace(firstCharIndex, q.isNullChars);
786                         break;
787                     case TYPE_NOT_NULL:
788                         sql.replace(firstCharIndex, q.isNotNullChars);
789                         break;
790                     case TYPE_REMOVE:
791                         sql.replace(firstCharIndex, lastCharIndex, ' ');
792                         break;
793                     default:
794                         throw BindingSupportImpl.getInstance().internal(
795                                 "Unknown CharSpan type: " + type);
796                 }
797             } else {
798                 sql.replace(firstCharIndex, originalSql);
799             }
800             paramIsNull = newParamIsNull;
801             return true;
802         }
803     }
804
805 }
806
Popular Tags