blob: f989c1f96a07e13cfd2deb5b944d9ea8d5f73eac [file] [log] [blame]
package org.eclipse.jdt.internal.core.index.impl;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.*;
import java.io.*;
import org.eclipse.jdt.internal.core.index.*;
/**
* 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
*/
public static final int MAX_FOOTPRINT= 2500000;
/**
* 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 Hashtable removedInAdds;
/**
* Files removed form the oldIndex.
*/
protected Hashtable removedInOld;
protected static final int CAN_MERGE= 0;
protected static final int MERGED= 1;
private File indexFile;
/**
* String representation of this index.
*/
private String toString;
public Index(File indexDirectory) throws IOException {
this(indexDirectory,".index"/*nonNLS*/);
}
public Index(File indexDirectory, String indexName) throws IOException {
super();
state= MERGED;
indexFile= new File(indexDirectory, indexName);
initialize();
}
public Index(String indexName) throws IOException {
this(indexName, null);
}
public Index(String indexName, String toString) throws IOException {
super();
state= MERGED;
indexFile= new File(indexName);
this.toString = toString;
initialize();
}
/**
* 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 Hashtable(11);
removedInOld= new Hashtable(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();
}
}
/**
* 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() throws IOException {
//initialisation of addsIndex
addsIndex= new InMemoryIndex();
addsIndexInput= new SimpleIndexInput(addsIndex);
//vectors who keep track of the removed Files
removedInAdds= new Hashtable(11);
removedInOld= new Hashtable(11);
// check whether existing index file can be read
if (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 {
//System.out.println("merge");
//initialisation of tempIndex
File tempFile= new File(indexFile.getAbsolutePath() + "TempVA"/*nonNLS*/);
boolean exists= indexFile.exists();
IndexInput mainIndexInput= new BlocksIndexInput(indexFile);
BlocksIndexOutput tempIndexOutput= new BlocksIndexOutput(tempFile);
//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);
//initialise remove vectors and addsindex, and change the state
removedInAdds.clear();
removedInOld.clear();
addsIndex.init();
addsIndexInput= new SimpleIndexInput(addsIndex);
state= MERGED;
}
/**
* @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();
}
}
/**
* @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() {
if (this.toString == null) return super.toString();
return this.toString;
}
}