blob: 123429ae22f98ec853f1b1e7c7e11af85576f15e [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2010 GK Software AG
*
* 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$
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.refactoring.adaptor;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractMethodMappingDeclaration;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CallinMappingDeclaration;
import org.eclipse.jdt.core.dom.CalloutMappingDeclaration;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.MethodMappingElement;
import org.eclipse.jdt.core.dom.MethodSpec;
import org.eclipse.jdt.core.dom.ParameterMapping;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
import org.eclipse.objectteams.otdt.internal.refactoring.RefactoringMessages;
import org.eclipse.osgi.util.NLS;
import org.eclipse.text.edits.TextEditGroup;
import base org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor;
import base org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor.DeclarationUpdate;
import base org.eclipse.jdt.internal.corext.refactoring.structure.ChangeSignatureProcessor.OccurrenceUpdate;
/**
* OT/J adaptations for the "Change Signature" Refactoring.
*
* @author stephan
*/
@SuppressWarnings({"restriction", "decapsulation", "rawtypes"}) // using several DOM lists which are not generified
public team class ChangeSignatureAdaptor {
/**
* Intercept situations where a signature change affects a MethodSpec,
* and add some more changes to compensate.
*/
protected team class Processor playedBy ChangeSignatureProcessor {
List getAddedInfos() -> List getAddedInfos();
List getDeletedInfos() -> List getDeletedInfos();
boolean isOrderSameAsInitial() -> boolean isOrderSameAsInitial();
List getFParameterInfos() -> get List fParameterInfos;
// FIXME(SH): parameter name result -> error duplicate local variable result
OccurrenceUpdate createOccurrenceUpdate(ASTNode node,
CompilationUnitRewrite cuRewrite, RefactoringStatus refResult)
<- replace
OccurrenceUpdate createOccurrenceUpdate(ASTNode node,
CompilationUnitRewrite cuRewrite, RefactoringStatus refResult);
@SuppressWarnings("basecall")
callin OccurrenceUpdate createOccurrenceUpdate(ASTNode node,
CompilationUnitRewrite cuRewrite, RefactoringStatus refResult)
{
// base method cannot handle method spec, so check this first:
if (node.getNodeType() == ASTNode.METHOD_SPEC) {
analyzeImpact(node, refResult);
activate(); // ensure nested role MethodSpecUpdate receives callin triggers
return new MethodSpecUpdate((MethodSpec)node, cuRewrite, refResult);
// don't bother to deactivate(), this direct team is temporary itself.
}
return base.createOccurrenceUpdate(node, cuRewrite, refResult);
}
/* Detect situations that are not fully supported by this refactoring and create INFOs for reporting. */
void analyzeImpact(ASTNode node, RefactoringStatus refResult) {
StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
boolean isSrcSide = false;
if (locationInParent == CalloutMappingDeclaration.ROLE_MAPPING_ELEMENT_PROPERTY) {
isSrcSide = true;
} else if (locationInParent == CallinMappingDeclaration.BASE_MAPPING_ELEMENTS_PROPERTY) {
isSrcSide = true;
}
boolean hasDelections = !getDeletedInfos().isEmpty();
boolean hasAdditions = !getAddedInfos().isEmpty();
if (!isSrcSide && hasAdditions) {
if (node.getParent().getNodeType() == ASTNode.CALLIN_MAPPING_DECLARATION)
refResult.merge(RefactoringStatus.createInfoStatus(RefactoringMessages.ChangeSignatureAdaptor_callinRoleArgAddIncomplete_info));
else
refResult.merge(RefactoringStatus.createInfoStatus(RefactoringMessages.ChangeSignatureAdaptor_calloutBaseArgAddIncomplete_info));
} else if (isSrcSide && hasDelections) {
if (node.getParent().getNodeType() == ASTNode.CALLIN_MAPPING_DECLARATION)
refResult.merge(RefactoringStatus.createInfoStatus(RefactoringMessages.ChangeSignatureAdaptor_callinBaseArgDeleteIncomplete_info));
else
refResult.merge(RefactoringStatus.createInfoStatus(RefactoringMessages.ChangeSignatureAdaptor_calloutRoleArgDeleteIncomplete_info));
}
}
/* pure gateway */
protected class OccurrenceUpdate playedBy OccurrenceUpdate
when (this.fMethodSpec != null) // otherwise role was created by lifting, should not intercept anything
{
MethodSpec fMethodSpec;
void addParamterMappings() <- after void reshuffleElements(); // final method cannot be bound from subrole
void addParamterMappings() {
// do nothing here, overrides will take over
}
}
/**
* The source side of a method mapping behaves similar to a method declaration.
* In contrast to our baseclass which operates on a MethodDeclaration (fMethodDecl),
* this role operates on a MethodSpec (fMethodSpec).
* In addition to a few adaptations made necessary by the above difference,
* this role also intercepts "reshuffleElements()" in order to add parameter
* mappings that may absorb signature reordering.
*/
@SuppressWarnings("basecall") // most callins truely replace the base behaviour
protected class MethodSpecUpdate extends OccurrenceUpdate playedBy DeclarationUpdate
{
CompilationUnitRewrite getCompilationUnitRewrite() -> CompilationUnitRewrite getCompilationUnitRewrite();
ASTRewrite getASTRewrite() -> ASTRewrite getASTRewrite();
protected MethodSpecUpdate(MethodSpec node, CompilationUnitRewrite cuRewrite, RefactoringStatus refResult)
{
base(null/*MethodDeclaration*/, cuRewrite, refResult);
this.fMethodSpec = node;
}
ListRewrite getParamgumentsRewrite() <- replace ListRewrite getParamgumentsRewrite();
callin ListRewrite getParamgumentsRewrite() {
return getASTRewrite().getListRewrite(fMethodSpec, MethodSpec.PARAMETERS_PROPERTY);
}
SimpleName getMethodNameNode() <- replace SimpleName getMethodNameNode();
callin SimpleName getMethodNameNode() {
return this.fMethodSpec.getName();
}
void changeJavadocTags() <- replace void changeJavadocTags();
callin void changeJavadocTags() {
// do nothing, no javadoc tags defined for method specs
}
void addDelegate() <- replace void addDelegate();
callin void addDelegate() {
// no delegates for method spec
}
void checkIfDeletedParametersUsed() <- replace void checkIfDeletedParametersUsed();
@SuppressWarnings("inferredcallout") // almost verbatim copy from base
callin void checkIfDeletedParametersUsed() {
for (Iterator iter= getDeletedInfos().iterator(); iter.hasNext();) {
ParameterInfo info= (ParameterInfo) iter.next();
SingleVariableDeclaration paramDecl= (SingleVariableDeclaration) fMethodSpec.parameters().get(info.getOldIndex());
TempOccurrenceAnalyzer analyzer= new TempOccurrenceAnalyzer(paramDecl, false);
analyzer.perform();
SimpleName[] paramRefs= analyzer.getReferenceNodes();
if (paramRefs.length > 0){
RefactoringStatusContext context= JavaStatusContext.create(fCuRewrite.getCu(), paramRefs[0]);
String typeName= getFullTypeName(fMethodSpec);
Object[] keys= new String[]{ BasicElementLabels.getJavaElementName(paramDecl.getName().getIdentifier()),
BasicElementLabels.getJavaElementName(fMethodSpec.getName().getIdentifier()),
BasicElementLabels.getJavaElementName(typeName)};
String msg= NLS.bind(RefactoringCoreMessages.ChangeSignatureRefactoring_parameter_used, keys);
fResult.addError(msg, context);
}
}
}
/* similar but unrelated to DeclarationUpdate.getFullTypeName(MethodDeclaration). */
private String getFullTypeName(MethodSpec decl) {
ASTNode node= decl;
while (node != null) {
node= node.getParent();
if (node instanceof AbstractTypeDeclaration) {
return ((AbstractTypeDeclaration) node).getName().getIdentifier();
}
}
return "MISSING"; //$NON-NLS-1$
}
@SuppressWarnings("inferredcallout")
@Override
void addParamterMappings() {
if (isOrderSameAsInitial())
return;
AbstractMethodMappingDeclaration mapping = (AbstractMethodMappingDeclaration) this.fMethodSpec.getParent();
if (mapping.hasParameterMapping()) {
fResult.merge(RefactoringStatus.createWarningStatus(RefactoringMessages.ChangeSignatureAdaptor_cannotUpdateParameterMapping_warning));
return;
}
// collect these pieces of information:
List otherSideArguments; // arguments of the mapping side that is not affected by this refactoring
boolean isRoleSide; // whether the affect method spec is on the role side
String parMapDirection; // either of "->" or "<-"
ChildListPropertyDescriptor parMapProp; // descriptor denoting the parameter mappings property within 'mapping'
if (mapping.getNodeType() == ASTNode.CALLIN_MAPPING_DECLARATION) {
parMapProp = CallinMappingDeclaration.PARAMETER_MAPPINGS_PROPERTY;
isRoleSide = this.fMethodSpec.getLocationInParent() == CallinMappingDeclaration.ROLE_MAPPING_ELEMENT_PROPERTY;
if (isRoleSide) {
List baseMappingElements = ((CallinMappingDeclaration)mapping).getBaseMappingElements();
if (baseMappingElements.size() > 1) {
fResult.merge(RefactoringStatus.createWarningStatus(RefactoringMessages.ChangeSignatureAdaptor_cannotCreateParamMap_MultiCallin_warning));
return;
}
otherSideArguments = ((MethodSpec)baseMappingElements.get(0)).parameters();
} else {
otherSideArguments = ((MethodSpec)mapping.getRoleMappingElement()).parameters();
}
parMapDirection = "<-"; //$NON-NLS-1$
} else {
parMapProp = CalloutMappingDeclaration.PARAMETER_MAPPINGS_PROPERTY;
isRoleSide = this.fMethodSpec.getLocationInParent() == CalloutMappingDeclaration.ROLE_MAPPING_ELEMENT_PROPERTY;
if (isRoleSide) {
MethodMappingElement baseMappingElement = ((CalloutMappingDeclaration)mapping).getBaseMappingElement();
if (baseMappingElement.getNodeType() == ASTNode.FIELD_ACCESS_SPEC) {
fResult.merge(RefactoringStatus.createWarningStatus(RefactoringMessages.ChangeSignatureAdaptor_cannotCreateParamMap_CTF_warning));
return;
}
otherSideArguments = ((MethodSpec)baseMappingElement).parameters();
} else {
otherSideArguments = ((MethodSpec)mapping.getRoleMappingElement()).parameters();
}
parMapDirection = "->"; //$NON-NLS-1$
}
maybeAddParamMappings(mapping, parMapProp, otherSideArguments, getFParameterInfos(), parMapDirection);
}
void maybeAddParamMappings(AbstractMethodMappingDeclaration mapping,
ChildListPropertyDescriptor parMapProp,
List otherSideArguments,
List parameterInfos,
String parMapDirection)
{
checkRelevance: {
for (int i= 0; i < parameterInfos.size(); i++) {
int oldIndex= ((ParameterInfo) parameterInfos.get(i)).getOldIndex();
if ( oldIndex > -1
&& oldIndex < otherSideArguments.size()
&& oldIndex != i)
{
break checkRelevance;
}
}
return; // no change relevant for a parameter mapping
}
AST ast = this.fMethodSpec.getAST();
TextEditGroup editGroup = new TextEditGroup("parametermappings"); //$NON-NLS-1$
ListRewrite parMapRewrite = getASTRewrite().getListRewrite(mapping, parMapProp);
for (int i= 0; i < parameterInfos.size(); i++) {
ParameterInfo info= (ParameterInfo) parameterInfos.get(i);
int oldIndex= info.getOldIndex();
if (oldIndex > -1 && oldIndex < otherSideArguments.size()) {
SingleVariableDeclaration arg = (SingleVariableDeclaration) otherSideArguments.get(oldIndex);
ParameterMapping parMap = ast.newParameterMapping();
parMap.setIdentifier((SimpleName) ASTNode.copySubtree(ast, arg.getName()));
parMap.setExpression(ast.newSimpleName(info.getNewName()));
parMap.setDirection(parMapDirection);
parMapRewrite.insertLast(parMap, editGroup);
}
}
}
}
}
}