blob: 584d0569a0ee96b7907ff392dc30c2ba3c1caf77 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2003, 2019 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
* $Id: MethodMappingResolver.java 23416 2010-02-03 19:59:31Z stephan $
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.mappings;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
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;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
/**
* This class implements the second-but-last OT-specific translation:
* Prepare for transforming callout/in mappings, by resolving method specs,
* type-checking etc.
*
* @author macwitte/haebor
* @version $Id: MethodMappingResolver.java 23416 2010-02-03 19:59:31Z stephan $
*/
public class MethodMappingResolver
{
private RoleModel _role;
private ClassScope _roleScope;
/** Index is the methods name+signature */
private Map<String, List<CalloutMappingDeclaration>> _foundRoleMethods
= new HashMap<String, List<CalloutMappingDeclaration>>();
boolean resolveBaseMethods;
/**
* @param role
*/
public MethodMappingResolver(RoleModel role, boolean resolveBaseMethods)
{
this._role = role;
this._roleScope = role.getAst().scope; // we definitely have an AST here
this.resolveBaseMethods = resolveBaseMethods;
}
/**
* Main entry for STATE_MAPPINGS_RESOLVED
*/
public boolean resolve(boolean doCallout)
{
AbstractMethodMappingDeclaration[] methodMappings =
this._role.getAst().callinCallouts;
if(methodMappings == null || methodMappings.length == 0)
{
// there are no mappings in this role!
return true;
}
// just in case this step was skipped when doing STATE_LENV_DONE_FIELDS_AND_METHODS:
this._roleScope.buildCallinCallouts();
boolean result = true;
for (int idx = 0; idx < methodMappings.length; idx++)
{
AbstractMethodMappingDeclaration methodMapping = methodMappings[idx];
if (methodMapping.isCallout() != doCallout)
continue;
if (!this._role.hasBaseclassProblem()) {
if (this._role.getBinding().baseclass() == null) {
this._roleScope.problemReporter().methodMappingNotInBoundRole(methodMapping, this._role.getAst());
methodMapping.tagAsHavingErrors();
this.resolveBaseMethods = false;
} else if (methodMapping.isCallin() && this._role.getBinding().baseclass().isInterface()) {
this._roleScope.problemReporter().callinBindingToInterface(methodMapping, this._role.getBinding().baseclass());
methodMapping.tagAsHavingErrors();
this.resolveBaseMethods = false;
}
}
methodMapping.resolveAnnotations();
if(methodMapping.isCallout())
{
if (this._role._playedByEnclosing) {
methodMapping.scope.problemReporter().calloutToEnclosing((CalloutMappingDeclaration)methodMapping, this._role);
result = false;
} else {
result &= resolveCalloutMapping((CalloutMappingDeclaration) methodMapping);
}
}
else // callin:
{
markOverriding((CallinMappingDeclaration) methodMapping);
result &= resolveCallinMapping((CallinMappingDeclaration) methodMapping);
}
}
if (doCallout) {
// check for double callout mappings
for (Iterator<String> iter = this._foundRoleMethods.keySet().iterator(); iter.hasNext();)
{
String roleMethodKey = iter.next();
result &= checkForDuplicateMethodMappings(roleMethodKey);
}
}
return result;
}
private void markOverriding(CallinMappingDeclaration mapping) {
if (!mapping.hasName())
return;
TeamModel currentTeam = this._role.getTeamModel();
RoleModel superRole = this._role.getExplicitSuperRole();
while (superRole != null) {
if (superRole.getTeamModel() != currentTeam)
break;
superRole.markCallinOverride(mapping.name, this._role);
superRole = superRole.getExplicitSuperRole();
}
}
/**
* Resolve everything about a callin binding except for argument mappings if present.
* Reports as many errors as can be found.
* @return true if no error occurred
*/
private boolean resolveCallinMapping(CallinMappingDeclaration callinMappingDeclaration)
{
// main resolving task:
callinMappingDeclaration.resolveMethodSpecs(this._role, this._role.getBaseTypeBinding(), this.resolveBaseMethods);
callinMappingDeclaration.binding._roleMethodBinding = callinMappingDeclaration.getRoleMethod();
return callinMappingDeclaration.getRoleMethod() != null;
}
/**
* Resolve everything about a callin binding except for argument mappings if present.
* Reports as many errors as can be found.
* @return true if no error occurred
*/
private boolean resolveCalloutMapping(CalloutMappingDeclaration calloutMappingDeclaration)
{
if (calloutMappingDeclaration.scope == null) { // severe error
assert calloutMappingDeclaration.hasErrors();
return false;
}
// main resolving task:
// A callout-with-signatures should always resolve its base method,
// because that base method could determine the static flag.
calloutMappingDeclaration.resolveMethodSpecs(this._role,this._role.getBaseTypeBinding(),
this.resolveBaseMethods||calloutMappingDeclaration.hasSignature);
// This binding is part of the interface part of a role:
MethodBinding roleMethodBinding = calloutMappingDeclaration.roleMethodSpec.resolvedMethod;
calloutMappingDeclaration.binding._roleMethodBinding = roleMethodBinding;
if (this.resolveBaseMethods) {
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
&& (roleMethodBinding.isValidBinding()
|| (roleMethodBinding.problemId() == ProblemReasons.NotFound && calloutMappingDeclaration.hasSignature))) // short-hand, method will be generated
{
// store the methodMapping in a map indexed by the role method's name&signature
// for later duplication check.
String methodKey = String.valueOf(CharOperation.concat(roleMethodBinding.selector, roleMethodBinding.signature()));
List<CalloutMappingDeclaration> mappings = this._foundRoleMethods.get(methodKey);
if (mappings == null)
{
mappings = new LinkedList<CalloutMappingDeclaration>();
this._foundRoleMethods.put(methodKey, mappings);
}
mappings.add(calloutMappingDeclaration);
}
return !calloutMappingDeclaration.hasErrors();
}
/**
* Report errors if there are more than one mapping for the given
* role-method.
* @param roleMethodKey name&signature of method to check
* @return true if there's only one mapping for the method else false.
*/
private boolean checkForDuplicateMethodMappings(String roleMethodKey)
{
List<CalloutMappingDeclaration> methodSpecs = this._foundRoleMethods.get(roleMethodKey);
if (methodSpecs.size() > 1)
{
for (Iterator<CalloutMappingDeclaration> iter = methodSpecs.iterator(); iter.hasNext();)
{
CalloutMappingDeclaration mapping = iter.next();
if (TypeBinding.equalsEquals(mapping.binding._declaringRoleClass, this._role.getBinding()))
{
mapping.scope.problemReporter().duplicateCalloutBinding(
this._role.getAst(), mapping.roleMethodSpec);
}
}
return false;
}
return true; // no dupes found
}
}