/******************************************************************************* * Copyright (c) 2008, 2011 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; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; 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.ltk.core.refactoring.participants.ResourceChangeChecker; import org.eclipse.ltk.core.refactoring.participants.ValidateEditChecker; import org.eclipse.osgi.util.NLS; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTProblem; import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration; import org.eclipse.cdt.core.dom.ast.IASTProblemExpression; import org.eclipse.cdt.core.dom.ast.IASTProblemStatement; import org.eclipse.cdt.core.dom.ast.IASTProblemTypeId; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTypeId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.ISourceRange; import org.eclipse.cdt.core.model.ISourceReference; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.cdt.internal.corext.util.CModelUtil; import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper; /** * The base class for all AST based refactorings, provides some common implementations for * AST creation, condition checking, change generating, and selection handling. * This class is intended as a replacement for CRefactoring. */ public abstract class CRefactoring2 extends Refactoring { protected String name = Messages.Refactoring_name; protected final ICProject project; protected final ITranslationUnit tu; protected final RefactoringStatus initStatus; protected final RefactoringASTCache astCache; protected Region selectedRegion; public CRefactoring2(ICElement element, ISelection selection, ICProject project, RefactoringASTCache astCache) { this.project = project; this.astCache = astCache; this.initStatus= new RefactoringStatus(); if (!(element instanceof ISourceReference)) { this.tu = null; initStatus.addFatalError(Messages.Refactoring_SelectionNotValid); return; } ISourceReference sourceRef= (ISourceReference) element; tu = CModelUtil.toWorkingCopy(sourceRef.getTranslationUnit()); if (selection instanceof ITextSelection) { this.selectedRegion = SelectionHelper.getRegion(selection); } else { try { ISourceRange sourceRange = sourceRef.getSourceRange(); this.selectedRegion = new Region(sourceRange.getIdStartPos(), sourceRange.getIdLength()); } catch (CModelException e) { CUIPlugin.log(e); } } } private class ProblemFinder extends ASTVisitor { private boolean problemFound = false; private final RefactoringStatus status; public ProblemFinder(RefactoringStatus status) { this.status = status; } { shouldVisitProblems = true; shouldVisitDeclarations = true; shouldVisitExpressions = true; shouldVisitStatements = true; shouldVisitTypeIds = true; } @Override public int visit(IASTProblem problem) { addWarningToState(); return ASTVisitor.PROCESS_CONTINUE; } @Override public int visit(IASTDeclaration declaration) { if (declaration instanceof IASTProblemDeclaration) { addWarningToState(); } return ASTVisitor.PROCESS_CONTINUE; } @Override public int visit(IASTExpression expression) { if (expression instanceof IASTProblemExpression) { addWarningToState(); } return ASTVisitor.PROCESS_CONTINUE; } @Override public int visit(IASTStatement statement) { if (statement instanceof IASTProblemStatement) { addWarningToState(); } return ASTVisitor.PROCESS_CONTINUE; } @Override public int visit(IASTTypeId typeId) { if (typeId instanceof IASTProblemTypeId) { addWarningToState(); } return ASTVisitor.PROCESS_CONTINUE; } public boolean hasProblem() { return problemFound; } private void addWarningToState() { if (!problemFound) { status.addWarning(Messages.Refactoring_CompileErrorInTU); problemFound = true; } } } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { if (pm == null) pm = new NullProgressMonitor(); pm.beginTask(Messages.CRefactoring_checking_final_conditions, 6); CheckConditionsContext context = createCheckConditionsContext(); RefactoringStatus result = checkFinalConditions(new SubProgressMonitor(pm, 5), context); if (result.hasFatalError()) { pm.done(); return result; } if (pm.isCanceled()) throw new OperationCanceledException(); result.merge(context.check(new SubProgressMonitor(pm, 1))); pm.done(); return result; } protected abstract RefactoringStatus checkFinalConditions(IProgressMonitor subProgressMonitor, CheckConditionsContext checkContext) throws CoreException, OperationCanceledException; @Override public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { SubMonitor sm = SubMonitor.convert(pm, 10); sm.subTask(Messages.Refactoring_PM_LoadTU); if (isProgressMonitorCanceld(sm, initStatus)) { return initStatus; } IASTTranslationUnit ast = getAST(tu, sm); if (ast == null) { initStatus.addError(NLS.bind(Messages.Refactoring_ParsingError, tu.getPath())); return initStatus; } if (isProgressMonitorCanceld(sm, initStatus)) { return initStatus; } sm.subTask(Messages.Refactoring_PM_CheckTU); checkAST(ast); sm.worked(2); sm.subTask(Messages.Refactoring_PM_InitRef); sm.done(); return initStatus; } protected static boolean isProgressMonitorCanceld(IProgressMonitor sm, RefactoringStatus status) { if (sm.isCanceled()) { status.addFatalError(Messages.Refactoring_CanceledByUser); return true; } return false; } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { ModificationCollector collector = new ModificationCollector(); collectModifications(pm, collector); CCompositeChange finalChange = collector.createFinalChange(); finalChange.setDescription(new RefactoringChangeDescriptor(getRefactoringDescriptor())); return finalChange; } abstract protected RefactoringDescriptor getRefactoringDescriptor(); abstract protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) throws CoreException, OperationCanceledException; @Override public String getName() { return name; } protected IASTTranslationUnit getAST(ITranslationUnit tu, IProgressMonitor pm) throws CoreException, OperationCanceledException { return astCache.getAST(tu, pm); } protected boolean checkAST(IASTTranslationUnit ast) { ProblemFinder problemFinder = new ProblemFinder(initStatus); ast.accept(problemFinder); return problemFinder.hasProblem(); } protected List<IASTName> findAllMarkedNames(IASTTranslationUnit ast) { final List<IASTName> names = new ArrayList<IASTName>(); ast.accept(new ASTVisitor() { { shouldVisitNames = true; } @Override public int visit(IASTName name) { if (name.isPartOfTranslationUnitFile() && SelectionHelper.isSelectionOnExpression(selectedRegion, name) && !(name instanceof ICPPASTQualifiedName)) { names.add(name); } return super.visit(name); } }); return names; } private CheckConditionsContext createCheckConditionsContext() throws CoreException { CheckConditionsContext result= new CheckConditionsContext(); result.add(new ValidateEditChecker(getValidationContext())); result.add(new ResourceChangeChecker()); return result; } }