Partial fix for Bug 352914 - Avoid unnecessary decapsulation accessors for callout to field
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
index c5aafc2..aa7eceb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
@@ -149,27 +149,37 @@
    					(ReferenceBinding)fieldLeafType, resolvedType().dimensions());
    		}
 
-   		// find accessor method which might have been generated already.
-		char[] accessorSelector = getSelector();
-		MethodBinding result = null;
-		if (!this.resolvedField.isPrivate()) // don't reuse accessor to private field from super-base (see Trac #232)
-			result = baseType.getMethod(scope, accessorSelector);
-		// NOTE: could be optimized if type has no such method but exact type already has.
-		//       but the the OTRE would need to be informed..
+   		if (   !baseType.isRole()
+   			&& this.calloutModifier == TerminalTokens.TokenNameget
+   			&& this.resolvedField.canBeSeenBy(scope.enclosingReceiverType(), this, scope)) 
+   		{
+   			// no accessor method needed, signal this fact by NOT setting selector to accessorSelector 
+   			// and not setting resolvedMethod below
+   			this.parameters = Binding.NO_PARAMETERS;
+   			return;
+   		} else {
+	   		// find accessor method which might have been generated already.
+			char[] accessorSelector = getSelector();
+			MethodBinding result = null;
+			if (!this.resolvedField.isPrivate()) // don't reuse accessor to private field from super-base (see Trac #232)
+				result = baseType.getMethod(scope, accessorSelector);
+			// NOTE: could be optimized if type has no such method but exact type already has.
+			//       but the the OTRE would need to be informed..
 
-		if (   result == null
-		    || !isMethodCompatible(result))
-		{
-			// record this field access for Attribute generation:
-			RoleModel roleModel = scope.enclosingSourceType().roleModel;
-			ReferenceBinding targetClass =  roleModel.addAccessedBaseField(this.resolvedField, this.calloutModifier);
+			if (   result == null
+			    || !isMethodCompatible(result))
+			{
+				// record this field access for Attribute generation:
+				RoleModel roleModel = scope.enclosingSourceType().roleModel;
+				ReferenceBinding targetClass =  roleModel.addAccessedBaseField(this.resolvedField, this.calloutModifier);
 
-			// create accessor method:
-			result = createMethod(targetClass, accessorSelector);
-			baseType.addMethod(result);
-		}
-		this.selector = accessorSelector;
-		this.resolvedMethod = result;
+				// create accessor method:
+				result = createMethod(targetClass, accessorSelector);
+				baseType.addMethod(result);
+			}
+			this.selector = accessorSelector;
+			this.resolvedMethod = result;
+   		}
 		this.parameters = this.resolvedMethod.getSourceParameters();
     }
 
@@ -243,6 +253,8 @@
      * (only staticness of the method makes it look differently.)
      */
     public TypeBinding[] resolvedParameters() {
+    	if (this.resolvedMethod == null)
+    		return this.parameters; // empty; FIXME(SH): handle "set" access
     	TypeBinding[] methodParams = super.resolvedParameters();
     	if (this.resolvedField.isStatic())
     		return methodParams; // no base argument when accessing a static field
@@ -276,13 +288,21 @@
 
 	public boolean checkBaseReturnType(CallinCalloutScope scope, int bindDir)
 	{
-		TypeBinding accessorReturnType = (this.calloutModifier  == TerminalTokens.TokenNameget) ?
-				(this.returnType != null ? this.returnType.resolvedType : null):
-				TypeBinding.VOID;
-		if (!TypeAnalyzer.isSameType(scope.enclosingSourceType(), accessorReturnType, this.resolvedMethod.returnType))
+		TypeBinding accessorReturnType;
+		TypeBinding baseReturnType;
+		if (this.calloutModifier  == TerminalTokens.TokenNameget) {
+			accessorReturnType = this.returnType != null ? this.returnType.resolvedType : null;
+			baseReturnType = this.resolvedField.type;
+		} else {
+			accessorReturnType = TypeBinding.VOID;
+			if (this.resolvedMethod != null)
+				baseReturnType = this.resolvedMethod.returnType;
+			else
+				baseReturnType = TypeBinding.VOID;
+		}
+		if (!TypeAnalyzer.isSameType(scope.enclosingSourceType(), accessorReturnType, baseReturnType))
 		{
-			// copied from MethodSpec, but may not be needed:
-			if (RoleTypeCreator.isCompatibleViaBaseAnchor(scope, accessorReturnType, this.returnType.resolvedType, bindDir))
+			if (RoleTypeCreator.isCompatibleViaBaseAnchor(scope, baseReturnType, accessorReturnType, bindDir))
 				return true;
 
 			scope.problemReporter().differentTypeInFieldSpec(this);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
index e858758..a1d9320 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
@@ -357,7 +357,7 @@
     /** Answer parameters of the resolved method (source visible ones only). */
     public TypeBinding[] resolvedParameters() {
     	// parameters are first choice, because these might be instantiated parameters
-    	if (this.parameters == null || this.parameters == Binding.NO_PARAMETERS)
+    	if ((this.parameters == null || this.parameters == Binding.NO_PARAMETERS) && this.resolvedMethod != null)
     		this.parameters = this.resolvedMethod.getSourceParameters();
 
     	return this.parameters;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
index 5b3ebb8..af980fe 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
@@ -23,6 +23,7 @@
 import org.eclipse.jdt.core.compiler.CharOperation;
 import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
@@ -53,6 +54,7 @@
     public MethodBinding    _roleMethodBinding;
     public ReferenceBinding _declaringRoleClass;
     public MethodBinding[]  _baseMethods = Binding.NO_METHODS;
+    public FieldBinding 	_baseField;
     public int              type;
     public int              callinModifier;  // TerminalTokens: before,after,replace
     public int              calloutModifier; // TerminalTokens: get or set
@@ -203,6 +205,8 @@
     		for (MethodBinding method : this._baseMethods)
     			if (!method.isValidBinding())
     				return method.problemId();
+    	if (this._baseField != null)
+    		return this._baseField.problemId();
     	return ProblemReasons.NoError;
     }
 
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
index 65e912a..f01fa1f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
@@ -551,45 +551,54 @@
 
         char[] selector = calloutDecl.baseMethodSpec.selector;
         ReferenceBinding baseType = this._role.getBaseTypeBinding();
-		Expression receiver = new CastExpression(
+		Expression receiver = gen.castExpression(
 							gen.baseNameReference(IOTConstants._OT_BASE),
 							gen.baseclassReference(baseType),
 							CastExpression.DO_WRAP);
 
-		MessageSend messageSend;
-    	if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()) {
-    		// tricky case: callout to a private role method (base-side)
-    		// requires the indirection via two wrapper methods (privateBridgeMethod)
-
-    		// compensate weakening:
-    		if (baseType instanceof WeakenedTypeBinding)
-    			baseType = ((WeakenedTypeBinding)baseType).getStrongType();
-
-    		// generated message send refers to public bridge, report decapsulation now:
-    		calloutDecl.scope.problemReporter().decapsulation(calloutDecl.baseMethodSpec, baseType, calloutDecl.scope);
-
-    		boolean isCalloutToField = calloutDecl.isCalloutToField();
-    		MethodBinding targetMethod = calloutDecl.baseMethodSpec.resolvedMethod;
-	    	messageSend = new PrivateRoleMethodCall(receiver, selector, arguments, isCalloutToField, 
-	    											calloutDecl.scope, baseType, targetMethod, gen);
-    	} else {
-    		if (calloutDecl.baseMethodSpec.isStatic() || calloutDecl.isCalloutToField())
-    			// we thought we should use an instance
-    			// but callout-to-static and normal c-t-f is sent to the base *class*
-    			receiver = gen.baseNameReference(baseType.getRealClass());
-
-    		messageSend = gen.messageSend(receiver, selector, arguments);
+		Expression baseAccess = null;
+		if (calloutDecl.isCalloutToField()) {
+			FieldAccessSpec fieldSpec = (FieldAccessSpec) calloutDecl.baseMethodSpec;
+			if (fieldSpec.resolvedMethod == null) {
+				// not using a decapsulation accessor but direct field access:
+				baseAccess = gen.qualifiedNameReference(new char[][] {
+						IOTConstants._OT_BASE,
+						fieldSpec.getFieldName()
+				});
+			}
+		} 
+		if (baseAccess == null) {
+			if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()) {
+	    		// tricky case: callout to a private role method (base-side)
+	    		// requires the indirection via two wrapper methods (privateBridgeMethod)
+	
+	    		// compensate weakening:
+	    		if (baseType instanceof WeakenedTypeBinding)
+	    			baseType = ((WeakenedTypeBinding)baseType).getStrongType();
+	
+	    		// generated message send refers to public bridge, report decapsulation now:
+	    		calloutDecl.scope.problemReporter().decapsulation(calloutDecl.baseMethodSpec, baseType, calloutDecl.scope);
+	
+	    		boolean isCalloutToField = calloutDecl.isCalloutToField();
+	    		MethodBinding targetMethod = calloutDecl.baseMethodSpec.resolvedMethod;
+		    	baseAccess = new PrivateRoleMethodCall(receiver, selector, arguments, isCalloutToField,
+		    										   calloutDecl.scope, baseType, targetMethod, gen);
+	    	} else {
+	    		if (calloutDecl.baseMethodSpec.isStatic() || calloutDecl.isCalloutToField())
+	    			// we thought we should use an instance
+	    			// but callout-to-static and normal c-t-f is sent to the base *class*
+	    			receiver = gen.baseNameReference(baseType.getRealClass());
+	
+	    		baseAccess = gen.messageSend(receiver, selector, arguments);
+	    	}
     	}
-        // TODO(SH): create receiver via gen, too.
-        messageSend.receiver.sourceStart = sStart;
-        messageSend.receiver.sourceEnd   = sEnd;
 
         boolean success = true;
         ArrayList<Statement> statements = new ArrayList<Statement>(3);
         if(returnType == TypeBinding.VOID)
 		{
        		// just the method call
-        	statements.add(messageSend);
+        	statements.add(baseAccess);
 
         	// generate empty return statement so that it gets a proper source position
         	statements.add(gen.returnStatement(null));
@@ -601,7 +610,7 @@
 						gen.returnStatement(
 								gen.potentialLift(
 										null,     // use default receiver
-										messageSend,
+										baseAccess,
 										returnType,
 										false))); // no reversing required
 			}
@@ -610,7 +619,7 @@
 				// return result with parameter mapping
 				success = transformCalloutMethodBodyResultMapping(
 					statements,
-					messageSend,
+					baseAccess,
 					calloutDecl,
 					roleMethodDeclaration);
 			}
@@ -1283,7 +1292,11 @@
 		callout.resolveMethodSpecs(roleClass.getRoleModel(), baseclass, true);
 		
 		calloutBinding.inferred= isSetter ? InferenceKind.FIELDSET : InferenceKind.FIELDGET;
-		calloutBinding._baseMethods = new MethodBinding[] {callout.baseMethodSpec.resolvedMethod};
+		if (callout.baseMethodSpec.resolvedMethod != null) {			
+			calloutBinding._baseMethods = new MethodBinding[] {callout.baseMethodSpec.resolvedMethod};
+		} else {
+			calloutBinding._baseField = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
+		}
 		roleClass.binding.addCallinCallouts(new CallinCalloutBinding[]{calloutBinding});
 
 		// translate to method declaration:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
index aadbd4b..163b403 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
@@ -34,6 +34,8 @@
 import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration;
 import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
 import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec;
 import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
 
 
@@ -163,13 +165,18 @@
 
 		calloutMappingDeclaration.binding._roleMethodBinding = roleMethodBinding;
 		if (this.resolveBaseMethods) {
-			if (   calloutMappingDeclaration.baseMethodSpec != null
-				&& calloutMappingDeclaration.baseMethodSpec.resolvedMethod != null)
-			{
-				calloutMappingDeclaration.binding._baseMethods = new MethodBinding[]{calloutMappingDeclaration.baseMethodSpec.resolvedMethod};
-			} else {
-				calloutMappingDeclaration.binding._baseMethods = Binding.NO_METHODS;
-				calloutMappingDeclaration.tagAsHavingErrors();
+			MethodSpec baseMethodSpec = calloutMappingDeclaration.baseMethodSpec;
+			if ( baseMethodSpec != null) {				
+				if (baseMethodSpec.resolvedMethod != null) {
+					calloutMappingDeclaration.binding._baseMethods = new MethodBinding[]{baseMethodSpec.resolvedMethod};
+				} else if (   baseMethodSpec instanceof FieldAccessSpec
+						   && ((FieldAccessSpec)baseMethodSpec).resolvedField != null) 
+				{
+					calloutMappingDeclaration.binding._baseField = ((FieldAccessSpec)baseMethodSpec).resolvedField;
+				} else {
+					calloutMappingDeclaration.binding._baseMethods = Binding.NO_METHODS;
+					calloutMappingDeclaration.tagAsHavingErrors();
+				}
 			}
 		}
 		if (   roleMethodBinding != null