/** * The caller will have pushed a DataValueFactory and value of that can be converted to the * correct type, e.g. int for a SQL INTEGER. * * <p>Thus upon entry the stack looks like: ...,dvf,value * * <p>If field is not null then it is used as the holder of the generated DataValueDescriptor to * avoid object creations on multiple passes through this code. The field may contain null or a * valid value. * * <p>This method then sets up to call the required method on DataValueFactory using the * dataValueMethodName(). The value left on the stack will be a DataValueDescriptor of the correct * type: * * <p>If the field contained a valid value then generated code will return that value rather than * a newly created object. If field was not-null then the generated code will set the value of * field to be the return from the DataValueFactory method call. Thus if the field was empty (set * to null) when this code is executed it will contain the newly generated value, otherwise it * will be reset to the same value. * * <p>...,dvd * * @see TypeCompiler#generateDataValue(MethodBuilder, int, LocalField) */ public void generateDataValue(MethodBuilder mb, int collationType, LocalField field) { String interfaceName = interfaceName(); // push the second argument /* If fieldName is null, then there is no * reusable wrapper (null), else we * reuse the field. */ if (field == null) { mb.pushNull(interfaceName); } else { mb.getField(field); } int argCount; if (pushCollationForDataValue(collationType)) { mb.push(collationType); argCount = 3; } else argCount = 2; mb.callMethod( VMOpcode.INVOKEINTERFACE, (String) null, dataValueMethodName(), interfaceName, argCount); if (field != null) { /* Store the result of the method call in the field, * so we can re-use the wrapper. */ mb.putField(field); } }
/** * Do code generation for this conversion of a value from the Java to the SQL domain. * * @param acb The ExpressionClassBuilder for the class we're generating * @param mb the method the expression will go into * @exception StandardException Thrown on error */ public void generateExpression(ExpressionClassBuilder acb, MethodBuilder mb) throws StandardException { TypeId resultType; String resultTypeName; /* ** Tell the Java node that it's value is being returned to the ** SQL domain. This way, it knows whether the checking for a null ** receiver is to be done at the Java level or the SQL level. */ javaNode.returnValueToSQLDomain(); /* Generate the receiver, if any. */ boolean hasReceiver = javaNode.generateReceiver(acb, mb); /* ** If the java expression has a receiver, we want to check whether ** it's null before evaluating the whole expression (to avoid ** a NullPointerException. */ if (hasReceiver) { /* ** There is a receiver. Generate a null SQL value to return ** in case the receiver is null. First, create a field to hold ** the null SQL value. */ String nullValueClass = getTypeCompiler().interfaceName(); LocalField nullValueField = acb.newFieldDeclaration(Modifier.PRIVATE, nullValueClass); /* ** There is a receiver. Generate the following to test ** for null: ** ** (receiverExpression == null) ? */ mb.conditionalIfNull(); mb.getField(nullValueField); acb.generateNullWithExpress(mb, getTypeCompiler(), getTypeServices().getCollationType()); /* ** We have now generated the expression to test, and the ** "true" side of the ?: operator. Finish the "true" side ** so we can generate the "false" side. */ mb.startElseCode(); } resultType = getTypeId(); TypeCompiler tc = getTypeCompiler(); resultTypeName = tc.interfaceName(); /* Allocate an object for re-use to hold the result of the conversion */ LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, resultTypeName); /* Generate the expression for the Java value under us */ javaNode.generateExpression(acb, mb); /* Generate the SQL value, which is always nullable */ acb.generateDataValue(mb, tc, getTypeServices().getCollationType(), field); /* ** If there was a receiver, the return value will be the result ** of the ?: operator. */ if (hasReceiver) { mb.completeConditional(); } }