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; | |
} | |
} |