| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| |
| *******************************************************************************/ |
| package org.eclipse.dltk.core.search.index; |
| |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.compiler.util.HashtableOfObject; |
| import org.eclipse.dltk.compiler.util.SimpleLookupTable; |
| import org.eclipse.dltk.compiler.util.SimpleSet; |
| import org.eclipse.dltk.core.search.SearchPattern; |
| import org.eclipse.dltk.core.search.indexing.IIndexConstants; |
| import org.eclipse.dltk.internal.core.util.SimpleWordSet; |
| |
| public class MemoryIndex { |
| |
| public int NUM_CHANGES = 500; // number of separate document changes... |
| // used to decide when to merge |
| |
| SimpleLookupTable docsToReferences; // document paths -> |
| // HashtableOfObject(category names -> |
| // set of words) |
| |
| SimpleWordSet allWords; // save space by locally interning the referenced |
| // words, since an indexer can generate numerous |
| // duplicates |
| |
| String lastDocumentName; |
| |
| HashtableOfObject lastReferenceTable; |
| |
| MemoryIndex() { |
| this.docsToReferences = new SimpleLookupTable(7); |
| this.allWords = new SimpleWordSet(7); |
| } |
| |
| void addDocumentNames(String substring, SimpleSet results) { |
| // assumed the disk index already skipped over documents which have been |
| // added/changed/deleted |
| Object[] paths = this.docsToReferences.keyTable; |
| Object[] referenceTables = this.docsToReferences.valueTable; |
| if (substring == null) { // add all new/changed documents |
| for (int i = 0, l = referenceTables.length; i < l; i++) |
| if (referenceTables[i] != null) |
| results.add(paths[i]); |
| } else { |
| for (int i = 0, l = referenceTables.length; i < l; i++) |
| if (referenceTables[i] != null |
| && ((String) paths[i]).startsWith(substring, 0)) |
| results.add(paths[i]); |
| } |
| } |
| |
| void addIndexEntry(char[] category, char[] key, String documentName) { |
| HashtableOfObject referenceTable; |
| if (documentName.equals(this.lastDocumentName)) |
| referenceTable = this.lastReferenceTable; |
| else { |
| // assumed a document was removed before its reindexed |
| referenceTable = (HashtableOfObject) this.docsToReferences |
| .get(documentName); |
| if (referenceTable == null) |
| this.docsToReferences.put(documentName, |
| referenceTable = new HashtableOfObject(3)); |
| this.lastDocumentName = documentName; |
| this.lastReferenceTable = referenceTable; |
| } |
| |
| SimpleWordSet existingWords = (SimpleWordSet) referenceTable |
| .get(category); |
| if (existingWords == null) |
| referenceTable.put(category, existingWords = new SimpleWordSet(1)); |
| |
| existingWords.add(this.allWords.add(key)); |
| } |
| |
| HashtableOfObject addQueryResults(char[][] categories, char[] key, |
| int matchRule, HashtableOfObject results) { |
| // assumed the disk index already skipped over documents which have been |
| // added/changed/deleted |
| // results maps a word -> EntryResult |
| Object[] paths = this.docsToReferences.keyTable; |
| Object[] referenceTables = this.docsToReferences.valueTable; |
| if (matchRule == (SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE) |
| && key != null) { |
| nextPath: for (int i = 0, l = referenceTables.length; i < l; i++) { |
| HashtableOfObject categoryToWords = (HashtableOfObject) referenceTables[i]; |
| if (categoryToWords != null) { |
| for (int j = 0, m = categories.length; j < m; j++) { |
| SimpleWordSet wordSet = (SimpleWordSet) categoryToWords |
| .get(categories[j]); |
| if (wordSet != null && wordSet.includes(key)) { |
| if (results == null) |
| results = new HashtableOfObject(13); |
| EntryResult result = (EntryResult) results.get(key); |
| if (result == null) |
| results.put(key, result = new EntryResult(key, |
| null)); |
| result.addDocumentName((String) paths[i]); |
| continue nextPath; |
| } |
| } |
| } |
| } |
| } else if ((matchRule & SearchPattern.R_REGEXP_MATCH) != 0) { |
| Pattern regexpPattern = Pattern |
| .compile( |
| new String(key), |
| (matchRule & SearchPattern.R_CASE_SENSITIVE) == 0 ? Pattern.CASE_INSENSITIVE |
| : 0); |
| for (int i = 0, l = referenceTables.length; i < l; i++) { |
| HashtableOfObject categoryToWords = (HashtableOfObject) referenceTables[i]; |
| if (categoryToWords != null) { |
| for (int j = 0, m = categories.length; j < m; j++) { |
| SimpleWordSet wordSet = (SimpleWordSet) categoryToWords |
| .get(categories[j]); |
| if (wordSet != null) { |
| char[][] words = wordSet.words; |
| for (int k = 0, n = words.length; k < n; k++) { |
| char[] word = words[k]; |
| if (word != null) { |
| char decodedWord[]; |
| int sep = CharOperation.indexOf( |
| IIndexConstants.SEPARATOR, word); |
| if (sep > 0) { |
| decodedWord = CharOperation.subarray( |
| word, 0, sep); |
| } else { |
| decodedWord = word; |
| } |
| if (regexpPattern.matcher( |
| new String(decodedWord)).matches()) { |
| if (results == null) |
| results = new HashtableOfObject(13); |
| EntryResult result = (EntryResult) results |
| .get(word); |
| if (result == null) |
| results.put(word, |
| result = new EntryResult( |
| word, null)); |
| result |
| .addDocumentName((String) paths[i]); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } else { |
| for (int i = 0, l = referenceTables.length; i < l; i++) { |
| HashtableOfObject categoryToWords = (HashtableOfObject) referenceTables[i]; |
| if (categoryToWords != null) { |
| for (int j = 0, m = categories.length; j < m; j++) { |
| SimpleWordSet wordSet = (SimpleWordSet) categoryToWords |
| .get(categories[j]); |
| if (wordSet != null) { |
| char[][] words = wordSet.words; |
| for (int k = 0, n = words.length; k < n; k++) { |
| char[] word = words[k]; |
| if (word != null |
| && Index.isMatch(key, word, matchRule)) { |
| if (results == null) |
| results = new HashtableOfObject(13); |
| EntryResult result = (EntryResult) results |
| .get(word); |
| if (result == null) |
| results.put(word, |
| result = new EntryResult(word, |
| null)); |
| result.addDocumentName((String) paths[i]); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return results; |
| } |
| |
| boolean hasChanged() { |
| return this.docsToReferences.elementSize > 0; |
| } |
| |
| void remove(String documentName) { |
| if (documentName.equals(this.lastDocumentName)) { |
| this.lastDocumentName = null; |
| this.lastReferenceTable = null; |
| } |
| this.docsToReferences.put(documentName, null); |
| } |
| |
| boolean shouldMerge() { |
| return this.docsToReferences.elementSize >= NUM_CHANGES; |
| } |
| } |