|
From: <caw...@us...> - 2007-05-01 14:54:58
|
Revision: 2407
http://svn.sourceforge.net/rubyeclipse/?rev=2407&view=rev
Author: cawilliams
Date: 2007-05-01 07:54:54 -0700 (Tue, 01 May 2007)
Log Message:
-----------
add the underlying code for us to enable the new type selection dialog. Also fix an error in the way I was generating index keys for type delcratiosn which caused us to never get any matches (I was storing the folder hierarchy in the place for type name and vice versa)
Modified Paths:
--------------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyScript.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchConstants.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchScope.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchPattern.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/NamedMember.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyType.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/BasicSearchEngine.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/RubySearchScope.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/indexing/SourceIndexerRequestor.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchEngine.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/TypeNameRequestor.java
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyScript.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyScript.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IRubyScript.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -375,4 +375,6 @@
IRubyElement getElementAt(int position) throws RubyModelException;
IType findPrimaryType();
+
+ IType[] getAllTypes() throws RubyModelException;
}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/IType.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -176,4 +176,16 @@
public ISourceFolder getSourceFolder();
+ public String getTypeQualifiedName(String string);
+
+ /**
+ * Returns the immediate member types declared by this type.
+ * The results are listed in the order in which they appear in the source or class file.
+ *
+ * @exception RubyModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource.
+ * @return the immediate member types declared by this type
+ */
+ IType[] getTypes() throws RubyModelException;
+
}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchConstants.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchConstants.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchConstants.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -50,4 +50,39 @@
* @since 2.0
*/
int WRITE_ACCESSES = 5;
+
+/* Nature of searched element */
+
+ /**
+ * The searched element is a type, which may include classes and modules.
+ */
+ int TYPE= 0;
+
+ /**
+ * The searched element is a method.
+ */
+ int METHOD= 1;
+
+ /**
+ * The searched element is a constructor.
+ */
+ int CONSTRUCTOR= 3;
+
+ /**
+ * The searched element is a field.
+ */
+ int FIELD= 4;
+
+ /**
+ * The searched element is a class.
+ * More selective than using {@link #TYPE}.
+ */
+ int CLASS= 5;
+
+ /**
+ * The searched element is a module.
+ * More selective than using {@link #TYPE}.
+ */
+ int MODULE= 6;
+
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchScope.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchScope.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/IRubySearchScope.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -1,6 +1,7 @@
package org.rubypeople.rdt.core.search;
import org.eclipse.core.runtime.IPath;
+import org.rubypeople.rdt.core.IRubyElement;
public interface IRubySearchScope {
@@ -68,4 +69,6 @@
*/
public boolean encloses(String resourcePath);
+ boolean encloses(IRubyElement element);
+
}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchEngine.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchEngine.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchEngine.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -0,0 +1,98 @@
+package org.rubypeople.rdt.core.search;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.core.WorkingCopyOwner;
+import org.rubypeople.rdt.internal.core.search.BasicSearchEngine;
+
+public class SearchEngine {
+
+ private BasicSearchEngine basicEngine;
+
+ /**
+ * Creates a new search engine.
+ */
+ public SearchEngine() {
+ this.basicEngine = new BasicSearchEngine();
+ }
+
+ /**
+ * Creates a new search engine with the given working copy owner.
+ * The working copies owned by this owner will take precedence over
+ * the primary compilation units in the subsequent search operations.
+ *
+ * @param workingCopyOwner the owner of the working copies that take precedence over their original compilation units
+ * @since 1.0
+ */
+ public SearchEngine(WorkingCopyOwner workingCopyOwner) {
+ this.basicEngine = new BasicSearchEngine(workingCopyOwner);
+ }
+
+ public static IRubySearchScope createWorkspaceScope() {
+ return BasicSearchEngine.createWorkspaceScope();
+ }
+
+ /**
+ * Searches for all top-level types and member types in the given scope.
+ * The search can be selecting specific types (given a package or a type name
+ * prefix and match modes).
+ *
+ * @param packageName the full name of the package of the searched types, or a prefix for this
+ * package, or a wild-carded string for this package.
+ * @param typeName the dot-separated qualified name of the searched type (the qualification include
+ * the enclosing types if the searched type is a member type), or a prefix
+ * for this type, or a wild-carded string for this type.
+ * @param matchRule one of
+ * <ul>
+ * <li>{@link SearchPattern#R_EXACT_MATCH} if the package name and type name are the full names
+ * of the searched types.</li>
+ * <li>{@link SearchPattern#R_PREFIX_MATCH} if the package name and type name are prefixes of the names
+ * of the searched types.</li>
+ * <li>{@link SearchPattern#R_PATTERN_MATCH} if the package name and type name contain wild-cards.</li>
+ * <li>{@link SearchPattern#R_CAMELCASE_MATCH} if type name are camel case of the names of the searched types.</li>
+ * </ul>
+ * combined with {@link SearchPattern#R_CASE_SENSITIVE},
+ * e.g. {@link SearchPattern#R_EXACT_MATCH} | {@link SearchPattern#R_CASE_SENSITIVE} if an exact and case sensitive match is requested,
+ * or {@link SearchPattern#R_PREFIX_MATCH} if a prefix non case sensitive match is requested.
+ * @param searchFor determines the nature of the searched elements
+ * <ul>
+ * <li>{@link IJavaSearchConstants#CLASS}: only look for classes</li>
+ * <li>{@link IJavaSearchConstants#INTERFACE}: only look for interfaces</li>
+ * <li>{@link IJavaSearchConstants#ENUM}: only look for enumeration</li>
+ * <li>{@link IJavaSearchConstants#ANNOTATION_TYPE}: only look for annotation type</li>
+ * <li>{@link IJavaSearchConstants#CLASS_AND_ENUM}: only look for classes and enumerations</li>
+ * <li>{@link IJavaSearchConstants#CLASS_AND_INTERFACE}: only look for classes and interfaces</li>
+ * <li>{@link IJavaSearchConstants#TYPE}: look for all types (ie. classes, interfaces, enum and annotation types)</li>
+ * </ul>
+ * @param scope the scope to search in
+ * @param nameRequestor the requestor that collects the results of the search
+ * @param waitingPolicy one of
+ * <ul>
+ * <li>{@link IJavaSearchConstants#FORCE_IMMEDIATE_SEARCH} if the search should start immediately</li>
+ * <li>{@link IJavaSearchConstants#CANCEL_IF_NOT_READY_TO_SEARCH} if the search should be cancelled if the
+ * underlying indexer has not finished indexing the workspace</li>
+ * <li>{@link IJavaSearchConstants#WAIT_UNTIL_READY_TO_SEARCH} if the search should wait for the
+ * underlying indexer to finish indexing the workspace</li>
+ * </ul>
+ * @param progressMonitor the progress monitor to report progress to, or <code>null</code> if no progress
+ * monitor is provided
+ * @exception JavaModelException if the search failed. Reasons include:
+ * <ul>
+ * <li>the classpath is incorrectly set</li>
+ * </ul>
+ * @since 1.0
+ */
+ public void searchAllTypeNames(
+ final char[] packageName,
+ final char[] typeName,
+ final int matchRule,
+ int searchFor,
+ IRubySearchScope scope,
+ final TypeNameRequestor nameRequestor,
+ int waitingPolicy,
+ IProgressMonitor progressMonitor) throws RubyModelException {
+
+ this.basicEngine.searchAllTypeNames(packageName, typeName, matchRule, searchFor, scope, nameRequestor, waitingPolicy, progressMonitor);
+ }
+
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchPattern.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchPattern.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/SearchPattern.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -1,6 +1,7 @@
package org.rubypeople.rdt.core.search;
import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.internal.compiler.parser.ScannerHelper;
import org.rubypeople.rdt.internal.core.search.indexing.IIndexConstants;
import org.rubypeople.rdt.internal.core.search.matching.ConstructorPattern;
import org.rubypeople.rdt.internal.core.search.matching.FieldPattern;
@@ -466,5 +467,325 @@
matchRule);
}
}
+
+ /**
+ * Answers true if the pattern matches the given name using CamelCase rules, or false otherwise.
+ * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
+ * <br>
+ * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
+ * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
+ * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
+ * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
+ * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
+ * names follow the lower CamelCase convention.
+ * <br>
+ * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
+ * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
+ * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
+ * <br><br>
+ * Examples:
+ * <ol>
+ * <li><pre>
+ * pattern = "NPE"
+ * name = NullPointerException / NoPermissionException
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * name = NullPointerException
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "npe"
+ * name = NullPointerException
+ * result => false
+ * </pre>
+ * </li>
+ * </ol>
+ * @see CharOperation#camelCaseMatch(char[], char[])
+ * Implementation has been entirely copied from this method except for array lengthes
+ * which were obviously replaced with calls to {@link String#length()}.
+ *
+ * @param pattern the given pattern
+ * @param name the given name
+ * @return true if the pattern matches the given name, false otherwise
+ * @since 3.2
+ */
+ public static final boolean camelCaseMatch(String pattern, String name) {
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ if (name == null)
+ return false; // null name cannot match
+
+ return camelCaseMatch(pattern, 0, pattern.length(), name, 0, name.length());
+ }
+ /**
+ * Answers true if a sub-pattern matches the subpart of the given name using CamelCase rules, or false otherwise.
+ * CamelCase matching does NOT accept explicit wild-cards '*' and '?' and is inherently case sensitive.
+ * Can match only subset of name/pattern, considering end positions as non-inclusive.
+ * The subpattern is defined by the patternStart and patternEnd positions.
+ * <br>
+ * CamelCase denotes the convention of writing compound names without spaces, and capitalizing every term.
+ * This function recognizes both upper and lower CamelCase, depending whether the leading character is capitalized
+ * or not. The leading part of an upper CamelCase pattern is assumed to contain a sequence of capitals which are appearing
+ * in the matching name; e.g. 'NPE' will match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
+ * uses a lowercase first character. In Java, type names follow the upper CamelCase convention, whereas method or field
+ * names follow the lower CamelCase convention.
+ * <br>
+ * The pattern may contain lowercase characters, which will be match in a case sensitive way. These characters must
+ * appear in sequence in the name. For instance, 'NPExcep' will match 'NullPointerException', but not 'NullPointerExCEPTION'
+ * or 'NuPoEx' will match 'NullPointerException', but not 'NoPointerException'.
+ * <br><br>
+ * Examples:
+ * <ol>
+ * <li><pre>
+ * pattern = "NPE"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NPE"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NoPermissionException
+ * nameStart = 0
+ * nameEnd = 21
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * patternStart = 0
+ * patternEnd = 6
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => true
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "NuPoEx"
+ * patternStart = 0
+ * patternEnd = 6
+ * name = NoPermissionException
+ * nameStart = 0
+ * nameEnd = 21
+ * result => false
+ * </pre>
+ * </li>
+ * <li><pre>
+ * pattern = "npe"
+ * patternStart = 0
+ * patternEnd = 3
+ * name = NullPointerException
+ * nameStart = 0
+ * nameEnd = 20
+ * result => false
+ * </pre>
+ * </li>
+ * </ol>
+ * @see CharOperation#camelCaseMatch(char[], int, int, char[], int, int)
+ * Implementation has been entirely copied from this method except for array lengthes
+ * which were obviously replaced with calls to {@link String#length()} and
+ * for array direct access which were replaced with calls to {@link String#charAt(int)}.
+ *
+ * @param pattern the given pattern
+ * @param patternStart the start index of the pattern, inclusive
+ * @param patternEnd the end index of the pattern, exclusive
+ * @param name the given name
+ * @param nameStart the start index of the name, inclusive
+ * @param nameEnd the end index of the name, exclusive
+ * @return true if a sub-pattern matches the subpart of the given name, false otherwise
+ * @since 3.2
+ */
+ public static final boolean camelCaseMatch(String pattern, int patternStart, int patternEnd, String name, int nameStart, int nameEnd) {
+ if (name == null)
+ return false; // null name cannot match
+ if (pattern == null)
+ return true; // null pattern is equivalent to '*'
+ if (patternEnd < 0) patternEnd = pattern.length();
+ if (nameEnd < 0) nameEnd = name.length();
+
+ if (patternEnd <= patternStart) return nameEnd <= nameStart;
+ if (nameEnd <= nameStart) return false;
+ // check first pattern char
+ if (name.charAt(nameStart) != pattern.charAt(patternStart)) {
+ // first char must strictly match (upper/lower)
+ return false;
+ }
+
+ char patternChar, nameChar;
+ int iPattern = patternStart;
+ int iName = nameStart;
+
+ // Main loop is on pattern characters
+ while (true) {
+
+ iPattern++;
+ iName++;
+
+ if (iPattern == patternEnd) {
+ // We have exhausted pattern, so it's a match
+ return true;
+ }
+
+ if (iName == nameEnd){
+ // We have exhausted name (and not pattern), so it's not a match
+ return false;
+ }
+
+ // For as long as we're exactly matching, bring it on (even if it's a lower case character)
+ if ((patternChar = pattern.charAt(iPattern)) == name.charAt(iName)) {
+ continue;
+ }
+
+ // If characters are not equals, then it's not a match if patternChar is lowercase
+ if (patternChar < ScannerHelper.MAX_OBVIOUS) {
+ if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & ScannerHelper.C_UPPER_LETTER) == 0) {
+ return false;
+ }
+ }
+ else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar)) {
+ return false;
+ }
+
+ // patternChar is uppercase, so let's find the next uppercase in name
+ while (true) {
+ if (iName == nameEnd){
+ // We have exhausted name (and not pattern), so it's not a match
+ return false;
+ }
+
+ nameChar = name.charAt(iName);
+
+ if (nameChar < ScannerHelper.MAX_OBVIOUS) {
+ if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) {
+ // nameChar is lowercase
+ iName++;
+ // nameChar is uppercase...
+ } else if (patternChar != nameChar) {
+ //.. and it does not match patternChar, so it's not a match
+ return false;
+ } else {
+ //.. and it matched patternChar. Back to the big loop
+ break;
+ }
+ }
+ else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) {
+ // nameChar is lowercase
+ iName++;
+ // nameChar is uppercase...
+ } else if (patternChar != nameChar) {
+ //.. and it does not match patternChar, so it's not a match
+ return false;
+ } else {
+ //.. and it matched patternChar. Back to the big loop
+ break;
+ }
+ }
+ // At this point, either name has been exhausted, or it is at an uppercase letter.
+ // Since pattern is also at an uppercase letter
+ }
+ }
+
+ /**
+ * Validate compatibility between given string pattern and match rule.
+ *<br>
+ * Optimized (ie. returned match rule is modified) combinations are:
+ * <ul>
+ * <li>{@link #R_PATTERN_MATCH} without any '*' or '?' in string pattern:
+ * pattern match bit is unset,
+ * </li>
+ * <li>{@link #R_PATTERN_MATCH} and {@link #R_PREFIX_MATCH} bits simultaneously set:
+ * prefix match bit is unset,
+ * </li>
+ * <li>{@link #R_PATTERN_MATCH} and {@link #R_CAMELCASE_MATCH} bits simultaneously set:
+ * camel case match bit is unset,
+ * </li>
+ * <li>{@link #R_CAMELCASE_MATCH} with invalid combination of uppercase and lowercase characters:
+ * camel case match bit is unset and replaced with prefix match pattern,
+ * </li>
+ * <li>{@link #R_CAMELCASE_MATCH} combined with {@link #R_PREFIX_MATCH} and {@link #R_CASE_SENSITIVE}
+ * bits is reduced to only {@link #R_CAMELCASE_MATCH} as Camel Case search is already prefix and case sensitive,
+ * </li>
+ * </ul>
+ *<br>
+ * Rejected (ie. returned match rule -1) combinations are:
+ * <ul>
+ * <li>{@link #R_REGEXP_MATCH} with any other match mode bit set,
+ * </li>
+ * </ul>
+ *
+ * @param stringPattern The string pattern
+ * @param matchRule The match rule
+ * @return Optimized valid match rule or -1 if an incompatibility was detected.
+ * @since 3.2
+ */
+ public static int validateMatchRule(String stringPattern, int matchRule) {
+
+ // Verify Regexp match rule
+ if ((matchRule & R_REGEXP_MATCH) != 0) {
+ if ((matchRule & R_PATTERN_MATCH) != 0 || (matchRule & R_PREFIX_MATCH) != 0 || (matchRule & R_CAMELCASE_MATCH) != 0) {
+ return -1;
+ }
+ }
+
+ // Verify Pattern match rule
+ int starIndex = stringPattern.indexOf('*');
+ int questionIndex = stringPattern.indexOf('?');
+ if (starIndex < 0 && questionIndex < 0) {
+ // reset pattern match bit if any
+ matchRule &= ~R_PATTERN_MATCH;
+ } else {
+ // force Pattern rule
+ matchRule |= R_PATTERN_MATCH;
+ }
+ if ((matchRule & R_PATTERN_MATCH) != 0) {
+ // remove Camel Case and Prefix match bits if any
+ matchRule &= ~R_CAMELCASE_MATCH;
+ matchRule &= ~R_PREFIX_MATCH;
+ }
+
+ // Verify Camel Case match rule
+ if ((matchRule & R_CAMELCASE_MATCH) != 0) {
+ // Verify sting pattern validity
+ int length = stringPattern.length();
+ boolean validCamelCase = true;
+ boolean uppercase = false;
+ for (int i=0; i<length && validCamelCase; i++) {
+ char ch = stringPattern.charAt(i);
+ validCamelCase = ScannerHelper.isJavaIdentifierStart(ch);
+ // at least one uppercase character is need in CamelCase pattern
+ // (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=136313)
+ if (!uppercase) uppercase = ScannerHelper.isUpperCase(ch);
+ }
+ validCamelCase = validCamelCase && uppercase;
+ // Verify bits compatibility
+ if (validCamelCase) {
+ if ((matchRule & R_PREFIX_MATCH) != 0) {
+ if ((matchRule & R_CASE_SENSITIVE) != 0) {
+ // This is equivalent to Camel Case match rule
+ matchRule &= ~R_PREFIX_MATCH;
+ matchRule &= ~R_CASE_SENSITIVE;
+ }
+ }
+ } else {
+ matchRule &= ~R_CAMELCASE_MATCH;
+ if ((matchRule & R_PREFIX_MATCH) == 0) {
+ matchRule |= R_PREFIX_MATCH;
+ matchRule |= R_CASE_SENSITIVE;
+ }
+ }
+ }
+ return matchRule;
+ }
+
}
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/TypeNameRequestor.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/TypeNameRequestor.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/search/TypeNameRequestor.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.core.search;
+
+/**
+ * A <code>TypeNameRequestor</code> collects search results from a <code>searchAllTypeNames</code>
+ * query to a <code>SearchEngine</code>. Clients must subclass this abstract class and pass
+ * an instance to the <code>SearchEngine.searchAllTypeNames(...)</code> method. Only top-level and
+ * member types are reported. Local types are not reported.
+ * <p>
+ * This class may be subclassed by clients.
+ * </p>
+ * @since 1.0
+ */
+public abstract class TypeNameRequestor {
+ /**
+ * Accepts a top-level or a member type.
+ * <p>
+ * The default implementation of this method does nothing.
+ * Subclasses should override.
+ * </p>
+ *
+ * @param modifiers the modifier flags of the type. Note that for source type,
+ * these flags may slightly differ from thoses get after resolution.
+ * For example an interface defined by <code>interface A {}</code>,
+ * although obviously public, will be returned false by <code>Flags.isPublic(modifiers)</code>
+ * due to the fact that its declaration does not explicitely define public flag.
+ * @see org.eclipse.jdt.core.Flags
+ * @param packageName the dot-separated name of the package of the type
+ * @param simpleTypeName the simple name of the type
+ * @param enclosingTypeNames if the type is a member type,
+ * the simple names of the enclosing types from the outer-most to the
+ * direct parent of the type (for example, if the class is x.y.A$B$C then
+ * the enclosing types are [A, B]. This is an empty array if the type
+ * is a top-level type.
+ * @param path the full path to the resource containing the type. If the resource is a .class file
+ * or a source file, this is the full path in the workspace to this resource. If the
+ * resource is an archive (that is, a .zip or .jar file), the path is composed of 2 paths separated
+ * by <code>IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR</code>:
+ * the first path is the full OS path to the archive (if it is an external archive),
+ * or the workspace relative <code>IPath</code> to the archive (if it is an internal archive),
+ * the second path is the path to the resource inside the archive.
+ */
+ public void acceptType(boolean isModule, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) {
+ // do nothing
+ }
+}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -1,7 +1,6 @@
package org.rubypeople.rdt.internal.compiler.parser;
-
public class ScannerHelper {
public final static int MAX_OBVIOUS = 128;
public final static int[] OBVIOUS_IDENT_CHAR_NATURES = new int[MAX_OBVIOUS];
@@ -101,5 +100,19 @@
}
}
return Character.toLowerCase(c);
+ }
+
+ public static boolean isUpperCase(char c) {
+ if (c < MAX_OBVIOUS) {
+ return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_UPPER_LETTER) != 0;
+ }
+ return Character.isUpperCase(c);
+ }
+
+ public static boolean isJavaIdentifierStart(char c) {
+ if (c < MAX_OBVIOUS) {
+ return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_IDENT_START) != 0;
+ }
+ return Character.isJavaIdentifierStart(c);
+ }
}
-}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/NamedMember.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/NamedMember.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/NamedMember.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -10,7 +10,11 @@
*******************************************************************************/
package org.rubypeople.rdt.internal.core;
+import org.rubypeople.rdt.core.IMember;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.RubyModelException;
+
public abstract class NamedMember extends Member {
/*
@@ -27,4 +31,26 @@
public String getElementName() {
return this.name;
}
+
+ public String getTypeQualifiedName(String enclosingTypeSeparator, boolean showParameters) throws RubyModelException {
+ NamedMember declaringType;
+ switch (this.parent.getElementType()) {
+ case IRubyElement.SCRIPT:
+ return this.name;
+ case IRubyElement.TYPE:
+ declaringType = (NamedMember) this.parent;
+ break;
+ case IRubyElement.FIELD:
+ case IRubyElement.METHOD:
+ declaringType = (NamedMember) ((IMember) this.parent).getDeclaringType();
+ break;
+ default:
+ return null;
+ }
+ StringBuffer buffer = new StringBuffer(declaringType.getTypeQualifiedName(enclosingTypeSeparator, showParameters));
+ buffer.append(enclosingTypeSeparator);
+ String simpleName = this.name.length() == 0 ? Integer.toString(this.occurrenceCount) : this.name;
+ buffer.append(simpleName);
+ return buffer.toString();
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyScript.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -645,7 +645,7 @@
}
/**
- * @see ICompilationUnit#getTypeNames()
+ * @see IRubyScript#getTypeNames()
*/
public IType[] getTypes() throws RubyModelException {
ArrayList list = getChildrenOfType(TYPE);
@@ -707,4 +707,29 @@
protected char getHandleMementoDelimiter() {
return RubyElement.JEM_RUBYSCRIPT;
}
+
+ /**
+ * @see IRubyScript#getAllTypes()
+ */
+ public IType[] getAllTypes() throws RubyModelException {
+ IRubyElement[] types = getTypes();
+ int i;
+ ArrayList allTypes = new ArrayList(types.length);
+ ArrayList typesToTraverse = new ArrayList(types.length);
+ for (i = 0; i < types.length; i++) {
+ typesToTraverse.add(types[i]);
+ }
+ while (!typesToTraverse.isEmpty()) {
+ IType type = (IType) typesToTraverse.get(0);
+ typesToTraverse.remove(type);
+ allTypes.add(type);
+ types = type.getTypes();
+ for (i = 0; i < types.length; i++) {
+ typesToTraverse.add(types[i]);
+ }
+ }
+ IType[] arrayOfAllTypes = new IType[allTypes.size()];
+ allTypes.toArray(arrayOfAllTypes);
+ return arrayOfAllTypes;
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyType.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyType.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyType.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -281,4 +281,26 @@
return null;
}
+ /**
+ * @see IType#getTypeQualifiedName(char)
+ */
+ public String getTypeQualifiedName(String enclosingTypeSeparator) {
+ try {
+ return getTypeQualifiedName(enclosingTypeSeparator, false/*don't show parameters*/);
+ } catch (RubyModelException e) {
+ // exception thrown only when showing parameters
+ return null;
+ }
+ }
+
+ /**
+ * @see IType
+ */
+ public IType[] getTypes() throws RubyModelException {
+ ArrayList list= getChildrenOfType(TYPE);
+ IType[] array= new IType[list.size()];
+ list.toArray(array);
+ return array;
+ }
+
}
\ No newline at end of file
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/BasicSearchEngine.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/BasicSearchEngine.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/BasicSearchEngine.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -10,6 +10,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
+import org.rubypeople.rdt.core.Flags;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.IType;
@@ -19,21 +20,31 @@
import org.rubypeople.rdt.core.search.IRubySearchConstants;
import org.rubypeople.rdt.core.search.IRubySearchScope;
import org.rubypeople.rdt.core.search.SearchDocument;
+import org.rubypeople.rdt.core.search.SearchEngine;
import org.rubypeople.rdt.core.search.SearchMatch;
import org.rubypeople.rdt.core.search.SearchParticipant;
import org.rubypeople.rdt.core.search.SearchPattern;
import org.rubypeople.rdt.core.search.SearchRequestor;
+import org.rubypeople.rdt.core.search.TypeNameRequestor;
import org.rubypeople.rdt.internal.core.DefaultWorkingCopyOwner;
import org.rubypeople.rdt.internal.core.RubyModelManager;
import org.rubypeople.rdt.internal.core.RubyProject;
import org.rubypeople.rdt.internal.core.RubyScript;
+import org.rubypeople.rdt.internal.core.search.indexing.IIndexConstants;
import org.rubypeople.rdt.internal.core.search.indexing.IndexManager;
import org.rubypeople.rdt.internal.core.search.matching.MatchLocator;
+import org.rubypeople.rdt.internal.core.search.matching.RubySearchPattern;
+import org.rubypeople.rdt.internal.core.search.matching.TypeDeclarationPattern;
+import org.rubypeople.rdt.internal.core.util.CharOperation;
import org.rubypeople.rdt.internal.core.util.Messages;
import org.rubypeople.rdt.internal.core.util.Util;
public class BasicSearchEngine {
+ // Type decl kinds
+ public static final int CLASS_DECL = 1;
+ public static final int MODULE_DECL = 2;
+
public static final boolean VERBOSE = false;
/*
@@ -48,7 +59,22 @@
*/
private WorkingCopyOwner workingCopyOwner;
+
+ /*
+ * Creates a new search basic engine.
+ */
+ public BasicSearchEngine() {
+ // will use working copies of PRIMARY owner
+ }
+
/**
+ * @see SearchEngine#SearchEngine(WorkingCopyOwner) for detailed comment.
+ */
+ public BasicSearchEngine(WorkingCopyOwner workingCopyOwner) {
+ this.workingCopyOwner = workingCopyOwner;
+ }
+
+ /**
* Searches for matches of a given search pattern. Search patterns can be created using helper
* methods (from a String pattern or a Ruby element) and encapsulate the description of what is
* being searched (for example, search method declarations in a case sensitive way).
@@ -270,4 +296,272 @@
return types;
}
}
+
+
+ /**
+ * Searches for all top-level types and member types in the given scope.
+ * The search can be selecting specific types (given a package or a type name
+ * prefix and match modes).
+ *
+ * @see SearchEngine#searchAllTypeNames(char[], char[], int, int, IJavaSearchScope, TypeNameRequestor, int, IProgressMonitor)
+ * for detailed comment
+ */
+ public void searchAllTypeNames(
+ final char[] packageName,
+ final char[] typeName,
+ final int matchRule,
+ int searchFor,
+ IRubySearchScope scope,
+ final TypeNameRequestor nameRequestor,
+ int waitingPolicy,
+ IProgressMonitor progressMonitor) throws RubyModelException {
+
+ if (VERBOSE) {
+ Util.verbose("BasicSearchEngine.searchAllTypeNames(char[], char[], int, int, IRubySearchScope, IRestrictedAccessTypeRequestor, int, IProgressMonitor)"); //$NON-NLS-1$
+ Util.verbose(" - package name: "+(packageName==null?"null":new String(packageName))); //$NON-NLS-1$ //$NON-NLS-2$
+ Util.verbose(" - type name: "+(typeName==null?"null":new String(typeName))); //$NON-NLS-1$ //$NON-NLS-2$
+ Util.verbose(" - match rule: "+getMatchRuleString(matchRule)); //$NON-NLS-1$
+ Util.verbose(" - search for: "+searchFor); //$NON-NLS-1$
+ Util.verbose(" - scope: "+scope); //$NON-NLS-1$
+ }
+
+ // Return on invalid combination of package and type names
+ if (packageName == null || packageName.length == 0) {
+ if (typeName != null && typeName.length == 0) {
+ if (VERBOSE) {
+ Util.verbose(" => return no result due to invalid empty values for package and type names!"); //$NON-NLS-1$
+ }
+ return;
+ }
+ }
+
+ IndexManager indexManager = RubyModelManager.getRubyModelManager().getIndexManager();
+ final char typeSuffix;
+ switch(searchFor){
+ case IRubySearchConstants.CLASS :
+ typeSuffix = IIndexConstants.CLASS_SUFFIX;
+ break;
+// case IRubySearchConstants.CLASS_AND_MODULE :
+// typeSuffix = IIndexConstants.CLASS_AND_MODULE_SUFFIX; FIXME Converge the TYPE_SUFFIX and CLASS_AND_MODULE_SUFFIX
+// break;
+ case IRubySearchConstants.MODULE :
+ typeSuffix = IIndexConstants.MODULE_SUFFIX;
+ break;
+ default :
+ typeSuffix = IIndexConstants.TYPE_SUFFIX;
+ break;
+ }
+ final TypeDeclarationPattern pattern = new TypeDeclarationPattern(
+ packageName,
+ null, // do find member types
+ typeName,
+ typeSuffix,
+ matchRule);
+
+ // Get working copy path(s). Store in a single string in case of only one to optimize comparison in requestor
+ final HashSet workingCopyPaths = new HashSet();
+ String workingCopyPath = null;
+ IRubyScript[] copies = getWorkingCopies();
+ final int copiesLength = copies == null ? 0 : copies.length;
+ if (copies != null) {
+ if (copiesLength == 1) {
+ workingCopyPath = copies[0].getPath().toString();
+ } else {
+ for (int i = 0; i < copiesLength; i++) {
+ IRubyScript workingCopy = copies[i];
+ workingCopyPaths.add(workingCopy.getPath().toString());
+ }
+ }
+ }
+ final String singleWkcpPath = workingCopyPath;
+
+ // Index requestor
+ IndexQueryRequestor searchRequestor = new IndexQueryRequestor(){
+ public boolean acceptIndexMatch(String documentPath, SearchPattern indexRecord, SearchParticipant participant) {
+ // Filter unexpected types
+ TypeDeclarationPattern record = (TypeDeclarationPattern)indexRecord;
+ if (record.enclosingTypeNames == IIndexConstants.ONE_ZERO_CHAR) {
+ return true; // filter out local and anonymous classes
+ }
+ switch (copiesLength) {
+ case 0:
+ break;
+ case 1:
+ if (singleWkcpPath.equals(documentPath)) {
+ return true; // fliter out *the* working copy
+ }
+ break;
+ default:
+ if (workingCopyPaths.contains(documentPath)) {
+ return true; // filter out working copies
+ }
+ break;
+ }
+
+ // Accept document path
+ if (match(record.typeSuffix, record.modifiers)) {
+ nameRequestor.acceptType(record.typeSuffix == IIndexConstants.MODULE_SUFFIX, record.pkg, record.simpleName, record.enclosingTypeNames, documentPath);
+ }
+ return true;
+ }
+ };
+
+ try {
+ if (progressMonitor != null) {
+ progressMonitor.beginTask(Messages.engine_searching, 100);
+ }
+ // add type names from indexes
+ indexManager.performConcurrentJob(
+ new PatternSearchJob(
+ pattern,
+ getDefaultSearchParticipant(), // Ruby search only
+ scope,
+ searchRequestor),
+ waitingPolicy,
+ progressMonitor == null ? null : new SubProgressMonitor(progressMonitor, 100));
+
+ // add type names from working copies
+ if (copies != null) {
+ for (int i = 0; i < copiesLength; i++) {
+ IRubyScript workingCopy = copies[i];
+ if (!scope.encloses(workingCopy)) continue;
+ final String path = workingCopy.getPath().toString();
+ if (workingCopy.isConsistent()) {
+ // TODO Clean this up and figure out what we use instead of package names...
+// IPackageDeclaration[] packageDeclarations = workingCopy.getPackageDeclarations();
+// char[] packageDeclaration = packageDeclarations.length == 0 ? CharOperation.NO_CHAR : packageDeclarations[0].getElementName().toCharArray();
+ char[] packageDeclaration = CharOperation.NO_CHAR;
+ IType[] allTypes = workingCopy.getAllTypes();
+ for (int j = 0, allTypesLength = allTypes.length; j < allTypesLength; j++) {
+ IType type = allTypes[j];
+ IRubyElement parent = type.getParent();
+ char[][] enclosingTypeNames;
+ if (parent instanceof IType) {
+ char[] parentQualifiedName = ((IType)parent).getTypeQualifiedName("::").toCharArray();
+ enclosingTypeNames = CharOperation.splitOn("::", parentQualifiedName);
+ } else {
+ enclosingTypeNames = CharOperation.NO_CHAR_CHAR;
+ }
+ char[] simpleName = type.getElementName().toCharArray();
+ int kind;
+ if (type.isClass()) {
+ kind = CLASS_DECL;
+ } else /*if (type.isModule())*/ {
+ kind = MODULE_DECL;
+ }
+ if (match(typeSuffix, packageName, typeName, matchRule, kind, packageDeclaration, simpleName)) {
+ nameRequestor.acceptType(type.isModule(), packageDeclaration, simpleName, enclosingTypeNames, path);
+ }
+ }
+ } else {
+ // TODO Parse and traverse AST, report all type declarations...
+ }
+ }
+ }
+ } finally {
+ if (progressMonitor != null) {
+ progressMonitor.done();
+ }
+ }
+ }
+
+ boolean match(char patternTypeSuffix, int modifiers) {
+ switch(patternTypeSuffix) {
+ case IIndexConstants.CLASS_SUFFIX :
+ return (modifiers & (Flags.AccModule)) == 0;
+ case IIndexConstants.CLASS_AND_MODULE_SUFFIX:
+ return true;
+ case IIndexConstants.MODULE_SUFFIX :
+ return (modifiers & Flags.AccModule) != 0;
+ }
+ return true;
+ }
+
+ boolean match(char patternTypeSuffix, char[] patternPkg, char[] patternTypeName, int matchRule, int typeKind, char[] pkg, char[] typeName) {
+ switch(patternTypeSuffix) {
+ case IIndexConstants.CLASS_SUFFIX :
+ if (typeKind != CLASS_DECL) return false;
+ break;
+ case IIndexConstants.CLASS_AND_MODULE_SUFFIX:
+ if (typeKind != CLASS_DECL && typeKind != MODULE_DECL) return false;
+ break;
+ case IIndexConstants.MODULE_SUFFIX :
+ if (typeKind != MODULE_DECL) return false;
+ break;
+ case IIndexConstants.TYPE_SUFFIX : // nothing
+ }
+
+ boolean isCaseSensitive = (matchRule & SearchPattern.R_CASE_SENSITIVE) != 0;
+ if (patternPkg != null && !CharOperation.equals(patternPkg, pkg, isCaseSensitive))
+ return false;
+
+ if (patternTypeName != null) {
+ boolean isCamelCase = (matchRule & SearchPattern.R_CAMELCASE_MATCH) != 0;
+ int matchMode = matchRule & RubySearchPattern.MATCH_MODE_MASK;
+ if (!isCaseSensitive && !isCamelCase) {
+ patternTypeName = CharOperation.toLowerCase(patternTypeName);
+ }
+ boolean matchFirstChar = !isCaseSensitive || patternTypeName[0] == typeName[0];
+ if (isCamelCase && matchFirstChar && CharOperation.camelCaseMatch(patternTypeName, typeName)) {
+ return true;
+ }
+ switch(matchMode) {
+ case SearchPattern.R_EXACT_MATCH :
+ if (!isCamelCase) {
+ return matchFirstChar && CharOperation.equals(patternTypeName, typeName, isCaseSensitive);
+ }
+ // fall through next case to match as prefix if camel case failed
+ case SearchPattern.R_PREFIX_MATCH :
+ return matchFirstChar && CharOperation.prefixEquals(patternTypeName, typeName, isCaseSensitive);
+ case SearchPattern.R_PATTERN_MATCH :
+ return CharOperation.match(patternTypeName, typeName, isCaseSensitive);
+ case SearchPattern.R_REGEXP_MATCH :
+ // TODO (frederic) implement regular expression match
+ break;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * @param matchRule
+ */
+ public static String getMatchRuleString(final int matchRule) {
+ if (matchRule == 0) {
+ return "R_EXACT_MATCH"; //$NON-NLS-1$
+ }
+ StringBuffer buffer = new StringBuffer();
+ for (int i=1; i<=8; i++) {
+ int bit = matchRule & (1<<(i-1));
+ if (bit != 0 && buffer.length()>0) buffer.append(" | "); //$NON-NLS-1$
+ switch (bit) {
+ case SearchPattern.R_PREFIX_MATCH:
+ buffer.append("R_PREFIX_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_CASE_SENSITIVE:
+ buffer.append("R_CASE_SENSITIVE"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_EQUIVALENT_MATCH:
+ buffer.append("R_EQUIVALENT_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_ERASURE_MATCH:
+ buffer.append("R_ERASURE_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_FULL_MATCH:
+ buffer.append("R_FULL_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_PATTERN_MATCH:
+ buffer.append("R_PATTERN_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_REGEXP_MATCH:
+ buffer.append("R_REGEXP_MATCH"); //$NON-NLS-1$
+ break;
+ case SearchPattern.R_CAMELCASE_MATCH:
+ buffer.append("R_CAMELCASE_MATCH"); //$NON-NLS-1$
+ break;
+ }
+ }
+ return buffer.toString();
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/RubySearchScope.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/RubySearchScope.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/RubySearchScope.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -442,4 +442,65 @@
return getPath(element.getParent(), relativeToRoot);
}
}
+
+ /* (non-Javadoc)
+ * @see IRubySearchScope#encloses(IRubyElement)
+ */
+ public boolean encloses(IRubyElement element) {
+ if (this.elements != null) {
+ for (int i = 0, length = this.elements.size(); i < length; i++) {
+ IRubyElement scopeElement = (IRubyElement)this.elements.get(i);
+ IRubyElement searchedElement = element;
+ while (searchedElement != null) {
+ if (searchedElement.equals(scopeElement))
+ return true;
+ searchedElement = searchedElement.getParent();
+ }
+ }
+ return false;
+ }
+ ISourceFolderRoot root = (ISourceFolderRoot) element.getAncestor(IRubyElement.SOURCE_FOLDER_ROOT);
+ if (root != null && root.isExternal()) {
+ // external
+ IPath rootPath = root.getPath();
+ String rootPathToString = rootPath.getDevice() == null ? rootPath.toString() : rootPath.toOSString();
+ IPath relativePath = getPath(element, true/*relative path*/);
+ return indexOf(rootPathToString, relativePath.toString()) >= 0;
+ }
+ // resource in workspace
+ String fullResourcePathString = getPath(element, false/*full path*/).toString();
+ return indexOf(fullResourcePathString) >= 0;
+ }
+
+ /**
+ * Returns paths list index of given path or -1 if not found.
+ * @param containerPath the path of the container, e.g.
+ * 1. /P/src
+ * 2. /P
+ * 3. /P/lib.jar
+ * 4. /home/mylib.jar
+ * 5. c:\temp\mylib.jar
+ * @param relativePath the forward slash path relatively to the container, e.g.
+ * 1. x/y/Z.class
+ * 2. x/y
+ * 3. X.java
+ * 4. (empty)
+ */
+ private int indexOf(String containerPath, String relativePath) {
+ // use the hash to get faster comparison
+ int length = this.containerPaths.length,
+ index = (containerPath.hashCode()& 0x7FFFFFFF) % length;
+ String currentContainerPath;
+ while ((currentContainerPath = this.containerPaths[index]) != null) {
+ if (currentContainerPath.equals(containerPath)) {
+ String currentRelativePath = this.relativePaths[index];
+ if (encloses(currentRelativePath, relativePath, index))
+ return index;
+ }
+ if (++index == length) {
+ index = 0;
+ }
+ }
+ return -1;
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/indexing/SourceIndexerRequestor.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/indexing/SourceIndexerRequestor.java 2007-05-01 14:53:45 UTC (rev 2406)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/search/indexing/SourceIndexerRequestor.java 2007-05-01 14:54:54 UTC (rev 2407)
@@ -78,7 +78,7 @@
if (type.superclass != null) {
superclass = type.superclass.toCharArray();
}
- indexer.addClassDeclaration(type.isModule ? Flags.AccModule : 0, type.name.toCharArray(), packName, null, superclass, mod, type.secondary);
+ indexer.addClassDeclaration(type.isModule ? Flags.AccModule : 0, packName, type.name.toCharArray(), null, superclass, mod, type.secondary);
}
public void exitConstructor(int endOffset) {
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|