001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.NewInvocationNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.services.loader.ClassInspector;
025:
026: import org.apache.derby.iapi.services.context.ContextManager;
027:
028: import org.apache.derby.iapi.services.compiler.MethodBuilder;
029: import org.apache.derby.iapi.services.compiler.LocalField;
030:
031: import org.apache.derby.iapi.services.sanity.SanityManager;
032:
033: import org.apache.derby.iapi.error.StandardException;
034: import org.apache.derby.iapi.services.i18n.MessageService;
035:
036: import org.apache.derby.iapi.sql.compile.CompilerContext;
037:
038: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
039:
040: import org.apache.derby.iapi.reference.SQLState;
041:
042: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
043:
044: import org.apache.derby.iapi.util.JBitSet;
045:
046: import org.apache.derby.catalog.AliasInfo;
047:
048: import java.lang.reflect.Member;
049: import java.lang.reflect.Modifier;
050:
051: import java.util.Vector;
052: import java.util.Enumeration;
053:
054: /**
055: * A NewInvocationNode represents a new object() invocation.
056: *
057: * @author Jerry Brenner
058: */
059: public class NewInvocationNode extends MethodCallNode {
060: // Whether or not to do a single instantiation
061: private boolean singleInstantiation = false;
062:
063: private boolean delimitedIdentifier;
064:
065: /**
066: * Initializer for a NewInvocationNode. Parameters are:
067: *
068: * <ul>
069: * <li>javaClassName The full package.class name of the class</li>
070: * <li>parameterList The parameter list for the constructor</li>
071: * </ul>
072: *
073: * @exception StandardException Thrown on error
074: */
075: public void init(Object javaClassName, Object params,
076: Object delimitedIdentifier) throws StandardException {
077: super .init("<init>");
078: addParms((Vector) params);
079:
080: this .javaClassName = (String) javaClassName;
081: this .delimitedIdentifier = ((Boolean) delimitedIdentifier)
082: .booleanValue();
083: }
084:
085: /**
086: * Mark this node as only needing to
087: * to a single instantiation. (We can
088: * reuse the object after newing it.)
089: */
090: void setSingleInstantiation() {
091: singleInstantiation = true;
092: }
093:
094: /**
095: * Get the resolved Classes of our parameters
096: *
097: * @return the Classes of our parameters
098: */
099: public Class[] getMethodParameterClasses() {
100: ClassInspector ci = getClassFactory().getClassInspector();
101:
102: Class[] parmTypeClasses = new Class[methodParms.length];
103: for (int i = 0; i < methodParms.length; i++) {
104: String className = methodParameterTypes[i];
105: try {
106: parmTypeClasses[i] = ci.getClass(className);
107: } catch (ClassNotFoundException cnfe) {
108: /* We should never get this exception since we verified
109: * that the classes existed at bind time. Just return null.
110: */
111: if (SanityManager.DEBUG) {
112: SanityManager.THROWASSERT("Unexpected exception - "
113: + cnfe);
114: }
115: return null;
116: }
117: }
118:
119: return parmTypeClasses;
120: }
121:
122: /**
123: * Bind this expression. This means binding the sub-expressions,
124: * as well as figuring out what the return type is for this expression.
125: *
126: * @param fromList The FROM list for the query this
127: * expression is in, for binding columns.
128: * @param subqueryList The subquery list being built as we find SubqueryNodes
129: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
130: *
131: * @return Nothing
132: *
133: * @exception StandardException Thrown on error
134: */
135:
136: public JavaValueNode bindExpression(FromList fromList,
137: SubqueryList subqueryList, Vector aggregateVector)
138: throws StandardException {
139: bindParameters(fromList, subqueryList, aggregateVector);
140:
141: javaClassName = verifyClassExist(javaClassName,
142: !delimitedIdentifier);
143: /*
144: ** Get the parameter type names out of the parameters and put them
145: ** in an array.
146: */
147: String[] parmTypeNames = getObjectSignature();
148: boolean[] isParam = getIsParam();
149: ClassInspector classInspector = getClassFactory()
150: .getClassInspector();
151:
152: /*
153: ** Find the matching constructor.
154: */
155: try {
156: /* First try with built-in types and mappings */
157: method = classInspector.findPublicConstructor(
158: javaClassName, parmTypeNames, null, isParam);
159:
160: /* If no match, then retry to match any possible combinations of
161: * object and primitive types.
162: */
163: if (method == null) {
164: String[] primParmTypeNames = getPrimitiveSignature(false);
165: method = classInspector.findPublicConstructor(
166: javaClassName, parmTypeNames,
167: primParmTypeNames, isParam);
168: }
169: } catch (ClassNotFoundException e) {
170: /*
171: ** If one of the classes couldn't be found, just act like the
172: ** method couldn't be found. The error lists all the class names,
173: ** which should give the user enough info to diagnose the problem.
174: */
175: method = null;
176: }
177:
178: if (method == null) {
179: /* Put the parameter type names into a single string */
180: String parmTypes = "";
181: for (int i = 0; i < parmTypeNames.length; i++) {
182: if (i != 0)
183: parmTypes += ", ";
184: parmTypes += (parmTypeNames[i].length() != 0 ? parmTypeNames[i]
185: : MessageService
186: .getTextMessage(SQLState.LANG_UNTYPED));
187: }
188:
189: throw StandardException.newException(
190: SQLState.LANG_NO_CONSTRUCTOR_FOUND, javaClassName,
191: parmTypes);
192: }
193:
194: methodParameterTypes = classInspector.getParameterTypes(method);
195:
196: for (int i = 0; i < methodParameterTypes.length; i++) {
197: if (classInspector.primitiveType(methodParameterTypes[i]))
198: methodParms[i].castToPrimitive(true);
199: }
200:
201: /* Set type info for any null parameters */
202: if (someParametersAreNull()) {
203: setNullParameterInfo(methodParameterTypes);
204: }
205:
206: /* Constructor always returns an object of type javaClassName */
207: if (SanityManager.DEBUG) {
208: SanityManager.ASSERT(javaClassName.equals(classInspector
209: .getType(method)),
210: "Constructor is wrong type, expected "
211: + javaClassName + " actual is "
212: + classInspector.getType(method));
213: }
214: setJavaTypeName(javaClassName);
215:
216: return this ;
217: }
218:
219: /**
220: * Categorize this predicate. Initially, this means
221: * building a bit map of the referenced tables for each predicate.
222: * If the source of this ColumnReference (at the next underlying level)
223: * is not a ColumnReference or a VirtualColumnNode then this predicate
224: * will not be pushed down.
225: *
226: * For example, in:
227: * select * from (select 1 from s) a (x) where x = 1
228: * we will not push down x = 1.
229: * NOTE: It would be easy to handle the case of a constant, but if the
230: * inner SELECT returns an arbitrary expression, then we would have to copy
231: * that tree into the pushed predicate, and that tree could contain
232: * subqueries and method calls.
233: * RESOLVE - revisit this issue once we have views.
234: *
235: * @param referencedTabs JBitSet with bit map of referenced FromTables
236: * @param simplePredsOnly Whether or not to consider method
237: * calls, field references and conditional nodes
238: * when building bit map
239: *
240: * @return boolean Whether or not source.expression is a ColumnReference
241: * or a VirtualColumnNode.
242: * @exception StandardException Thrown on error
243: */
244: public boolean categorize(JBitSet referencedTabs,
245: boolean simplePredsOnly) throws StandardException {
246: /* We stop here when only considering simple predicates
247: * as we don't consider new opeators when looking
248: * for null invariant predicates.
249: */
250: if (simplePredsOnly) {
251: return false;
252: }
253:
254: boolean pushable = true;
255:
256: pushable = pushable
257: && super .categorize(referencedTabs, simplePredsOnly);
258:
259: return pushable;
260: }
261:
262: /**
263: * Build a JBitSet of all of the tables that we are
264: * correlated with.
265: *
266: * @param correlationMap The JBitSet of the tables that we are correlated with.
267: */
268: void getCorrelationTables(JBitSet correlationMap)
269: throws StandardException {
270: CollectNodesVisitor getCRs = new CollectNodesVisitor(
271: ColumnReference.class);
272: super .accept(getCRs);
273: Vector colRefs = getCRs.getList();
274: for (Enumeration e = colRefs.elements(); e.hasMoreElements();) {
275: ColumnReference ref = (ColumnReference) e.nextElement();
276: if (ref.getCorrelated()) {
277: correlationMap.set(ref.getTableNumber());
278: }
279: }
280: }
281:
282: /**
283: * Is this class assignable to the specified class?
284: * This is useful for the VTI interface where we want to see
285: * if the class implements java.sql.ResultSet.
286: *
287: * @param toClassName The java class name we want to assign to
288: *
289: * @return boolean Whether or not this class is assignable to
290: * the specified class
291: *
292: * @exception StandardException Thrown on error
293: */
294: protected boolean assignableTo(String toClassName)
295: throws StandardException {
296: ClassInspector classInspector = getClassFactory()
297: .getClassInspector();
298: return classInspector.assignableTo(javaClassName, toClassName);
299: }
300:
301: /**
302: * Is this class have a public method with the specified signiture
303: * This is useful for the VTI interface where we want to see
304: * if the class has the option static method for returning the
305: * ResultSetMetaData.
306: *
307: * @param methodName The method name we are looking for
308: * @param staticMethod Whether or not the method we are looking for is static
309: *
310: * @return Member The Member representing the method (or null
311: * if the method doesn't exist).
312: *
313: * @exception StandardException Thrown on error
314: */
315: protected Member findPublicMethod(String methodName,
316: boolean staticMethod) throws StandardException {
317: Member publicMethod;
318:
319: /*
320: ** Get the parameter type names out of the parameters and put them
321: ** in an array.
322: */
323: String[] parmTypeNames = getObjectSignature();
324: boolean[] isParam = getIsParam();
325:
326: ClassInspector classInspector = getClassFactory()
327: .getClassInspector();
328:
329: try {
330: publicMethod = classInspector.findPublicMethod(
331: javaClassName, methodName, parmTypeNames, null,
332: isParam, staticMethod, false);
333:
334: /* If no match, then retry to match any possible combinations of
335: * object and primitive types.
336: */
337: if (publicMethod == null) {
338: String[] primParmTypeNames = getPrimitiveSignature(false);
339: publicMethod = classInspector
340: .findPublicMethod(javaClassName, methodName,
341: parmTypeNames, primParmTypeNames,
342: isParam, staticMethod, false);
343: }
344: } catch (ClassNotFoundException e) {
345: /* We should always be able to find the class at this point
346: * since the protocol is to check to see if it exists
347: * before checking for a method off of it. Anyway, just return
348: * null if the class doesn't exist, since the method doesn't
349: * exist in that case.
350: */
351: if (SanityManager.DEBUG) {
352: SanityManager
353: .THROWASSERT("Unexpected ClassNotFoundException for javaClassName");
354: }
355: return null;
356: }
357:
358: return publicMethod;
359: }
360:
361: /**
362: * Do code generation for this method call
363: *
364: * @param acb The ExpressionClassBuilder for the class we're generating
365: * @param mb The method the expression will go into
366: *
367: *
368: * @exception StandardException Thrown on error
369: */
370:
371: public void generateExpression(ExpressionClassBuilder acb,
372: MethodBuilder mb) throws StandardException {
373: /* If this node is for an ungrouped aggregator,
374: * then we generate a conditional
375: * wrapper so that we only new the aggregator once.
376: * (fx == null) ? fx = new ... : fx
377: */
378: LocalField objectFieldLF = null;
379: if (singleInstantiation) {
380: /* Declare the field */
381: objectFieldLF = acb.newFieldDeclaration(Modifier.PRIVATE,
382: javaClassName);
383:
384: // now we fill in the body of the conditional
385:
386: mb.getField(objectFieldLF);
387: mb.conditionalIfNull();
388: }
389:
390: mb.pushNewStart(javaClassName);
391: int nargs = generateParameters(acb, mb);
392: mb.pushNewComplete(nargs);
393:
394: if (singleInstantiation) {
395:
396: mb.putField(objectFieldLF);
397: mb.startElseCode();
398: mb.getField(objectFieldLF);
399: mb.completeConditional();
400: }
401: }
402: }
|