/******************************************************************************* * Copyright (c) 2008, 2016 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 * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.hidemethod; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; 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.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.ast.cpp.ICPPMember; import org.eclipse.cdt.core.dom.rewrite.ASTRewrite; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexBinding; import org.eclipse.cdt.core.index.IIndexName; import org.eclipse.cdt.core.model.CoreModelUtil; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.AccessContext; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; import org.eclipse.cdt.internal.corext.util.CModelUtil; import org.eclipse.cdt.internal.ui.editor.ITranslationUnitEditorInput; import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; import org.eclipse.cdt.internal.ui.refactoring.CRefactoringDescriptor; import org.eclipse.cdt.internal.ui.refactoring.ClassMemberInserter; import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; import org.eclipse.cdt.internal.ui.refactoring.utils.DefinitionFinder; import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper; import org.eclipse.cdt.internal.ui.refactoring.utils.VisibilityEnum; import org.eclipse.cdt.internal.ui.util.EditorUtility; /** * @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 methodName; private IASTDeclaration methodDeclaration; public HideMethodRefactoring(ICElement element, ISelection selection, ICProject project) { super(element, selection, project); name = Messages.HideMethodRefactoring_HIDE_METHOD; } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { SubMonitor sm = SubMonitor.convert(pm, 10); try { super.checkInitialConditions(sm.newChild(8)); if (initStatus.hasFatalError()) { return initStatus; } if (isProgressMonitorCanceled(sm, initStatus)) return initStatus; List<IASTName> names = findAllMarkedNames(); if (names.isEmpty()) { initStatus.addFatalError(Messages.HideMethodRefactoring_NoNameSelected); return initStatus; } IASTName name = names.get(names.size() - 1); methodName = DefinitionFinder.getMemberDeclaration(name, refactoringContext, sm.newChild(1)); if (methodName == null) { initStatus.addFatalError(Messages.HideMethodRefactoring_NoMethodNameSelected); return initStatus; } IASTDeclarator decl = (IASTDeclarator) methodName.getParent(); decl = CPPVisitor.findOutermostDeclarator(decl); methodDeclaration = (IASTDeclaration) decl.getParent(); if (methodDeclaration == null || !(methodDeclaration.getParent() instanceof ICPPASTCompositeTypeSpecifier)) { initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods); return initStatus; } if (isProgressMonitorCanceled(sm, initStatus)) return initStatus; if (methodDeclaration instanceof IASTFunctionDefinition) { IASTDeclarator declarator = ((IASTFunctionDefinition) methodDeclaration).getDeclarator(); if (ASTQueries.findInnermostDeclarator(declarator).getName().getRawSignature().equals(name.getRawSignature())) { if (!(declarator instanceof IASTFunctionDeclarator)) { initStatus.addFatalError(Messages.HideMethodRefactoring_CanOnlyHideMethods); return initStatus; } } } else if (methodDeclaration instanceof IASTSimpleDeclaration) { for (IASTDeclarator declarator : ((IASTSimpleDeclaration) methodDeclaration).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; } IASTCompositeTypeSpecifier classNode = ASTQueries.findAncestorWithType(methodName, IASTCompositeTypeSpecifier.class); if (classNode == null) { initStatus.addError(Messages.HideMethodRefactoring_EnclosingClassNotFound); } if (checkIfPrivate(classNode, methodDeclaration)) { initStatus.addError(Messages.HideMethodRefactoring_IsAlreadyPrivate); } return initStatus; } finally { sm.done(); } } 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, CheckConditionsContext checkContext) throws CoreException, OperationCanceledException { SubMonitor sm = SubMonitor.convert(pm, 10); try { RefactoringStatus status = new RefactoringStatus(); IIndex index = getIndex(); IIndexBinding methodBinding = index.adaptBinding(methodName.resolveBinding()); if (methodBinding == null) return null; List<IASTName> references = new ArrayList<IASTName>(); Set<String> searchedFiles = new HashSet<String>(); IEditorPart[] dirtyEditors = EditorUtility.getDirtyEditors(true); SubMonitor loopProgress = sm.newChild(3).setWorkRemaining(dirtyEditors.length); for (IEditorPart editor : dirtyEditors) { if (sm.isCanceled()) { throw new OperationCanceledException(); } IEditorInput editorInput = editor.getEditorInput(); if (editorInput instanceof ITranslationUnitEditorInput) { ITranslationUnit tu = CModelUtil.toWorkingCopy(((ITranslationUnitEditorInput) editorInput).getTranslationUnit()); searchedFiles.add(tu.getLocation().toOSString()); IASTTranslationUnit ast = getAST(tu, loopProgress.newChild(1)); for (IASTName reference : ast.getReferences(methodBinding)) { if (!AccessContext.isAccessible(methodBinding, ICPPMember.v_private, reference)) { status.addWarning(Messages.HideMethodRefactoring_HasExternalReferences); return status; } } } } IIndexName[] referencesFromIndex = index.findReferences(methodBinding); int remainingCount = referencesFromIndex.length; loopProgress = sm.newChild(6).setWorkRemaining(remainingCount); for (IIndexName name : referencesFromIndex) { if (sm.isCanceled()) { throw new OperationCanceledException(); } ITranslationUnit tu = CoreModelUtil.findTranslationUnitForLocation( name.getFile().getLocation(), null); if (searchedFiles.add(tu.getLocation().toOSString())) { IASTTranslationUnit ast = getAST(tu, loopProgress.newChild(1)); for (IASTName reference : ast.getReferences(methodBinding)) { if (!AccessContext.isAccessible(methodBinding, ICPPMember.v_private, reference)) { status.addWarning(Messages.HideMethodRefactoring_HasExternalReferences); return status; } } ArrayUtil.addAll(references, ast.getReferences(methodBinding)); } loopProgress.setWorkRemaining(--remainingCount); } return status; } finally { sm.done(); } } @Override protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) throws CoreException, OperationCanceledException { ASTRewrite rewriter = collector.rewriterForTranslationUnit(methodName.getTranslationUnit()); TextEditGroup editGroup = new TextEditGroup(Messages.HideMethodRefactoring_FILE_CHANGE_TEXT+ methodName.getRawSignature()); ICPPASTCompositeTypeSpecifier classDefinition = (ICPPASTCompositeTypeSpecifier) methodDeclaration.getParent(); ClassMemberInserter.createChange(classDefinition, VisibilityEnum.v_private, methodDeclaration, false, collector); rewriter.remove(methodDeclaration, editGroup); } private List<IASTName> findAllMarkedNames() throws OperationCanceledException, CoreException { final ArrayList<IASTName> namesVector = new ArrayList<IASTName>(); IASTTranslationUnit ast = getAST(tu, null); ast.accept(new ASTVisitor() { { shouldVisitNames = true; } @Override public int visit(IASTName name) { if (name.isPartOfTranslationUnitFile() && SelectionHelper.doesNodeOverlapWithRegion(name, selectedRegion)) { if (!(name instanceof ICPPASTQualifiedName)) { namesVector.add(name); } } return super.visit(name); } }); return namesVector; } @Override protected RefactoringDescriptor getRefactoringDescriptor() { Map<String, String> arguments = getArgumentMap(); RefactoringDescriptor desc = new HideMethodRefactoringDescriptor( project.getProject().getName(), "Hide Method Refactoring", "Hide Method " + methodName.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(CRefactoringDescriptor.FILE_NAME, tu.getLocationURI().toString()); arguments.put(CRefactoringDescriptor.SELECTION, selectedRegion.getOffset() + "," + selectedRegion.getLength()); //$NON-NLS-1$ return arguments; } }