/******************************************************************************* * Copyright © 2011, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.gen.java.templates; import org.eclipse.edt.gen.Constants; import org.eclipse.edt.gen.GenerationException; import org.eclipse.edt.gen.java.CommonUtilities; import org.eclipse.edt.gen.java.Context; import org.eclipse.edt.javart.util.JavaAliaser; import org.eclipse.edt.mof.codegen.api.TabbedWriter; import org.eclipse.edt.mof.egl.*; import org.eclipse.edt.mof.egl.utils.TypeUtils; public class TypeTemplate extends JavaTemplate { public void preGen(Type type, Context ctx) { // when we get here, it is because a type is being referenced by the original part being generated. Add it to the // types used table if it doesn't already exist. The first thing we want to check for, is to make sure the // unqualified type name (the last node in the name) is not already taken. If it is already taken, we can't do an // import for it and when used throughout the program, will have to be fully qualified at all times. CommonUtilities.processImport(aliasPackageName(ctx.getNativeImplementationMapping(type.getClassifier())), ctx); // in the case where the model name doesn't match the interface name, then we also need to process the interface // name as it will exist if (ctx.getNativeMapping(ctx.getNativeMapping(type.getClassifier().getTypeSignature())) != null) CommonUtilities.processImport(aliasPackageName(ctx.getNativeMapping(type.getClassifier().getTypeSignature())), ctx); if (ctx.mapsToPrimitiveType(type.getClassifier())) { // if this primitive type is really a primitive, it will map back to the java object. We want to use that java // object instead of the primitive mapped simply to a java object. For example, eglx.lang.ESmallint maps to short // (the primitive) and then short maps to the java object java.lang.Short. We want to always use the object for // the imports. if (ctx.getPrimitiveMapping(ctx.getPrimitiveMapping(type.getClassifier().getTypeSignature())) != null) CommonUtilities.processImport(aliasPackageName(ctx.getPrimitiveMapping(ctx.getPrimitiveMapping(type.getClassifier().getTypeSignature()))), ctx); else CommonUtilities.processImport(aliasPackageName(ctx.getPrimitiveMapping(type.getClassifier().getTypeSignature())), ctx); } } private String aliasPackageName(String imported) { String type; int lastDot = imported.lastIndexOf('.'); if (lastDot == -1) type = imported; else type = CommonUtilities.packageName(imported.substring(0, lastDot)) + '.' + JavaAliaser.getAlias(imported.substring(lastDot + 1)); return type; } public Boolean isAssignmentArrayMatchingWanted(Type type, Context ctx) { // types can override this to cause/prevent type matching of array literals to be ignored. return true; } public Boolean isAssignmentBreakupWanted(Type type, Context ctx, Assignment expr) { // types can override this to cause/prevent an compound assignment expression to be broken up // the arg contains the operation being asked about. we always want certain ones broken up if (expr.getOperator().equals("**=") || expr.getOperator().equals("?:=") || expr.getOperator().equals("::=") || (expr.getLHS() instanceof MemberName && CommonUtilities.getPropertyFunction(((MemberName) expr.getLHS()).getNamedElement(), true, ctx) != null)) return true; else return false; } public Boolean isListReorganizationWanted(Type type, Context ctx, Expression arg) { // types can override this to cause/prevent list reorganization to be done return true; } public Boolean isMathLibDecimalBoxingWanted(Type type, Context ctx) { // types can override this to cause/prevent mathlib decimals/precision boxing to be done return false; } public Boolean isStringLibFormatBoxingWanted(Type type, Context ctx) { return false; } public void genInstantiation(Type type, Context ctx, TabbedWriter out) { out.print("new "); ctx.invoke(genRuntimeTypeName, type, ctx, out, TypeNameKind.JavaImplementation); out.print("("); ctx.invoke(genConstructorOptions, type, ctx, out); out.print(")"); } public void genInstantiation(Type type, Context ctx, TabbedWriter out, Field arg) { ctx.invoke(genInstantiation, type, ctx, out); } public void genInvocation(Type type, Context ctx, TabbedWriter out, InvocationExpression arg) { ctx.invoke(genInvocation, arg, ctx, out); } public void genDefaultValue(Type type, Context ctx, TabbedWriter out, TypedElement arg) { if (arg.isNullable()) out.print("null"); else ctx.invoke(genDefaultValue, type, ctx, out); } public void genDefaultValue(Type type, Context ctx, TabbedWriter out, Expression arg) { if (arg.isNullable()) out.print("null"); else ctx.invoke(genDefaultValue, type, ctx, out); } public void genDefaultValue(Type type, Context ctx, TabbedWriter out) { if (TypeUtils.isReferenceType(type)) ctx.invoke(genInstantiation, type, ctx, out); else out.print("\"Invalid default value\""); } public void genRuntimeConstraint(Type type, Context ctx, TabbedWriter out) { ctx.invoke(genRuntimeClassTypeName, type, ctx, out, TypeNameKind.EGLImplementation); } public void genRuntimeTypeName(Type type, Context ctx, TabbedWriter out) { ctx.invoke(genRuntimeTypeName, type, ctx, out, TypeNameKind.JavaPrimitive); } public void genRuntimeTypeExtension(Type type, Context ctx, TabbedWriter out) { // no default } public void genRuntimeClassTypeName(Type type, Context ctx, TabbedWriter out, TypeNameKind arg) { ctx.invoke(genRuntimeTypeName, type, ctx, out, arg); out.print(".class"); } public void genRuntimeTypeName(Type type, Context ctx, TabbedWriter out, TypeNameKind arg) { // are we looking for the default (java primitive) or specifically java primitive, if it exists if (arg == TypeNameKind.JavaPrimitive) { if (ctx.mapsToPrimitiveType(type.getClassifier())) { out.print(ctx.getPrimitiveMapping(type.getClassifier())); return; } } // are we looking for the java object if (arg == TypeNameKind.JavaObject) { if (ctx.mapsToPrimitiveType(type.getClassifier())) { String item = ctx.getPrimitiveMapping(type.getClassifier()); if (ctx.getPrimitiveMapping(item) == null) out.print(item); else out.print(ctx.getPrimitiveMapping(item)); return; } } // we couldn't resolve the java types, so we have to check for the java implementation name if (arg == TypeNameKind.JavaImplementation) { if (ctx.mapsToPrimitiveType(type.getClassifier())) { String item = ctx.getPrimitiveMapping(type.getClassifier()); if (ctx.getPrimitiveMapping(item) == null) out.print(item); else out.print(ctx.getPrimitiveMapping(item)); return; } } // type an egl implementation name if (arg == TypeNameKind.EGLImplementation) { if (ctx.mapsToNativeType(type.getClassifier())) { out.print(ctx.getNativeImplementationMapping(type.getClassifier())); return; } } // select the proper default to use. we have run out of options if (arg == TypeNameKind.JavaImplementation) out.print(ctx.getNativeImplementationMapping(type.getClassifier())); else // must be an egl interface name we want out.print(ctx.getNativeInterfaceMapping(type.getClassifier())); } public void genConstructorOptions(Type type, Context ctx, TabbedWriter out) { // no default } public void genTypeDependentOptions(Type type, Context ctx, TabbedWriter out) { // no default } public void genConversionOperationOptions(Type type, Context ctx, TabbedWriter out) { // Default to the type-dependent options. ctx.invoke(genTypeDependentOptions, type, ctx, out); } public Integer genFieldTypeClassName(Type type, Context ctx, TabbedWriter out, Integer arrayDimensions) { ctx.invoke(genRuntimeTypeName, type, ctx, out, TypeNameKind.EGLImplementation); return arrayDimensions; } public void genAssignment(Type type, Context ctx, TabbedWriter out, Expression arg1, Expression arg2, String arg3) { // if the lhs is non-nullable but the rhs is nullable, we have a special case if (!arg1.isNullable() && arg2.isNullable()) { if (TypeUtils.isReferenceType(arg1.getType())) { ctx.invoke(genExpression, arg1, ctx, out); out.print(arg3 + " "); if (!(arg2 instanceof BoxingExpression)) { out.print("("); ctx.invoke(genRuntimeTypeName, arg2.getType(), ctx, out, TypeNameKind.JavaObject); ctx.invoke(genRuntimeTypeExtension, arg2.getType(), ctx, out); out.print(") "); } // we don't want to do a checkNullable on the temporary variables that are logically not nullable if (arg2 instanceof MemberName && ((MemberName) arg2).getId().startsWith(Constants.temporaryVariableLogicallyNotNullablePrefix)) assignmentSource(arg1, arg2, ctx, out); else { out.print("org.eclipse.edt.javart.util.JavartUtil.checkNullable("); assignmentSource(arg1, arg2, ctx, out); out.print(")"); } // check to see if we are unboxing RHS temporary variables (inout and out types only) if (CommonUtilities.isBoxedOutputTemp(arg2, ctx)) out.print(".ezeUnbox()"); } else { // if this is a well-behaved assignment, we can avoid the temporary if (org.eclipse.edt.gen.CommonUtilities.hasSideEffects(arg2, ctx)) { String temporary = ctx.nextTempName(); ctx.invoke(genRuntimeTypeName, arg1.getType(), ctx, out, TypeNameKind.JavaObject); out.print(" " + temporary + " = "); assignmentSource(arg1, arg2, ctx, out); out.println(";"); ctx.invoke(genExpression, arg1, ctx, out); out.print(arg3 + " "); if (!(arg2 instanceof BoxingExpression)) { out.print("("); ctx.invoke(genRuntimeTypeName, arg2.getType(), ctx, out, TypeNameKind.JavaObject); ctx.invoke(genRuntimeTypeExtension, arg2.getType(), ctx, out); out.print(") "); } out.print("org.eclipse.edt.javart.util.JavartUtil.checkNullable(" + temporary + ")"); } else if (TypeUtils.isReferenceType(arg2.getType())) { ctx.invoke(genExpression, arg1, ctx, out); out.print(arg3); out.print("org.eclipse.edt.javart.util.JavartUtil.checkNullable("); assignmentSource(arg1, arg2, ctx, out); out.print(")"); } else { ctx.invoke(genExpression, arg1, ctx, out); out.print(arg3 + " "); if (!(arg2 instanceof BoxingExpression)) { out.print("("); ctx.invoke(genRuntimeTypeName, arg2.getType(), ctx, out, TypeNameKind.JavaObject); ctx.invoke(genRuntimeTypeExtension, arg2.getType(), ctx, out); out.print(") "); } out.print("org.eclipse.edt.javart.util.JavartUtil.checkNullable("); assignmentSource(arg1, arg2, ctx, out); out.print(")"); } } } else { ctx.invoke(genExpression, arg1, ctx, out); out.print(arg3); assignmentSource(arg1, arg2, ctx, out); // check to see if we are unboxing RHS temporary variables (inout and out types only) if (CommonUtilities.isBoxedOutputTemp(arg2, ctx)) out.print(".ezeUnbox()"); } } public static void assignmentSource( Expression lhs, Expression rhs, Context ctx, TabbedWriter out ) { // Generate something special for the RHS of an assignment when: // 1) assigning bytes(x) to bytes(y), and x < y // 2) assigning bytes to bytes(x) // 3) assigning 'any as bytes(x)' to bytes(x) // 4) assigning 'any as bytes' to bytes // 5) assigning a function to a delegate Type lhsType = lhs.getType(); Type rhsType = rhs.getType(); if ( rhsType != null && TypeUtils.getTypeKind( rhsType ) == TypeUtils.TypeKind_BYTES && lhsType != null && TypeUtils.getTypeKind( lhsType ) == TypeUtils.TypeKind_BYTES ) { if ( ( lhsType instanceof SequenceType && rhsType instanceof SequenceType && ((SequenceType)rhsType).getLength() < ((SequenceType)lhsType).getLength() ) || ( rhs instanceof AsExpression && ((AsExpression)rhs).getObjectExpr().getType() instanceof ParameterizableType && TypeUtils.getTypeKind( ((AsExpression)rhs).getObjectExpr().getType() ) == TypeUtils.TypeKind_BYTES ) ) { assignShorterBytesToLongerBytes( rhs, rhsType, lhs, lhsType, ctx, out ); } else if ( rhs instanceof AsExpression && TypeUtils.getTypeKind( ((AsExpression)rhs).getObjectExpr().getType() ) == TypeUtils.TypeKind_ANY ) { int lhsLength = lhsType instanceof SequenceType ? ((SequenceType)lhsType).getLength() : 0; ctx.invoke( genRuntimeTypeName, rhsType, ctx, out, TypeNameKind.EGLImplementation ); out.print( ".ezeAssignFromAny(" ); if ( lhsLength > 0 ) { if ( lhs instanceof MemberName && ctx.get( "generating declaration of " + ((MemberName)lhs).getMember() + ((MemberName)lhs).getMember().hashCode() ) != null ) { out.print( "new byte[" + lhsLength + ']' ); } else { ctx.invoke( genExpression, lhs, ctx, out ); } out.print( ", " ); out.print( lhsLength ); } else { out.print( "null" ); } out.print( ", " ); ctx.invoke( genExpression, rhs, ctx, out ); out.print( ')' ); } else { ctx.invoke( genExpression, rhs, ctx, out ); } } else if ( lhsType instanceof Delegate && (rhs instanceof MemberName || rhs instanceof MemberAccess) && CommonUtilities.getMember( rhs ) instanceof Function ) { String functionSig = ((Function)CommonUtilities.getMember( rhs )).getSignature(); ctx.put( "Delegate_signature_for_function_" + functionSig, ((Delegate)lhsType).getTypeSignature() ); ctx.invoke( genExpression, rhs, ctx, out ); ctx.remove( "Delegate_signature_for_function_" + functionSig ); } else { ctx.invoke( genExpression, rhs, ctx, out ); } } static private void assignShorterBytesToLongerBytes( Expression rhs, Type rhsType, Expression lhs, Type lhsType, Context ctx, TabbedWriter out ) { int lhsLength = ((SequenceType)lhsType).getLength(); if ( rhs instanceof BytesLiteral ) { StringBuilder value = new StringBuilder( ((BytesLiteral)rhs).getValue() ); while ( value.length() / 2 < lhsLength ) { value.append( "00" ); } BytesLiteral newRHS = ctx.getFactory().createBytesLiteral(); newRHS.setValue( value.toString() ); ctx.invoke( genExpression, newRHS, ctx, out ); } else { ctx.invoke( genRuntimeTypeName, rhsType, ctx, out, TypeNameKind.EGLImplementation ); out.print( ".ezeAssignToLonger(" ); if ( lhs instanceof MemberName && ctx.get( "generating declaration of " + ((MemberName)lhs).getMember() + ((MemberName)lhs).getMember().hashCode() ) != null ) { out.print( "new byte[" + lhsLength + ']' ); } else { ctx.invoke( genExpression, lhs, ctx, out ); } out.print( ", " ); out.print( lhsLength ); out.print( ", " ); ctx.invoke( genExpression, rhs, ctx, out ); out.print( ')' ); } } public void genIsaExpression(Type type, Context ctx, TabbedWriter out, IsAExpression arg) { if (arg.getObjectExpr().getType().getTypeSignature().equalsIgnoreCase(arg.getEType().getTypeSignature())) { if (arg.getObjectExpr().isNullable()) { out.print("("); ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); out.print(" == null ? false : true)"); } else out.print("true"); } else if (arg.getObjectExpr().getType().getClassifier() != null && arg.getObjectExpr().getType().getClassifier().getTypeSignature().equalsIgnoreCase(arg.getEType().getTypeSignature())) { if (arg.getObjectExpr().isNullable()) { out.print("("); ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); out.print(" == null ? false : true)"); } else out.print("true"); } else if (arg.getObjectExpr().getType().getClassifier() != null && arg.getEType().getClassifier() != null && arg.getObjectExpr().getType().getClassifier().getTypeSignature().equalsIgnoreCase(arg.getEType().getClassifier().getTypeSignature())) { out.print("false"); } else if (ctx.mapsToPrimitiveType(arg.getEType())) { ctx.invoke(genRuntimeTypeName, arg.getEType(), ctx, out, TypeNameKind.EGLImplementation); out.print(".ezeIsa("); // do a callout to allow certain source types to decide to create a boxing expression ctx.invoke(genIsaExpressionBoxing, arg.getObjectExpr().getType(), ctx, out, arg); // then process the isa operation ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); ctx.invoke(genTypeDependentOptions, arg.getEType(), ctx, out); out.print(")"); } else { out.print("org.eclipse.edt.runtime.java.eglx.lang.EAny.ezeIsa("); ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); out.print(", "); ctx.invoke(genRuntimeClassTypeName, arg.getEType(), ctx, out, TypeNameKind.EGLImplementation); out.print(")"); } } public void genConversionOperation(Type type, Context ctx, TabbedWriter out, AsExpression arg) { // check to see if a conversion is required if (arg.getConversionOperation() != null) { out.print(ctx.getNativeImplementationMapping((Classifier) arg.getConversionOperation().getContainer()) + "."); out.print(arg.getConversionOperation().getCaseSensitiveName()); out.print("("); // do a callout to allow certain source types to decide to create a boxing expression ctx.invoke(genAsExpressionBoxing, arg.getObjectExpr().getType(), ctx, out, arg); // then process the conversion operation ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); ctx.invoke(genTypeDependentOptions, arg.getEType(), ctx, out); out.print(")"); } else if (ctx.mapsToPrimitiveType(arg.getEType())) { ctx.invoke(genRuntimeTypeName, arg.getEType(), ctx, out, TypeNameKind.EGLImplementation); out.print(".ezeCast("); // do a callout to allow certain source types to decide to create a boxing expression ctx.invoke(genAsExpressionBoxing, arg.getObjectExpr().getType(), ctx, out, arg); // then process the conversion operation ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); ctx.invoke(genTypeDependentOptions, arg.getEType(), ctx, out); out.print(")"); } else { out.print("org.eclipse.edt.runtime.java.eglx.lang.EAny.ezeCast("); ctx.invoke(genExpression, arg.getObjectExpr(), ctx, out); out.print(", "); ctx.invoke(genRuntimeClassTypeName, arg.getEType(), ctx, out, TypeNameKind.JavaImplementation); out.print(")"); } } public void genAsExpressionBoxing(Type type, Context ctx, TabbedWriter out, AsExpression arg) { // do nothing } public void genIsaExpressionBoxing(Type type, Context ctx, TabbedWriter out, IsAExpression arg) { // do nothing } public void genContainerBasedInvocationBoxing(Type type, Context ctx, TabbedWriter out, InvocationExpression arg) { // do nothing } public void genInvocationArgumentBoxing(Type type, Context ctx, TabbedWriter out, InvocationExpression arg1, Integer arg2) { // do nothing } public void genReturnStatement(Type type, Context ctx, TabbedWriter out, ReturnStatement arg) { ctx.invoke(genReturnStatement, arg, ctx, out); } public void genBinaryExpression(Type type, Context ctx, TabbedWriter out, BinaryExpression arg) throws GenerationException { // if either side of this expression is nullable, or if either side is an array access, // or if there is no direct java operation, we need to use the runtime if ((arg.getLHS().isNullable() || arg.getRHS().isNullable()) || (arg.getLHS() instanceof ArrayAccess || arg.getRHS() instanceof ArrayAccess) || CommonUtilities.getNativeJavaOperation(arg, ctx).length() == 0) { out.print(ctx.getNativeImplementationMapping((Type) arg.getOperation().getContainer()) + "."); out.print(CommonUtilities.getNativeRuntimeOperationName(arg)); out.print("("); ctx.invoke(genExpression, arg.getLHS(), ctx, out, arg.getOperation().getParameters().get(0)); out.print(", "); ctx.invoke(genExpression, arg.getRHS(), ctx, out, arg.getOperation().getParameters().get(1)); out.print(")" + CommonUtilities.getNativeRuntimeComparisionOperation(arg)); } else { ctx.invoke(genExpression, arg.getLHS(), ctx, out); out.print(CommonUtilities.getNativeJavaOperation(arg, ctx)); ctx.invoke(genExpression, arg.getRHS(), ctx, out); } } public void genUnaryExpression(Type type, Context ctx, TabbedWriter out, UnaryExpression arg) throws GenerationException { // if either side of this expression is nullable if (arg.getExpression().isNullable()) { out.print(ctx.getNativeImplementationMapping((Type) arg.getOperation().getContainer()) + "."); out.print(CommonUtilities.getNativeRuntimeOperationName(arg)); out.print("("); ctx.invoke(genExpression, arg.getExpression(), ctx, out, arg.getOperation().getParameters().get(0)); out.print(")"); } else { // we only need to check for minus sign and if found, we need to change it to -() if (arg.getOperator().equals(UnaryExpression.Op_NEGATE) || arg.getOperator().equals(UnaryExpression.Op_NOT) || arg.getOperator().equals(UnaryExpression.Op_BITWISENOT)) { out.print(arg.getOperator() + "("); ctx.invoke(genExpression, arg.getExpression(), ctx, out); out.print(")"); } else ctx.invoke(genExpression, arg.getExpression(), ctx, out); } } public void genContainerBasedAssignment(Type type, Context ctx, TabbedWriter out, Assignment arg1, Field arg2) { ctx.invoke(genAssignment, arg1, ctx, out); } public void genContainerBasedArrayAccess(Type type, Context ctx, TabbedWriter out, ArrayAccess arg1, Field arg2) { ctx.invoke(genArrayAccess, arg1, ctx, out); } public void genContainerBasedMemberAccess(Type type, Context ctx, TabbedWriter out, MemberAccess arg1, Member arg2) { ctx.invoke(genMemberAccess, arg1, ctx, out); } public void genContainerBasedMemberName(Type type, Context ctx, TabbedWriter out, MemberName arg1, Member arg2) { ctx.invoke(genMemberName, arg1, ctx, out); } public void genContainerBasedNewExpression(Type type, Context ctx, TabbedWriter out, Expression arg) { ctx.invoke(genNewExpression, arg, ctx, out); } public void genContainerBasedInvocation(Type type, Context ctx, TabbedWriter out, Expression arg) { ctx.invoke(genInvocation, arg, ctx, out); } public void genTypeBasedAssignment(Type type, Context ctx, TabbedWriter out, Assignment arg) { String operator = "="; if (arg.getOperator() != null && arg.getOperator().length() > 0) operator = arg.getOperator(); ctx.invoke(genAssignment, arg.getLHS(), ctx, out, arg.getRHS(), " " + CommonUtilities.getNativeJavaAssignment(operator) + " "); } }