| /******************************************************************************* |
| * Copyright (c) 2000, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.search.indexing; |
| |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Locale; |
| import java.util.Map; |
| 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.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.SearchDocument; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.core.search.SearchParticipant; |
| import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; |
| import org.eclipse.jdt.internal.compiler.SourceElementParser; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; |
| import org.eclipse.jdt.internal.compiler.util.JRTUtil; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.compiler.util.SimpleSet; |
| import org.eclipse.jdt.internal.core.ClasspathEntry; |
| import org.eclipse.jdt.internal.core.JavaModel; |
| import org.eclipse.jdt.internal.core.JavaModelManager; |
| import org.eclipse.jdt.internal.core.JavaProject; |
| import org.eclipse.jdt.internal.core.index.DiskIndex; |
| import org.eclipse.jdt.internal.core.index.FileIndexLocation; |
| import org.eclipse.jdt.internal.core.index.Index; |
| import org.eclipse.jdt.internal.core.index.IndexLocation; |
| import org.eclipse.jdt.internal.core.search.BasicSearchEngine; |
| import org.eclipse.jdt.internal.core.search.PatternSearchJob; |
| import org.eclipse.jdt.internal.core.search.processing.IJob; |
| import org.eclipse.jdt.internal.core.search.processing.JobManager; |
| import org.eclipse.jdt.internal.core.util.Messages; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public class IndexManager extends JobManager implements IIndexConstants { |
| |
| // key = containerPath, value = indexLocation path |
| // indexLocation path is created by appending an index file name to the getJavaPluginWorkingLocation() path |
| public SimpleLookupTable indexLocations = new SimpleLookupTable(); |
| // key = indexLocation path, value = an index |
| private SimpleLookupTable indexes = new SimpleLookupTable(); |
| |
| /** |
| * The new indexer is disabled, see bug 544898 |
| */ |
| // private Indexer indexer = Indexer.getInstance(); |
| |
| /* need to save ? */ |
| private boolean needToSave = false; |
| private IPath javaPluginLocation = null; |
| |
| /* can only replace a current state if its less than the new one */ |
| // key = indexLocation path, value = index state integer |
| private SimpleLookupTable indexStates = null; |
| private File indexNamesMapFile = new File(getSavedIndexesDirectory(), "indexNamesMap.txt"); //$NON-NLS-1$ |
| private File participantIndexNamesFile = new File(getSavedIndexesDirectory(), "participantsIndexNames.txt"); //$NON-NLS-1$ |
| private boolean javaLikeNamesChanged = true; |
| public static final Integer SAVED_STATE = 0; |
| public static final Integer UPDATING_STATE = 1; |
| public static final Integer UNKNOWN_STATE = 2; |
| public static final Integer REBUILDING_STATE = 3; |
| public static final Integer REUSE_STATE = 4; |
| |
| private final IndexNamesRegistry nameRegistry = new IndexNamesRegistry(new File(getSavedIndexesDirectory(), |
| "savedIndexNames.txt"), getJavaPluginWorkingLocation()); //$NON-NLS-1$ |
| // search participants who register indexes with the index manager |
| private SimpleLookupTable participantsContainers = null; |
| private boolean participantUpdated = false; |
| |
| // should JDT manage (update, delete as needed) pre-built indexes? |
| public static final String MANAGE_PRODUCT_INDEXES_PROPERTY = "jdt.core.manageProductIndexes"; //$NON-NLS-1$ |
| private static final boolean IS_MANAGING_PRODUCT_INDEXES_PROPERTY = Boolean.getBoolean(MANAGE_PRODUCT_INDEXES_PROPERTY); |
| |
| // Debug |
| public static boolean DEBUG = false; |
| |
| |
| 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 |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| Object state = getIndexStates().get(indexLocation); |
| Integer currentIndexState = state == null ? UNKNOWN_STATE : (Integer) state; |
| if (currentIndexState.compareTo(REBUILDING_STATE) >= 0) return; // already rebuilding the index |
| |
| int compare = newIndexState.compareTo(currentIndexState); |
| if (compare > 0) { |
| // so UPDATING_STATE replaces SAVED_STATE and REBUILDING_STATE replaces everything |
| updateIndexState(indexLocation, newIndexState); |
| } else if (compare < 0 && this.indexes.get(indexLocation) == null) { |
| // if already cached index then there is nothing more to do |
| rebuildIndex(indexLocation, containerPath); |
| } |
| } |
| /** |
| * Trigger addition of a resource to an index |
| * Note: the actual operation is performed in background |
| */ |
| public void addBinary(IFile resource, IPath containerPath) { |
| if (JavaCore.getPlugin() == null) return; |
| SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); |
| SearchDocument document = participant.getDocument(resource.getFullPath().toString()); |
| IndexLocation 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, SourceElementParser parser) { |
| if (JavaCore.getPlugin() == null) return; |
| SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); |
| SearchDocument document = participant.getDocument(resource.getFullPath().toString()); |
| document.setParser(parser); |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| scheduleDocumentIndexing(document, containerPath, indexLocation, participant); |
| } |
| /* |
| * Removes unused indexes from disk. |
| */ |
| public void cleanUpIndexes() { |
| SimpleSet knownPaths = new SimpleSet(); |
| IJavaSearchScope scope = BasicSearchEngine.createWorkspaceScope(); |
| PatternSearchJob job = new PatternSearchJob(null, SearchEngine.getDefaultSearchParticipant(), scope, null); |
| Index[] selectedIndexes = job.getIndexes(null); |
| for (int i = 0, l = selectedIndexes.length; i < l; i++) { |
| IndexLocation IndexLocation = selectedIndexes[i].getIndexLocation(); |
| knownPaths.add(IndexLocation); |
| } |
| |
| if (this.indexStates != null) { |
| Object[] keys = this.indexStates.keyTable; |
| IndexLocation[] locations = new IndexLocation[this.indexStates.elementSize]; |
| int count = 0; |
| for (int i = 0, l = keys.length; i < l; i++) { |
| IndexLocation key = (IndexLocation) keys[i]; |
| if (key != null && !knownPaths.includes(key)) |
| locations[count++] = key; |
| } |
| if (count > 0) |
| removeIndexesState(locations); |
| } |
| deleteIndexFiles(knownPaths, null); |
| } |
| /** |
| * Compute the pre-built index location for a specified URL |
| */ |
| public synchronized IndexLocation computeIndexLocation(IPath containerPath, final URL newIndexURL) { |
| IndexLocation indexLocation = (IndexLocation) this.indexLocations.get(containerPath); |
| if (indexLocation == null) { |
| if(newIndexURL != null) { |
| indexLocation = IndexLocation.createIndexLocation(newIndexURL); |
| // update caches |
| indexLocation = (IndexLocation) getIndexStates().getKey(indexLocation); |
| this.indexLocations.put(containerPath, indexLocation); |
| } |
| } |
| else { |
| // an existing index location exists - make sure it has not changed (i.e. the URL has not changed) |
| URL existingURL = indexLocation.getUrl(); |
| if (newIndexURL != null) { |
| // if either URL is different then the index location has been updated so rebuild. |
| if(!newIndexURL.equals(existingURL)) { |
| // URL has changed so remove the old index and create a new one |
| this.removeIndex(containerPath); |
| // create a new one |
| indexLocation = IndexLocation.createIndexLocation(newIndexURL); |
| // update caches |
| indexLocation = (IndexLocation) getIndexStates().getKey(indexLocation); |
| this.indexLocations.put(containerPath, indexLocation); |
| } |
| } |
| } |
| return indexLocation; |
| } |
| public synchronized IndexLocation computeIndexLocation(IPath containerPath) { |
| IndexLocation indexLocation = (IndexLocation) this.indexLocations.get(containerPath); |
| if (indexLocation == null) { |
| String pathString = containerPath.toOSString(); |
| CRC32 checksumCalculator = new CRC32(); |
| 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$ |
| // to share the indexLocation between the indexLocations and indexStates tables, get the key from the indexStates table |
| indexLocation = (IndexLocation) getIndexStates().getKey(new FileIndexLocation(new File(getSavedIndexesDirectory(), fileName))); |
| this.indexLocations.put(containerPath, indexLocation); |
| } |
| return indexLocation; |
| } |
| /** |
| * Use {@link #deleteIndexFiles(IProgressMonitor)} |
| */ |
| public final void deleteIndexFiles() { |
| deleteIndexFiles(null); |
| } |
| public void deleteIndexFiles(IProgressMonitor monitor) { |
| if (DEBUG) |
| Util.verbose("Deleting index files"); //$NON-NLS-1$ |
| this.nameRegistry.delete(); // forget saved indexes & delete each index file |
| deleteIndexFiles(null, monitor); |
| } |
| private void deleteIndexFiles(SimpleSet pathsToKeep, IProgressMonitor monitor) { |
| File[] indexesFiles = getSavedIndexesDirectory().listFiles(); |
| if (indexesFiles == null) return; |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor, indexesFiles.length); |
| for (int i = 0, l = indexesFiles.length; i < l; i++) { |
| subMonitor.split(1); |
| String fileName = indexesFiles[i].getAbsolutePath(); |
| if (pathsToKeep != null && pathsToKeep.includes(new FileIndexLocation(indexesFiles[i]))) continue; |
| String suffix = ".index"; //$NON-NLS-1$ |
| if (fileName.regionMatches(true, fileName.length() - suffix.length(), suffix, 0, suffix.length())) { |
| if (VERBOSE || DEBUG) |
| Util.verbose("Deleting index file " + indexesFiles[i]); //$NON-NLS-1$ |
| indexesFiles[i].delete(); |
| } |
| } |
| } |
| /* |
| * Creates an empty index at the given location, for the given container path, if none exist. |
| */ |
| public synchronized void ensureIndexExists(IndexLocation indexLocation, IPath containerPath) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(containerPath); |
| SimpleLookupTable states = getIndexStates(); |
| Object state = states.get(indexLocation); |
| if (state == null) { |
| updateIndexState(indexLocation, REBUILDING_STATE); |
| getIndex(containerPath, indexLocation, true, true); |
| } |
| } |
| public SourceElementParser getSourceElementParser(IJavaProject project, ISourceElementRequestor requestor) { |
| // disable task tags to speed up parsing |
| Map options = project.getOptions(true); |
| options.put(JavaCore.COMPILER_TASK_TAGS, ""); //$NON-NLS-1$ |
| |
| SourceElementParser parser = new IndexingParser( |
| requestor, |
| new DefaultProblemFactory(Locale.getDefault()), |
| new CompilerOptions(options), |
| true, // index local declarations |
| true, // optimize string literals |
| false); // do not use source javadoc parser to speed up parsing |
| parser.reportOnlyOneSyntaxError = true; |
| |
| // Always check javadoc while indexing |
| parser.javadocParser.checkDocComment = true; |
| parser.javadocParser.reportProblems = false; |
| |
| return parser; |
| } |
| /** |
| * Returns the index for a given index location |
| * |
| * @param indexLocation The path of the index file |
| * @return The corresponding index or <code>null</code> if not found |
| */ |
| public synchronized Index getIndex(IndexLocation indexLocation) { |
| return (Index) this.indexes.get(indexLocation); // is null if unknown, call if the containerPath must be computed |
| } |
| /** |
| * 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) { |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| return getIndex(containerPath, indexLocation, reuseExistingFile, createIfMissing); |
| } |
| /** |
| * 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, IndexLocation indexLocation, boolean reuseExistingFile, boolean createIfMissing) { |
| // Path is already canonical per construction |
| Index index = getIndex(indexLocation); |
| if (index == null) { |
| Object state = getIndexStates().get(indexLocation); |
| Integer currentIndexState = state == null ? UNKNOWN_STATE : (Integer) state; |
| if (currentIndexState == UNKNOWN_STATE) { |
| // should only be reachable for query jobs |
| // IF you put an index in the cache, then AddJarFileToIndex fails because it thinks there is nothing to do |
| rebuildIndex(indexLocation, containerPath); |
| return null; |
| } |
| |
| // index isn't cached, consider reusing an existing index file |
| String containerPathString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); |
| if (reuseExistingFile) { |
| if (indexLocation.exists()) { // check before creating index so as to avoid creating a new empty index if file is missing |
| try { |
| 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 && currentIndexState != REUSE_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$ |
| 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 |
| rebuildIndex(indexLocation, containerPath); |
| return null; |
| } |
| if (currentIndexState == REUSE_STATE) { |
| // supposed to be in reuse state but error in the index file, so reindex. |
| if (VERBOSE) |
| Util.verbose("-> cannot reuse given index: "+indexLocation+" path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ |
| if(!IS_MANAGING_PRODUCT_INDEXES_PROPERTY) { |
| this.indexLocations.put(containerPath, null); |
| indexLocation = computeIndexLocation(containerPath); |
| rebuildIndex(indexLocation, containerPath); |
| } |
| else { |
| rebuildIndex(indexLocation, containerPath, true); |
| } |
| 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$ |
| 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; |
| } |
| /** |
| * Returns all the existing indexes for a list of index locations. |
| * Note that this may trigger some indexes recreation work |
| * |
| * @param locations The list of of the index files path |
| * @return The corresponding indexes list. |
| */ |
| public Index[] getIndexes(IndexLocation[] locations, IProgressMonitor progressMonitor) { |
| // acquire the in-memory indexes on the fly |
| int length = locations.length; |
| Index[] locatedIndexes = new Index[length]; |
| int count = 0; |
| if (this.javaLikeNamesChanged) { |
| this.javaLikeNamesChanged = hasJavaLikeNamesChanged(); |
| } |
| for (int i = 0; i < length; i++) { |
| if (progressMonitor != null && progressMonitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| // may trigger some index recreation work |
| IndexLocation indexLocation = locations[i]; |
| Index index = getIndex(indexLocation); |
| if (index == null) { |
| // only need containerPath if the index must be built |
| IPath containerPath = (IPath) this.indexLocations.keyForValue(indexLocation); |
| if (containerPath != null) {// sanity check |
| index = getIndex(containerPath, indexLocation, true /*reuse index file*/, false /*do not create if none*/); |
| if (index != null && this.javaLikeNamesChanged && !index.isIndexForJar()) { |
| // When a change in java like names extension has been detected, all |
| // non jar files indexes (i.e. containing sources) need to be rebuilt. |
| // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=286379 |
| File indexFile = index.getIndexFile(); |
| if (indexFile.exists()) { |
| if (DEBUG) |
| Util.verbose("Change in javaLikeNames - removing index file for " + containerPath ); //$NON-NLS-1$ |
| indexFile.delete(); |
| } |
| this.indexes.put(indexLocation, null); |
| rebuildIndex(indexLocation, containerPath); |
| index = null; |
| } |
| } else { |
| if (indexLocation.isParticipantIndex() && indexLocation.exists()) { // the index belongs to non-jdt search participant |
| try { |
| IPath container = getParticipantsContainer(indexLocation); |
| if (container != null) { |
| index = new Index(indexLocation, container.toOSString(), true /*reuse index file*/); |
| this.indexes.put(indexLocation, index); |
| } |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| if (index != null) |
| locatedIndexes[count++] = index; // only consider indexes which are ready |
| } |
| if (this.javaLikeNamesChanged) { |
| writeJavaLikeNamesFile(); |
| this.javaLikeNamesChanged = false; |
| } |
| if (count < length) { |
| System.arraycopy(locatedIndexes, 0, locatedIndexes=new Index[count], 0, count); |
| } |
| return locatedIndexes; |
| } |
| public synchronized Index getIndexForUpdate(IPath containerPath, boolean reuseExistingFile, boolean createIfMissing) { |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| if (getIndexStates().get(indexLocation) == REBUILDING_STATE) |
| return 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(); |
| File indexesDirectoryPath = getSavedIndexesDirectory(); |
| char[][] savedNames = this.nameRegistry.read(null); |
| if (savedNames != null) { |
| for (int i = 1, l = savedNames.length; i < l; i++) { // first name is saved signature, see readIndexState() |
| char[] savedName = savedNames[i]; |
| if (savedName.length > 0) { |
| IndexLocation indexLocation = new FileIndexLocation(new File(indexesDirectoryPath, String.valueOf(savedName))); // shares indexesDirectoryPath's segments |
| if (VERBOSE) |
| Util.verbose("Reading saved index file " + indexLocation); //$NON-NLS-1$ |
| this.indexStates.put(indexLocation, SAVED_STATE); |
| } |
| } |
| } else { |
| // All the index files are getting deleted and hence there is no need to |
| // further check for change in javaLikeNames. |
| writeJavaLikeNamesFile(); |
| this.javaLikeNamesChanged = false; |
| deleteIndexFiles(); |
| } |
| readIndexMap(); |
| return this.indexStates; |
| } |
| private IPath getParticipantsContainer(IndexLocation indexLocation) { |
| if (this.participantsContainers == null) { |
| readParticipantsIndexNamesFile(); |
| } |
| return (IPath)this.participantsContainers.get(indexLocation); |
| } |
| private IPath getJavaPluginWorkingLocation() { |
| if (this.javaPluginLocation != null) return this.javaPluginLocation; |
| |
| IPath stateLocation = JavaCore.getPlugin().getStateLocation(); |
| return this.javaPluginLocation = stateLocation; |
| } |
| private File getSavedIndexesDirectory() { |
| return new File(getJavaPluginWorkingLocation().toOSString()); |
| } |
| /* |
| * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=286379 |
| * Returns true if there is a change in javaLikeNames since it |
| * has been last stored. |
| * The javaLikeNames stored in the file javaLikeNames.txt |
| * is compared with the current javaLikeNames and if there is a change, this |
| * function returns true. If the file javaLikeNames.txt doesn't exist and there |
| * is only one javaLikeName (.java), then this returns false so that no-reindexing |
| * happens. |
| */ |
| private boolean hasJavaLikeNamesChanged() { |
| char[][] currentNames = Util.getJavaLikeExtensions(); |
| int current = currentNames.length; |
| char[][] prevNames = readJavaLikeNamesFile(); |
| if (prevNames == null) { |
| if (VERBOSE && current != 1) |
| Util.verbose("No Java like names found and there is atleast one non-default javaLikeName", System.err); //$NON-NLS-1$ |
| return (current != 1); //Ignore if only java |
| } |
| int prev = prevNames.length; |
| if (current != prev) { |
| if (VERBOSE) |
| Util.verbose("Java like names have changed", System.err); //$NON-NLS-1$ |
| return true; |
| } |
| if (current > 1) { |
| // Sort the current java like names. |
| // Copy the array to avoid modifying the Util static variable |
| System.arraycopy(currentNames, 0, currentNames = new char[current][], 0, current); |
| Util.sort(currentNames); |
| } |
| |
| // The JavaLikeNames would have been sorted before getting stored in the file, |
| // hence just do a direct compare. |
| for (int i = 0; i < current; i++) { |
| if (!CharOperation.equals(currentNames[i],prevNames[i])) { |
| if (VERBOSE) |
| Util.verbose("Java like names have changed", System.err); //$NON-NLS-1$ |
| return true; |
| } |
| } |
| return false; |
| } |
| public void indexDocument(SearchDocument searchDocument, SearchParticipant searchParticipant, Index index, IPath indexLocation) { |
| try { |
| searchDocument.setIndex(index); |
| searchParticipant.indexDocument(searchDocument, indexLocation); |
| } finally { |
| searchDocument.setIndex(null); |
| } |
| } |
| public void indexResolvedDocument(SearchDocument searchDocument, SearchParticipant searchParticipant, Index index, IPath indexLocation) { |
| searchParticipant.resolveDocument(searchDocument); |
| ReadWriteMonitor monitor = index.monitor; |
| if (monitor == null) |
| return; // index got deleted since acquired |
| try { |
| monitor.enterWrite(); // ask permission to write |
| searchDocument.setIndex(index); |
| searchParticipant.indexResolvedDocument(searchDocument, indexLocation); |
| } finally { |
| searchDocument.setIndex(null); |
| monitor.exitWrite(); |
| } |
| } |
| /** |
| * Trigger addition of the entire content of a project |
| * Note: the actual operation is performed in background |
| */ |
| public void indexAll(IProject project) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeDirty(project); |
| if (JavaCore.getPlugin() == null) return; |
| |
| try { |
| // Disable index manager to avoid synchronization lock contention when adding new index requests to the queue. |
| disable(); |
| |
| // Also request indexing of binaries on the classpath |
| // determine the new children |
| try { |
| JavaModel model = JavaModelManager.getJavaModelManager().getJavaModel(); |
| JavaProject javaProject = (JavaProject) model.getJavaProject(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. |
| IClasspathEntry[] entries = javaProject.getResolvedClasspath(); |
| for (int i = 0; i < entries.length; i++) { |
| IClasspathEntry entry= entries[i]; |
| if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) |
| indexLibrary(entry.getPath(), project, ((ClasspathEntry)entry).getLibraryIndexLocation()); |
| } |
| } catch(JavaModelException e){ // cannot retrieve classpath info |
| } |
| |
| // check if the same request is not already in the queue |
| IndexRequest request = new IndexAllProject(project, this); |
| if (!isJobWaiting(request)) |
| request(request); |
| } finally { |
| // Enable index manager after adding all new index requests to the queue. |
| enable(); |
| } |
| } |
| public void indexLibrary(IPath path, IProject requestingProject, URL indexURL) { |
| this.indexLibrary(path, requestingProject, indexURL, false); |
| } |
| |
| private IndexRequest getRequest(Object target, IPath jPath, IndexLocation indexFile, IndexManager manager, boolean updateIndex) { |
| return isJrt(((File) target).getName()) ? new AddJrtToIndex(jPath, indexFile, this, updateIndex) : |
| new AddJarFileToIndex(jPath, indexFile, this, updateIndex); |
| } |
| |
| private boolean isJrt(String fileName) { |
| return fileName != null && fileName.endsWith(JRTUtil.JRT_FS_JAR); |
| } |
| /** |
| * Trigger addition of a library to an index |
| * Note: the actual operation is performed in background |
| */ |
| public void indexLibrary(IPath path, IProject requestingProject, URL indexURL, final boolean updateIndex) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(path); |
| // requestingProject is no longer used to cancel jobs but leave it here just in case |
| IndexLocation indexFile = null; |
| boolean forceIndexUpdate = false; |
| if(indexURL != null) { |
| if(IS_MANAGING_PRODUCT_INDEXES_PROPERTY) { |
| indexFile = computeIndexLocation(path, indexURL); |
| if(!updateIndex && !indexFile.exists()) { |
| forceIndexUpdate = true; |
| } |
| else { |
| forceIndexUpdate = updateIndex; |
| } |
| } |
| else { |
| indexFile = IndexLocation.createIndexLocation(indexURL); |
| } |
| } |
| if (JavaCore.getPlugin() == null) return; |
| IndexRequest request = null; |
| Object target = JavaModel.getTarget(path, true); |
| if (target instanceof IFile) { |
| request = isJrt(((IFile) target).getFullPath().toOSString()) ? |
| new AddJrtToIndex((IFile) target, indexFile, this, forceIndexUpdate) : |
| new AddJarFileToIndex((IFile) target, indexFile, this, forceIndexUpdate); |
| } else if (target instanceof File) { |
| request = getRequest(target, path, indexFile, this, forceIndexUpdate); |
| } else if (target instanceof IContainer) { |
| request = new IndexBinaryFolder((IContainer) target, this); |
| } else { |
| return; |
| } |
| |
| // check if the same request is not already in the queue |
| if (!isJobWaiting(request)) |
| request(request); |
| } |
| |
| synchronized boolean addIndex(IPath containerPath, IndexLocation indexFile) { |
| getIndexStates().put(indexFile, REUSE_STATE); |
| this.indexLocations.put(containerPath, indexFile); |
| Index index = getIndex(containerPath, indexFile, true, false); |
| if (index == null) { |
| indexFile.close(); |
| this.indexLocations.put(containerPath, null); |
| return false; |
| } |
| writeIndexMapFile(); |
| return true; |
| } |
| |
| /** |
| * Index the content of the given source folder. |
| */ |
| public void indexSourceFolder(JavaProject javaProject, IPath sourceFolder, char[][] inclusionPatterns, char[][] exclusionPatterns) { |
| IProject project = javaProject.getProject(); |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(sourceFolder); |
| 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 (isJobWaiting(request)) return; |
| } |
| |
| request(new AddFolderToIndex(sourceFolder, project, inclusionPatterns, exclusionPatterns, this)); |
| } |
| public synchronized void jobWasCancelled(IPath containerPath) { |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| Index index = getIndex(indexLocation); |
| if (index != null) { |
| index.monitor = null; |
| this.indexes.removeKey(indexLocation); |
| } |
| 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. |
| */ |
| @Override |
| 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. |
| */ |
| @Override |
| protected void notifyIdle(long idlingTime){ |
| if (idlingTime > 1000 && this.needToSave) saveIndexes(); |
| } |
| /** |
| * Name of the background process |
| */ |
| @Override |
| public String processName(){ |
| return Messages.process_name; |
| } |
| private char[][] readJavaLikeNamesFile() { |
| try { |
| String pathName = getJavaPluginWorkingLocation().toOSString(); |
| File javaLikeNamesFile = new File(pathName, "javaLikeNames.txt"); //$NON-NLS-1$ |
| if (!javaLikeNamesFile.exists()) |
| return null; |
| char[] javaLikeNames = org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(javaLikeNamesFile, null); |
| if (javaLikeNames.length > 0) { |
| char[][] names = CharOperation.splitOn('\n', javaLikeNames); |
| return names; |
| } |
| } catch (IOException ignored) { |
| if (VERBOSE) |
| Util.verbose("Failed to read javaLikeNames file"); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| private void rebuildIndex(IndexLocation indexLocation, IPath containerPath) { |
| rebuildIndex(indexLocation, containerPath, false); |
| } |
| private void rebuildIndex(IndexLocation indexLocation, IPath containerPath, final boolean updateIndex) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(containerPath); |
| Object target = JavaModel.getTarget(containerPath, true); |
| if (target == null) return; |
| |
| if (VERBOSE) |
| Util.verbose("-> request to rebuild index: "+indexLocation+" path: "+containerPath); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| updateIndexState(indexLocation, REBUILDING_STATE); |
| IndexRequest request = null; |
| if (target instanceof IProject) { |
| IProject p = (IProject) target; |
| if (JavaProject.hasJavaNature(p)) |
| request = new IndexAllProject(p, this); |
| } else if (target instanceof IFolder) { |
| request = new IndexBinaryFolder((IFolder) target, this); |
| } else if (target instanceof IFile) { |
| request = isJrt(((IFile) target).getFullPath().toOSString()) ? |
| new AddJrtToIndex((IFile) target, null, this, updateIndex) : |
| new AddJarFileToIndex((IFile) target, null, this, updateIndex); |
| } else if (target instanceof File) { |
| request = getRequest(target, containerPath, null, this, updateIndex); |
| } |
| if (request != null) |
| 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) { |
| // only called to over write an existing cached index... |
| String containerPathString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); |
| try { |
| // Path is already canonical |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| Index index = getIndex(indexLocation); |
| ReadWriteMonitor monitor = index == null ? null : index.monitor; |
| |
| if (VERBOSE) |
| Util.verbose("-> recreating index: "+indexLocation+" for path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ |
| 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){ |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(indexedContainer); |
| 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 || DEBUG) |
| Util.verbose("removing index " + containerPath); //$NON-NLS-1$ |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(containerPath); |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| Index index = getIndex(indexLocation); |
| File indexFile = null; |
| if (index != null) { |
| index.monitor = null; |
| indexFile = index.getIndexFile(); |
| } |
| if (indexFile == null) |
| indexFile = indexLocation.getIndexFile(); // index is not cached yet, but still want to delete the file |
| if (this.indexStates.get(indexLocation) == REUSE_STATE) { |
| indexLocation.close(); |
| this.indexLocations.put(containerPath, null); |
| } else if (indexFile != null && indexFile.exists()) { |
| if (DEBUG) |
| Util.verbose("removing index file " + indexFile); //$NON-NLS-1$ |
| indexFile.delete(); |
| } |
| this.indexes.removeKey(indexLocation); |
| if (IS_MANAGING_PRODUCT_INDEXES_PROPERTY) { |
| this.indexLocations.removeKey(containerPath); |
| } |
| updateIndexState(indexLocation, null); |
| } |
| /** |
| * Removes all indexes whose paths start with (or are equal to) the given path. |
| */ |
| public synchronized void removeIndexPath(IPath path) { |
| if (VERBOSE || DEBUG) |
| Util.verbose("removing index path " + path); //$NON-NLS-1$ |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(path); |
| Object[] keyTable = this.indexes.keyTable; |
| Object[] valueTable = this.indexes.valueTable; |
| IndexLocation[] locations = null; |
| int max = this.indexes.elementSize; |
| int count = 0; |
| for (int i = 0, l = keyTable.length; i < l; i++) { |
| IndexLocation indexLocation = (IndexLocation) keyTable[i]; |
| if (indexLocation == null) |
| continue; |
| if (indexLocation.startsWith(path)) { |
| Index index = (Index) valueTable[i]; |
| index.monitor = null; |
| if (locations == null) |
| locations = new IndexLocation[max]; |
| locations[count++] = indexLocation; |
| if (this.indexStates.get(indexLocation) == REUSE_STATE) { |
| indexLocation.close(); |
| } else { |
| if (DEBUG) |
| Util.verbose("removing index file " + indexLocation); //$NON-NLS-1$ |
| indexLocation.delete(); |
| } |
| } else { |
| max--; |
| } |
| } |
| if (locations != null) { |
| for (int i = 0; i < count; i++) |
| this.indexes.removeKey(locations[i]); |
| removeIndexesState(locations); |
| if (this.participantsContainers != null) { |
| boolean update = false; |
| for (int i = 0; i < count; i++) { |
| if (this.participantsContainers.get(locations[i]) != null) { |
| update = true; |
| this.participantsContainers.removeKey(locations[i]); |
| } |
| } |
| if (update) writeParticipantsIndexNamesFile(); |
| } |
| } |
| } |
| /** |
| * Removes all indexes whose paths start with (or are equal to) the given path. |
| */ |
| public synchronized void removeIndexFamily(IPath path) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(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++) |
| removeIndex((IPath) toRemove.get(i)); |
| } |
| /** |
| * Remove the content of the given source folder from the index. |
| */ |
| public void removeSourceFolderFromIndex(JavaProject javaProject, IPath sourceFolder, char[][] inclusionPatterns, char[][] exclusionPatterns) { |
| // New index is disabled, see bug 544898 |
| // this.indexer.makeWorkspacePathDirty(sourceFolder); |
| IProject project = javaProject.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 (isJobWaiting(request)) return; |
| } |
| |
| request(new RemoveFolderFromIndex(sourceFolder, inclusionPatterns, exclusionPatterns, project, this)); |
| } |
| /** |
| * Flush current state |
| */ |
| @Override |
| public void reset() { |
| super.reset(); |
| synchronized (this) { |
| if (this.indexes != null) { |
| this.indexes = new SimpleLookupTable(); |
| this.indexStates = null; |
| } |
| this.indexLocations = new SimpleLookupTable(); |
| this.javaPluginLocation = null; |
| } |
| } |
| /** |
| * Resets the index for a given path. |
| * Returns true if the index was reset, false otherwise. |
| */ |
| public synchronized boolean resetIndex(IPath containerPath) { |
| // only called to over write an existing cached index... |
| String containerPathString = containerPath.getDevice() == null ? containerPath.toString() : containerPath.toOSString(); |
| try { |
| // Path is already canonical |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| Index index = getIndex(indexLocation); |
| if (VERBOSE) { |
| Util.verbose("-> reseting index: "+indexLocation+" for path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if (index == null) { |
| // the index does not exist, try to recreate it |
| return recreateIndex(containerPath) != null; |
| } |
| index.reset(); |
| return true; |
| } catch (IOException e) { |
| // The file could not be created. Possible reason: the project has been deleted. |
| if (VERBOSE) { |
| Util.verbose("-> failed to reset index for path: "+containerPathString); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| return false; |
| } |
| } |
| /** |
| * {@link #saveIndex(Index)} will only update the state if there are no other jobs running against the same |
| * underlying resource for this index. Pre-built indexes must be in a {@link #REUSE_STATE} state even if |
| * there is another job to run against it as the subsequent job will find the index and not save it in the |
| * right state. |
| * Refer to https://bugs.eclipse.org/bugs/show_bug.cgi?id=405932 |
| */ |
| public void savePreBuiltIndex(Index index) throws IOException { |
| if (index.hasChanged()) { |
| if (VERBOSE) |
| Util.verbose("-> saving pre-build index " + index.getIndexLocation()); //$NON-NLS-1$ |
| index.save(); |
| } |
| synchronized (this) { |
| updateIndexState(index.getIndexLocation(), REUSE_STATE); |
| } |
| } |
| public 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.getIndexLocation()); //$NON-NLS-1$ |
| index.save(); |
| } |
| synchronized (this) { |
| IPath containerPath = new Path(index.containerPath); |
| if (this.jobEnd > this.jobStart) { |
| 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; |
| } |
| } |
| IndexLocation indexLocation = computeIndexLocation(containerPath); |
| 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) { |
| Object[] valueTable = this.indexes.valueTable; |
| for (int i = 0, l = valueTable.length; i < l; i++) { |
| Index index = (Index) valueTable[i]; |
| if (index != null) |
| toSave.add(index); |
| } |
| } |
| |
| 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 { |
| 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(); |
| } |
| } |
| if (this.participantsContainers != null && this.participantUpdated) { |
| writeParticipantsIndexNamesFile(); |
| this.participantUpdated = false; |
| } |
| this.needToSave = !allSaved; |
| } |
| public void scheduleDocumentIndexing(final SearchDocument searchDocument, IPath container, final IndexLocation indexLocation, final SearchParticipant searchParticipant) { |
| // New index is disabled, see bug 544898 |
| // IPath targetLocation = JavaIndex.getLocationForPath(new Path(searchDocument.getPath())); |
| // if (targetLocation != null) { |
| // this.indexer.makeDirty(targetLocation); |
| // } |
| request(new IndexRequest(container, this) { |
| @Override |
| public boolean execute(IProgressMonitor progressMonitor) { |
| if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true; |
| |
| /* ensure no concurrent write access to index */ |
| Index index = 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 |
| final Path indexPath = new Path(indexLocation.getCanonicalFilePath()); |
| try { |
| monitor.enterWrite(); // ask permission to write |
| indexDocument(searchDocument, searchParticipant, index, indexPath); |
| } finally { |
| monitor.exitWrite(); // free write lock |
| } |
| if (searchDocument.shouldIndexResolvedDocument()) { |
| indexResolvedDocument(searchDocument, searchParticipant, index, indexPath); |
| } |
| return true; |
| } |
| @Override |
| public String toString() { |
| return "indexing " + searchDocument.getPath(); //$NON-NLS-1$ |
| } |
| @Override |
| public boolean waitNeeded() { |
| return false; |
| } |
| }); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(10); |
| buffer.append(super.toString()); |
| buffer.append("In-memory indexes:\n"); //$NON-NLS-1$ |
| int count = 0; |
| Object[] valueTable = this.indexes.valueTable; |
| for (int i = 0, l = valueTable.length; i < l; i++) { |
| Index index = (Index) valueTable[i]; |
| if (index != null) |
| buffer.append(++count).append(" - ").append(index.toString()).append('\n'); //$NON-NLS-1$ |
| } |
| return buffer.toString(); |
| } |
| |
| private void readIndexMap() { |
| try { |
| char[] indexMaps = org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(this.indexNamesMapFile, null); |
| char[][] names = CharOperation.splitOn('\n', indexMaps); |
| if (names.length >= 3) { |
| // First line is DiskIndex signature (see writeIndexMapFile()) |
| String savedSignature = DiskIndex.SIGNATURE; |
| if (savedSignature.equals(new String(names[0]))) { |
| for (int i = 1, l = names.length-1 ; i < l ; i+=2) { |
| IndexLocation indexPath = IndexLocation.createIndexLocation(new URL(new String(names[i]))); |
| if (indexPath == null) continue; |
| this.indexLocations.put(new Path(new String(names[i+1])), indexPath ); |
| this.indexStates.put(indexPath, REUSE_STATE); |
| } |
| } |
| } |
| } catch (IOException ignored) { |
| if (VERBOSE) |
| Util.verbose("Failed to read saved index file names"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| private void readParticipantsIndexNamesFile() { |
| SimpleLookupTable containers = new SimpleLookupTable(3); |
| try { |
| char[] participantIndexNames = org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(this.participantIndexNamesFile, null); |
| if (participantIndexNames.length > 0) { |
| char[][] names = CharOperation.splitOn('\n', participantIndexNames); |
| if (names.length >= 3) { |
| // First line is DiskIndex signature (see writeParticipantsIndexNamesFile()) |
| if (DiskIndex.SIGNATURE.equals(new String(names[0]))) { |
| for (int i = 1, l = names.length-1 ; i < l ; i+=2) { |
| IndexLocation indexLocation = new FileIndexLocation(new File(new String(names[i])), true); |
| containers.put(indexLocation, new Path(new String(names[i+1]))); |
| } |
| } |
| } |
| } |
| } catch (IOException ignored) { |
| if (VERBOSE) |
| Util.verbose("Failed to read participant index file names"); //$NON-NLS-1$ |
| } |
| this.participantsContainers = containers; |
| return; |
| } |
| private synchronized void removeIndexesState(IndexLocation[] locations) { |
| 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; |
| |
| writeSavedIndexNamesFile(); |
| writeIndexMapFile(); |
| } |
| private synchronized void updateIndexState(IndexLocation indexLocation, Integer indexState) { |
| if (indexLocation == null) |
| throw new IllegalArgumentException(); |
| |
| 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); |
| } |
| |
| writeSavedIndexNamesFile(); |
| |
| if (VERBOSE) { |
| if (indexState == null) { |
| Util.verbose("-> index state removed for: "+indexLocation); //$NON-NLS-1$ |
| } else { |
| 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$ |
| else if (indexState == REUSE_STATE) state = "REUSE"; //$NON-NLS-1$ |
| Util.verbose("-> index state updated to: " + state + " for: "+indexLocation); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| } |
| public void updateParticipant(IPath indexPath, IPath containerPath) { |
| if (this.participantsContainers == null) { |
| readParticipantsIndexNamesFile(); |
| } |
| IndexLocation indexLocation = new FileIndexLocation(indexPath.toFile(), true); |
| if (this.participantsContainers.get(indexLocation) == null) { |
| this.participantsContainers.put(indexLocation, containerPath); |
| this.participantUpdated = true; |
| } |
| } |
| private void writeJavaLikeNamesFile() { |
| BufferedWriter writer = null; |
| String pathName = getJavaPluginWorkingLocation().toOSString(); |
| try { |
| char[][] currentNames = Util.getJavaLikeExtensions(); |
| int length = currentNames.length; |
| if (length > 1) { |
| // Sort the current java like names. |
| // Copy the array to avoid modifying the Util static variable |
| System.arraycopy(currentNames, 0, currentNames=new char[length][], 0, length); |
| Util.sort(currentNames); |
| } |
| File javaLikeNamesFile = new File(pathName, "javaLikeNames.txt"); //$NON-NLS-1$ |
| writer = new BufferedWriter(new FileWriter(javaLikeNamesFile)); |
| for (int i = 0; i < length-1; i++) { |
| writer.write(currentNames[i]); |
| writer.write('\n'); |
| } |
| if (length > 0) |
| writer.write(currentNames[length-1]); |
| |
| } catch (IOException ignored) { |
| if (VERBOSE) |
| Util.verbose("Failed to write javaLikeNames file", System.err); //$NON-NLS-1$ |
| } finally { |
| if (writer != null) { |
| try { |
| writer.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| private void writeIndexMapFile() { |
| BufferedWriter writer = null; |
| try { |
| writer = new BufferedWriter(new FileWriter(this.indexNamesMapFile)); |
| writer.write(DiskIndex.SIGNATURE); |
| writer.write('\n'); |
| Object[] keys = this.indexStates.keyTable; |
| Object[] states = this.indexStates.valueTable; |
| for (int i = 0, l = states.length; i < l; i++) { |
| IndexLocation location = (IndexLocation)keys[i]; |
| if (location != null && states[i] == REUSE_STATE) { |
| IPath container = (IPath)this.indexLocations.keyForValue(location); |
| if (container != null) { |
| writer.write(location.toString()); |
| writer.write('\n'); |
| writer.write(container.toOSString()); |
| 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 |
| } |
| } |
| } |
| } |
| private void writeParticipantsIndexNamesFile() { |
| BufferedWriter writer = null; |
| try { |
| writer = new BufferedWriter(new FileWriter(this.participantIndexNamesFile)); |
| writer.write(DiskIndex.SIGNATURE); |
| writer.write('\n'); |
| Object[] indexFiles = this.participantsContainers.keyTable; |
| Object[] containers = this.participantsContainers.valueTable; |
| for (int i = 0, l = indexFiles.length; i < l; i++) { |
| IndexLocation indexFile = (IndexLocation)indexFiles[i]; |
| if (indexFile != null) { |
| writer.write(indexFile.getIndexFile().getPath()); |
| writer.write('\n'); |
| writer.write(((IPath)containers[i]).toOSString()); |
| writer.write('\n'); |
| } |
| } |
| } catch (IOException ignored) { |
| if (VERBOSE) |
| Util.verbose("Failed to write participant index file names", System.err); //$NON-NLS-1$ |
| } finally { |
| if (writer != null) { |
| try { |
| writer.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| private void writeSavedIndexNamesFile() { |
| Object[] keys = this.indexStates.keyTable; |
| Object[] states = this.indexStates.valueTable; |
| int numToSave = 0; |
| for (int i = 0, l = states.length; i < l; i++) { |
| IndexLocation key = (IndexLocation) keys[i]; |
| if (key != null && states[i] == SAVED_STATE) { |
| numToSave++; |
| } |
| } |
| char[][] arrays = new char[numToSave][]; |
| int idx = 0; |
| for (int i = 0, l = states.length; i < l; i++) { |
| IndexLocation key = (IndexLocation) keys[i]; |
| if (key != null && states[i] == SAVED_STATE) { |
| arrays[idx++] = key.fileName().toCharArray(); |
| } |
| } |
| this.nameRegistry.write(arrays); |
| } |
| } |