📄 bindvariable.java
字号:
/* * Copyright 2004-2005 Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.josql.expressions;import java.util.List;import java.util.Map;import java.util.HashMap;import com.gentlyweb.utils.Getter;import org.josql.Query;import org.josql.QueryExecutionException;import org.josql.QueryParseException;import org.josql.internal.Utilities;/** * This class represents a "bind variable" used within a SQL statement. * A bind variable can be either: * <ul> * <li>Named - a named bind variable is prefixed with ":", the value of the * variable is set in the {@link Query} object via: {@link Query#setVariable(String,Object)}. * The parser used (javacc generated) is a little picky about "reserved keywords" and as * such you cannot use the following as the names of bind variables: select, from, limit, * execute, on, all, results, where, having, order, by, group. If you <b>must</b> use one of * reserved names then suffix it with "$", i.e. "execute$", "limit$" and so on.</li> * <li>Anonymous - an anonymous bind variable is represented as "?". The value of the * variable is set in the {@link Query} object via: {@link Query#setVariable(int,Object)}. * Anonymous variables are assigned an internal integer index starting at 1 and they increase * by 1.</li> * <li>Special - there are 3 types of "special" bind variable: * <ul> * <li><b>:_currobj</b> - Indicates the current object within the execution scope.</li> * <li><b>:_allobjs</b> - Indicates the current set of objects within the execution scope.</li> * <li><b>:_query</b> - Indicates the Query object.</li> * <li><b>:_grpby</b> - Indicates the Group By object. If there are more than 1 group by * objects then :_grpby1 ... :_grpby2 ... :_grpbyX can be used to identify which * group by object you mean. Remember that the group by object only has meaning * if there is a group by statement.</li> * <li><b>:_grpbys</b> - Indicates the set of group bys, this will be a list of lists, one * list for each group by key, and each group by column will be in that list. * <li><b>:_groupby</b> - A synonym for <b>:_grpby</b>.</li> * <li><b>:_groupbys</b> - A synonym for <b>:_grpbys</b>.</li> * </ul> * <p> * Special bind variables are mainly used in function calls however they can also be used * just about anywhere. * </li> * </ul> * <h3>Examples</h3> * <pre> * SELECT :_query, * :_allobjs, * :_currobj * FROM java.lang.Object * * SELECT name * FROM java.io.File * WHERE length(:_currobj, name) > :length * AND length > avg(:_query,:_allobjs,length) * AND path LIKE '%' + ? * </pre> * <h3>Accessors</h3> * <p> * It is also possible for bind variables (including the special variables) to have accessors. * For example: * <pre> * SELECT :_query.variables * FROM java.lang.Object * </pre> * <p> * Would cause all the bind variables in the query to be returned. * Also, if the ? in the next query is an instance of <code>java.lang.String</code>. * <pre> * SELECT ?.length * FROM java.lang.Object * </pre> * <p> * Last Modified By: $Author: barrygently $<br /> * Last Modified On: $Date: 2004/12/20 16:22:43 $<br /> * Current Revision: $Revision: 1.2 $<br /> */public class BindVariable extends ValueExpression{ public static final String SPECIAL_NAME_PREFIX = "_"; private static Map SPECIAL_VAR_NAMES; static { BindVariable.SPECIAL_VAR_NAMES = new HashMap (); BindVariable.SPECIAL_VAR_NAMES.put (Query.QUERY_BIND_VAR_NAME, ""); BindVariable.SPECIAL_VAR_NAMES.put (Query.CURR_OBJ_VAR_NAME, ""); BindVariable.SPECIAL_VAR_NAMES.put (Query.ALL_OBJS_VAR_NAME, ""); BindVariable.SPECIAL_VAR_NAMES.put (Query.GRPBY_OBJ_VAR_NAME, ""); BindVariable.SPECIAL_VAR_NAMES.put (Query.GRPBY_OBJ_VAR_NAME_SYNONYM, ""); BindVariable.SPECIAL_VAR_NAMES.put (Query.PARENT_BIND_VAR_NAME, ""); } private String name = null; private Object val = null; private boolean anon = false; private String acc = null; private Getter get = null; private boolean groupByVar = false; private int groupByInd = 0; public boolean equals (Object o) { if (o == null) { return false; } if (!(o instanceof BindVariable)) { return false; } BindVariable b = (BindVariable) o; if ((b.getName () != null) && (this.name != null) && (b.getName ().equals (this.name)) ) { if ((this.acc != null) && (b.getAccessor () != null) && (b.getAccessor ().equals (this.acc)) ) { return true; } } return false; } public String getAccessor () { return this.acc; } public void setAccessor (String a) { this.acc = a; } /** * Get the expected return type. * The exact class returned here is dependent (obviously) on what the bind variable * represents. Wherever possible it attempts to get the most specific class for the * variable. It is generally better to set the variables prior to executing the: * {@link Query#parse(String)} method to ensure that the correct class is returned here. * * @param q The Query object. * @return The return type class or <code>java.lang.Object.class</code> if the class * cannot be determined. * @throws QueryParseException If the type cannot be determined. */ public Class getExpectedReturnType (Query q) throws QueryParseException { if (this.get != null) { return this.get.getType (); } if (this.val != null) { return this.val.getClass (); } return q.getVariableClass (this.name); } /** * Initialises this bind variable. * If the bind variable is "anonymous" then a name is gained for it from the * Query object. If there is a value then it is gained from the Query object and * cached. Also, if there is an accessor defined then it is inited where possible. * * @param q The Query object. * @throws QueryParseException If the bind variable cannot be inited. */ public void init (Query q) throws QueryParseException { if (this.anon) { this.name = q.getAnonymousBindVariableName (); } String n = this.name.toLowerCase (); if ((n.startsWith (Query.GRPBY_OBJ_VAR_NAME)) || (n.startsWith (Query.GRPBY_OBJ_VAR_NAME_SYNONYM)) ) { List grpBys = q.getGroupByColumns (); // This is a group by... check for a group by clause. if (grpBys == null) { throw new QueryParseException ("Use of special group by object bind variable: " + name + " is not valid when there are no GROUP BY clauses defined."); } String rest = ""; // Trim it down to see if there is a number. if (n.startsWith (Query.GRPBY_OBJ_VAR_NAME)) { rest = n.substring (Query.GRPBY_OBJ_VAR_NAME.length ()); } if (n.startsWith (Query.GRPBY_OBJ_VAR_NAME_SYNONYM)) { rest = n.substring (Query.GRPBY_OBJ_VAR_NAME_SYNONYM.length ()); } int grpbyind = 1; if (rest.length () > 0) { // Should be a number. try { grpbyind = Integer.parseInt (rest); } catch (Exception e) { throw new QueryParseException ("Special bind variable name: " + this.name + " is not valid, expected an integer number at end of name for indexing into GROUP BYs."); } if (grpbyind < 1) { throw new QueryParseException ("Special bind variable name: " + this.name + " is not valid, integer to index GROUP BYs must be a minimum of 1."); } if (grpbyind > (grpBys.size ())) { throw new QueryParseException ("Special bind variable name: " + this.name + " is not valid, integer references GROUP BY: " + grpbyind + " however there are only: " + grpBys.size () + " GROUP BYs defined."); } } this.groupByVar = true; this.groupByInd = grpbyind; } else { if (n.startsWith (BindVariable.SPECIAL_NAME_PREFIX)) { // Make sure it's valid. if (!BindVariable.SPECIAL_VAR_NAMES.containsKey (n)) { throw new QueryParseException ("Bind variable name: " + name + " is not valid, bind variable names starting with: " + BindVariable.SPECIAL_NAME_PREFIX + " are reserved, and must be one of: " + BindVariable.SPECIAL_VAR_NAMES.keySet ()); } } } // See if we already have this bind variable set... this.val = q.getVariable (this.name); // See if we have a "trailing" accessor. if ((this.val != null) && (this.acc != null) ) { this.initGetter (this.val); try { this.val = this.get.getValue (this.val); } catch (Exception e) { throw new QueryParseException ("Unable to get value from accessor: " + this.acc + " and class: " + this.val.getClass ().getName () + " from bind variable: " + this.name, e); } } // See if we can init the getter... there are times when it // is possible even if the bind variable isn't available yet. if ((this.acc != null) && (this.get == null) ) { // Not over keen on this method but it will do for now... // It precludes the init occurring if we are working on java.lang.Object // objects... but how many times will that happen? Class c = q.getVariableClass (this.name); if (!c.isInstance (new Object ())) { // Init the getter. this.initGetter (c); } } } public String getName () { return this.name; } public boolean isAnonymous () { return this.anon; } public void setAnonymous (boolean v) { this.anon = v; } public void setName (String name) { this.name = name; } private void initGetter (Object o) { // Get the class for the value. Class c = o.getClass (); this.initGetter (c); } private void initGetter (Class c) { this.get = new Getter (this.acc, c); } /** * Gets the value of this bind variable. * * @param o The current object. Note that this variable isn't used in this method. * @param q The Query object. * @return The value. * @throws QueryExecutionException If something goes wrong during the accessing * of the value. */ public Object getValue (Object o, Query q) throws QueryExecutionException { if (this.groupByVar) { o = q.getGroupByVariable (this.groupByInd); } else { o = q.getVariable (this.name); } if ((this.acc != null) && (this.get == null) && (o != null) ) { // Unable to get the accessor... this.initGetter (o); } if (this.get != null) { try { o = this.get.getValue (o); } catch (Exception e) { throw new QueryExecutionException ("Unable to get value for accessor: " + this.acc + ", class: " + this.get.getBaseClass ().getName () + " from bind variable: " + this.name, e); } } return o; } /** * Returns whether the value of this bind variable represents a <code>true</code> * value. See: {@link ArithmeticExpression#isTrue(Object,Query)} for details of how * the return value is determined. * * @param o The current object. Not used in this method. * @param q The Query object. * @return <code>true</code> if the bind variable evaluates to <code>true</code>. * @throws QueryExecutionException If a problem occurs during evaluation. */ public boolean isTrue (Object o, Query q) throws QueryExecutionException { o = this.getValue (o, q); if (o == null) { return false; } if (Utilities.isNumber (o)) { return Utilities.getDouble (o) > 0; } // Not null so return true... return true; } /** * Evaluates the value of this bind variable. This is just a thin-wrapper around: * {@link #getValue(Object,Query)}. * * @param o The current object, not used in this method. * @param q The Query object. * @return The value of this bind variable. * @throws QueryExecutionException If there is a problem getting the value. */ public Object evaluate (Object o, Query q) throws QueryExecutionException { return this.getValue (o, q); } /** * Returns a string version of this bind variable. * Returns in the form: ? | ":" [ "_" ] Name * * @return A string version of the bind variable. */ public String toString () { StringBuffer buf = new StringBuffer (); if (this.anon) { buf.append ("?"); } else { buf.append (":"); buf.append (this.name); } if (this.acc != null) { buf.append ("."); buf.append (this.acc); } if (this.isBracketed ()) { buf.insert (0, "("); buf.append (")"); } return buf.toString (); } /** * Will always return false since a bind variable cannot be fixed. * * @param q The Query object. * @return <code>false</code> always. */ public boolean hasFixedResult (Query q) { // A bind variable cannot have a fixed result. return false; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -