package com.aptana.ide.index.core; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IPath; import com.aptana.ide.internal.index.core.DiskIndex; import com.aptana.ide.internal.index.core.MemoryIndex; import com.aptana.ide.internal.index.core.ReadWriteMonitor; public class Index { private static final int MATCH_RULE_INDEX_MASK = SearchPattern.EXACT_MATCH | SearchPattern.PREFIX_MATCH | SearchPattern.PATTERN_MATCH | SearchPattern.CASE_SENSITIVE; // Separator to use after the container path public static final char DEFAULT_SEPARATOR = '/'; public char separator = DEFAULT_SEPARATOR; private MemoryIndex memoryIndex; private DiskIndex diskIndex; ReadWriteMonitor monitor; public Index(String path) throws IOException { IPath containerPath = IndexManager.getInstance().computeIndexLocation(path); String containerPathString = containerPath.getDevice() == null ? containerPath.toString() : containerPath .toOSString(); this.memoryIndex = new MemoryIndex(); this.monitor = new ReadWriteMonitor(); this.diskIndex = new DiskIndex(containerPathString); this.diskIndex.initialize(); } public void addEntry(String category, String key, String documentPath) { this.memoryIndex.addEntry(category, key, documentPath); } public List<QueryResult> query(String[] categories, String key, int matchRule) throws IOException { if (this.memoryIndex.shouldMerge() && monitor.exitReadEnterWrite()) { try { save(); } finally { monitor.exitWriteEnterRead(); } } Map<String, QueryResult> results; int rule = matchRule & MATCH_RULE_INDEX_MASK; if (this.memoryIndex.hasChanged()) { results = this.diskIndex.addQueryResults(categories, key, rule, this.memoryIndex); results = this.memoryIndex.addQueryResults(categories, key, rule, results); } else { results = this.diskIndex.addQueryResults(categories, key, rule, null); } if (results == null) return null; return new ArrayList<QueryResult>(results.values()); } public static boolean isMatch(String pattern, String word, int matchRule) { if (pattern == null) return true; int patternLength = pattern.length(); int wordLength = word.length(); if (patternLength == 0) return matchRule != SearchPattern.EXACT_MATCH; switch (matchRule) { case SearchPattern.EXACT_MATCH: return patternLength == wordLength && pattern.equalsIgnoreCase(word); case SearchPattern.PREFIX_MATCH: return patternLength <= wordLength && word.toLowerCase().startsWith(pattern.toLowerCase()); case SearchPattern.PATTERN_MATCH: return patternMatch(pattern.toLowerCase(), word.toLowerCase()); case SearchPattern.EXACT_MATCH | SearchPattern.CASE_SENSITIVE: return patternLength == wordLength && pattern.equals(word); case SearchPattern.PREFIX_MATCH | SearchPattern.CASE_SENSITIVE: return patternLength <= wordLength && word.startsWith(pattern); case SearchPattern.PATTERN_MATCH | SearchPattern.CASE_SENSITIVE: return patternMatch(pattern, word); } return false; } private static boolean patternMatch(String pattern, String word) { // TODO Sort of like a regexp match, just handle * and ? wildcards in the pattern if (pattern.equals("*")) return true; return false; } public void save() throws IOException { // must own the write lock of the monitor if (!hasChanged()) return; int numberOfChanges = this.memoryIndex.numberOfChanges(); this.diskIndex = this.diskIndex.mergeWith(this.memoryIndex); this.memoryIndex = new MemoryIndex(); if (numberOfChanges > 1000) System.gc(); // reclaim space if the MemoryIndex was very BIG } private boolean hasChanged() { return memoryIndex.hasChanged(); } /** * Remove all indices for a given document * @param containerRelativePath */ public void remove(String containerRelativePath) { this.memoryIndex.remove(containerRelativePath); } public void removeCategories(String... categoryNames) { try { this.memoryIndex.removeCategories(categoryNames); this.diskIndex = this.diskIndex.removeCategories(categoryNames, this.memoryIndex); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }