/******************************************************************************* * 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; } }