/*******************************************************************************
* Copyright (c) 2006, 2015 Wind River Systems, 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:
* Markus Schorn - initial API and implementation
* Andrew Ferguson (Symbian)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IIndexLocationConverter;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.ISignificantMacros;
import org.eclipse.cdt.internal.core.index.FileContentKey;
import org.eclipse.cdt.internal.core.index.IIndexFragment;
import org.eclipse.cdt.internal.core.index.IIndexFragmentFile;
import org.eclipse.cdt.internal.core.index.IWritableIndex.IncludeInformation;
import org.eclipse.cdt.internal.core.index.IWritableIndexFragment;
import org.eclipse.cdt.internal.core.model.TranslationUnit;
import org.eclipse.cdt.internal.core.pdom.db.ChunkCache;
import org.eclipse.cdt.internal.core.pdom.db.DBProperties;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMLinkageFactory;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMFile;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMMacro;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMMacroReferenceName;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.core.runtime.CoreException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class WritablePDOM extends PDOM implements IWritableIndexFragment {
private boolean fClearedBecauseOfVersionMismatch= false;
private boolean fCreatedFromScratch= false;
private ASTFilePathResolver fPathResolver;
private PDOMFile fileBeingUpdated;
private PDOMFile uncommittedFile;
private FileContentKey uncommittedKey;
public WritablePDOM(File dbPath, IIndexLocationConverter locationConverter,
Map<String, IPDOMLinkageFactory> linkageFactoryMappings) throws CoreException {
this(dbPath, locationConverter, ChunkCache.getSharedInstance(), linkageFactoryMappings);
}
public WritablePDOM(File dbPath, IIndexLocationConverter locationConverter, ChunkCache cache,
Map<String, IPDOMLinkageFactory> linkageFactoryMappings) throws CoreException {
super(dbPath, locationConverter, cache, linkageFactoryMappings);
}
public void setASTFilePathResolver(ASTFilePathResolver resolver) {
fPathResolver= resolver;
}
@Override
public IIndexFragmentFile addFile(int linkageID, IIndexFileLocation location, ISignificantMacros sigMacros) throws CoreException {
if (uncommittedKey != null && uncommittedKey.equals(new FileContentKey(linkageID, location, sigMacros)))
return uncommittedFile;
return super.addFile(linkageID, location, sigMacros);
}
@Override
public IIndexFragmentFile addUncommittedFile(int linkageID, IIndexFileLocation location,
ISignificantMacros significantMacros) throws CoreException {
uncommittedKey = new FileContentKey(linkageID, location, significantMacros);
fileBeingUpdated = getFile(linkageID, location, significantMacros);
PDOMLinkage linkage= createLinkage(linkageID);
uncommittedFile = new PDOMFile(linkage, location, linkageID, significantMacros);
return uncommittedFile;
}
@Override
public IIndexFragmentFile commitUncommittedFile() throws CoreException {
if (uncommittedFile == null)
return null;
int defectiveStateChange = uncommittedFile.getTimestamp() == 0 ? 1 : 0;
int unresolvedIncludeStateChange = uncommittedFile.hasUnresolvedInclude() ? 1 : 0;
PDOMFile file;
if (fileBeingUpdated == null) {
// New file, insert it into the index.
file = uncommittedFile;
getFileIndex().insert(file.getRecord());
} else {
// Existing file.
if (fileBeingUpdated.getTimestamp() == 0)
defectiveStateChange -= 1;
if (fileBeingUpdated.hasUnresolvedInclude())
unresolvedIncludeStateChange -= 1;
fileBeingUpdated.replaceContentsFrom(uncommittedFile);
file = fileBeingUpdated;
fileBeingUpdated = null;
}
if (defectiveStateChange > 0) {
getIndexOfDefectiveFiles().insert(file.getRecord());
} else if (defectiveStateChange < 0) {
getIndexOfDefectiveFiles().delete(file.getRecord());
}
if (unresolvedIncludeStateChange > 0) {
getIndexOfFilesWithUnresolvedIncludes().insert(file.getRecord());
} else if (unresolvedIncludeStateChange < 0) {
getIndexOfFilesWithUnresolvedIncludes().delete(file.getRecord());
}
fEvent.fFilesWritten.add(uncommittedKey.getLocation());
uncommittedFile = null;
uncommittedKey = null;
return file;
}
@Override
public void clearUncommittedFile() throws CoreException {
if (uncommittedFile != null) {
try {
uncommittedFile.clear();
uncommittedFile.delete();
} finally {
uncommittedFile = null;
uncommittedKey = null;
fileBeingUpdated = null;
}
}
}
@Override
public void addFileContent(IIndexFragmentFile sourceFile, IncludeInformation[] includes,
IASTPreprocessorStatement[] macros, IASTName[][] names, ASTFilePathResolver pathResolver,
YieldableIndexLock lock) throws CoreException, InterruptedException {
assert sourceFile.getIndexFragment() == this;
PDOMFile pdomFile = (PDOMFile) sourceFile;
pdomFile.addMacros(macros);
final ASTFilePathResolver origResolver= fPathResolver;
fPathResolver= pathResolver;
try {
pdomFile.addNames(names, lock);
} finally {
fPathResolver= origResolver;
}
// Includes expose the temporary file in the index, we must not yield the lock beyond this point.
pdomFile.addIncludesTo(includes);
final IIndexFileLocation location = pdomFile.getLocation();
if (location != null) {
fEvent.fClearedFiles.remove(location);
fEvent.fFilesWritten.add(location);
}
}
@Override
public void clearFile(IIndexFragmentFile file) throws CoreException {
assert file.getIndexFragment() == this;
IIndexFileLocation location = file.getLocation();
PDOMFile pdomFile = (PDOMFile) file;
pdomFile.clear();
IIndexInclude include = pdomFile.getParsedInContext();
if (include != null) {
PDOMFile includedBy = (PDOMFile) include.getIncludedBy();
if (includedBy.getTimestamp() > 0)
getIndexOfFilesWithUnresolvedIncludes().insert(includedBy.getRecord());
}
fEvent.fClearedFiles.add(location);
}
@Override
public void clear() throws CoreException {
super.clear();
}
@Override
public void flush() throws CoreException {
super.flush();
}
@Override
public void setProperty(String propertyName, String value) throws CoreException {
if (IIndexFragment.PROPERTY_FRAGMENT_FORMAT_ID.equals(propertyName)
|| IIndexFragment.PROPERTY_FRAGMENT_FORMAT_VERSION.equals(propertyName)) {
throw new IllegalArgumentException("Property " + value + " may not be written to"); //$NON-NLS-1$ //$NON-NLS-2$
}
new DBProperties(db, PROPERTIES).setProperty(propertyName, value);
}
/**
* Uses the specified location converter to update each internal representation of a file
* location. The file index is rebuilt with the new representations. Individual PDOMFile records
* are unmoved so as to maintain referential integrity with other PDOM records.
*
* <b>A write-lock must be obtained before calling this method</b>
*
* @param newConverter the converter to use to update internal file representations
* @throws CoreException
*/
public void rewriteLocations(final IIndexLocationConverter newConverter) throws CoreException {
final List<PDOMFile> pdomfiles = new ArrayList<PDOMFile>();
getFileIndex().accept(new IBTreeVisitor() {
@Override
public int compare(long record) throws CoreException {
return 0;
}
@Override
public boolean visit(long record) throws CoreException {
PDOMFile file = PDOMFile.recreateFile(WritablePDOM.this, record);
pdomfiles.add(file);
return true;
}
});
clearFileIndex();
final List<PDOMFile> notConverted = new ArrayList<PDOMFile>();
for (PDOMFile file : pdomfiles) {
String internalFormat = newConverter.toInternalFormat(file.getLocation());
if (internalFormat != null) {
file.setInternalLocation(internalFormat);
getFileIndex().insert(file.getRecord());
} else {
notConverted.add(file);
}
}
// remove content where converter returns null
for (PDOMFile file : notConverted) {
file.convertIncludersToUnresolved();
file.clear();
}
}
boolean isClearedBecauseOfVersionMismatch() {
return fClearedBecauseOfVersionMismatch;
}
void setClearedBecauseOfVersionMismatch(boolean clearedBecauseOfVersionMismatch) {
fClearedBecauseOfVersionMismatch = clearedBecauseOfVersionMismatch;
}
boolean isCreatedFromScratch() {
return fCreatedFromScratch;
}
void setCreatedFromScratch(boolean createdFromScratch) {
fCreatedFromScratch = createdFromScratch;
}
@Override
protected final boolean isPermanentlyReadOnly() {
return false;
}
/**
* Returns the best file for the given location, linkage, and translation unit.
* May return {@code null}, if no such file exists.
*
* The "best" file (variant) is the one with the most content, as measured
* by the total number of macros defined in the file. The rationale is that
* often one of the variants will contain most of the code and the others
* just small pieces, and we are usually interested in the one with most of
* the code. As a tiebreaker, a variant that was parsed in the context of a
* source file is preferred, since a header parsed outside of the context of
* a code file may not represent code that a compiler actually sees.
*
* @param linkageID the id of the linkage in which the file has been parsed.
* @param location the IIndexFileLocation representing the location of the file
* @param tu the translation unit from which 'location' originates
* @return the best file for the location, or {@code null} if the file is not
* present in the index
* @throws CoreException
*/
private PDOMFile getBestFile(int linkageID, IIndexFileLocation location, ITranslationUnit tu) throws CoreException {
IIndexFile[] files = getFiles(linkageID, location);
IIndexFile best = null;
int bestScore = -1;
for (IIndexFile file : files) {
int score = file.getMacros().length * 2;
if (tu != null && TranslationUnit.isSourceFile(TranslationUnit.getParsedInContext(file), tu.getCProject().getProject()))
score++;
if (score > bestScore) {
bestScore = score;
best = file;
}
}
return (PDOMFile) best;
}
public PDOMFile getFileForASTNode(int linkageID, IASTNode node) throws CoreException {
if (fPathResolver != null && node != null) {
IASTFileLocation loc= node.getFileLocation();
if (loc != null) {
IASTPreprocessorIncludeStatement owner= loc.getContextInclusionStatement();
ISignificantMacros sigMacros= owner != null ? owner.getSignificantMacros() : ISignificantMacros.NONE;
if (sigMacros != null) {
IIndexFileLocation location = fPathResolver.resolveASTPath(loc.getFileName());
if (uncommittedKey != null && uncommittedKey.equals(new FileContentKey(linkageID, location, sigMacros)))
return fileBeingUpdated != null ? fileBeingUpdated : uncommittedFile;
return getBestFile(linkageID, location, node.getTranslationUnit().getOriginatingTranslationUnit());
}
}
}
return null;
}
@Override
public boolean hasLastingDefinition(PDOMBinding binding) throws CoreException {
if (fileBeingUpdated == null) {
return binding.hasDefinition();
}
// Definitions in fileBeingUpdated will be removed when the file is committed, so look for
// a definition elsewhere.
for (PDOMName name = binding.getFirstDefinition(); name != null; name = name.getNextInBinding()) {
if (!fileBeingUpdated.getPDOM().equals(name.getPDOM()) ||
fileBeingUpdated.getRecord() != name.getFileRecord()) {
return true;
}
}
return false;
}
@Override
protected boolean isCommitted(PDOMName name) throws CoreException {
return uncommittedFile == null || !uncommittedFile.getPDOM().equals(name.getPDOM()) ||
uncommittedFile.getRecord() != name.getFileRecord();
}
@Override
protected boolean isCommitted(PDOMMacro name) throws CoreException {
return uncommittedFile == null || !uncommittedFile.getPDOM().equals(name.getPDOM()) ||
uncommittedFile.getRecord() != name.getFileRecord();
}
@Override
protected boolean isCommitted(PDOMMacroReferenceName name) throws CoreException {
return uncommittedFile == null || !uncommittedFile.getPDOM().equals(name.getPDOM()) ||
uncommittedFile.getRecord() != name.getFileRecord();
}
@Override
public long getDatabaseSizeBytes() {
return getDB().getSizeBytes();
}
}