blob: 5724f318b5d3b2a66da8155a9d94424be40c3347 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.index.impl;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.internal.core.index.IDocument;
import org.eclipse.cdt.internal.core.index.IEntryResult;
import org.eclipse.cdt.internal.core.index.IIndex;
import org.eclipse.cdt.internal.core.index.IIndexer;
import org.eclipse.cdt.internal.core.index.IQueryResult;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
/**
* An Index is used to create an index on the disk, and to make queries. It uses a set of
* indexers and a mergeFactory. The index fills an inMemoryIndex up
* to it reaches a certain size, and then merges it with a main index on the disk.
* <br> <br>
* The changes are only taken into account by the queries after a merge.
*/
public class Index implements IIndex {
/**
* Maximum size of the index in memory.
*/
public static final int MAX_FOOTPRINT= 10000000;
/**
* Index in memory, who is merged with mainIndex each times it
* reaches a certain size.
*/
protected InMemoryIndex addsIndex;
protected IndexInput addsIndexInput;
/**
* State of the indexGenerator: addsIndex empty <=> MERGED, or
* addsIndex not empty <=> CAN_MERGE
*/
protected int state;
/**
* Files removed form the addsIndex.
*/
protected Map removedInAdds;
/**
* Files removed form the oldIndex.
*/
protected Map removedInOld;
protected static final int CAN_MERGE= 0;
protected static final int MERGED= 1;
private File indexFile;
/**
* String representation of this index.
*/
public String toString;
public Index(File indexDirectory, boolean reuseExistingFile) throws IOException {
this(indexDirectory,".index", reuseExistingFile); //$NON-NLS-1$
}
public Index(File indexDirectory, String indexName, boolean reuseExistingFile) throws IOException {
super();
state= MERGED;
indexFile= new File(indexDirectory, indexName);
initialize(reuseExistingFile);
}
public Index(String indexName, boolean reuseExistingFile) throws IOException {
this(indexName, null, reuseExistingFile);
}
public Index(String indexName, String toString, boolean reuseExistingFile) throws IOException {
super();
state= MERGED;
indexFile= new File(indexName);
this.toString = toString;
initialize(reuseExistingFile);
}
/**
* Indexes the given document, using the appropriate indexer registered in the indexerRegistry.
* If the document already exists in the index, it overrides the previous one. The changes will be
* taken into account after a merge.
*/
public void add(IDocument document, IIndexer indexer) throws IOException {
if (timeToMerge()) {
merge();
}
IndexedFile indexedFile= addsIndex.getIndexedFile(document.getName());
if (indexedFile != null /*&& removedInAdds.get(document.getName()) == null*/
)
remove(indexedFile, MergeFactory.ADDS_INDEX);
IndexerOutput output= new IndexerOutput(addsIndex);
indexer.index(document, output);
state= CAN_MERGE;
}
/**
* Returns true if the index in memory is not empty, so
* merge() can be called to fill the mainIndex with the files and words
* contained in the addsIndex.
*/
protected boolean canMerge() {
return state == CAN_MERGE;
}
/**
* Initialises the indexGenerator.
*/
public void empty() throws IOException {
if (indexFile.exists()){
indexFile.delete();
//initialisation of mainIndex
InMemoryIndex mainIndex= new InMemoryIndex();
IndexOutput mainIndexOutput= new BlocksIndexOutput(indexFile);
if (!indexFile.exists())
mainIndex.save(mainIndexOutput);
}
//initialisation of addsIndex
addsIndex= new InMemoryIndex();
addsIndexInput= new SimpleIndexInput(addsIndex);
//vectors who keep track of the removed Files
removedInAdds= new HashMap(11);
removedInOld= new HashMap(11);
}
/**
* @see IIndex#getIndexFile
*/
public File getIndexFile() {
return indexFile;
}
/**
* @see IIndex#getNumDocuments
*/
public int getNumDocuments() throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
input.open();
return input.getNumFiles();
} finally {
input.close();
}
}
/**
* @see IIndex#getNumWords
*/
public int getNumWords() throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
input.open();
return input.getNumWords();
} finally {
input.close();
}
}
/**
* @see IIndex#getNumWords
*/
public int getNumIncludes() throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
input.open();
return input.getNumIncludes();
} finally {
input.close();
}
}
/**
* Returns the path corresponding to a given document number
*/
public String getPath(int documentNumber) throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
input.open();
IndexedFile file = input.getIndexedFile(documentNumber);
if (file == null) return null;
return file.getPath();
} finally {
input.close();
}
}
/**
* see IIndex.hasChanged
*/
public boolean hasChanged() {
return canMerge();
}
/**
* Initialises the indexGenerator.
*/
public void initialize(boolean reuseExistingFile) throws IOException {
//initialisation of addsIndex
addsIndex= new InMemoryIndex();
addsIndexInput= new SimpleIndexInput(addsIndex);
//vectors who keep track of the removed Files
removedInAdds= new HashMap(11);
removedInOld= new HashMap(11);
// check whether existing index file can be read
if (reuseExistingFile && indexFile.exists()) {
IndexInput mainIndexInput= new BlocksIndexInput(indexFile);
try {
mainIndexInput.open();
} catch(IOException e) {
BlocksIndexInput input = (BlocksIndexInput)mainIndexInput;
try {
input.opened = true;
input.close();
} finally {
input.opened = false;
}
indexFile.delete();
mainIndexInput = null;
throw e;
}
mainIndexInput.close();
} else {
InMemoryIndex mainIndex= new InMemoryIndex();
IndexOutput mainIndexOutput= new BlocksIndexOutput(indexFile);
mainIndex.save(mainIndexOutput);
}
}
/**
* Merges the in memory index and the index on the disk, and saves the results on the disk.
*/
protected void merge() throws IOException {
//initialisation of tempIndex
File tempFile= new File(indexFile.getAbsolutePath() + "TempVA"); //$NON-NLS-1$
IndexInput mainIndexInput= new BlocksIndexInput(indexFile);
BlocksIndexOutput tempIndexOutput= new BlocksIndexOutput(tempFile);
try {
//invoke a mergeFactory
new MergeFactory(
mainIndexInput,
addsIndexInput,
tempIndexOutput,
removedInOld,
removedInAdds).merge();
//rename the file created to become the main index
File mainIndexFile= (File) mainIndexInput.getSource();
File tempIndexFile= (File) tempIndexOutput.getDestination();
mainIndexFile.delete();
tempIndexFile.renameTo(mainIndexFile);
} finally {
//initialise remove vectors and addsindex, and change the state
removedInAdds.clear();
removedInOld.clear();
addsIndex.init();
addsIndexInput= new SimpleIndexInput(addsIndex);
state= MERGED;
//flush the CDT log
CCorePlugin.getDefault().cdtLog.flushLog();
}
}
/**
* @see IIndex#query
*/
public IQueryResult[] query(String word) throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
return input.query(word);
} finally {
input.close();
}
}
public IEntryResult[] queryEntries(char[] prefix) throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
return input.queryEntriesPrefixedBy(prefix);
} finally {
input.close();
}
}
/**
* @see IIndex#queryInDocumentNames
*/
public IQueryResult[] queryInDocumentNames(String word) throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
return input.queryInDocumentNames(word);
} finally {
input.close();
}
}
/**
* @see IIndex#queryPrefix
*/
public IQueryResult[] queryPrefix(char[] prefix) throws IOException {
//save();
IndexInput input= new BlocksIndexInput(indexFile);
try {
return input.queryFilesReferringToPrefix(prefix);
} finally {
input.close();
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.internal.core.sourcedependency.IDependencyTree#getFileDepencies(int)
*/
public String[] getFileDependencies(IPath filePath) throws IOException {
// List tempFileReturn = new ArrayList();
//
// IndexedFile indexFile = addsIndex.getIndexedFile(filePath.toString());
//
// if (indexFile == null)
// return new String[0];
//
// int fileNum = indexFile.getFileNumber();
// IncludeEntry[] tempEntries = addsIndex.getIncludeEntries();
// for (int i=0; i<tempEntries.length; i++)
// {
// int[] fileRefs = tempEntries[i].getRefs();
// for (int j=0; j<fileRefs.length; j++)
// {
// if (fileRefs[j] == fileNum)
// {
// char[] tempFile = tempEntries[i].getFile();
// StringBuffer tempString = new StringBuffer();
// tempString.append(tempFile);
// tempFileReturn.add(tempString.toString());
// break;
// }
// }
// }
// return (String []) tempFileReturn.toArray(new String[tempFileReturn.size()]);
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.internal.core.index.IIndex#getFileDependencies(org.eclipse.core.resources.IFile)
*/
public String[] getFileDependencies(IFile file) throws IOException {
IndexInput input= new BlocksIndexInput(indexFile);
int fileNum=0;
List tempFileReturn = new ArrayList();
try {
IDocument temp = new IFileDocument(file);
input.open();
IndexedFile inFile = input.getIndexedFile(temp);
fileNum =inFile.getFileNumber();
IncludeEntry[] tempEntries = input.queryIncludeEntries(fileNum);
for (int i=0; i<tempEntries.length; i++)
{
char[] tempFile = tempEntries[i].getFile();
StringBuffer tempString = new StringBuffer();
tempString.append(tempFile);
tempFileReturn.add(tempString.toString());
}
}
finally{input.close();}
return (String []) tempFileReturn.toArray(new String[tempFileReturn.size()]);
}
/**
* @see IIndex#remove
*/
public void remove(String documentName) throws IOException {
IndexedFile file= addsIndex.getIndexedFile(documentName);
if (file != null) {
//the file is in the adds Index, we remove it from this one
Int lastRemoved= (Int) removedInAdds.get(documentName);
if (lastRemoved != null) {
int fileNum= file.getFileNumber();
if (lastRemoved.value < fileNum)
lastRemoved.value= fileNum;
} else
removedInAdds.put(documentName, new Int(file.getFileNumber()));
} else {
//we remove the file from the old index
removedInOld.put(documentName, new Int(1));
}
state= CAN_MERGE;
}
/**
* Removes the given document from the given index (MergeFactory.ADDS_INDEX for the
* in memory index, MergeFactory.OLD_INDEX for the index on the disk).
*/
protected void remove(IndexedFile file, int index) throws IOException {
String name= file.getPath();
if (index == MergeFactory.ADDS_INDEX) {
Int lastRemoved= (Int) removedInAdds.get(name);
if (lastRemoved != null) {
if (lastRemoved.value < file.getFileNumber())
lastRemoved.value= file.getFileNumber();
} else
removedInAdds.put(name, new Int(file.getFileNumber()));
} else if (index == MergeFactory.OLD_INDEX)
removedInOld.put(name, new Int(1));
else
throw new Error();
state= CAN_MERGE;
}
/**
* @see IIndex#save
*/
public void save() throws IOException {
if (canMerge())
merge();
}
/**
* Returns true if the in memory index reaches a critical size,
* to merge it with the index on the disk.
*/
protected boolean timeToMerge() {
return (addsIndex.getFootprint() >= MAX_FOOTPRINT);
}
public String toString() {
String str = this.toString;
if (str == null) str = super.toString();
str += "(length: "+ getIndexFile().length() +")"; //$NON-NLS-1$ //$NON-NLS-2$
return str;
}
}