package org.rubypeople.rdt.core.search; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.rubypeople.rdt.core.IParent; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.IType; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.internal.core.Openable; import org.rubypeople.rdt.internal.core.index.EntryResult; import org.rubypeople.rdt.internal.core.search.HandleFactory; import org.rubypeople.rdt.internal.core.search.indexing.IIndexConstants; import org.rubypeople.rdt.internal.core.search.indexing.InternalSearchDocument; public abstract class SearchDocument extends InternalSearchDocument { private static HandleFactory factory = new HandleFactory(); private IRubyScript script; private String documentPath; private SearchParticipant participant; public SearchDocument(String documentPath, SearchParticipant participant) { this.documentPath = documentPath; this.participant = participant; } public void addIndexEntry(char[] category, char[] key) { super.addIndexEntry(category, key); } /** * Removes all index entries from the index for the given document. * This method must be called from * {@link SearchParticipant#indexDocument(SearchDocument document, org.eclipse.core.runtime.IPath indexPath)}. */ public void removeAllIndexEntries() { super.removeAllIndexEntries(); } public Set<String> getElementNamesOfType(int type) { Set<String> names = new HashSet<String>(); try { EntryResult[] results = index.query(new char[][] {getCategory(type)}, new char[] {'*'}, SearchPattern.R_PATTERN_MATCH); for (int i = 0; i < results.length; i++) { String name = new String(results[i].getWord()); names.add(name); } } catch (IOException e) { RubyCore.log(e); } return names; } public List<IRubyElement> getElementsOfType(int type) { IRubyScript script = getScript(); return getChildrenOfType(script, type); } private IRubyScript getScript() { if (this.script == null) { Openable openable = factory.createOpenable(documentPath); this.script = (IRubyScript) openable; } return this.script; } /** * Returns the path to the original document to publicly mention in index * or search results. This path is a string that uniquely identifies the document. * Most of the time it is a workspace-relative path, but it can also be a file system path, * or a path inside a zip file. * * @return the path to the document */ public final String getPath() { return this.documentPath; } private List<IRubyElement> getChildrenOfType(IParent parent, int type) { List<IRubyElement> elements = new ArrayList<IRubyElement>(); if (parent == null) return elements; try { IRubyElement[] children = parent.getChildren(); if (children == null) return elements; for (int i = 0; i < children.length; i++) { if (children[i].isType(type)) elements.add(children[i]); if (children[i] instanceof IParent) { IParent childParent = (IParent) children[i]; elements.addAll(getChildrenOfType(childParent, type)); } } } catch (RubyModelException e) { // ignore } return elements; } public void removeElement(IRubyElement element) { // FIXME Rebuild the index?! } public void addElement(IRubyElement element) { addIndexEntry(getCategory(element), element.getElementName().toCharArray()); } private char[] getCategory(IRubyElement element) { return getCategory(element.getElementType()); } private char[] getCategory(int elementType) { switch (elementType) { case IRubyElement.TYPE: return IIndexConstants.TYPE_DECL; case IRubyElement.METHOD: return IIndexConstants.METHOD_DECL; case IRubyElement.CONSTANT: case IRubyElement.GLOBAL: case IRubyElement.CLASS_VAR: case IRubyElement.INSTANCE_VAR: case IRubyElement.LOCAL_VARIABLE: return IIndexConstants.FIELD_DECL; default: return new char[0]; } } public IType findType(String name) { return (IType) findElement(IRubyElement.TYPE, name); } private IRubyElement findElement(int type, String name) { IRubyScript script = getScript(); List<IRubyElement> children = getChildrenOfType(script, type); for (IRubyElement element : children) { if (element.getElementName().equals(name)) return element; } return null; } /** * Returns the contents of this document. * Contents may be different from actual resource at corresponding document * path due to preprocessing. * <p> * This method must be implemented in subclasses. * </p><p> * Note: some implementation may choose to cache the contents directly on the * document for performance reason. However, this could induce scalability issues due * to the fact that collections of documents are manipulated throughout the search * operation, and cached contents would then consume lots of memory until they are * all released at once in the end. * </p> * * @return the contents of this document, * or <code>null</code> if none */ public abstract char[] getCharContents(); /** * Returns the participant that created this document. * * @return the participant that created this document */ public final SearchParticipant getParticipant() { return this.participant; } }