blob: da7efe21b30bc99edde65b0ed82ecbe3748a726a [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2016 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* 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
* $Id: SelectionOnBaseCallMessageSend.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.codeassist;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.internal.codeassist.select.SelectionNodeFound;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
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.BaseCallMessageSend;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
/**
* @author haebor
*/
public class SelectionOnBaseCallMessageSend extends BaseCallMessageSend
{
/**
* @param wrappee
*/
public SelectionOnBaseCallMessageSend(MessageSend wrappee, int baseEndPosition)
{
super(wrappee, baseEndPosition);
}
public TypeBinding resolveType(BlockScope scope)
{
try {
super.resolveType(scope);
} catch (SelectionNodeFound snf) {
if ( (snf.binding instanceof MethodBinding)
&& MethodModel.isFakedMethod((MethodBinding)snf.binding))
{
// fake method (e.g. basecall surrogate) is not valid, continue below
} else {
throw snf;
}
}
MessageSend wrappee = this._sendOrig; // _wrappee might have been replaced with an Assignment.
// tolerate some error cases
if(wrappee.binding == null ||
!(wrappee.binding.isValidBinding() ||
wrappee.binding.problemId() == ProblemReasons.NotVisible
|| wrappee.binding.problemId() == ProblemReasons.InheritedNameHidesEnclosingName
|| wrappee.binding.problemId() == ProblemReasons.NonStaticReferenceInConstructorInvocation
|| wrappee.binding.problemId() == ProblemReasons.NonStaticReferenceInStaticContext)) {
throw new SelectionNodeFound();
}
else
{
// wrappee.binding is the base call surrogate, find the proper enclosing method instead:
MethodScope methodScope = scope.methodScope();
if (methodScope == null)
throw new SelectionNodeFound();
SourceTypeBinding site = scope.enclosingSourceType();
MethodDeclaration callinDecl = findEnclosingCallinMethod(methodScope, null);
if (callinDecl == null)
throw new SelectionNodeFound();
MethodBinding callinMethod = callinDecl.binding;
MemberTypeBinding role = site.isLocalType() ? (MemberTypeBinding)callinMethod.declaringClass : (MemberTypeBinding)site;
// find base methods bound in callin mappings which have callinMethod as their role method:
MethodBinding[] baseBindings = findBaseMethodBindings(role, callinMethod);
if(baseBindings == null || baseBindings.length == 0) {
throw new SelectionNodeFound();
} else {
throw new SelectionNodesFound(baseBindings);
}
}
}
/**
* Looks up methods in the corresponding base class that match the roleMethod
* @param role
* @param roleMethod
* @return array of matching methods or null.
*/
private MethodBinding[] findBaseMethodBindings(MemberTypeBinding role, MethodBinding roleMethod)
{
List<CallinMappingDeclaration> foundMappings = findMappings(role, roleMethod);
ReferenceBinding baseClass = role.getRealClass().baseclass();
if(foundMappings.isEmpty() || baseClass == null)
{
return null;
}
int baseMethodsCount = 0;
for (CallinMappingDeclaration mapping : foundMappings)
baseMethodsCount += mapping.baseMethodSpecs.length;
MethodBinding[] baseMethodBindings = new MethodBinding[baseMethodsCount];
for (CallinMappingDeclaration foundMapping : foundMappings)
for(int idx = 0; idx < foundMapping.baseMethodSpecs.length; idx++)
baseMethodBindings[--baseMethodsCount] = foundMapping.baseMethodSpecs[idx].resolvedMethod;
return baseMethodBindings;
}
/**
* Try to find all CallinMethodMappings that reference the given roleMethod
* @param role Where to look for
* @param roleMethod Method that should be referenced by a CallinMapping
* @return non-null list of matching callin mappings.
*/
private List<CallinMappingDeclaration> findMappings(MemberTypeBinding role, MethodBinding roleMethod)
{
TypeDeclaration roleDeclaration = role.model.getAst();
ArrayList<CallinMappingDeclaration> foundMappings = new ArrayList<CallinMappingDeclaration>();
AbstractMethodMappingDeclaration[] mappings = roleDeclaration.callinCallouts;
if(mappings != null) {
for(int idx = 0; idx < mappings.length; idx++)
{
if(mappings[idx].binding._roleMethodBinding == roleMethod)
{
if(mappings[idx] instanceof CallinMappingDeclaration)
{
foundMappings.add((CallinMappingDeclaration)mappings[idx]);
}
}
}
}
return foundMappings;
}
public StringBuffer printExpression(int indent, StringBuffer output)
{
MessageSend wrappee = (MessageSend) this._wrappee;
output.append("<SelectOnBaseCallMessageSend:"); //$NON-NLS-1$
if (!wrappee.receiver.isImplicitThis()) wrappee.receiver.printExpression(0, output).append('.');
output.append(wrappee.selector).append('(');
if (wrappee.arguments != null)
{
for (int i = 1; i < wrappee.arguments.length; i++) // 1: skip initial arg 'isSuperAccess'
{
if (i > 0) output.append(", "); //$NON-NLS-1$
wrappee.arguments[i].printExpression(0, output);
}
}
return output.append(")>"); //$NON-NLS-1$
}
}