/*******************************************************************************
* Copyright (c) 2008, 2009 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences and others
* 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
*
* Contributors:
* Institute for Software - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.hidemethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.ui.refactoring.AddDeclarationNodeToClassChange;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoringDescription;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.utils.DeclarationFinder;
import org.eclipse.cdt.internal.ui.refactoring.utils.DeclarationFinderDO;
import org.eclipse.cdt.internal.ui.refactoring.utils.ExpressionFinder;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.TranslationUnitHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.VisibilityEnum;
/**
* @author Guido Zgraggen IFS
*/
public class HideMethodRefactoring extends CRefactoring {
public static final String ID = "org.eclipse.cdt.internal.ui.refactoring.hidemethod.HideMethodRefactoring"; //$NON-NLS-1$
private IASTName methodToHide;
private IASTDeclaration methodToHideDecl;
private DeclarationFinderDO declData;
public HideMethodRefactoring(IFile file, ISelection selection, ICElement element, ICProject project) {
super(file, selection, element, project);
name = Messages.HideMethodRefactoring_HIDE_METHOD;
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
SubMonitor sm = SubMonitor.convert(pm, 10);
try {
lockIndex();
try {
super.checkInitialConditions(sm.newChild(6));
if (initStatus.hasFatalError()) {
return initStatus;
}
if (isProgressMonitorCanceld(sm, initStatus)) return initStatus;
IASTName name;
ArrayList<IASTName> names = findAllMarkedNames();
if (names.size() < 1) {
initStatus.addFatalError(Messages.HideMethodRefactoring_NoNameSelected);
return initStatus;
}
name = names.get(names.size()-1);
sm.worked(1);
if (isProgressMonitorCanceld(sm, initStatus)) return initStatus;
declData = DeclarationFinder.getDeclaration(name, getIndex());
if (declData == null || declData.name == null) {
initStatus.addFatalError(Messages.HideMethodRefactoring_NoMethodNameSelected);
return initStatus;
}
methodToHide = declData.name;
sm.worked(1);
methodToHideDecl = NodeHelper.findSimpleDeclarationInParents(methodToHide);
if (methodToHideDecl == null) {
initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods);
return initStatus;
}
if (!(methodToHideDecl.getParent() instanceof ICPPASTCompositeTypeSpecifier)) {
methodToHideDecl = NodeHelper.findFunctionDefinitionInAncestors(methodToHide);
}
if (isProgressMonitorCanceld(sm, initStatus)) return initStatus;
sm.worked(1);
if (methodToHideDecl instanceof IASTFunctionDefinition) {
IASTDeclarator declarator = ((IASTFunctionDefinition)methodToHideDecl).getDeclarator();
if (CPPVisitor.findInnermostDeclarator(declarator).getName().getRawSignature().equals(name.getRawSignature())) {
if (!(declarator instanceof IASTFunctionDeclarator)) {
initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods);
return initStatus;
}
}
} else if (methodToHideDecl instanceof IASTSimpleDeclaration) {
for(IASTDeclarator declarator : ((IASTSimpleDeclaration) methodToHideDecl).getDeclarators()) {
if (declarator.getName().getRawSignature().equals(name.getRawSignature())) {
if (!(declarator instanceof IASTFunctionDeclarator)) {
initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods);
return initStatus;
}
}
}
} else {
initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods);
return initStatus;
}
sm.worked(1);
IASTCompositeTypeSpecifier classNode = NodeHelper.findClassInAncestors(methodToHide);
if (classNode == null) {
initStatus.addError(Messages.HideMethodRefactoring_EnclosingClassNotFound);
}
if (checkIfPrivate(classNode, methodToHideDecl)) {
initStatus.addError(Messages.HideMethodRefactoring_IsAlreadyPrivate);
}
sm.done();
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return initStatus;
}
private boolean checkIfPrivate(IASTCompositeTypeSpecifier classNode, IASTDeclaration decl) {
IASTDeclaration[] members = classNode.getMembers();
int currentVisibility = ICPPASTVisibilityLabel.v_private;
if (IASTCompositeTypeSpecifier.k_struct == classNode.getKey()) {
currentVisibility = ICPPASTVisibilityLabel.v_public;
}
for (IASTDeclaration declaration : members) {
if (declaration instanceof ICPPASTVisibilityLabel) {
currentVisibility =((ICPPASTVisibilityLabel) declaration).getVisibility();
}
if (declaration != null) {
if (decl == declaration) {
break;
}
}
}
if (ICPPASTVisibilityLabel.v_private == currentVisibility) {
return true;
}
return false;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
RefactoringStatus finalConditions = null;
try {
lockIndex();
try {
finalConditions = super.checkFinalConditions(pm);
for(IIndexName pdomref : declData.allNamesPDom) {
declData.filename = pdomref.getFileLocation().getFileName();
if (pdomref instanceof PDOMName) {
PDOMName pdomName = (PDOMName)pdomref;
if (pdomName.isDeclaration()) {
continue;
}
if (pdomName.isDefinition()) {
continue;
}
}
IASTTranslationUnit transUtmp = TranslationUnitHelper.loadTranslationUnit(declData.filename, false);
IASTName expName = ExpressionFinder.findExpressionInTranslationUnit(transUtmp, pdomref);
IASTFunctionDeclarator funcDec = findEnclosingFunction(expName);
IASTCompositeTypeSpecifier encClass2;
if (funcDec == null) {
encClass2 = NodeHelper.findClassInAncestors(expName);
}
else {
encClass2 = NodeHelper.findClassInAncestors(funcDec);
}
IASTCompositeTypeSpecifier encClass = NodeHelper.findClassInAncestors(methodToHide);
if (!NodeHelper.isSameNode(encClass, encClass2)) {
finalConditions.addWarning(Messages.HideMethodRefactoring_HasExternalReferences);
break;
}
}
return finalConditions;
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return finalConditions;
}
private IASTFunctionDeclarator findEnclosingFunction(IASTNode node) throws CoreException {
IASTCompoundStatement compStat = NodeHelper.findCompoundStatementInAncestors(node);
if (compStat == null) {
return null;
}
IASTNode parent = compStat.getParent();
if (parent instanceof IASTFunctionDefinition) {
IASTDeclarator declarator = ((IASTFunctionDefinition)parent).getDeclarator();
IASTName declaratorName = getLastName(CPPVisitor.findInnermostDeclarator(declarator));
DeclarationFinderDO data = DeclarationFinder.getDeclaration(declaratorName, getIndex());
if (data == null || data.name == null) {
return null;
}
if (data.name.getParent() instanceof IASTFunctionDeclarator) {
return (IASTFunctionDeclarator) data.name.getParent();
}
return null;
} else if (parent instanceof IASTTranslationUnit) {
return null;
}
return findEnclosingFunction(parent);
}
private IASTName getLastName(IASTDeclarator declarator) {
IASTName declaratorName = declarator.getName();
if (declaratorName instanceof ICPPASTQualifiedName) {
IASTName[] declaratorNames = ((ICPPASTQualifiedName) declaratorName).getNames();
declaratorName = declaratorNames[declaratorNames.length-1];
}
return declaratorName;
}
@Override
protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) throws CoreException, OperationCanceledException {
try {
lockIndex();
try {
ASTRewrite rewriter = collector.rewriterForTranslationUnit(declData.transUnit);
TextEditGroup editGroup = new TextEditGroup(Messages.HideMethodRefactoring_FILE_CHANGE_TEXT+ methodToHide.getRawSignature());
ICPPASTCompositeTypeSpecifier classDefinition = (ICPPASTCompositeTypeSpecifier) methodToHideDecl.getParent();
AddDeclarationNodeToClassChange.createChange(classDefinition, VisibilityEnum.v_private, methodToHideDecl, false, collector);
rewriter.remove(methodToHideDecl, editGroup);
} finally {
unlockIndex();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
protected RefactoringDescriptor getRefactoringDescriptor() {
Map<String, String> arguments = getArgumentMap();
RefactoringDescriptor desc = new HideMethodRefactoringDescription( project.getProject().getName(), "Hide Method Refactoring", "Hide Method " + methodToHide.getRawSignature(), arguments); //$NON-NLS-1$//$NON-NLS-2$
return desc;
}
private Map<String, String> getArgumentMap() {
Map<String, String> arguments = new HashMap<String, String>();
arguments.put(CRefactoringDescription.FILE_NAME, file.getLocationURI().toString());
arguments.put(CRefactoringDescription.SELECTION, region.getOffset() + "," + region.getLength()); //$NON-NLS-1$
return arguments;
}
}