blob: 0630c87f0555e51e0e5b94285b1d25dcc2cd6dc7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation 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
*
*******************************************************************************/
package org.eclipse.dltk.core.search.indexing;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.CRC32;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.compiler.CharOperation;
import org.eclipse.dltk.compiler.ISourceElementRequestor;
import org.eclipse.dltk.compiler.util.SimpleLookupTable;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceElementParser;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.search.BasicSearchEngine;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchDocument;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.core.search.SearchParticipant;
import org.eclipse.dltk.core.search.index.Index;
import org.eclipse.dltk.core.search.index.MixinIndex;
import org.eclipse.dltk.internal.core.BuildpathEntry;
import org.eclipse.dltk.internal.core.Model;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.ScriptProject;
import org.eclipse.dltk.internal.core.search.PatternSearchJob;
import org.eclipse.dltk.internal.core.search.processing.IJob;
import org.eclipse.dltk.internal.core.search.processing.JobManager;
import org.eclipse.dltk.internal.core.util.Messages;
import org.eclipse.dltk.internal.core.util.Util;
public class IndexManager extends JobManager implements IIndexConstants {
public SimpleLookupTable indexLocations = new SimpleLookupTable();
/*
* key = an IPath, value = an Index
*/
private Map indexes = new HashMap(5);
/* need to save ? */
private boolean needToSave = false;
private static final CRC32 checksumCalculator = new CRC32();
private IPath javaPluginLocation = null;
/* can only replace a current state if its less than the new one */
private SimpleLookupTable indexStates = null;
private File savedIndexNamesFile = new File(
this.getScriptPluginWorkingLocation()
.append("savedIndexNames.txt").toOSString()); //$NON-NLS-1$
public static Integer SAVED_STATE = new Integer(0);
public static Integer UPDATING_STATE = new Integer(1);
public static Integer UNKNOWN_STATE = new Integer(2);
public static Integer REBUILDING_STATE = new Integer(3);
public static List sourcesToMixin = new ArrayList();
public synchronized void aboutToUpdateIndex(IPath containerPath,
Integer newIndexState) {
// newIndexState is either UPDATING_STATE or REBUILDING_STATE
// must tag the index as inconsistent, in case we exit before the update
// job is started
String indexLocation = this.computeIndexLocation(containerPath);
Object state = this.getIndexStates().get(indexLocation);
Integer currentIndexState = state == null ? UNKNOWN_STATE
: (Integer) state;
if (currentIndexState.equals(REBUILDING_STATE)) {
return; // already rebuilding the index
}
int compare = newIndexState.compareTo(currentIndexState);
if (compare > 0) {
// so UPDATING_STATE replaces SAVED_STATE and REBUILDING_STATE
// replaces everything
this.updateIndexState(indexLocation, newIndexState);
} else if (compare < 0 && this.indexes.get(indexLocation) == null) {
// if already cached index then there is nothing more to do
this.rebuildIndex(indexLocation, containerPath);
}
}
// /**
// * Trigger addition of a resource to an index Note: the actual operation
// is
// * performed in background
// */
// public void addExternal(IFile resource, IPath containerPath) {
// if (DLTKCore.getPlugin() == null)
// return;
// SearchParticipant participant =
// SearchEngine.getDefaultSearchParticipant();
// SearchDocument document =
// participant.getDocument(resource.getFullPath().toString());
// String indexLocation = computeIndexLocation(containerPath);
// scheduleDocumentIndexing(document, containerPath, indexLocation,
// participant);
// }
/**
* Trigger addition of a resource to an index Note: the actual operation is
* performed in background
*/
public void addSource(IFile resource, IPath containerPath,
ISourceElementParser parser, SourceIndexerRequestor requestor,
IDLTKLanguageToolkit toolkit) {
if (DLTKCore.getPlugin() == null) {
return;
}
SearchParticipant participant = SearchEngine
.getDefaultSearchParticipant();
SearchDocument document = participant.getDocument(resource
.getFullPath().toString());
((InternalSearchDocument) document).parser = parser;
((InternalSearchDocument) document).requestor = requestor;
((InternalSearchDocument) document).toolkit = toolkit;
String indexLocation = this.computeIndexLocation(containerPath);
this.scheduleDocumentIndexing(document, containerPath, indexLocation,
participant);
}
/**
* Trigger addition of a resource to an index Note: the actual operation is
* performed in background
*/
public void addBinary(IFile resource, IPath containerPath) {
if (DLTKCore.getPlugin() == null) {
return;
}
SearchParticipant participant = SearchEngine
.getDefaultSearchParticipant();
SearchDocument document = participant.getDocument(resource
.getFullPath().toString());
String indexLocation = this.computeIndexLocation(containerPath);
this.scheduleDocumentIndexing(document, containerPath, indexLocation,
participant);
}
/*
* Removes unused indexes from disk.
*/
public void cleanUpIndexes() {
SimpleLookupTable knownPaths = new SimpleLookupTable();
IDLTKSearchScope scope = BasicSearchEngine.createWorkspaceScope(null);
PatternSearchJob job = new PatternSearchJob(null, SearchEngine
.getDefaultSearchParticipant(), scope, null);
Index[] selectedIndexes = job.getIndexes(null);
for (int j = 0, max = selectedIndexes.length; j < max; j++) {
// TODO should use getJavaPluginWorkingLocation()+index simple name
// to avoid bugs such as
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=62267
String path = selectedIndexes[j].getIndexFile().getAbsolutePath();
knownPaths.put(path, path);
}
if (this.indexStates != null) {
Object[] keys = this.indexStates.keyTable;
int keysLength = keys.length;
int updates = 0;
String locations[] = new String[keysLength];
for (int i = 0, l = keys.length; i < l; i++) {
String key = (String) keys[i];
if (key != null && !knownPaths.containsKey(key)) {
locations[updates++] = key;
}
}
if (updates > 0) {
this.removeIndexesState(locations);
}
}
File indexesDirectory = new File(this.getScriptPluginWorkingLocation()
.toOSString());
if (indexesDirectory.isDirectory()) {
File[] indexesFiles = indexesDirectory.listFiles();
if (indexesFiles != null) {
for (int i = 0, indexesFilesLength = indexesFiles.length; i < indexesFilesLength; i++) {
String fileName = indexesFiles[i].getAbsolutePath();
if (!knownPaths.containsKey(fileName)
&& fileName.toLowerCase().endsWith(".index")) { //$NON-NLS-1$
if (VERBOSE) {
Util
.verbose("Deleting index file " + indexesFiles[i]); //$NON-NLS-1$
}
indexesFiles[i].delete();
}
}
}
}
}
public String computeIndexLocation(IPath containerPath) {
String indexLocation = (String) this.indexLocations.get(containerPath);
if (indexLocation == null) {
String pathString = containerPath.toOSString();
checksumCalculator.reset();
checksumCalculator.update(pathString.getBytes());
String fileName = Long.toString(checksumCalculator.getValue())
+ ".index"; //$NON-NLS-1$
if (VERBOSE) {
Util
.verbose("-> index name for " + pathString + " is " + fileName); //$NON-NLS-1$ //$NON-NLS-2$
}
indexLocation = this.getScriptPluginWorkingLocation().append(fileName)
.toOSString();
this.indexLocations.put(containerPath, indexLocation);
}
return indexLocation;
}
/*
* Creates an empty index at the given location, for the given container
* path, if none exist.
*/
public void ensureIndexExists(String indexLocation, IPath containerPath) {
SimpleLookupTable states = this.getIndexStates();
Object state = states.get(indexLocation);
if (state == null) {
this.updateIndexState(indexLocation, REBUILDING_STATE);
this.getIndex(containerPath, indexLocation, true, true);
}
}
public SourceIndexerRequestor getSourceRequestor(
IScriptProject scriptProject) {
IDLTKLanguageToolkit toolkit = null;
try {
toolkit = DLTKLanguageManager.getLanguageToolkit(scriptProject);
} catch (CoreException e) {
e.printStackTrace();
}
if (toolkit != null) {
return DLTKLanguageManager.createSourceRequestor(toolkit
.getNatureId());
}
return null;
}
public ISourceElementParser getSourceElementParser(IScriptProject project,
ISourceElementRequestor requestor) {
// disable task tags to speed up parsing
// Map options = project.getOptions(true);
// options.put(DLTKCore.COMPILER_TASK_TAGS, ""); //$NON-NLS-1$
IDLTKLanguageToolkit toolkit = null;
try {
toolkit = DLTKLanguageManager.getLanguageToolkit(project);
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (toolkit != null) {
ISourceElementParser parser = null;
try {
parser = DLTKLanguageManager.getSourceElementParser(toolkit
.getNatureId());
parser.setRequestor(requestor);
} catch (CoreException e) {
}
return parser;
}
return null;
}
/**
* Returns the index for a given project, according to the following
* algorithm: - if index is already in memory: answers this one back - if
* (reuseExistingFile) then read it and return this index and record it in
* memory - if (createIfMissing) then create a new empty index and record it
* in memory
*
* Warning: Does not check whether index is consistent (not being used)
*/
public synchronized Index getIndex(IPath containerPath,
boolean reuseExistingFile, boolean createIfMissing) {
String indexLocation = this.computeIndexLocation(containerPath);
return this.getIndex(containerPath, indexLocation, reuseExistingFile,
createIfMissing);
}
/**
* This indexes aren't required to be rebuilded.
*
* @param id
* @return
*/
public synchronized Index getSpecialIndex(String id, String path,
String containerPath/* , IProject project */) {
// String containerPath = project.getFullPath().toOSString();
// String path = project.getFullPath();
boolean mixin = id.equals("mixin"); //$NON-NLS-1$
String indexLocation = this.computeIndexLocation(new Path("#special#" + id //$NON-NLS-1$
+ "#" + path)); //$NON-NLS-1$
Index index = (Index) this.indexes.get(indexLocation);
if (index == null) {
Object state = this.getIndexStates().get(indexLocation);
Integer currentIndexState = state == null ? UNKNOWN_STATE
: (Integer) state;
// index isn't cached, consider reusing an existing index file
File indexFile = new File(indexLocation);
if (indexFile.exists()) { // check before creating index so as
// to avoid creating a new empty
// index if file is missing
try {
if (mixin) {
index = new MixinIndex(indexLocation, containerPath,
true
/* reuse index file */
);
} else {
index = new Index(indexLocation, containerPath, true /*
* reuse
* index
* file
*/);
}
this.indexes.put(indexLocation, index);
return index;
} catch (IOException e) {
// failed to read the existing file or its no longer
// compatible
if (currentIndexState != REBUILDING_STATE) { // rebuild
/*
* index if existing file is corrupt, unless the index
* is already being rebuilt
*/
if (VERBOSE) {
Util
.verbose("-> cannot reuse existing index: " + indexLocation + " path: " + id); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/* index = null; */// will fall thru to createIfMissing
// & create a empty index for the
// rebuild all job to populate
}
}
// index wasn't found on disk, consider creating an empty new one
try {
if (VERBOSE) {
Util
.verbose("-> create empty index: " + indexLocation + " path: " + id); //$NON-NLS-1$ //$NON-NLS-2$
}
if (mixin) {
index = new MixinIndex(indexLocation, containerPath, false
/*
* do not reuse index file
*/
);
} else {
index = new Index(indexLocation, containerPath, false /*
* do
* not
* reuse
* index
* file
*/);
}
this.indexes.put(indexLocation, index);
return index;
} catch (IOException e) {
if (VERBOSE) {
Util
.verbose("-> unable to create empty index: " + indexLocation + " path: " + containerPath); //$NON-NLS-1$ //$NON-NLS-2$
}
// The file could not be created. Possible reason: the
// project has been deleted.
return null;
}
}
// System.out.println(" index name: " + path.toOSString() + " <----> " +
// index.getIndexFile().getName());
this.updateIndexState(indexLocation, REBUILDING_STATE);
return index;
}
/**
* Returns the index for a given project, according to the following
* algorithm: - if index is already in memory: answers this one back - if
* (reuseExistingFile) then read it and return this index and record it in
* memory - if (createIfMissing) then create a new empty index and record it
* in memory
*
* Warning: Does not check whether index is consistent (not being used)
*/
public synchronized Index getIndex(IPath containerPath,
String indexLocation, boolean reuseExistingFile,
boolean createIfMissing) {
boolean mixin = containerPath.toString().startsWith("#special#mixin#"); //$NON-NLS-1$
// Path is already canonical per construction
Index index = (Index) this.indexes.get(indexLocation);
if (index == null) {
Object state = this.getIndexStates().get(indexLocation);
Integer currentIndexState = state == null ? UNKNOWN_STATE
: (Integer) state;
if (currentIndexState.equals(UNKNOWN_STATE)) {
// should only be reachable for query jobs
// IF you put an index in the cache, then AddArchiveFileToIndex
// fails because it thinks there is nothing to do
this.rebuildIndex(indexLocation, containerPath);
if( !mixin ) {
return null;
}
}
// index isn't cached, consider reusing an existing index file
String containerPathString = containerPath.getDevice() == null ? containerPath
.toString()
: containerPath.toOSString();
if (reuseExistingFile) {
File indexFile = new File(indexLocation);
if (indexFile.exists()) { // check before creating index so as
// to avoid creating a new empty
// index if file is missing
try {
if (mixin) {
index = new MixinIndex(indexLocation,
containerPathString, true /*
* reuse index
* file
*/);
} else {
index = new Index(indexLocation,
containerPathString, true /*
* reuse index
* file
*/);
}
this.indexes.put(indexLocation, index);
return index;
} catch (IOException e) {
// failed to read the existing file or its no longer
// compatible
if (currentIndexState != REBUILDING_STATE) { // rebuild
/*
* index if existing file is corrupt, unless the
* index is already being rebuilt
*/
if (VERBOSE) {
Util
.verbose("-> cannot reuse existing index: " + indexLocation + " path: " + containerPathString); //$NON-NLS-1$ //$NON-NLS-2$
}
this.rebuildIndex(indexLocation, containerPath);
return null;
}
/* index = null; */// will fall thru to createIfMissing
// & create a empty index for the
// rebuild all job to populate
}
}
if (currentIndexState == SAVED_STATE) { // rebuild index if
// existing file is
// missing
this.rebuildIndex(indexLocation, containerPath);
return null;
}
}
// index wasn't found on disk, consider creating an empty new one
if (createIfMissing) {
try {
if (VERBOSE) {
Util
.verbose("-> create empty index: " + indexLocation + " path: " + containerPathString); //$NON-NLS-1$ //$NON-NLS-2$
}
if (mixin) {
index = new MixinIndex(indexLocation,
containerPathString, false /*
* do not reuse
* index file
*/);
} else {
index = new Index(indexLocation, containerPathString,
false /*
* do not reuse index file
*/);
}
this.indexes.put(indexLocation, index);
return index;
} catch (IOException e) {
if (VERBOSE) {
Util
.verbose("-> unable to create empty index: " + indexLocation + " path: " + containerPathString); //$NON-NLS-1$ //$NON-NLS-2$
}
// The file could not be created. Possible reason: the
// project has been deleted.
return null;
}
}
}
// System.out.println(" index name: " + path.toOSString() + " <----> " +
// index.getIndexFile().getName());
return index;
}
public synchronized Index getIndex(String indexLocation) {
return (Index) this.indexes.get(indexLocation); // is null if unknown, call
// if the containerPath must
// be computed
}
public synchronized Index getIndexForUpdate(IPath containerPath,
boolean reuseExistingFile, boolean createIfMissing) {
String indexLocation = this.computeIndexLocation(containerPath);
if (this.getIndexStates().get(indexLocation) == REBUILDING_STATE) {
return this.getIndex(containerPath, indexLocation, reuseExistingFile,
createIfMissing);
}
return null; // abort the job since the index has been removed from
// the REBUILDING_STATE
}
private SimpleLookupTable getIndexStates() {
if (this.indexStates != null) {
return this.indexStates;
}
this.indexStates = new SimpleLookupTable();
char[] savedIndexNames = this.readIndexState();
if (savedIndexNames.length > 0) {
char[][] names = CharOperation.splitOn('\n', savedIndexNames);
if (names.length > 0) {
// check to see if workspace has moved, if so then do not trust
// saved indexes
File indexesDirectory = new File(
this.getScriptPluginWorkingLocation().toOSString());
char[] dirName = indexesDirectory.getAbsolutePath()
.toCharArray();
int delimiterPos = dirName.length;
if (CharOperation.match(names[0], 0, delimiterPos, dirName, 0,
delimiterPos, true)) {
for (int i = 0, l = names.length; i < l; i++) {
char[] name = names[i];
if (name.length > 0) {
this.indexStates.put(new String(name), SAVED_STATE);
}
}
} else {
this.savedIndexNamesFile.delete(); // forget saved indexes &
// delete each index file
File[] files = indexesDirectory.listFiles();
if (files != null) {
for (int i = 0, l = files.length; i < l; i++) {
String fileName = files[i].getAbsolutePath();
if (fileName.toLowerCase().endsWith(".index")) { //$NON-NLS-1$
if (VERBOSE) {
Util
.verbose("Deleting index file " + files[i]); //$NON-NLS-1$
}
files[i].delete();
}
}
}
}
}
}
return this.indexStates;
}
private IPath getScriptPluginWorkingLocation() {
if (this.javaPluginLocation != null) {
return this.javaPluginLocation;
}
IPath stateLocation = DLTKCore.getPlugin().getStateLocation();
return this.javaPluginLocation = stateLocation;
}
public void indexDocument(SearchDocument searchDocument,
SearchParticipant searchParticipant, Index index,
IPath indexLocation) {
try {
((InternalSearchDocument) searchDocument).index = index;
searchParticipant.indexDocument(searchDocument, indexLocation);
} finally {
((InternalSearchDocument) searchDocument).index = null;
}
}
/**
* Trigger addition of the entire content of a project Note: the actual
* operation is performed in background
*/
public void indexAll(IProject project) {
if (DLTKCore.getPlugin() == null) {
return;
}
// Also request indexing of binaries on the buildpath
// determine the new children
try {
Model model = ModelManager.getModelManager().getModel();
ScriptProject scriptProject = (ScriptProject) model
.getScriptProject(project);
// only consider immediate libraries - each project will do the same
// NOTE: force to resolve CP variables before calling indexer -
// 19303, so that initializers
// will be run in the current thread.
IBuildpathEntry[] entries = scriptProject.getResolvedBuildpath(
true/* ignoreUnresolvedEntry */,
false/* don't generateMarkerOnError */, false/*
* don't
* returnResolutionInProgress
*/);
for (int i = 0; i < entries.length; i++) {
IBuildpathEntry entry = entries[i];
if (entry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY) {
this.indexLibrary(entry.getPath(), project, ((BuildpathEntry)entry).fullInclusionPatternChars(), ((BuildpathEntry)entry).fullExclusionPatternChars());
}
}
} catch (ModelException e) { // cannot retrieve buildpath info
}
// check if the same request is not already in the queue
IndexRequest request = new IndexAllProject(project, this);
if (!this.isJobWaiting(request)) {
this.request(request);
}
}
/**
* Trigger addition of a library to an index Note: the actual operation is
* performed in background
*
* @param exclusionPatterns
* @param inclusionPatterns
*/
public void indexLibrary(IPath path, IProject requestingProject, char[][] inclusionPatterns,
char[][] exclusionPatterns) {
// requestingProject is no longer used to cancel jobs but leave it here
// just in case
if (DLTKCore.getPlugin() == null) {
return;
}
Object target = Model.getTarget(ResourcesPlugin.getWorkspace()
.getRoot(), path, true);
IndexRequest request = null;
if (target instanceof IFile) {
// request = new AddArchiveFileToIndex((IFile) target, this);
return;
} else if (target instanceof java.io.File) {
if (((java.io.File) target).isFile()) {
// request = new AddArchiveFileToIndex(path, this);
return;
} else {
request = new AddExternalFolderToIndex(path, requestingProject,
inclusionPatterns, exclusionPatterns, this);
}
} else if (target instanceof IContainer) {
// request = new IndexContainerFolder((IContainer) target, this,
// requestingProject);
return;
} else if (target == null
&& path.toString().startsWith(
IBuildpathEntry.BUILTIN_EXTERNAL_ENTRY_STR)) {
request = new AddBuiltinFolderToIndex(path, requestingProject, this);
}
// check if the same request is not already in the queue
// TODO: Uncheck this. After adding some library indexing.
if (request != null) {
if (!this.isJobWaiting(request)) {
this.request(request);
}
}
}
/**
* Index the content of the given source folder.
*/
public void indexSourceFolder(ScriptProject scriptProject,
IPath sourceFolder, char[][] inclusionPatterns,
char[][] exclusionPatterns) {
IProject project = scriptProject.getProject();
if (this.jobEnd > this.jobStart) {
// skip it if a job to index the project is already in the queue
IndexRequest request = new IndexAllProject(project, this);
if (this.isJobWaiting(request)) {
return;
}
}
this.request(new AddFolderToIndex(sourceFolder, project,
inclusionPatterns, exclusionPatterns, this));
}
public void jobWasCancelled(IPath containerPath) {
String indexLocation = this.computeIndexLocation(containerPath);
Object o = this.indexes.get(indexLocation);
if (o instanceof Index) {
((Index) o).monitor = null;
this.indexes.remove(indexLocation);
}
this.updateIndexState(indexLocation, UNKNOWN_STATE);
}
/**
* Advance to the next available job, once the current one has been
* completed. Note: clients awaiting until the job count is zero are still
* waiting at this point.
*/
protected synchronized void moveToNextJob() {
// remember that one job was executed, and we will need to save indexes
// at some point
this.needToSave = true;
super.moveToNextJob();
}
/**
* No more job awaiting.
*/
protected void notifyIdle(long idlingTime) {
if (idlingTime > 1000 && this.needToSave) {
this.saveIndexes();
}
}
/**
* Name of the background process
*/
public String processName() {
return Messages.process_name;
}
private void rebuildIndex(String indexLocation, IPath containerPath) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace == null) {
return;
}
Object target = Model.getTarget(workspace.getRoot(), containerPath,
true);
if (target == null) {
return;
}
if (VERBOSE) {
Util
.verbose("-> request to rebuild index: " + indexLocation + " path: " + containerPath.toOSString()); //$NON-NLS-1$ //$NON-NLS-2$
}
this.updateIndexState(indexLocation, REBUILDING_STATE);
IndexRequest request = null;
if (target instanceof IProject) {
IProject p = (IProject) target;
if (ScriptProject.hasScriptNature(p)) {
request = new IndexAllProject(p, this);
}
} else if (target instanceof IFolder) {
// request = new IndexBinaryFolder((IFolder) target, this);
return;
} else if (target instanceof IFile) {
// request = new AddArchiveFileToIndex((IFile) target, this);
return;
} else if (target instanceof java.io.File) {
// request = new AddArchiveFileToIndex(containerPath, this);
return;
}
if (request != null) {
this.request(request);
}
}
/**
* Recreates the index for a given path, keeping the same read-write
* monitor. Returns the new empty index or null if it didn't exist before.
* Warning: Does not check whether index is consistent (not being used)
*/
public synchronized Index recreateIndex(IPath containerPath) {
boolean mixin = containerPath.toString().startsWith("#special#mixin#"); //$NON-NLS-1$
// only called to over write an existing cached index...
String containerPathString = containerPath.getDevice() == null ? containerPath
.toString()
: containerPath.toOSString();
try {
// Path is already canonical
String indexLocation = this.computeIndexLocation(containerPath);
Index index = (Index) this.indexes.get(indexLocation);
ReadWriteMonitor monitor = index == null ? null : index.monitor;
if (VERBOSE) {
Util
.verbose("-> recreating index: " + indexLocation + " for path: " + containerPathString); //$NON-NLS-1$ //$NON-NLS-2$
}
if (mixin) {
index = new MixinIndex(indexLocation, containerPathString,
false /*
* reuse index file
*/);
} else {
index = new Index(indexLocation, containerPathString, false /*
* do
* not
* reuse
* index
* file
*/);
}
this.indexes.put(indexLocation, index);
index.monitor = monitor;
return index;
} catch (IOException e) {
// The file could not be created. Possible reason: the project has
// been deleted.
if (VERBOSE) {
Util
.verbose("-> failed to recreate index for path: " + containerPathString); //$NON-NLS-1$
e.printStackTrace();
}
return null;
}
}
/**
* Trigger removal of a resource to an index Note: the actual operation is
* performed in background
*/
public void remove(String containerRelativePath, IPath indexedContainer) {
this.request(new RemoveFromIndex(containerRelativePath, indexedContainer,
this));
}
/**
* Removes the index for a given path. This is a no-op if the index did not
* exist.
*/
public synchronized void removeIndex(IPath containerPath) {
if (VERBOSE) {
Util.verbose("removing index " + containerPath); //$NON-NLS-1$
}
String indexLocation = this.computeIndexLocation(containerPath);
File indexFile = new File(indexLocation);
if (indexFile.exists()) {
indexFile.delete();
}
Object o = this.indexes.get(indexLocation);
if (o instanceof Index) {
((Index) o).monitor = null;
}
this.indexes.remove(indexLocation);
this.updateIndexState(indexLocation, null);
}
/**
* Removes all indexes whose paths start with (or are equal to) the given
* path.
*/
public synchronized void removeIndexPath(IPath path) {
Set keySet = this.indexes.keySet();
Iterator keys = keySet.iterator();
String[] locations = null;
int max = keySet.size();
int ptr = 0;
while (keys.hasNext()) {
String indexLocation = (String) keys.next();
IPath indexPath = new Path(indexLocation);
if (path.isPrefixOf(indexPath)) {
Index index = (Index) this.indexes.get(indexLocation);
if (index != null) {
index.monitor = null;
}
if (locations == null) {
locations = new String[max];
}
locations[ptr++] = indexLocation;
File indexFile = new File(indexLocation);
if (indexFile.exists()) {
indexFile.delete();
}
} else if (locations == null) {
max--;
}
}
if (locations != null) {
for (int i = 0; i < ptr; i++) {
this.indexes.remove(locations[i]);
}
this.removeIndexesState(locations);
}
}
/**
* Removes all indexes whose paths start with (or are equal to) the given
* path.
*/
public synchronized void removeIndexFamily(IPath path) {
// only finds cached index files... shutdown removes all non-cached
// index files
ArrayList toRemove = null;
Object[] containerPaths = this.indexLocations.keyTable;
for (int i = 0, length = containerPaths.length; i < length; i++) {
IPath containerPath = (IPath) containerPaths[i];
if (containerPath == null) {
continue;
}
if (path.isPrefixOf(containerPath)) {
if (toRemove == null) {
toRemove = new ArrayList();
}
toRemove.add(containerPath);
}
}
if (toRemove != null) {
for (int i = 0, length = toRemove.size(); i < length; i++) {
this.removeIndex((IPath) toRemove.get(i));
}
}
}
/**
* Remove the content of the given source folder from the index.
*/
public void removeSourceFolderFromIndex(ScriptProject scriptProject,
IPath sourceFolder, char[][] inclusionPatterns,
char[][] exclusionPatterns) {
IProject project = scriptProject.getProject();
if (this.jobEnd > this.jobStart) {
// skip it if a job to index the project is already in the queue
IndexRequest request = new IndexAllProject(project, this);
if (this.isJobWaiting(request)) {
return;
}
}
this.request(new RemoveFolderFromIndex(sourceFolder, inclusionPatterns,
exclusionPatterns, project, this));
}
/**
* Flush current state
*/
public synchronized void reset() {
super.reset();
if (this.indexes != null) {
this.indexes = new HashMap(5);
this.indexStates = null;
}
this.indexLocations = new SimpleLookupTable();
this.javaPluginLocation = null;
}
public synchronized void saveIndex(Index index) throws IOException {
// must have permission to write from the write monitor
if (index.hasChanged()) {
if (VERBOSE) {
Util.verbose("-> saving index " + index.getIndexFile()); //$NON-NLS-1$
}
index.save();
}
// TODO should use getJavaPluginWorkingLocation()+index simple name to
// avoid bugs such as
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=62267
String indexLocation = index.getIndexFile().getPath();
if (this.jobEnd > this.jobStart) {
Object containerPath = this.indexLocations
.keyForValue(indexLocation);
if (containerPath != null) {
synchronized (this) {
for (int i = this.jobEnd; i > this.jobStart; i--) { // skip
// the
// current
// job
IJob job = this.awaitingJobs[i];
if (job instanceof IndexRequest) {
if (((IndexRequest) job).containerPath
.equals(containerPath)) {
return;
}
}
}
}
}
}
this.updateIndexState(indexLocation, SAVED_STATE);
}
/**
* Commit all index memory changes to disk
*/
public void saveIndexes() {
// only save cached indexes... the rest were not modified
ArrayList toSave = new ArrayList();
synchronized (this) {
for (Iterator iter = this.indexes.values().iterator(); iter
.hasNext();) {
Object o = iter.next();
if (o instanceof Index) {
toSave.add(o);
}
}
}
boolean allSaved = true;
for (int i = 0, length = toSave.size(); i < length; i++) {
Index index = (Index) toSave.get(i);
ReadWriteMonitor monitor = index.monitor;
if (monitor == null) {
continue; // index got deleted since acquired
}
try {
// take read lock before checking if index has changed
// don't take write lock yet since it can cause a deadlock (see
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=50571)
monitor.enterRead();
if (index.hasChanged()) {
if (monitor.exitReadEnterWrite()) {
try {
this.saveIndex(index);
} catch (IOException e) {
if (VERBOSE) {
Util
.verbose(
"-> got the following exception while saving:", System.err); //$NON-NLS-1$
e.printStackTrace();
}
allSaved = false;
} finally {
monitor.exitWriteEnterRead();
}
} else {
allSaved = false;
}
}
} finally {
monitor.exitRead();
}
}
this.needToSave = !allSaved;
}
public void scheduleDocumentIndexing(final SearchDocument searchDocument,
IPath container, final String indexLocation,
final SearchParticipant searchParticipant) {
this.request(new IndexRequest(container, this) {
public boolean execute(IProgressMonitor progressMonitor) {
if (this.isCancelled || progressMonitor != null
&& progressMonitor.isCanceled()) {
return true;
}
/* ensure no concurrent write access to index */
Index index = IndexManager.this.getIndex(this.containerPath, indexLocation, true, /*
* reuse
* index
* file
*/true /*
* create if none
*/);
if (index == null) {
return true;
}
ReadWriteMonitor monitor = index.monitor;
if (monitor == null) {
return true; // index got deleted since acquired
}
try {
monitor.enterWrite(); // ask permission to write
IndexManager.this.indexDocument(searchDocument, searchParticipant, index,
new Path(indexLocation));
} finally {
monitor.exitWrite(); // free write lock
}
return true;
}
public String toString() {
return "indexing " + searchDocument.getPath(); //$NON-NLS-1$
}
});
}
public String toString() {
StringBuffer buffer = new StringBuffer(10);
buffer.append(super.toString());
buffer.append("In-memory indexes:\n"); //$NON-NLS-1$
int count = 0;
for (Iterator iter = this.indexes.values().iterator(); iter.hasNext();) {
buffer.append(++count)
.append(" - ").append(iter.next().toString()).append('\n'); //$NON-NLS-1$
}
return buffer.toString();
}
private char[] readIndexState() {
try {
return org.eclipse.dltk.compiler.util.Util.getFileCharContent(
this.savedIndexNamesFile, null);
} catch (IOException ignored) {
if (VERBOSE) {
Util.verbose("Failed to read saved index file names"); //$NON-NLS-1$
}
return new char[0];
}
}
private synchronized void removeIndexesState(String[] locations) {
this.getIndexStates(); // ensure the states are initialized
int length = locations.length;
boolean changed = false;
for (int i = 0; i < length; i++) {
if (locations[i] == null) {
continue;
}
if ((this.indexStates.removeKey(locations[i]) != null)) {
changed = true;
if (VERBOSE) {
Util
.verbose("-> index state updated to: ? for: " + locations[i]); //$NON-NLS-1$
}
}
}
if (!changed) {
return;
}
this.writeSavedIndexNamesFile();
}
private synchronized void updateIndexState(String indexLocation,
Integer indexState) {
this.getIndexStates(); // ensure the states are initialized
if (indexState != null) {
if (indexState.equals(this.indexStates.get(indexLocation))) {
return; // not changed
}
this.indexStates.put(indexLocation, indexState);
} else {
if (!this.indexStates.containsKey(indexLocation)) {
return; // did not exist anyway
}
this.indexStates.removeKey(indexLocation);
}
this.writeSavedIndexNamesFile();
if (VERBOSE) {
String state = "?"; //$NON-NLS-1$
if (indexState == SAVED_STATE) {
state = "SAVED"; //$NON-NLS-1$
} else if (indexState == UPDATING_STATE) {
state = "UPDATING"; //$NON-NLS-1$
} else if (indexState == UNKNOWN_STATE) {
state = "UNKNOWN"; //$NON-NLS-1$
} else if (indexState == REBUILDING_STATE) {
state = "REBUILDING"; //$NON-NLS-1$
}
Util
.verbose("-> index state updated to: " + state + " for: " + indexLocation); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void writeSavedIndexNamesFile() {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(this.savedIndexNamesFile));
Object[] keys = this.indexStates.keyTable;
Object[] states = this.indexStates.valueTable;
for (int i = 0, l = states.length; i < l; i++) {
if (states[i] == SAVED_STATE) {
writer.write((String) keys[i]);
writer.write('\n');
}
}
} catch (IOException ignored) {
if (VERBOSE) {
Util.verbose(
"Failed to write saved index file names", System.err); //$NON-NLS-1$
}
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
// ignore
}
}
}
}
public synchronized void rebuild() {
this.disable();
File indexesDirectory = new File(this.getScriptPluginWorkingLocation()
.toOSString());
// this.
if (indexesDirectory.isDirectory()) {
File[] indexesFiles = indexesDirectory.listFiles();
if (indexesFiles != null) {
for (int i = 0, indexesFilesLength = indexesFiles.length; i < indexesFilesLength; i++) {
indexesFiles[i].delete();
}
}
}
this.reset();
// this.indexAll(project)
this.enable();
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject[] projects = root.getProjects();
for (int i = 0; i < projects.length; i++) {
if( DLTKLanguageManager.hasScriptNature(projects[i]) ) {
this.indexAll(projects[i]);
}
}
}
}