| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.index; |
| |
| import java.io.*; |
| |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.search.*; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
| import org.eclipse.jdt.internal.core.util.*; |
| import org.eclipse.jdt.internal.core.search.indexing.InternalSearchDocument; |
| import org.eclipse.jdt.internal.core.search.indexing.ReadWriteMonitor; |
| |
| /** |
| * An <code>Index</code> maps document names to their referenced words in various categories. |
| * |
| * Queries can search a single category or several at the same time. |
| * |
| * Indexes are not synchronized structures and should only be queried/updated one at a time. |
| */ |
| |
| public class Index { |
| |
| public String printableName; |
| public ReadWriteMonitor monitor; |
| |
| protected DiskIndex diskIndex; |
| protected MemoryIndex memoryIndex; |
| |
| /** |
| * Returns the path represented by pathString converted back to a path relative to the local file system. |
| * |
| * @param pathString the path to convert: |
| * <ul> |
| * <li>an absolute IPath (relative to the workspace root) if the path represents a resource in the |
| * workspace |
| * <li>a relative IPath (relative to the workspace root) followed by JAR_FILE_ENTRY_SEPARATOR |
| * followed by an absolute path (relative to the jar) if the path represents a .class file in |
| * an internal jar |
| * <li>an absolute path (relative to the file system) followed by JAR_FILE_ENTRY_SEPARATOR |
| * followed by an absolute path (relative to the jar) if the path represents a .class file in |
| * an external jar |
| * </ul> |
| * @return the converted path: |
| * <ul> |
| * <li>the original pathString if the path represents a resource in the workspace |
| * <li>an absolute path (relative to the file system) followed by JAR_FILE_ENTRY_SEPARATOR |
| * followed by an absolute path (relative to the jar) if the path represents a .class file in |
| * an external or internal jar |
| * </ul> |
| */ |
| public static String convertPath(String pathString) { |
| int index = pathString.indexOf(IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR); |
| if (index == -1) return pathString; |
| |
| Path jarPath = new Path(pathString.substring(0, index)); |
| return (jarPath.isAbsolute() ? jarPath.toOSString() : jarPath.makeAbsolute().toString()) |
| + pathString.substring(index, pathString.length()); |
| } |
| public static boolean isMatch(char[] pattern, char[] word, int matchRule) { |
| if (pattern == null) return true; |
| |
| switch(matchRule) { |
| case SearchPattern.R_EXACT_MATCH : |
| return CharOperation.equals(pattern, word, false); |
| case SearchPattern.R_PREFIX_MATCH : |
| return CharOperation.prefixEquals(pattern, word, false); |
| case SearchPattern.R_PATTERN_MATCH : |
| return CharOperation.match(pattern, word, false); |
| case SearchPattern.R_EXACT_MATCH + SearchPattern.R_CASE_SENSITIVE : |
| return CharOperation.equals(pattern, word); |
| case SearchPattern.R_PREFIX_MATCH + SearchPattern.R_CASE_SENSITIVE : |
| return CharOperation.prefixEquals(pattern, word); |
| case SearchPattern.R_PATTERN_MATCH + SearchPattern.R_CASE_SENSITIVE : |
| return CharOperation.match(pattern, word, true); |
| } |
| return false; |
| } |
| |
| |
| public Index(String fileName, String printableName, boolean reuseExistingFile) throws IOException { |
| this.printableName = printableName; |
| this.monitor = new ReadWriteMonitor(); |
| |
| this.memoryIndex = new MemoryIndex(); |
| this.diskIndex = new DiskIndex(fileName); |
| this.diskIndex.initialize(reuseExistingFile); |
| } |
| public void addIndexEntry(char[] category, char[] key, InternalSearchDocument document) { |
| this.memoryIndex.addIndexEntry(category, key, document); |
| } |
| public File getIndexFile() { |
| if (this.diskIndex == null) return null; |
| |
| return this.diskIndex.getIndexFile(); |
| } |
| public boolean hasChanged() { |
| return this.memoryIndex.hasChanged(); |
| } |
| /** |
| * Returns the entries containing the given key in a group of categories, or null if no matches are found. |
| * The matchRule dictates whether its an exact, prefix or pattern match, as well as case sensitive or insensitive. |
| * If the key is null then all entries in specified categories are returned. |
| */ |
| public EntryResult[] query(char[][] categories, char[] key, int matchRule) throws IOException { |
| if (this.memoryIndex.shouldMerge() && monitor.exitReadEnterWrite()) { |
| try { |
| save(); |
| } finally { |
| monitor.exitWriteEnterRead(); |
| } |
| } |
| |
| HashtableOfObject results; |
| if (this.memoryIndex.hasChanged()) { |
| results = this.diskIndex.addQueryResults(categories, key, matchRule, this.memoryIndex); |
| this.memoryIndex.addQueryResults(categories, key, matchRule, results); |
| } else { |
| results = this.diskIndex.addQueryResults(categories, key, matchRule, null); |
| } |
| if (results.elementSize == 0) return null; |
| |
| EntryResult[] entryResults = new EntryResult[results.elementSize]; |
| int count = 0; |
| Object[] values = results.valueTable; |
| for (int i = 0, l = values.length; i < l; i++) { |
| EntryResult result = (EntryResult) values[i]; |
| if (result != null) |
| entryResults[count++] = result; |
| } |
| return entryResults; |
| } |
| /** |
| * Returns the document names that contain the given substring, if null then returns all of them. |
| */ |
| public String[] queryDocumentNames(String substring) throws IOException { |
| SimpleSet results; |
| if (this.memoryIndex.hasChanged()) { |
| results = this.diskIndex.addDocumentNames(substring, this.memoryIndex); |
| this.memoryIndex.addDocumentNames(substring, results); |
| } else { |
| results = this.diskIndex.addDocumentNames(substring, null); |
| } |
| if (results.elementSize == 0) return null; |
| |
| String[] documentNames = new String[results.elementSize]; |
| int count = 0; |
| Object[] paths = results.values; |
| for (int i = 0, l = paths.length; i < l; i++) |
| if (paths[i] != null) |
| documentNames[count++] = (String) paths[i]; |
| return documentNames; |
| } |
| public void remove(String documentName) { |
| this.memoryIndex.remove(documentName); |
| } |
| public void save() throws IOException { |
| // must own the write lock of the monitor |
| if (!hasChanged()) return; |
| |
| int numberOfChanges = this.memoryIndex.docsToReferences.elementSize; |
| this.diskIndex = this.diskIndex.mergeWith(this.memoryIndex); |
| this.memoryIndex = new MemoryIndex(); |
| if (numberOfChanges > 1000) |
| System.gc(); // reclaim space if the MemoryIndex was very BIG |
| } |
| public void startQuery() throws IOException { |
| if (this.diskIndex != null) |
| this.diskIndex.startQuery(); |
| } |
| public void stopQuery() throws IOException { |
| if (this.diskIndex != null) |
| this.diskIndex.stopQuery(); |
| } |
| public String toString() { |
| if (this.printableName != null) return this.printableName; |
| return super.toString(); |
| } |
| } |