/*******************************************************************************
* 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;
import java.util.ArrayList;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
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.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.osgi.util.NLS;
import org.eclipse.cdt.core.CCorePlugin;
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.index.IIndex;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
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.ui.refactoring.utils.SelectionHelper;
/**
* The base class for all other refactorings, provides some common implementations for
* condition checking, change generating, selection handling and translation unit loading.
* @deprecated Use CRefactoring2.
*/
@Deprecated
public abstract class CRefactoring extends Refactoring {
private static final int AST_STYLE =
ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT | ITranslationUnit.AST_SKIP_INDEXED_HEADERS;
protected String name = Messages.Refactoring_name;
protected IFile file;
protected Region region;
protected RefactoringStatus initStatus;
protected IASTTranslationUnit ast;
protected ICProject project;
private IIndex fIndex;
public CRefactoring(IFile file, ISelection selection, ICElement element, ICProject proj) {
project = proj;
if (element instanceof ISourceReference) {
ISourceReference sourceRef= (ISourceReference) element;
ITranslationUnit tu = sourceRef.getTranslationUnit();
IResource res= tu.getResource();
if (res instanceof IFile)
this.file= (IFile) res;
try {
final ISourceRange sourceRange = sourceRef.getSourceRange();
this.region = new Region(sourceRange.getIdStartPos(), sourceRange.getIdLength());
} catch (CModelException e) {
CUIPlugin.log(e);
}
} else {
this.file = file;
this.region = SelectionHelper.getRegion(selection);
}
this.initStatus= new RefactoringStatus();
if (this.file == null || region == null) {
initStatus.addFatalError(Messages.Refactoring_SelectionNotValid);
}
}
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 {
RefactoringStatus status = new RefactoringStatus();
return status;
}
@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;
}
if (!loadTranslationUnit(initStatus, sm.newChild(8))) {
initStatus.addError(Messages.Refactoring_CantLoadTU);
return initStatus;
}
if (isProgressMonitorCanceld(sm, initStatus)) {
return initStatus;
}
sm.subTask(Messages.Refactoring_PM_CheckTU);
translationUnitHasProblem();
if (translationUnitIsAmbiguous()) {
initStatus.addError(Messages.Refactoring_Ambiguity);
}
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 = null;
try {
lockIndex();
finalChange = collector.createFinalChange();
} catch (InterruptedException e) {
throw new OperationCanceledException();
} finally {
unlockIndex();
}
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 boolean loadTranslationUnit(RefactoringStatus status, IProgressMonitor mon) {
SubMonitor subMonitor = SubMonitor.convert(mon, 10);
if (file != null) {
try {
subMonitor.subTask(Messages.Refactoring_PM_ParseTU);
ast = loadTranslationUnit(file);
if (ast == null) {
subMonitor.done();
return false;
}
subMonitor.worked(2);
if (isProgressMonitorCanceld(subMonitor, initStatus)) {
return true;
}
subMonitor.subTask(Messages.Refactoring_PM_MergeComments);
subMonitor.worked(8);
} catch (CoreException e) {
status.addFatalError(e.getMessage());
subMonitor.done();
return false;
}
} else {
status.addFatalError(Messages.NO_FILE);
subMonitor.done();
return false;
}
subMonitor.done();
return true;
}
protected IASTTranslationUnit loadTranslationUnit(IFile file) throws CoreException {
ITranslationUnit tu = (ITranslationUnit) CCorePlugin.getDefault().getCoreModel().create(file);
if (tu == null) {
initStatus.addFatalError(NLS.bind(Messages.CRefactoring_FileNotFound, file.getName()));
return null;
}
return tu.getAST(fIndex, AST_STYLE);
}
protected boolean translationUnitHasProblem() {
ProblemFinder pf = new ProblemFinder(initStatus);
ast.accept(pf);
return pf.hasProblem();
}
protected boolean translationUnitIsAmbiguous() {
// ambiguities are resolved before the tu is passed to the refactoring.
return false;
}
public void lockIndex() throws CoreException, InterruptedException {
if (fIndex == null) {
ICProject[] projects= CoreModel.getDefault().getCModel().getCProjects();
fIndex= CCorePlugin.getIndexManager().getIndex(projects);
}
fIndex.acquireReadLock();
}
public void unlockIndex() {
if (fIndex != null) {
fIndex.releaseReadLock();
}
// Marc-Andre Laperle : I don't think we want to null this out,
// if the lock is acquired twice then the lock can only be released once
//fIndex= null;
}
public IIndex getIndex() {
return fIndex;
}
public IASTTranslationUnit getUnit() {
return ast;
}
protected ArrayList<IASTName> findAllMarkedNames() {
final ArrayList<IASTName> namesVector = new ArrayList<IASTName>();
ast.accept(new ASTVisitor() {
{
shouldVisitNames = true;
}
@Override
public int visit(IASTName name) {
if (SelectionHelper.isInSameFileSelection(region, name, file)) {
if (!(name instanceof ICPPASTQualifiedName)) {
namesVector.add(name);
}
}
return super.visit(name);
}
});
return namesVector;
}
}