001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019:
020: package org.apache.beehive.controls.system.jdbc.parser;
021:
022: import org.apache.beehive.controls.api.context.ControlBeanContext;
023: import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
024:
025: import java.lang.reflect.Method;
026: import java.util.ArrayList;
027: import java.util.Arrays;
028:
029: /**
030: * Represents a fragement from the SQL annotation's statement member which begins with '{sql:'.
031: * Substitution fragements are unique in that they are fully evaluated BEFORE a PreparedStatement
032: * is generated.
033: * <p/>
034: * Supported 'sql:' escapes are subst and fn. subst is the default mode, and will be used if 'sql: '
035: * is specified.
036: *
037: * The <tt>fn</tt> variant of this construct has a very ridgid syntax at this point. It must conform to:
038: *
039: * <pre>
040: * {sql:fn in(x,{y})}
041: * </pre>
042: *
043: * where the '{y}' could also be some literal term.
044: */
045: public final class SqlSubstitutionFragment extends SqlFragmentContainer {
046:
047: private boolean _hasParamValue = false;
048:
049: /**
050: * Constructor for subst or function with no param substitution
051: *
052: * @param child An child which is contained in this fragment.
053: */
054: protected SqlSubstitutionFragment(SqlFragment child) {
055: super ();
056: addChild(child);
057: }
058:
059: /**
060: * Constructor for a function which includes a ReflectionFragment
061: *
062: * @param lf A LiteralFragment which contains the text up to the parameter substitution.
063: * @param rf The ReflectionFragment containing the parameter substitution
064: * @param lff A LiteralFragment which contains any text which occures after the parameter substitution.
065: */
066: protected SqlSubstitutionFragment(LiteralFragment lf,
067: ReflectionFragment rf, LiteralFragment lff) {
068: super ();
069: addChild(lf);
070: addChild(rf);
071: addChild(lff);
072: }
073:
074: /**
075: * Always true for this fragment type
076: * @return true
077: */
078: protected boolean isDynamicFragment() {
079: return true;
080: }
081:
082: /**
083: * Will be true for this fragment type only if one of its children contains
084: * a complex sql fragment.
085: * @return true if there are param values which need to be retrieved.
086: */
087: protected boolean hasParamValue() {
088: return _hasParamValue;
089: }
090:
091: /**
092: * Get the parameter values from this fragment and its children. An SqlSubstitutionFragment
093: * only contains parameters if one of its children has a complex value type.
094: *
095: * @param context A ControlBeanContext instance
096: * @param m The annotated method
097: * @param args The method parameters
098: * @return Array of objects.
099: */
100: protected Object[] getParameterValues(ControlBeanContext context,
101: Method m, Object[] args) {
102: ArrayList<Object> paramValues = new ArrayList<Object>();
103: for (SqlFragment frag : _children) {
104: if (frag.hasComplexValue(context, m, args)) {
105: paramValues.addAll(Arrays.asList(frag
106: .getParameterValues(context, m, args)));
107: }
108: }
109: return paramValues.toArray();
110: }
111:
112: /**
113: * Return the text for a PreparedStatement from this fragment. This type of fragment
114: * typically evaluates any reflection parameters at this point. The exception
115: * is if the reflected result is a ComplexSqlFragment, it that case the sql text
116: * is retrieved from the fragment in this method. The parameter values are
117: * retrieved in the getParameterValues method of this class.
118: *
119: * @param context A ControlBeanContext instance
120: * @param m The annotated method
121: * @param args The method parameters
122: * @return A String containing the value of this fragment and its children
123: */
124: protected String getPreparedStatementText(
125: ControlBeanContext context, Method m, Object[] args) {
126:
127: StringBuilder sb = new StringBuilder();
128: for (SqlFragment frag : _children) {
129:
130: boolean complexFragment = frag.hasComplexValue(context, m,
131: args);
132: if (frag.hasParamValue() && !complexFragment) {
133: Object[] pValues = frag.getParameterValues(context, m,
134: args);
135: for (Object o : pValues) {
136: sb.append(processSqlParams(o));
137: }
138: } else {
139: _hasParamValue |= complexFragment;
140: sb.append(frag.getPreparedStatementText(context, m,
141: args));
142: }
143: }
144: return sb.toString();
145: }
146:
147: // ////////////////////////////////////////////// Private Methods //////////////////////////////////////////////
148:
149: /**
150: * Check for the cases of a null or array type param value. If array type build a string of the array values
151: * seperated by commas.
152: *
153: * @param value
154: * @return String containing value.
155: */
156: private String processSqlParams(Object value) {
157:
158: Object[] arr = null;
159: if (value != null) {
160: arr = TypeMappingsFactory.toObjectArray(value);
161: }
162:
163: if (value == null || (arr != null && arr.length == 0)) {
164: return "";
165: } else if (arr != null) {
166: StringBuilder result = new StringBuilder();
167: for (int i = 0; i < arr.length; i++) {
168: if (i > 0) {
169: result.append(',');
170: result.append(arr[i].toString());
171: } else {
172: result.append(arr[i].toString());
173: }
174: }
175: return result.toString();
176: } else {
177: return value.toString();
178: }
179: }
180: }
|