/*******************************************************************************
* Copyright (c) 2010, 2011 Google, Inc 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:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.ui.services.IDisposable;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
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.editor.ASTProvider;
/**
* Cache containing ASTs for the translation units participating in refactoring.
* The cache object has to be disposed of after use. Failure to do so may cause
* loss of index lock.
* <p>
* This class is not thread-safe.
*/
public class RefactoringASTCache implements IDisposable {
private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS
| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT
| ITranslationUnit.AST_SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS
| ITranslationUnit.AST_PARSE_INACTIVE_CODE;
private final Map<ITranslationUnit, IASTTranslationUnit> fASTCache;
private IIndex fIndex;
private IASTTranslationUnit fSharedAST;
private boolean fDisposed;
public RefactoringASTCache() {
fASTCache = new ConcurrentHashMap<ITranslationUnit, IASTTranslationUnit>();
}
/**
* Returns an AST for the given translation unit. The AST is built for the working
* copy of the translation unit if such working copy exists. The returned AST is
* a shared one whenever possible.
* <p>
* An AST returned by this method should not be accessed concurrently by multiple threads.
* <p>
* <b>NOTE</b>: No references to the AST or its nodes can be kept after calling
* the {@link #dispose()} method.
*
* @param tu The translation unit.
* @param pm A progress monitor.
* @return An AST, or <code>null</code> if the AST cannot be obtained.
*/
public IASTTranslationUnit getAST(ITranslationUnit tu, IProgressMonitor pm)
throws CoreException, OperationCanceledException {
Assert.isTrue(!fDisposed, "RefactoringASTCache is already disposed"); //$NON-NLS-1$
getIndex(); // Make sure the index is locked.
if (pm != null && pm.isCanceled())
throw new OperationCanceledException();
tu= CModelUtil.toWorkingCopy(tu);
// Try to get a shared AST before creating our own.
IASTTranslationUnit ast= fASTCache.get(tu);
if (ast == null) {
if (fSharedAST != null && tu.equals(fSharedAST.getOriginatingTranslationUnit())) {
ast = fSharedAST;
} else {
ast = ASTProvider.getASTProvider().acquireSharedAST(tu, fIndex,
ASTProvider.WAIT_ACTIVE_ONLY, pm);
if (ast == null) {
if (pm != null && pm.isCanceled())
throw new OperationCanceledException();
ast= tu.getAST(fIndex, PARSE_MODE);
fASTCache.put(tu, ast);
} else {
if (fSharedAST != null) {
ASTProvider.getASTProvider().releaseSharedAST(fSharedAST);
}
fSharedAST = ast;
}
}
}
if (pm != null) {
pm.done();
}
return ast;
}
/**
* Returns the index that can be safely used for reading until the cache is disposed.
*
* @return The index.
*/
public IIndex getIndex() throws CoreException, OperationCanceledException {
Assert.isTrue(!fDisposed, "RefactoringASTCache is already disposed"); //$NON-NLS-1$
if (fIndex == null) {
ICProject[] projects = CoreModel.getDefault().getCModel().getCProjects();
IIndex index = CCorePlugin.getIndexManager().getIndex(projects);
try {
index.acquireReadLock();
} catch (InterruptedException e) {
throw new OperationCanceledException();
}
fIndex = index;
}
return fIndex;
}
/**
* @see IDisposable#dispose()
*/
public void dispose() {
Assert.isTrue(!fDisposed, "RefactoringASTCache.dispose() called more than once"); //$NON-NLS-1$
fDisposed = true;
if (fSharedAST != null) {
ASTProvider.getASTProvider().releaseSharedAST(fSharedAST);
}
if (fIndex != null) {
fIndex.releaseReadLock();
}
}
@Override
protected void finalize() throws Throwable {
if (!fDisposed)
CUIPlugin.logError("RefactoringASTCache was not disposed"); //$NON-NLS-1$
super.finalize();
}
}