bug 473392 - [compiler][otdre] ClassCastException when callin is
suppressed by base predicate but result lifting still happens 
- fixed by new arg "isBaseCall" to _OT$callNext
- also resolved FIXMEs regarding callins with multiple base methods
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
index 2e821ac..55a25a1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
@@ -114,6 +114,7 @@
 	
 	// for call next:
 	private static final char[] BASE_CALL_ARGS  = "baseCallArguments".toCharArray();   //$NON-NLS-1$
+	private static final char[] IS_BASE_CALL = "isBaseCall".toCharArray(); //$NON-NLS-1$
 
 	// for call{replace,before,after}:
 	static final char[][] REPLACE_ARG_NAMES = new char[][]{_BASE$, TEAMS, INDEX, CALLIN_ID, BOUND_METHOD_ID, ARGUMENTS};
@@ -753,10 +754,11 @@
 				if (isReplace) { 
 
 					// default: callNext:
-					Expression[] callArgs = new Expression[REPLACE_ARG_NAMES.length+1];
+					Expression[] callArgs = new Expression[REPLACE_ARG_NAMES.length+2];
 					for (int idx=0; idx < REPLACE_ARG_NAMES.length; idx++)
 						callArgs[idx] = gen.singleNameReference(REPLACE_ARG_NAMES[idx]);
-					callArgs[callArgs.length-1] = gen.nullLiteral(); // no explicit baseCallArguments
+					callArgs[callArgs.length-2] = gen.nullLiteral(); // no explicit baseCallArguments
+					callArgs[callArgs.length-1] = gen.booleanLiteral(false); // not a base call  
 					statements.add(gen.caseStatement(null)); 													// default:
 					statements.add(gen.returnStatement(															//    return _OT$callNext(..);
 										gen.messageSend(
@@ -844,7 +846,8 @@
 				gen.argument(CALLIN_ID, 		gen.createArrayTypeReference(TypeBinding.INT, 1)),
 				gen.argument(BOUND_METHOD_ID, 	gen.typeReference(TypeBinding.INT)),
 				gen.argument(ARGUMENTS, 		gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)),
-				gen.argument(BASE_CALL_ARGS, 	gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1))
+				gen.argument(BASE_CALL_ARGS, 	gen.qualifiedArrayTypeReference(TypeConstants.JAVA_LANG_OBJECT, 1)),
+				gen.argument(IS_BASE_CALL,		gen.typeReference(TypeBinding.BOOLEAN))
 		};
 		// super call directly passes all these args through:
 		Expression[] superArgs = new Expression[args.length];
@@ -862,94 +865,98 @@
 		swStat.expression = gen.arrayReference(gen.singleNameReference(CALLIN_ID), gen.singleNameReference(INDEX));	// switch(callinId[index]) { ...
 		List<Statement> swStatements = new ArrayList<Statement>(); 
 		for (CallinMappingDeclaration mapping : callinDecls) {
-			List<Statement> caseBlockStats = new ArrayList<Statement>();
 			int nLabels = 0;
-			for (MethodSpec baseSpec : mapping.baseMethodSpecs) 
-				caseBlockStats.add(gen.caseStatement(gen.intLiteral(baseSpec.getCallinId(aTeam))));					// case bseSpecCallinId:
-			int nRoleArgs = mapping.getRoleMethod().getSourceParamLength();
-			TypeBinding[] roleParams = mapping.getRoleMethod().getSourceParameters();
-			for (int i=0; i<roleParams.length; i++)
-				if (roleParams[i].isRole() && TeamModel.isTeamContainingRole(teamDecl.binding, (ReferenceBinding) roleParams[i]))
-					roleParams[i] = TeamModel.strengthenRoleType(teamDecl.binding, roleParams[i]);
-
-			List<Statement> repackingStats = new ArrayList<Statement>();
-			
-			if (mapping.positions != null) {
-				int[] poss = mapping.positions;
-				nLabels = caseBlockStats.size();
-				int argOffset = 0; // some methods have their real arguments at an offset
-				MethodSpec baseSpec = mapping.baseMethodSpecs[0]; // TODO(SH): check all base methods??
-				if (baseSpec.isCallin())
-					argOffset += 6;
-				if (baseSpec.isStatic() && baseSpec.getDeclaringClass().isRole())
-					argOffset += 2;
-				for (int i=0; i<poss.length; i++)
-					// arguments[basepos] = baseCallArguments[i]
-					if (poss[i] > 0) {
-						// FIXME(SH): this is cheating: should obtain translation info from actual 
-						// parameter mapping (see cast in test432_expressionInReplaceParameterMapping11)
-						TypeBinding roleSideParameter = roleParams[i];
-						 // FIXME(SH): per basemethod:
-						TypeBinding baseSideParameter = mapping.baseMethodSpecs[0].resolvedParameters()[poss[i]-1];
-						Expression roleSideArgument = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);//   ... baseCallArguments[i] ...
-						if (TypeBinding.notEquals(roleSideParameter, baseSideParameter))
-							roleSideArgument = gen.resolvedCastExpression(roleSideArgument, roleSideParameter, CastExpression.RAW);
-						TypeBinding roleSideLeaf = roleSideParameter.leafComponentType();
-						TypeBinding baseSideLeaf = baseSideParameter.leafComponentType();
-						if (   roleSideLeaf.isRole() 
-							&& ((ReferenceBinding)roleSideLeaf).baseclass().isCompatibleWith(baseSideLeaf))
-							roleSideArgument = new PotentialLowerExpression(roleSideArgument, baseSideParameter, gen.thisReference());
-						repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), 	//   arguments[p] = baseCallArguments[i];
-								                                             poss[i]-1+argOffset), // 0 represents result
-														  roleSideArgument));
+			for (MethodSpec baseSpec : mapping.baseMethodSpecs) {
+				List<Statement> caseBlockStats = new ArrayList<Statement>();
+				caseBlockStats.add(gen.caseStatement(gen.intLiteral(baseSpec.getCallinId(aTeam))));					// case baseSpecCallinId:
+				int nRoleArgs = mapping.getRoleMethod().getSourceParamLength();
+				TypeBinding[] roleParams = mapping.getRoleMethod().getSourceParameters();
+				for (int i=0; i<roleParams.length; i++)
+					if (roleParams[i].isRole() && TeamModel.isTeamContainingRole(teamDecl.binding, (ReferenceBinding) roleParams[i]))
+						roleParams[i] = TeamModel.strengthenRoleType(teamDecl.binding, roleParams[i]);
+	
+				List<Statement> repackingStats = new ArrayList<Statement>();
+				
+				if (mapping.positions != null) {
+					int[] poss = mapping.positions;
+					nLabels = caseBlockStats.size();
+					int argOffset = 0; // some methods have their real arguments at an offset
+					if (baseSpec.isCallin())
+						argOffset += 6;
+					if (baseSpec.isStatic() && baseSpec.getDeclaringClass().isRole())
+						argOffset += 2;
+					for (int i=0; i<poss.length; i++)
+						// arguments[basepos] = baseCallArguments[i]
+						if (poss[i] > 0) {
+							// FIXME(SH): this is cheating: should obtain translation info from actual 
+							// parameter mapping (see cast in test432_expressionInReplaceParameterMapping11)
+							TypeBinding roleSideParameter = roleParams[i];
+							TypeBinding baseSideParameter = baseSpec.resolvedParameters()[poss[i]-1];
+							Expression roleSideArgument = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);//   ... baseCallArguments[i] ...
+							if (TypeBinding.notEquals(roleSideParameter, baseSideParameter))
+								roleSideArgument = gen.resolvedCastExpression(roleSideArgument, roleSideParameter, CastExpression.RAW);
+							TypeBinding roleSideLeaf = roleSideParameter.leafComponentType();
+							TypeBinding baseSideLeaf = baseSideParameter.leafComponentType();
+							if (   roleSideLeaf.isRole() 
+								&& ((ReferenceBinding)roleSideLeaf).baseclass().isCompatibleWith(baseSideLeaf))
+								roleSideArgument = new PotentialLowerExpression(roleSideArgument, baseSideParameter, gen.thisReference());
+							repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), 	//   arguments[p] = baseCallArguments[i];
+									                                             poss[i]-1+argOffset), // 0 represents result
+															  roleSideArgument));
+						}
+				} else if (nRoleArgs > 0) {
+					for (int i=0; i<nRoleArgs; i++) {
+						Expression basecallArg = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);
+						if (baseSpec.argNeedsTranslation(i)) {
+							basecallArg = new PotentialLowerExpression(gen.castExpression(basecallArg,
+																				 			gen.typeReference(roleParams[i]),
+																				 			CastExpression.RAW),
+																	   baseSpec.resolvedParameters()[i],
+																	   gen.qualifiedThisReference(teamDecl.binding));
+						}
+						repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), i),	//    arguments[i] = lower?(baseCallArguments[i])
+								  		   				  basecallArg));
 					}
-			} else if (nRoleArgs > 0) {
-				for (int i=0; i<nRoleArgs; i++) {
-					Expression basecallArg = gen.arrayReference(gen.singleNameReference(BASE_CALL_ARGS), i);
-					if (mapping.baseMethodSpecs[0].argNeedsTranslation(i)) { // FIXME(SH): per basemethod!
-						basecallArg = new PotentialLowerExpression(gen.castExpression(basecallArg,
-																			 			gen.typeReference(roleParams[i]),
-																			 			CastExpression.RAW),
-																   mapping.baseMethodSpecs[0].resolvedParameters()[i], // FIXME(SH): per basemethod!
-																   gen.qualifiedThisReference(teamDecl.binding));
-					}
-					repackingStats.add(gen.assignment(gen.arrayReference(gen.singleNameReference(ARGUMENTS), i),	//    arguments[i] = lower?(baseCallArguments[i])
-							  		   				  basecallArg));
 				}
-			}
-			caseBlockStats.add(gen.ifStatement(gen.nullCheck(gen.singleNameReference(BASE_CALL_ARGS)),				//    if (baseCallArgs == null) {} { arguments[i] = ...; ... } 
-											   gen.emptyStatement(),
-											   gen.block(repackingStats.toArray(new Statement[repackingStats.size()]))));
-
-			Expression result = genSuperCallNext(gen, teamDecl.binding, superArgs);									//    return cast+lift?(super._OT$callNext(..));
-			if (mapping.baseMethodSpecs[0].returnNeedsTranslation) { // FIXME(SH): per basemethod!
-				// lifting:
-				TypeBinding[]/*role,base*/ returnTypes = getReturnTypes(mapping, 0);
-				//   who is responsible for lifting: the team or the current role?
-				ReferenceBinding currentRole = mapping.scope.enclosingReceiverType();
-				Expression liftReceiver = (isRoleOfCurrentRole(currentRole, returnTypes[0]))
-					? Lifting.liftCall(mapping.scope,
-										gen.thisReference(),
-										gen.castExpression(gen.singleNameReference(IOTConstants.BASE), gen.typeReference(currentRole.baseclass()), CastExpression.RAW),
-										currentRole.baseclass(), currentRole, false)
-								// TODO: might want to extend the signature of callNext to pass the current role to avoid this lifting?
-					: genTeamThis(gen, returnTypes[0]);
-				result = Lifting.liftCall(mapping.scope,
-										  liftReceiver,
-										  gen.castExpression(result,
-												  			 gen.baseclassReference(returnTypes[1]),
-												  			 CastExpression.RAW),
-										  returnTypes[1],
-										  returnTypes[0],
-										  false,
-										  gen);
-			}
-			caseBlockStats.add(gen.returnStatement(result));
-			
-			if (caseBlockStats.size() > nLabels) { // any action added ?
-				swStatements.addAll(caseBlockStats);
-			}
-		}																											// } // end-switch
+				caseBlockStats.add(gen.ifStatement(gen.nullCheck(gen.singleNameReference(BASE_CALL_ARGS)),				//    if (baseCallArgs == null) {} { arguments[i] = ...; ... } 
+												   gen.emptyStatement(),
+												   gen.block(repackingStats.toArray(new Statement[repackingStats.size()]))));
+	
+				Expression result = genSuperCallNext(gen, teamDecl.binding, superArgs);									//    return cast+lift?(super._OT$callNext(..));
+				if (baseSpec.returnNeedsTranslation) {
+					// lifting:
+					TypeBinding[]/*role,base*/ returnTypes = getReturnTypes(mapping, 0);
+					//   who is responsible for lifting: the team or the current role?
+					ReferenceBinding currentRole = mapping.scope.enclosingReceiverType();
+					Expression liftReceiver = (isRoleOfCurrentRole(currentRole, returnTypes[0]))
+						? Lifting.liftCall(mapping.scope,
+											gen.thisReference(),
+											gen.castExpression(gen.singleNameReference(IOTConstants.BASE), gen.typeReference(currentRole.baseclass()), CastExpression.RAW),
+											currentRole.baseclass(), currentRole, false)
+									// TODO: might want to extend the signature of callNext to pass the current role to avoid this lifting?
+						: genTeamThis(gen, returnTypes[0]);
+					caseBlockStats.add(gen.localVariable(RESULT, gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), result));
+					caseBlockStats.add(gen.ifStatement(gen.singleNameReference(IS_BASE_CALL), 
+							gen.assignment(gen.singleNameReference(RESULT), 
+											Lifting.liftCall(mapping.scope,
+											  liftReceiver,
+											  gen.castExpression(gen.singleNameReference(RESULT),
+													  			 gen.baseclassReference(returnTypes[1]),
+													  			 CastExpression.RAW),
+											  returnTypes[1],
+											  returnTypes[0],
+											  false,
+											  gen)),
+							null));
+					result = gen.singleNameReference(RESULT);
+				}
+				caseBlockStats.add(gen.returnStatement(result));
+				
+				if (caseBlockStats.size() > nLabels) { // any action added ?
+					swStatements.addAll(caseBlockStats);
+				}
+			}																											// } // end-switch
+		}
 		if (swStatements.size() == 0)
 			return; // don't add useless method
 		
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/TransformStatementsVisitor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/TransformStatementsVisitor.java
index 2e822c6..7e7daac 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/TransformStatementsVisitor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/TransformStatementsVisitor.java
@@ -168,7 +168,7 @@
 	    			case OTDRE:
 		    			AstGenerator gen = new AstGenerator(messageSend);
 		    			if (args == null || args.length == 0) {
-		    				args = new Expression[] { gen.nullLiteral() };
+		    				args = new Expression[] { gen.nullLiteral(), gen.booleanLiteral(true) /*isBaseCall*/ };
 		    			} else {
 		    				Expression[] boxedArgs = new Expression[args.length];
 		    				for (int i = 0; i < args.length; i++) {
@@ -190,7 +190,8 @@
 								boxedArgs[i] = args[i];
 		    				}
 		    				args = new Expression[] {
-		    					gen.arrayAllocation(gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), 1, boxedArgs)
+		    					gen.arrayAllocation(gen.qualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT), 1, boxedArgs),
+		    					gen.booleanLiteral(true) // isBaseCall
 		    				};	
 		    			}
 		    			break;
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/ITeam.java b/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/ITeam.java
index 77250e0..b4a1fa1 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/ITeam.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/ITeam.java
@@ -217,5 +217,5 @@
 	public Object _OT$callOrigStatic(int callinId, int boundMethodId, Object[] args);
 
 	/** NOT API, used from code generated by OTDRE */
-	public Object _OT$callNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs);
+	public Object _OT$callNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs, boolean isBaseCall);
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/Team.java b/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/Team.java
index c4e064e..613bf97 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/Team.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/objectteams/Team.java
@@ -573,9 +573,10 @@
 	 *                      This id is needed for a base call.
 	 * @param args original packed arguments.
 	 * @param baseCallArgs packed arguments as provided to the base call.
+	 * @param isBaseCall true if we are invoked as a base call (vs. dispatch to the next team in the chain).
 	 * @return possibly boxed result
 	 */
-	public Object _OT$callNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs)
+	public Object _OT$callNext(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs, boolean isBaseCall)
 	{
 		return _OT$terminalCallNext(baze, teams, idx, callinIds, boundMethodId, args, baseCallArgs);
 	}
@@ -642,7 +643,7 @@
 	 */
 	public Object _OT$callReplace(IBoundBase2 baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args) {
 		// default; override with code from replace callin bindings.
-		return _OT$callNext(baze, teams, idx, callinIds, boundMethodId, args, null);
+		return _OT$callNext(baze, teams, idx, callinIds, boundMethodId, args, null, false);
 	}
 
 	/**
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/callinbinding/CallinWithTranslation.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/callinbinding/CallinWithTranslation.java
index 6c905f0..dccacd2 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/callinbinding/CallinWithTranslation.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/callinbinding/CallinWithTranslation.java
@@ -439,4 +439,54 @@
             },
             "->OK<-");
     }
+
+    public void testBug473392() {
+    	runConformTest(
+    		new String[] {
+    	"t1/MyTeam.java",
+    			"package t1;\n" + 
+    			"\n" + 
+    			"import b1.MyBase;\n" + 
+    			"\n" + 
+    			"public team class MyTeam {\n" + 
+    			"    protected team class Mid { \n" + 
+    			"        \n" + 
+    			"        protected class R playedBy MyBase {\n" + 
+    			"            callin void rm1(R r) {\n" + 
+    			"                System.out.println(\"rm1 \" + r.getClass().getName());\n" + 
+    			"                base.rm1(r);\n" + 
+    			"            }\n" + 
+    			"            callin1: rm1 <- replace id, process;\n" + 
+    			"            callin R rm2(R r) {\n" + 
+    			"                System.out.println(\"rm2\");\n" + 
+    			"                return this;\n" + 
+    			"            }\n" + 
+    			"            callin2: rm2 <- replace id\n" + 
+    			"                base when (false);\n" + 
+    			"\n" + 
+    			"            precedence callin2, callin1;\n" + 
+    			"        }\n" + 
+    			"    }\n" + 
+    			"    \n" + 
+    			"    public static void main(String[] args) {\n" + 
+    			"        new MyTeam().new Mid().activate();\n" + 
+    			"        new MyBase().process(new MyBase());\n" + 
+    			"    }\n" + 
+    			"}\n",
+    	"b1/MyBase.java",
+    			"package b1;\n" + 
+    			"\n" + 
+    			"public class MyBase {\n" + 
+    			"    MyBase id(MyBase other) { return this; }\n" + 
+    			"    public MyBase process(MyBase other) {\n" + 
+    			"        other = id(other);\n" + 
+    			"        System.out.println(other.getClass().getName());\n" + 
+    			"        return null;\n" + 
+    			"    }\n" + 
+    			"}\n"
+    		},
+    		"rm1 t1.MyTeam$__OT__Mid$__OT__R\n" + 
+			"rm1 t1.MyTeam$__OT__Mid$__OT__R\n" + 
+			"b1.MyBase");
+    }
 }