Package org.aspectj.org.eclipse.jdt.internal.core.search.matching

Source Code of org.aspectj.org.eclipse.jdt.internal.core.search.matching.MatchLocator

/*******************************************************************************
* Copyright (c) 2000, 2007 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.aspectj.org.eclipse.jdt.internal.core.search.matching;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipFile;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.aspectj.org.eclipse.jdt.core.Flags;
import org.aspectj.org.eclipse.jdt.core.IClassFile;
import org.aspectj.org.eclipse.jdt.core.IJavaElement;
import org.aspectj.org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.aspectj.org.eclipse.jdt.core.IJavaProject;
import org.aspectj.org.eclipse.jdt.core.IMember;
import org.aspectj.org.eclipse.jdt.core.IMethod;
import org.aspectj.org.eclipse.jdt.core.IPackageFragment;
import org.aspectj.org.eclipse.jdt.core.IPackageFragmentRoot;
import org.aspectj.org.eclipse.jdt.core.ISourceRange;
import org.aspectj.org.eclipse.jdt.core.IType;
import org.aspectj.org.eclipse.jdt.core.JavaModelException;
import org.aspectj.org.eclipse.jdt.core.Signature;
import org.aspectj.org.eclipse.jdt.core.compiler.*;
import org.aspectj.org.eclipse.jdt.core.search.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
import org.aspectj.org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.ITypeRequestor;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfIntValues;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.Messages;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.aspectj.org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.aspectj.org.eclipse.jdt.internal.core.BinaryMember;
import org.aspectj.org.eclipse.jdt.internal.core.BinaryType;
import org.aspectj.org.eclipse.jdt.internal.core.ClassFile;
import org.aspectj.org.eclipse.jdt.internal.core.CompilationUnit;
import org.aspectj.org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.aspectj.org.eclipse.jdt.internal.core.JavaElement;
import org.aspectj.org.eclipse.jdt.internal.core.JavaModelManager;
import org.aspectj.org.eclipse.jdt.internal.core.JavaProject;
import org.aspectj.org.eclipse.jdt.internal.core.LocalVariable;
import org.aspectj.org.eclipse.jdt.internal.core.NameLookup;
import org.aspectj.org.eclipse.jdt.internal.core.Openable;
import org.aspectj.org.eclipse.jdt.internal.core.PackageFragment;
import org.aspectj.org.eclipse.jdt.internal.core.PackageFragmentRoot;
import org.aspectj.org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.aspectj.org.eclipse.jdt.internal.core.SourceMapper;
import org.aspectj.org.eclipse.jdt.internal.core.SourceMethod;
import org.aspectj.org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.aspectj.org.eclipse.jdt.internal.core.index.Index;
import org.aspectj.org.eclipse.jdt.internal.core.search.*;
import org.aspectj.org.eclipse.jdt.internal.core.util.HandleFactory;
import org.aspectj.org.eclipse.jdt.internal.core.util.Util;

public class MatchLocator implements ITypeRequestor {

public static final int MAX_AT_ONCE;
static {
  long maxMemory = Runtime.getRuntime().maxMemory();   
  int ratio = (int) Math.round(((double) maxMemory) / (64 * 0x100000));
  switch (ratio) {
    case 0:
    case 1:
      MAX_AT_ONCE = 100;
      break;
    case 2:
      MAX_AT_ONCE = 200;
      break;
    case 3:
      MAX_AT_ONCE = 300;
      break;
    default:
      MAX_AT_ONCE = 400;
      break;
  }
}

// permanent state
public SearchPattern pattern;
public PatternLocator patternLocator;
public int matchContainer;
public SearchRequestor requestor;
public IJavaSearchScope scope;
public IProgressMonitor progressMonitor;

public org.aspectj.org.eclipse.jdt.core.ICompilationUnit[] workingCopies;
public HandleFactory handleFactory;

// cache of all super type names if scope is hierarchy scope
public char[][][] allSuperTypeNames;

// the following is valid for the current project
public MatchLocatorParser parser;
private Parser basicParser;
public INameEnvironment nameEnvironment;
public NameLookup nameLookup;
public LookupEnvironment lookupEnvironment;
public HierarchyResolver hierarchyResolver;

public CompilerOptions options;

// management of PossibleMatch to be processed
public int numberOfMatches; // (numberOfMatches - 1) is the last unit in matchesToProcess
public PossibleMatch[] matchesToProcess;
public PossibleMatch currentPossibleMatch;

/*
* Time spent in the IJavaSearchResultCollector
*/
public long resultCollectorTime = 0;

// Progress information
int progressStep;
int progressWorked;

// Binding resolution and cache
CompilationUnitScope unitScope;
SimpleLookupTable bindings;

// Cache for method handles
HashSet methodHandles;

/**
* An ast visitor that visits local type declarations.
*/
public class LocalDeclarationVisitor extends ASTVisitor {
  IJavaElement enclosingElement;
  Binding enclosingElementBinding;
  MatchingNodeSet nodeSet;
  HashtableOfIntValues occurrencesCounts = new HashtableOfIntValues(); // key = class name (char[]), value = occurrenceCount (int)
  public LocalDeclarationVisitor(IJavaElement enclosingElement, Binding enclosingElementBinding, MatchingNodeSet nodeSet) {
    this.enclosingElement = enclosingElement;
    this.enclosingElementBinding = enclosingElementBinding;
    this.nodeSet = nodeSet;
  }
  public boolean visit(TypeDeclaration typeDeclaration, BlockScope unused) {
    try {
      char[] simpleName;
      if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {       
        simpleName = CharOperation.NO_CHAR;
      } else {
        simpleName = typeDeclaration.name;
      }
      int occurrenceCount = occurrencesCounts.get(simpleName);
      if (occurrenceCount == HashtableOfIntValues.NO_VALUE) {
        occurrenceCount = 1;
      } else {
        occurrenceCount = occurrenceCount + 1;
      }
      occurrencesCounts.put(simpleName, occurrenceCount);
      if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {       
        reportMatching(typeDeclaration, this.enclosingElement, -1, nodeSet, occurrenceCount);
      } else {
        Integer level = (Integer) nodeSet.matchingNodes.removeKey(typeDeclaration);
        reportMatching(typeDeclaration, this.enclosingElement, level != null ? level.intValue() : -1, nodeSet, occurrenceCount);
      }
      return false; // don't visit members as this was done during reportMatching(...)
    } catch (CoreException e) {
      throw new WrappedCoreException(e);
    }
  }
}

public static class WorkingCopyDocument extends JavaSearchDocument {
  public org.aspectj.org.eclipse.jdt.core.ICompilationUnit workingCopy;
  WorkingCopyDocument(org.aspectj.org.eclipse.jdt.core.ICompilationUnit workingCopy, SearchParticipant participant) {
    super(workingCopy.getPath().toString(), participant);
    this.charContents = ((CompilationUnit)workingCopy).getContents();
    this.workingCopy = workingCopy;
  }
  public String toString() {
    return "WorkingCopyDocument for " + getPath(); //$NON-NLS-1$
  }
}
 
public static class WrappedCoreException extends RuntimeException {
  private static final long serialVersionUID = 8354329870126121212L; // backward compatible
  public CoreException coreException;
  public WrappedCoreException(CoreException coreException) {
    this.coreException = coreException;
  }
}

public static SearchDocument[] addWorkingCopies(InternalSearchPattern pattern, SearchDocument[] indexMatches, org.aspectj.org.eclipse.jdt.core.ICompilationUnit[] copies, SearchParticipant participant) {
  // working copies take precedence over corresponding compilation units
  HashMap workingCopyDocuments = workingCopiesThatCanSeeFocus(copies, pattern.focus, pattern.isPolymorphicSearch(), participant);
  SearchDocument[] matches = null;
  int length = indexMatches.length;
  for (int i = 0; i < length; i++) {
    SearchDocument searchDocument = indexMatches[i];
    if (searchDocument.getParticipant() == participant) {
      SearchDocument workingCopyDocument = (SearchDocument) workingCopyDocuments.remove(searchDocument.getPath());
      if (workingCopyDocument != null) {
        if (matches == null) {
          System.arraycopy(indexMatches, 0, matches = new SearchDocument[length], 0, length);
        }
        matches[i] = workingCopyDocument;
      }
    }
  }
  if (matches == null) { // no working copy
    matches = indexMatches;
  }
  int remainingWorkingCopiesSize = workingCopyDocuments.size();
  if (remainingWorkingCopiesSize != 0) {
    System.arraycopy(matches, 0, matches = new SearchDocument[length+remainingWorkingCopiesSize], 0, length);
    Iterator iterator = workingCopyDocuments.values().iterator();
    int index = length;
    while (iterator.hasNext()) {
      matches[index++] = (SearchDocument) iterator.next();
    }
  }
  return matches;
}

public static void setFocus(InternalSearchPattern pattern, IJavaElement focus) {
  pattern.focus = focus;
}

/*
* Returns the working copies that can see the given focus.
*/
private static HashMap workingCopiesThatCanSeeFocus(org.aspectj.org.eclipse.jdt.core.ICompilationUnit[] copies, IJavaElement focus, boolean isPolymorphicSearch, SearchParticipant participant) {
  if (copies == null) return new HashMap();
  if (focus != null) {
    while (!(focus instanceof IJavaProject) && !(focus instanceof JarPackageFragmentRoot)) {
      focus = focus.getParent();
    }
  }
  HashMap result = new HashMap();
  for (int i=0, length = copies.length; i<length; i++) {
    org.aspectj.org.eclipse.jdt.core.ICompilationUnit workingCopy = copies[i];
    IPath projectOrJar = MatchLocator.getProjectOrJar(workingCopy).getPath();
    if (focus == null || IndexSelector.canSeeFocus(focus, isPolymorphicSearch, projectOrJar)) {
      result.put(
        workingCopy.getPath().toString(),
        new WorkingCopyDocument(workingCopy, participant)
      );
    }
  }
  return result;
}

public static ClassFileReader classFileReader(IType type) {
  IClassFile classFile = type.getClassFile();
  JavaModelManager manager = JavaModelManager.getJavaModelManager();
  if (classFile.isOpen())
    return (ClassFileReader) manager.getInfo(type);

  PackageFragment pkg = (PackageFragment) type.getPackageFragment();
  IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent();
  try {
    if (!root.isArchive())
      return Util.newClassFileReader(type.getResource());

    ZipFile zipFile = null;
    try {
      IPath zipPath = root.getPath();
      if (JavaModelManager.ZIP_ACCESS_VERBOSE)
        System.out.println("(" + Thread.currentThread() + ") [MatchLocator.classFileReader()] Creating ZipFile on " + zipPath); //$NON-NLS-1$  //$NON-NLS-2$
      zipFile = manager.getZipFile(zipPath);
      String classFileName = classFile.getElementName();
      String path = Util.concatWith(pkg.names, classFileName, '/');
      return ClassFileReader.read(zipFile, path);
    } finally {
      manager.closeZipFile(zipFile);
    }
  } catch (ClassFormatException e) {
    // invalid class file: return null
  } catch (CoreException e) {
    // cannot read class file: return null
  } catch (IOException e) {
    // cannot read class file: return null
  }
  return null;
}

public static SearchPattern createAndPattern(final SearchPattern leftPattern, final SearchPattern rightPattern) {
  return new AndPattern(0/*no kind*/, 0/*no rule*/) {
    SearchPattern current = leftPattern;
    public SearchPattern currentPattern() {
      return this.current;
    }
    protected boolean hasNextQuery() {
      if (this.current == leftPattern) {
        this.current = rightPattern;
        return true;
      }
      return false;
    }
    protected void resetQuery() {
      this.current = leftPattern;
    }
  };
}

/**
* Query a given index for matching entries. Assumes the sender has opened the index and will close when finished.
*/
public static void findIndexMatches(InternalSearchPattern pattern, Index index, IndexQueryRequestor requestor, SearchParticipant participant, IJavaSearchScope scope, IProgressMonitor monitor) throws IOException {
  pattern.findIndexMatches(index, requestor, participant, scope, monitor);
}

public static IJavaElement getProjectOrJar(IJavaElement element) {
  while (!(element instanceof IJavaProject) && !(element instanceof JarPackageFragmentRoot)) {
    element = element.getParent();
  }
  return element;
}

public static IJavaElement projectOrJarFocus(InternalSearchPattern pattern) {
  return pattern == null || pattern.focus == null ? null : getProjectOrJar(pattern.focus);
}

public MatchLocator(
  SearchPattern pattern,
  SearchRequestor requestor,
  IJavaSearchScope scope,
  IProgressMonitor progressMonitor) {
   
  this.pattern = pattern;
  this.patternLocator = PatternLocator.patternLocator(this.pattern);
  this.matchContainer = this.patternLocator.matchContainer();
  this.requestor = requestor;
  this.scope = scope;
  this.progressMonitor = progressMonitor;
}
/**
* Add an additional binary type
*/
public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) {
  this.lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction);
}
/**
* Add an additional compilation unit into the loop
*  ->  build compilation unit declarations, their bindings and record their results.
*/
public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) {
  // Switch the current policy and compilation result for this unit to the requested one.
  CompilationResult unitResult = new CompilationResult(sourceUnit, 1, 1, this.options.maxProblemsPerUnit);
  try {
    CompilationUnitDeclaration parsedUnit = basicParser().dietParse(sourceUnit, unitResult);
    this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction);
    this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
  } catch (AbortCompilationUnit e) {
    // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
    // one requested further along to resolve sourceUnit.
    if (unitResult.compilationUnit == sourceUnit) { // only report once
      //requestor.acceptResult(unitResult.tagAsAccepted());
    } else {
      throw e; // want to abort enclosing request to compile
    }
  }
  // Display unit error in debug mode
  if (BasicSearchEngine.VERBOSE) {
    if (unitResult.problemCount > 0) {
      System.out.println(unitResult);
    }
  }
}
/**
* Add additional source types
*/
public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) {
  // case of SearchableEnvironment of an IJavaProject is used
  ISourceType sourceType = sourceTypes[0];
  while (sourceType.getEnclosingType() != null)
    sourceType = sourceType.getEnclosingType();
  if (sourceType instanceof SourceTypeElementInfo) {
    // get source
    SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) sourceType;
    IType type = elementInfo.getHandle();
    ICompilationUnit sourceUnit = (ICompilationUnit) type.getCompilationUnit();
    accept(sourceUnit, accessRestriction);
  } else {
    CompilationResult result = new CompilationResult(sourceType.getFileName(), 1, 1, 0);
    CompilationUnitDeclaration unit =
      SourceTypeConverter.buildCompilationUnit(
        sourceTypes,
        SourceTypeConverter.FIELD_AND_METHOD // need field and methods
        | SourceTypeConverter.MEMBER_TYPE, // need member types
        // no need for field initialization
        this.lookupEnvironment.problemReporter,
        result);
    this.lookupEnvironment.buildTypeBindings(unit, accessRestriction);
    this.lookupEnvironment.completeTypeBindings(unit, true);
  }
protected Parser basicParser() {
  if (this.basicParser == null) {
    ProblemReporter problemReporter =
      new ProblemReporter(
        DefaultErrorHandlingPolicies.proceedWithAllProblems(),
        this.options,
        new DefaultProblemFactory());
    this.basicParser = new Parser(problemReporter, false);
    this.basicParser.reportOnlyOneSyntaxError = true;
  }
  return this.basicParser;
}
/*
* Caches the given binary type in the lookup environment and returns it.
* Returns the existing one if already cached.
* Returns null if source type binding was cached.
*/
protected BinaryTypeBinding cacheBinaryType(IType type, IBinaryType binaryType) throws JavaModelException {
  IType enclosingType = type.getDeclaringType();
  if (enclosingType != null)
    cacheBinaryType(enclosingType, null); // cache enclosing types first, so that binary type can be found in lookup enviroment
  if (binaryType == null) {
    ClassFile classFile = (ClassFile) type.getClassFile();
    try {
      binaryType = getBinaryInfo(classFile, classFile.getResource());
    } catch (CoreException e) {
      if (e instanceof JavaModelException) {
        throw (JavaModelException) e;
      } else {
        throw new JavaModelException(e);
      }
    }
  }
  BinaryTypeBinding binding = this.lookupEnvironment.cacheBinaryType(binaryType, null /*no access restriction*/);
  if (binding == null) { // it was already cached as a result of a previous query
    char[][] compoundName = CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray());
    ReferenceBinding referenceBinding = this.lookupEnvironment.getCachedType(compoundName);
    if (referenceBinding != null && (referenceBinding instanceof BinaryTypeBinding))
      binding = (BinaryTypeBinding) referenceBinding; // if the binding could be found and if it comes from a binary type
  }
  return binding;
}
/*
* Computes the super type names of the focus type if any.
*/
protected char[][][] computeSuperTypeNames(IType focusType) {
  String fullyQualifiedName = focusType.getFullyQualifiedName();
  int lastDot = fullyQualifiedName.lastIndexOf('.');
  char[] qualification = lastDot == -1 ? CharOperation.NO_CHAR : fullyQualifiedName.substring(0, lastDot).toCharArray();
  char[] simpleName = focusType.getElementName().toCharArray();

  SuperTypeNamesCollector superTypeNamesCollector =
    new SuperTypeNamesCollector(
      this.pattern,
      simpleName,
      qualification,
      new MatchLocator(this.pattern, this.requestor, this.scope, this.progressMonitor), // clone MatchLocator so that it has no side effect
      focusType,
      this.progressMonitor);
  try {
    this.allSuperTypeNames = superTypeNamesCollector.collect();
  } catch (JavaModelException e) {
    // problem collecting super type names: leave it null
  }
  return this.allSuperTypeNames;
}
/**
* Creates an IMethod from the given method declaration and type.
*/
protected IJavaElement createHandle(AbstractMethodDeclaration method, IJavaElement parent) {
  if (!(parent instanceof IType)) return parent;

  IType type = (IType) parent;
  Argument[] arguments = method.arguments;
  int argCount = arguments == null ? 0 : arguments.length;
  if (type.isBinary()) {
    // don't cache the methods of the binary type
    // fall thru if its a constructor with a synthetic argument... find it the slower way
    ClassFileReader reader = classFileReader(type);
    if (reader != null) {
      IBinaryMethod[] methods = reader.getMethods();
      if (methods != null) {
        // build arguments names
        boolean firstIsSynthetic = false;
        if (reader.isMember() && method.isConstructor() && !Flags.isStatic(reader.getModifiers())) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48261
          firstIsSynthetic = true;
          argCount++;
        }
        char[][] argumentTypeNames = new char[argCount][];
        for (int i = 0; i < argCount; i++) {
          char[] typeName = null;
          if (i == 0 && firstIsSynthetic) {
            typeName = type.getDeclaringType().getFullyQualifiedName().toCharArray();
          } else if (arguments != null) {
            TypeReference typeRef = arguments[firstIsSynthetic ? i - 1 : i].type;
            typeName = CharOperation.concatWith(typeRef.getTypeName(), '.');
            for (int k = 0, dim = typeRef.dimensions(); k < dim; k++)
              typeName = CharOperation.concat(typeName, new char[] {'[', ']'});
          }
          if (typeName == null) {
            // invalid type name
            return null;
          }
          argumentTypeNames[i] = typeName;
        }
       
        // return binary method
        return createBinaryMethodHandle(type, method.selector, argumentTypeNames);
      }
    }
    return null;
  }

  String[] parameterTypeSignatures = new String[argCount];
  if (arguments != null) {
    for (int i = 0; i < argCount; i++) {
      TypeReference typeRef = arguments[i].type;
      char[] typeName = CharOperation.concatWith(typeRef.getParameterizedTypeName(), '.');
      parameterTypeSignatures[i] = Signature.createTypeSignature(typeName, false);
    }
  }

  return createMethodHandle(type, new String(method.selector), parameterTypeSignatures);
}
/*
* Create binary method handle
*/
IMethod createBinaryMethodHandle(IType type, char[] methodSelector, char[][] argumentTypeNames) {
  ClassFileReader reader = MatchLocator.classFileReader(type);
  if (reader != null) {
    IBinaryMethod[] methods = reader.getMethods();
    if (methods != null) {
      int argCount = argumentTypeNames == null ? 0 : argumentTypeNames.length;
      nextMethod : for (int i = 0, methodsLength = methods.length; i < methodsLength; i++) {
        IBinaryMethod binaryMethod = methods[i];
        char[] selector = binaryMethod.isConstructor() ? type.getElementName().toCharArray() : binaryMethod.getSelector();
        if (CharOperation.equals(selector, methodSelector)) {
          char[] signature = binaryMethod.getGenericSignature();
          if (signature == null) signature = binaryMethod.getMethodDescriptor();
          char[][] parameterTypes = Signature.getParameterTypes(signature);
          if (argCount != parameterTypes.length) continue nextMethod;
          if (argumentTypeNames != null) {
            for (int j = 0; j < argCount; j++) {
              char[] parameterTypeName = ClassFileMatchLocator.convertClassFileFormat(parameterTypes[j]);
              if (!CharOperation.endsWith(Signature.toCharArray(Signature.getTypeErasure(parameterTypeName)), CharOperation.replaceOnCopy(argumentTypeNames[j], '$', '.')))
                continue nextMethod;
              parameterTypes[j] = parameterTypeName;
            }
          }
          return (IMethod) createMethodHandle(type, new String(selector), CharOperation.toStrings(parameterTypes));
        }
      }
    }
  }
  return null;
}
/*
* Create method handle.
* Store occurences for create handle to retrieve possible duplicate ones.
*/
private IJavaElement createMethodHandle(IType type, String methodName, String[] parameterTypeSignatures) {
  IMethod methodHandle = type.getMethod(methodName, parameterTypeSignatures);
  if (methodHandle instanceof SourceMethod) {
    while (this.methodHandles.contains(methodHandle)) {
      ((SourceMethod) methodHandle).occurrenceCount++;
    }
  }
  this.methodHandles.add(methodHandle);
  return methodHandle;
}
/**
* Creates an IField from the given field declaration and type.
*/
protected IJavaElement createHandle(FieldDeclaration fieldDeclaration, TypeDeclaration typeDeclaration, IJavaElement parent) {
  if (!(parent instanceof IType)) return parent;
  IType type = (IType) parent;

  switch (fieldDeclaration.getKind()) {
    case AbstractVariableDeclaration.FIELD :
    case AbstractVariableDeclaration.ENUM_CONSTANT :
      return ((IType) parent).getField(new String(fieldDeclaration.name));
  }
  if (type.isBinary()) {
    // do not return initializer for binary types
    // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=98378
    return type;
  }
  // find occurence count of the given initializer in its type declaration
  int occurrenceCount = 0;
  FieldDeclaration[] fields = typeDeclaration.fields;
  for (int i = 0, length = fields.length; i < length; i++) {
    if (fields[i].getKind() == AbstractVariableDeclaration.INITIALIZER) {
      occurrenceCount++;
      if (fields[i].equals(fieldDeclaration)) break;
    }
  }
  return ((IType) parent).getInitializer(occurrenceCount);
}
/**
* Create an handle for a local variable declartion (may be a local variable or type parameter).
*/
protected IJavaElement createHandle(AbstractVariableDeclaration variableDeclaration, IJavaElement parent) {
  switch (variableDeclaration.getKind()) {
    case AbstractVariableDeclaration.LOCAL_VARIABLE:
      return new LocalVariable((JavaElement)parent,
        new String(variableDeclaration.name),
        variableDeclaration.declarationSourceStart,
        variableDeclaration.declarationSourceEnd,
        variableDeclaration.sourceStart,
        variableDeclaration.sourceEnd,
        new String(variableDeclaration.type.resolvedType.signature())
      );
    case AbstractVariableDeclaration.PARAMETER:
      return new LocalVariable((JavaElement)parent,
        new String(variableDeclaration.name),
        variableDeclaration.declarationSourceStart,
        variableDeclaration.declarationSourceEnd,
        variableDeclaration.sourceStart,
        variableDeclaration.sourceEnd,
        new String(variableDeclaration.type.resolvedType.signature())
      );
    case AbstractVariableDeclaration.TYPE_PARAMETER:
      return new org.aspectj.org.eclipse.jdt.internal.core.TypeParameter((JavaElement)parent, new String(variableDeclaration.name));
  }
  return null;
}
/*
* Creates hierarchy resolver if needed.
* Returns whether focus is visible.
*/
protected boolean createHierarchyResolver(IType focusType, PossibleMatch[] possibleMatches) {
  // cache focus type if not a possible match
  char[][] compoundName = CharOperation.splitOn('.', focusType.getFullyQualifiedName().toCharArray());
  boolean isPossibleMatch = false;
  for (int i = 0, length = possibleMatches.length; i < length; i++) {
    if (CharOperation.equals(possibleMatches[i].compoundName, compoundName)) {
      isPossibleMatch = true;
      break;
    }
  }
  if (!isPossibleMatch) {
    if (focusType.isBinary()) {
      try {
        cacheBinaryType(focusType, null);
      } catch (JavaModelException e) {
        return false;
      }
    } else {
      // cache all types in the focus' compilation unit (even secondary types)
      accept((ICompilationUnit) focusType.getCompilationUnit(), null /*TODO no access restriction*/);
    }
  }

  // resolve focus type
  this.hierarchyResolver = new HierarchyResolver(this.lookupEnvironment, null/*hierarchy is not going to be computed*/);
  ReferenceBinding binding = this.hierarchyResolver.setFocusType(compoundName);
  return binding != null && binding.isValidBinding() && (binding.tagBits & TagBits.HierarchyHasProblems) == 0;
}
/**
* Creates an IImportDeclaration from the given import statement
*/
protected IJavaElement createImportHandle(ImportReference importRef) {
  char[] importName = CharOperation.concatWith(importRef.getImportName(), '.');
  if ((importRef.bits & ASTNode.OnDemand) != 0)
    importName = CharOperation.concat(importName, ".*" .toCharArray()); //$NON-NLS-1$
  Openable openable = this.currentPossibleMatch.openable;
  if (openable instanceof CompilationUnit)
    return ((CompilationUnit) openable).getImport(new String(importName));

  // binary types do not contain import statements so just answer the top-level type as the element
  IType binaryType = ((ClassFile) openable).getType();
  String typeName = binaryType.getElementName();
  int lastDollar = typeName.lastIndexOf('$');
  if (lastDollar == -1) return binaryType;
  return createTypeHandle(typeName.substring(0, lastDollar));
}
/**
* Creates an IImportDeclaration from the given import statement
*/
protected IJavaElement createPackageDeclarationHandle(CompilationUnitDeclaration unit) {
  if (unit.isPackageInfo()) {
    char[] packName = CharOperation.concatWith(unit.currentPackage.getImportName(), '.');
    Openable openable = this.currentPossibleMatch.openable;
    if (openable instanceof CompilationUnit) {
      return ((CompilationUnit) openable).getPackageDeclaration(new String(packName));
    }
  }
  return createTypeHandle(new String(unit.getMainTypeName()));
}
/**
* Creates an IType from the given simple top level type name.
*/
protected IType createTypeHandle(String simpleTypeName) {
  Openable openable = this.currentPossibleMatch.openable;
  if (openable instanceof CompilationUnit)
    return ((CompilationUnit) openable).getType(simpleTypeName);

  IType binaryType = ((ClassFile) openable).getType();
  String binaryTypeQualifiedName = binaryType.getTypeQualifiedName();
  if (simpleTypeName.equals(binaryTypeQualifiedName))
    return binaryType; // answer only top-level types, sometimes the classFile is for a member/local type

  // type name may be null for anonymous (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=164791)
  String classFileName = simpleTypeName.length() == 0 ? binaryTypeQualifiedName : simpleTypeName;
  IClassFile classFile = binaryType.getPackageFragment().getClassFile(classFileName + SuffixConstants.SUFFIX_STRING_class);
  return classFile.getType();
}
protected boolean encloses(IJavaElement element) {
  return element != null && this.scope.encloses(element);
}
/* (non-Javadoc)
* Return info about last type argument of a parameterized type reference.
* These info are made of concatenation of 2 int values which are respectively
*  depth and end position of the last type argument.
* For example, this method will return 0x300000020 for type ref List<List<List<String>>>
* if end position of type reference "String" equals 32.
*/
private long findLastTypeArgumentInfo(TypeReference typeRef) {
  // Get last list of type arguments for parameterized qualified type reference
  TypeReference lastTypeArgument = typeRef;
  int depth = 0;
  while (true) {
    TypeReference[] lastTypeArguments = null;
    if (lastTypeArgument instanceof ParameterizedQualifiedTypeReference) {
      ParameterizedQualifiedTypeReference pqtRef = (ParameterizedQualifiedTypeReference) lastTypeArgument;
      for (int i=pqtRef.typeArguments.length-1; i>=0 && lastTypeArguments==null; i--) {
        lastTypeArguments = pqtRef.typeArguments[i];
      }
    }
    // Get last type argument for single type reference of last list of argument of parameterized qualified type reference
    TypeReference last = null;
    if (lastTypeArgument instanceof ParameterizedSingleTypeReference || lastTypeArguments != null) {
      if (lastTypeArguments == null) {
        lastTypeArguments = ((ParameterizedSingleTypeReference)lastTypeArgument).typeArguments;
      }
      if (lastTypeArguments != null) {
        for (int i=lastTypeArguments.length-1; i>=0 && last==null; i++) {
          last = lastTypeArguments[i];
        }
      }
    }
    if (last == null) break;
    depth++;
    lastTypeArgument = last;
  }
  // Current type reference is not parameterized. So, it is the last type argument
  return (((long) depth) << 32) + lastTypeArgument.sourceEnd;
}
protected IBinaryType getBinaryInfo(ClassFile classFile, IResource resource) throws CoreException {
  BinaryType binaryType = (BinaryType) classFile.getType();
  if (classFile.isOpen())
    return (IBinaryType) binaryType.getElementInfo(); // reuse the info from the java model cache

  // create a temporary info
  IBinaryType info;
  try {
    PackageFragment pkg = (PackageFragment) classFile.getParent();
    PackageFragmentRoot root = (PackageFragmentRoot) pkg.getParent();
    if (root.isArchive()) {
      // class file in a jar
      String classFileName = classFile.getElementName();
      String classFilePath = Util.concatWith(pkg.names, classFileName, '/');
      ZipFile zipFile = null;
      try {
        zipFile = ((JarPackageFragmentRoot) root).getJar();
        info = ClassFileReader.read(zipFile, classFilePath);
      } finally {
        JavaModelManager.getJavaModelManager().closeZipFile(zipFile);
      }
    } else {
      // class file in a directory
      info = Util.newClassFileReader(resource);
    }
    if (info == null) throw binaryType.newNotPresentException();
    return info;
  } catch (ClassFormatException e) {
    //e.printStackTrace();
    return null;
  } catch (java.io.IOException e) {
    throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
  }
}
protected IType getFocusType() {
  return this.scope instanceof HierarchyScope ? ((HierarchyScope) this.scope).focusType : null;
}
protected void getMethodBodies(CompilationUnitDeclaration unit, MatchingNodeSet nodeSet) {
  if (unit.ignoreMethodBodies) {
    unit.ignoreFurtherInvestigation = true;
    return; // if initial diet parse did not work, no need to dig into method bodies.
  }

  // save existing values to restore them at the end of the parsing process
  // see bug 47079 for more details
  int[] oldLineEnds = this.parser.scanner.lineEnds;
  int oldLinePtr = this.parser.scanner.linePtr;
 
  try {
    CompilationResult compilationResult = unit.compilationResult;
    this.parser.scanner.setSource(compilationResult);

    if (this.parser.javadocParser.checkDocComment) {
      char[] contents = compilationResult.compilationUnit.getContents();
      this.parser.javadocParser.scanner.setSource(contents);
    }
    this.parser.nodeSet = nodeSet;
    this.parser.parseBodies(unit);
  } finally {
    this.parser.nodeSet = null;
    // this is done to prevent any side effects on the compilation unit result
    // line separator positions array.
    this.parser.scanner.lineEnds = oldLineEnds;
    this.parser.scanner.linePtr = oldLinePtr;
  }
}
protected TypeBinding getType(Object typeKey, char[] typeName) {
  if (this.unitScope == null || typeName == null || typeName.length == 0) return null;
  // Try to get binding from cache
  Binding binding = (Binding) this.bindings.get(typeKey);
  if (binding != null) {
    if (binding instanceof TypeBinding && binding.isValidBinding())
      return (TypeBinding) binding;
    return null;
  }
  // Get binding from unit scope
  char[][] compoundName = CharOperation.splitOn('.', typeName);
  TypeBinding typeBinding = this.unitScope.getType(compoundName, compoundName.length);
  this.bindings.put(typeKey, typeBinding);
  return typeBinding.isValidBinding() ? typeBinding : null;
}
public MethodBinding getMethodBinding(MethodPattern methodPattern) {
  if (this.unitScope == null) return null;
  // Try to get binding from cache
  Binding binding = (Binding) this.bindings.get(methodPattern);
  if (binding != null) {
    if (binding instanceof MethodBinding && binding.isValidBinding())
      return (MethodBinding) binding;
    return null;
  }
  //  Get binding from unit scope
  char[] typeName = PatternLocator.qualifiedPattern(methodPattern.declaringSimpleName, methodPattern.declaringQualification);
  if (typeName == null) {
    if (methodPattern.declaringType == null) return null;
    typeName = methodPattern.declaringType.getFullyQualifiedName().toCharArray();
  }
  TypeBinding declaringTypeBinding = getType(typeName, typeName);
  if (declaringTypeBinding != null) {
    if (declaringTypeBinding.isArrayType()) {
      declaringTypeBinding = declaringTypeBinding.leafComponentType();
    }
    if (!declaringTypeBinding.isBaseType()) {
      char[][] parameterTypes = methodPattern.parameterSimpleNames;
      if (parameterTypes == null) return null;
      int paramTypeslength = parameterTypes.length;
      ReferenceBinding referenceBinding = (ReferenceBinding) declaringTypeBinding;
      MethodBinding[] methods = referenceBinding.getMethods(methodPattern.selector);
      int methodsLength = methods.length;
      TypeVariableBinding[] refTypeVariables = referenceBinding.typeVariables();
      int typeVarLength = refTypeVariables==null ? 0 : refTypeVariables.length;
      for (int i=0; i<methodsLength; i++) {
        TypeBinding[] methodParameters = methods[i].parameters;
        int paramLength = methodParameters==null ? 0 : methodParameters.length;
        TypeVariableBinding[] methodTypeVariables = methods[i].typeVariables;
        int methTypeVarLength = methodTypeVariables==null ? 0 : methodTypeVariables.length;
        boolean found = false;
        if (methodParameters != null && paramLength == paramTypeslength) {
          for (int p=0; p<paramLength; p++) {
            if (CharOperation.equals(methodParameters[p].sourceName(), parameterTypes[p])) {
              // param erasure match
              found = true;
            } else {
              // type variable
              found = false;
              if (refTypeVariables != null) {
                for (int v=0; v<typeVarLength; v++) {
                  if (!CharOperation.equals(refTypeVariables[v].sourceName, parameterTypes[p])) {
                    found = false;
                    break;
                  }
                  found = true;
                }
              }
              if (!found && methodTypeVariables != null) {
                for (int v=0; v<methTypeVarLength; v++) {
                  if (!CharOperation.equals(methodTypeVariables[v].sourceName, parameterTypes[p])) {
                    found = false;
                    break;
                  }
                  found = true;
                }
              }
              if (!found) break;
            }
          }
        }
        if (found) {
          this.bindings.put(methodPattern, methods[i]);
          return methods[i];
        }
      }
    }
  }
  this.bindings.put(methodPattern, new ProblemMethodBinding(methodPattern.selector, null, ProblemReasons.NotFound));
  return null;
}
protected boolean hasAlreadyDefinedType(CompilationUnitDeclaration parsedUnit) {
  CompilationResult result = parsedUnit.compilationResult;
  if (result == null) return false;
  for (int i = 0; i < result.problemCount; i++)
    if (result.problems[i].getID() == IProblem.DuplicateTypes)
      return true;
  return false;
/**
* Create a new parser for the given project, as well as a lookup environment.
*/
public void initialize(JavaProject project, int possibleMatchSize) throws JavaModelException {
  // clean up name environment only if there are several possible match as it is reused
  // when only one possible match (bug 58581)
  if (this.nameEnvironment != null && possibleMatchSize != 1)
    this.nameEnvironment.cleanup();

  SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(this.workingCopies);
 
  // if only one possible match, a file name environment costs too much,
  // so use the existing searchable  environment which will populate the java model
  // only for this possible match and its required types.
  this.nameEnvironment = possibleMatchSize == 1
    ? (INameEnvironment) searchableEnvironment
    : (INameEnvironment) new JavaSearchNameEnvironment(project, this.workingCopies);

  // create lookup environment
  Map map = project.getOptions(true);
  map.put(CompilerOptions.OPTION_TaskTags, ""); //$NON-NLS-1$
  this.options = new CompilerOptions(map);
  ProblemReporter problemReporter =
    new ProblemReporter(
      DefaultErrorHandlingPolicies.proceedWithAllProblems(),
      this.options,
      new DefaultProblemFactory());
  this.lookupEnvironment = new LookupEnvironment(this, this.options, problemReporter, this.nameEnvironment);

  this.parser = MatchLocatorParser.createParser(problemReporter, this);
 
  // basic parser needs also to be reset as project options may have changed
  // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=163072
  this.basicParser = null;

  // remember project's name lookup
  this.nameLookup = searchableEnvironment.nameLookup;

  // initialize queue of units
  this.numberOfMatches = 0;
  this.matchesToProcess = new PossibleMatch[possibleMatchSize];
}
protected void locateMatches(JavaProject javaProject, PossibleMatch[] possibleMatches, int start, int length) throws CoreException {
  initialize(javaProject, length);

  // create and resolve binding (equivalent to beginCompilation() in Compiler)
  boolean mustResolvePattern = ((InternalSearchPattern)this.pattern).mustResolve;
  boolean mustResolve = mustResolvePattern;
  this.patternLocator.mayBeGeneric = this.options.sourceLevel >= ClassFileConstants.JDK1_5;
  boolean bindingsWereCreated = mustResolve;
  try {
    for (int i = start, maxUnits = start + length; i < maxUnits; i++) {
      PossibleMatch possibleMatch = possibleMatches[i];
      try {
        if (!parseAndBuildBindings(possibleMatch, mustResolvePattern)) continue;
        // Currently we only need to resolve over pattern flag if there's potential parameterized types
        if (this.patternLocator.mayBeGeneric) {
          // If pattern does not resolve then rely on possible match node set resolution
          // which may have been modified while locator was adding possible matches to it
          if (!mustResolvePattern && !mustResolve) {
            mustResolve = possibleMatch.nodeSet.mustResolve;
            bindingsWereCreated = mustResolve;
          }
        } else {
          // Reset matching node resolution with pattern one if there's no potential parameterized type
          // to minimize side effect on previous search behavior
          possibleMatch.nodeSet.mustResolve = mustResolvePattern;
        }
        // possible match node resolution has been merged with pattern one, so rely on it to know
        // whether we need to process compilation unit now or later
        if (!possibleMatch.nodeSet.mustResolve) {
          if (this.progressMonitor != null) {
            this.progressWorked++;
            if ((this.progressWorked%this.progressStep)==0) this.progressMonitor.worked(this.progressStep);
          }
          process(possibleMatch, bindingsWereCreated);
          if (this.numberOfMatches>0 && this.matchesToProcess[this.numberOfMatches-1] == possibleMatch) {
            // forget last possible match as it was processed
            this.numberOfMatches--;
          }
        }
      } finally {
        if (!possibleMatch.nodeSet.mustResolve)
          possibleMatch.cleanUp();
      }
    }
    if (mustResolve)
      this.lookupEnvironment.completeTypeBindings();

    // create hierarchy resolver if needed
    IType focusType = getFocusType();
    if (focusType == null) {
      this.hierarchyResolver = null;
    } else if (!createHierarchyResolver(focusType, possibleMatches)) {
      // focus type is not visible, use the super type names instead of the bindings
      if (computeSuperTypeNames(focusType) == null) return;
    }
  } catch (AbortCompilation e) {
    bindingsWereCreated = false;
  }

  if (!mustResolve) {
    return;
  }
 
  // possible match resolution
  for (int i = 0; i < this.numberOfMatches; i++) {
    if (this.progressMonitor != null && this.progressMonitor.isCanceled())
      throw new OperationCanceledException();
    PossibleMatch possibleMatch = this.matchesToProcess[i];
    this.matchesToProcess[i] = null; // release reference to processed possible match
    try {
      process(possibleMatch, bindingsWereCreated);
    } catch (AbortCompilation e) {
      // problem with class path: it could not find base classes
      // continue and try next matching openable reporting innacurate matches (since bindings will be null)
      bindingsWereCreated = false;
    } catch (JavaModelException e) {
      // problem with class path: it could not find base classes
      // continue and try next matching openable reporting innacurate matches (since bindings will be null)
      bindingsWereCreated = false;
    } finally {
      if (this.progressMonitor != null) {
        this.progressWorked++;
        if ((this.progressWorked%this.progressStep)==0) this.progressMonitor.worked(this.progressStep);
      }
      if (this.options.verbose)
        System.out.println(
          Messages.bind(Messages.compilation_done,
            new String[] {
              String.valueOf(i + 1),
              String.valueOf(this.numberOfMatches),
              new String(possibleMatch.parsedUnit.getFileName())
            }));
      // cleanup compilation unit result
      possibleMatch.cleanUp();
    }
  }
}
/**
* Locate the matches amongst the possible matches.
*/
protected void locateMatches(JavaProject javaProject, PossibleMatchSet matchSet, int expected) throws CoreException {
  PossibleMatch[] possibleMatches = matchSet.getPossibleMatches(javaProject.getPackageFragmentRoots());
  int length = possibleMatches.length;
  // increase progress from duplicate matches not stored in matchSet while adding...
  if (this.progressMonitor != null && expected>length) {
    this.progressWorked += expected-length;
    this.progressMonitor.worked( expected-length);
  }
  // locate matches (processed matches are limited to avoid problem while using VM default memory heap size)
  for (int index = 0; index < length;) {
    int max = Math.min(MAX_AT_ONCE, length - index);
    locateMatches(javaProject, possibleMatches, index, max);
    index += max;
  }
  this.patternLocator.clear();
}
/**
* Locate the matches in the given files and report them using the search requestor.
*/
public void locateMatches(SearchDocument[] searchDocuments) throws CoreException {
  int docsLength = searchDocuments.length;
  if (BasicSearchEngine.VERBOSE) {
    System.out.println("Locating matches in documents ["); //$NON-NLS-1$
    for (int i = 0; i < docsLength; i++)
      System.out.println("\t" + searchDocuments[i]); //$NON-NLS-1$
    System.out.println("]"); //$NON-NLS-1$
  }

  // init infos for progress increasing
  int n = docsLength<1000 ? Math.min(Math.max(docsLength/200+1, 2),4) : 5 *(docsLength/1000);
  this.progressStep = docsLength < n ? 1 : docsLength / n; // step should not be 0
  this.progressWorked = 0;

  // extract working copies
  ArrayList copies = new ArrayList();
  for (int i = 0; i < docsLength; i++) {
    SearchDocument document = searchDocuments[i];
    if (document instanceof WorkingCopyDocument) {
      copies.add(((WorkingCopyDocument)document).workingCopy);
    }
  }
  int copiesLength = copies.size();
  this.workingCopies = new org.aspectj.org.eclipse.jdt.core.ICompilationUnit[copiesLength];
  copies.toArray(this.workingCopies);

  JavaModelManager manager = JavaModelManager.getJavaModelManager();
  this.bindings = new SimpleLookupTable();
  try {
    // optimize access to zip files during search operation
    manager.cacheZipFiles();

    // initialize handle factory (used as a cache of handles so as to optimize space)
    if (this.handleFactory == null)
      this.handleFactory = new HandleFactory();

    if (this.progressMonitor != null) {
      this.progressMonitor.beginTask("", searchDocuments.length); //$NON-NLS-1$
    }

    // initialize pattern for polymorphic search (ie. method reference pattern)
    this.patternLocator.initializePolymorphicSearch(this);

    JavaProject previousJavaProject = null;
    PossibleMatchSet matchSet = new PossibleMatchSet();
    Util.sort(searchDocuments, new Util.Comparer() {
      public int compare(Object a, Object b) {
        return ((SearchDocument)a).getPath().compareTo(((SearchDocument)b).getPath());
      }
    });
    int displayed = 0; // progress worked displayed
    String previousPath = null;
    for (int i = 0; i < docsLength; i++) {
      if (this.progressMonitor != null && this.progressMonitor.isCanceled()) {
        throw new OperationCanceledException();
      }

      // skip duplicate paths
      SearchDocument searchDocument = searchDocuments[i];
      searchDocuments[i] = null; // free current document
      String pathString = searchDocument.getPath();
      if (i > 0 && pathString.equals(previousPath)) {
        if (this.progressMonitor != null) {
          this.progressWorked++;
          if ((this.progressWorked%this.progressStep)==0) this.progressMonitor.worked(this.progressStep);
        }
        displayed++;
        continue;
      }
      previousPath = pathString;

      Openable openable;
      org.aspectj.org.eclipse.jdt.core.ICompilationUnit workingCopy = null;
      if (searchDocument instanceof WorkingCopyDocument) {
        workingCopy = ((WorkingCopyDocument)searchDocument).workingCopy;
        openable = (Openable) workingCopy;
      } else {
        openable = this.handleFactory.createOpenable(pathString, this.scope);
      }
      if (openable == null) {
        if (this.progressMonitor != null) {
          this.progressWorked++;
          if ((this.progressWorked%this.progressStep)==0) this.progressMonitor.worked(this.progressStep);
        }
        displayed++;
        continue; // match is outside classpath
      }

      // create new parser and lookup environment if this is a new project
      IResource resource = null;
      JavaProject javaProject = (JavaProject) openable.getJavaProject();
      resource = workingCopy != null ? workingCopy.getResource() : openable.getResource();
      if (resource == null)
        resource = javaProject.getProject(); // case of a file in an external jar
      if (!javaProject.equals(previousJavaProject)) {
        // locate matches in previous project
        if (previousJavaProject != null) {
          try {
            locateMatches(previousJavaProject, matchSet, i-displayed);
            displayed = i;
          } catch (JavaModelException e) {
            // problem with classpath in this project -> skip it
          }
          matchSet.reset();
        }
        previousJavaProject = javaProject;
      }
      matchSet.add(new PossibleMatch(this, resource, openable, searchDocument, ((InternalSearchPattern) this.pattern).mustResolve));
    }

    // last project
    if (previousJavaProject != null) {
      try {
        locateMatches(previousJavaProject, matchSet, docsLength-displayed);
      } catch (JavaModelException e) {
        // problem with classpath in last project -> ignore
      }
    }

  } finally {
    if (this.progressMonitor != null)
      this.progressMonitor.done();
    if (this.nameEnvironment != null)
      this.nameEnvironment.cleanup();
    manager.flushZipFiles();
    this.bindings = null;
  }
}
/**
* Locates the package declarations corresponding to this locator's pattern.
*/
public void locatePackageDeclarations(SearchParticipant participant) throws CoreException {
  locatePackageDeclarations(this.pattern, participant);
}
/**
* Locates the package declarations corresponding to the search pattern.
*/
protected void locatePackageDeclarations(SearchPattern searchPattern, SearchParticipant participant) throws CoreException {
  if (searchPattern instanceof OrPattern) {
    SearchPattern[] patterns = ((OrPattern) searchPattern).patterns;
    for (int i = 0, length = patterns.length; i < length; i++)
      locatePackageDeclarations(patterns[i], participant);
  } else if (searchPattern instanceof PackageDeclarationPattern) {
    IJavaElement focus = ((InternalSearchPattern) searchPattern).focus;
    if (focus != null) {
      if (encloses(focus)) {
        SearchMatch match = new PackageDeclarationMatch(focus.getAncestor(IJavaElement.PACKAGE_FRAGMENT), SearchMatch.A_ACCURATE, -1, -1, participant, focus.getResource());
        report(match);
      }
      return;
    }
    PackageDeclarationPattern pkgPattern = (PackageDeclarationPattern) searchPattern;
    boolean isWorkspaceScope = this.scope == JavaModelManager.getJavaModelManager().getWorkspaceScope();
    IPath[] scopeProjectsAndJars =  isWorkspaceScope ? null : this.scope.enclosingProjectsAndJars();
    int scopeLength = isWorkspaceScope ? 0 : scopeProjectsAndJars.length;
    IJavaProject[] projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
    SimpleSet packages = new SimpleSet();
    for (int i = 0, length = projects.length; i < length; i++) {
      IJavaProject javaProject = projects[i];
      // Verify that project belongs to the scope
      if (!isWorkspaceScope) {
        boolean found = false;
        for (int j=0; j<scopeLength; j++) {
          if (javaProject.getPath().equals(scopeProjectsAndJars[j])) {
            found = true;
            break;
          }
        }
        if (!found) continue;
      }
      // Get all project package fragment names
      this.nameLookup = ((JavaProject) projects[i]).newNameLookup(this.workingCopies);
      IPackageFragment[] packageFragments = this.nameLookup.findPackageFragments(new String(pkgPattern.pkgName), false, true);
      int pLength = packageFragments == null ? 0 : packageFragments.length;
      // Report matches avoiding duplicate names
      for (int p=0; p<pLength; p++) {
        IPackageFragment fragment = packageFragments[p];
        if (packages.addIfNotIncluded(fragment) == null) continue;
        if (encloses(fragment)) {
          IResource resource = fragment.getResource();
          if (resource == null) // case of a file in an external jar
            resource = javaProject.getProject();
          try {
            if (encloses(fragment)) {
              SearchMatch match = new PackageDeclarationMatch(fragment, SearchMatch.A_ACCURATE, -1, -1, participant, resource);
              report(match);
            }
          } catch (JavaModelException e) {
            throw e;
          } catch (CoreException e) {
            throw new JavaModelException(e);
          }
        }
      }
    }
  }
}
//*/
protected IType lookupType(ReferenceBinding typeBinding) {
  if (typeBinding == null) return null;

  char[] packageName = typeBinding.qualifiedPackageName();
  IPackageFragment[] pkgs = this.nameLookup.findPackageFragments(
    (packageName == null || packageName.length == 0)
      ? IPackageFragment.DEFAULT_PACKAGE_NAME
      : new String(packageName),
    false);

  // iterate type lookup in each package fragment
  char[] sourceName = typeBinding.qualifiedSourceName();
  String typeName = new String(sourceName);
  int acceptFlag = 0;
  if (typeBinding.isAnnotationType()) {
    acceptFlag = NameLookup.ACCEPT_ANNOTATIONS;
  } else if (typeBinding.isEnum()) {
    acceptFlag = NameLookup.ACCEPT_ENUMS;
  } else if (typeBinding.isInterface()) {
    acceptFlag = NameLookup.ACCEPT_INTERFACES;
  } else if (typeBinding.isClass()) {
    acceptFlag = NameLookup.ACCEPT_CLASSES;
  }
  if (pkgs != null) {
    for (int i = 0, length = pkgs.length; i < length; i++) {
      IType type = this.nameLookup.findType(typeName, pkgs[i],  false,  acceptFlag, true/*consider secondary types*/);
      if (type != null) return type;
    }
  }

  // search inside enclosing element
  char[][] qualifiedName = CharOperation.splitOn('.', sourceName);
  int length = qualifiedName.length;
  if (length == 0) return null;

  IType type = createTypeHandle(new String(qualifiedName[0])); // find the top-level type
  if (type == null) return null;

  for (int i = 1; i < length; i++) {
    type = type.getType(new String(qualifiedName[i]));
    if (type == null) return null;
  }
  if (type.exists()) return type;
  return null;
}
public SearchMatch newDeclarationMatch(
    IJavaElement element,
    Binding binding,
    int accuracy,
    int offset, 
    int length) {
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  return newDeclarationMatch(element, binding, accuracy, offset, length, participant, resource);
}

public SearchMatch newDeclarationMatch(
    IJavaElement element,
    Binding binding,
    int accuracy,
    int offset, 
    int length,
    SearchParticipant participant,
    IResource resource) {
  switch (element.getElementType()) {
    case IJavaElement.PACKAGE_FRAGMENT:
      return new PackageDeclarationMatch(element, accuracy, offset, length, participant, resource);
    case IJavaElement.TYPE:
      return new TypeDeclarationMatch(binding == null ? element : ((JavaElement) element).resolved(binding), accuracy, offset, length, participant, resource);
    case IJavaElement.FIELD:
      return new FieldDeclarationMatch(binding == null ? element : ((JavaElement) element).resolved(binding), accuracy, offset, length, participant, resource);
    case IJavaElement.METHOD:
      return new MethodDeclarationMatch(binding == null ? element : ((JavaElement) element).resolved(binding), accuracy, offset, length, participant, resource);
    case IJavaElement.LOCAL_VARIABLE:
      return new LocalVariableDeclarationMatch(element, accuracy, offset, length, participant, resource);
    case IJavaElement.PACKAGE_DECLARATION:
      return new PackageDeclarationMatch(element, accuracy, offset, length, participant, resource);
    case IJavaElement.TYPE_PARAMETER:
      return new TypeParameterDeclarationMatch(element, accuracy, offset, length, participant, resource);
    default:
      return null;
  }
}

public SearchMatch newFieldReferenceMatch(
    IJavaElement enclosingElement,
    Binding enclosingBinding,
    int accuracy,
    int offset, 
    int length,
    ASTNode reference) {
  int bits = reference.bits;
  boolean isCoupoundAssigned = (bits & ASTNode.IsCompoundAssigned) != 0;
  boolean isReadAccess = isCoupoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0;
  boolean isWriteAccess = isCoupoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0;
  boolean insideDocComment = (bits & ASTNode.InsideJavadoc) != 0;
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  if (enclosingBinding != null)
    enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding);
  return new FieldReferenceMatch(enclosingElement, accuracy, offset, length, isReadAccess, isWriteAccess, insideDocComment, participant, resource);
}

public SearchMatch newLocalVariableReferenceMatch(
    IJavaElement enclosingElement,
    int accuracy,
    int offset, 
    int length,
    ASTNode reference) {
  int bits = reference.bits;
  boolean isCoupoundAssigned = (bits & ASTNode.IsCompoundAssigned) != 0;
  boolean isReadAccess = isCoupoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0;
  boolean isWriteAccess = isCoupoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0;
  boolean insideDocComment = (bits & ASTNode.InsideJavadoc) != 0;
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  return new LocalVariableReferenceMatch(enclosingElement, accuracy, offset, length, isReadAccess, isWriteAccess, insideDocComment, participant, resource);
}

public SearchMatch newMethodReferenceMatch(
    IJavaElement enclosingElement,
    Binding enclosingBinding,
    int accuracy,
    int offset, 
    int length,
    boolean isConstructor,
    boolean isSynthetic,
    ASTNode reference) {
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  boolean insideDocComment = (reference.bits & ASTNode.InsideJavadoc) != 0;
  if (enclosingBinding != null)
    enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding);
  boolean isOverridden = (accuracy & PatternLocator.SUPER_INVOCATION_FLAVOR) != 0;
  return new MethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, isOverridden, insideDocComment, participant, resource);
}

public SearchMatch newPackageReferenceMatch(
    IJavaElement enclosingElement,
    int accuracy,
    int offset, 
    int length,
    ASTNode reference) {
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  boolean insideDocComment = (reference.bits & ASTNode.InsideJavadoc) != 0;
  return new PackageReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
}

public SearchMatch newTypeParameterReferenceMatch(
    IJavaElement enclosingElement,
    int accuracy,
    int offset, 
    int length,
    ASTNode reference) {
  int bits = reference.bits;
  boolean insideDocComment = (bits & ASTNode.InsideJavadoc) != 0;
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  return new TypeParameterReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
}

public TypeReferenceMatch newTypeReferenceMatch(
    IJavaElement enclosingElement,
    Binding enclosingBinding,
    int accuracy,
    int offset, 
    int length,
    ASTNode reference) {
  SearchParticipant participant = getParticipant();
  IResource resource = this.currentPossibleMatch.resource;
  boolean insideDocComment = (reference.bits & ASTNode.InsideJavadoc) != 0;
  if (enclosingBinding != null)
    enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding);
  return new TypeReferenceMatch(enclosingElement, accuracy, offset, length, insideDocComment, participant, resource);
}

public TypeReferenceMatch newTypeReferenceMatch(
    IJavaElement enclosingElement,
    Binding enclosingBinding,
    int accuracy,
    ASTNode reference) {
  return newTypeReferenceMatch(enclosingElement, enclosingBinding, accuracy, reference.sourceStart, reference.sourceEnd-reference.sourceStart+1, reference);
}

/**
* Add the possibleMatch to the loop
*  ->  build compilation unit declarations, their bindings and record their results.
*/
protected boolean parseAndBuildBindings(PossibleMatch possibleMatch, boolean mustResolve) throws CoreException {
  if (this.progressMonitor != null && this.progressMonitor.isCanceled())
    throw new OperationCanceledException();

  try {
    if (BasicSearchEngine.VERBOSE)
      System.out.println("Parsing " + possibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$

    this.parser.nodeSet = possibleMatch.nodeSet;
    CompilationResult unitResult = new CompilationResult(possibleMatch, 1, 1, this.options.maxProblemsPerUnit);
    CompilationUnitDeclaration parsedUnit = this.parser.dietParse(possibleMatch, unitResult);
    if (parsedUnit != null) {
      if (!parsedUnit.isEmpty()) {
        if (mustResolve) {
          this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
        }
        if (hasAlreadyDefinedType(parsedUnit)) return false; // skip type has it is hidden so not visible
        getMethodBodies(parsedUnit, possibleMatch.nodeSet);
        if (this.patternLocator.mayBeGeneric && !mustResolve && possibleMatch.nodeSet.mustResolve) {
          // special case: possible match node set force resolution although pattern does not
          // => we need to build types for this compilation unit
          this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
        }
      }
 
      // add the possibleMatch with its parsedUnit to matchesToProcess
      possibleMatch.parsedUnit = parsedUnit;
      int size = this.matchesToProcess.length;
      if (this.numberOfMatches == size)
        System.arraycopy(this.matchesToProcess, 0, this.matchesToProcess = new PossibleMatch[size == 0 ? 1 : size * 2], 0, this.numberOfMatches);
      this.matchesToProcess[this.numberOfMatches++] = possibleMatch;
    }
  } finally {
    this.parser.nodeSet = null;
  }
  return true;
}
/*
* Process a compilation unit already parsed and build.
*/
protected void process(PossibleMatch possibleMatch, boolean bindingsWereCreated) throws CoreException {
  this.currentPossibleMatch = possibleMatch;
  CompilationUnitDeclaration unit = possibleMatch.parsedUnit;
  try {
    if (unit.isEmpty()) {
      if (this.currentPossibleMatch.openable instanceof ClassFile) {
        ClassFile classFile = (ClassFile) this.currentPossibleMatch.openable;
        IBinaryType info = getBinaryInfo(classFile, this.currentPossibleMatch.resource);
        if (info != null) {
          boolean mayBeGeneric = this.patternLocator.mayBeGeneric;
          this.patternLocator.mayBeGeneric = false; // there's no longer generics in class files
          try {
            new ClassFileMatchLocator().locateMatches(this, classFile, info);
          }
          finally {
            this.patternLocator.mayBeGeneric = mayBeGeneric;
          }
        }
      }
      return;
    }
    if (hasAlreadyDefinedType(unit)) return; // skip type has it is hidden so not visible

    // Move getMethodBodies to #parseAndBuildings(...) method to allow possible match resolution management
    //getMethodBodies(unit);

    boolean mustResolve = (((InternalSearchPattern)this.pattern).mustResolve || possibleMatch.nodeSet.mustResolve);
    if (bindingsWereCreated && mustResolve) {
      if (unit.types != null) {
        if (BasicSearchEngine.VERBOSE)
          System.out.println("Resolving " + this.currentPossibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$
 
        this.lookupEnvironment.unitBeingCompleted = unit;
        reduceParseTree(unit);
 
        if (unit.scope != null) {
          // fault in fields & methods
          unit.scope.faultInTypes();
        }
        unit.resolve();
      } else if (unit.isPackageInfo()) {
        if (BasicSearchEngine.VERBOSE)
          System.out.println("Resolving " + this.currentPossibleMatch.openable.toStringWithAncestors()); //$NON-NLS-1$
        unit.resolve();
      }
    }
    reportMatching(unit, mustResolve);
  } catch (AbortCompilation e) {
    // could not resolve: report inaccurate matches
    reportMatching(unit, false); // do not resolve when cu has errors
    if (!(e instanceof AbortCompilationUnit)) {
      // problem with class path
      throw e;
    }
  } finally {
    this.lookupEnvironment.unitBeingCompleted = null;
    this.currentPossibleMatch = null;
  }
}
protected void purgeMethodStatements(TypeDeclaration type, boolean checkEachMethod) {
  checkEachMethod = checkEachMethod
    && this.currentPossibleMatch.nodeSet.hasPossibleNodes(type.declarationSourceStart, type.declarationSourceEnd);
  AbstractMethodDeclaration[] methods = type.methods;
  if (methods != null) {
    if (checkEachMethod) {
      for (int j = 0, length = methods.length; j < length; j++) {
        AbstractMethodDeclaration method = methods[j];
        if (!this.currentPossibleMatch.nodeSet.hasPossibleNodes(method.declarationSourceStart, method.declarationSourceEnd)) {
          method.statements = null;
          method.javadoc = null;
        }
      }
    } else {
      for (int j = 0, length = methods.length; j < length; j++) {
        methods[j].statements = null;
        methods[j].javadoc = null;
      }
    }
  }

  TypeDeclaration[] memberTypes = type.memberTypes;
  if (memberTypes != null)
    for (int i = 0, l = memberTypes.length; i < l; i++)
      purgeMethodStatements(memberTypes[i], checkEachMethod);
}
/**
* Called prior to the unit being resolved. Reduce the parse tree where possible.
*/
protected void reduceParseTree(CompilationUnitDeclaration unit) {
  // remove statements from methods that have no possible matching nodes
  TypeDeclaration[] types = unit.types;
  for (int i = 0, l = types.length; i < l; i++)
    purgeMethodStatements(types[i], true);
}
public SearchParticipant getParticipant() {
  return this.currentPossibleMatch.document.getParticipant();
}

protected void report(SearchMatch match) throws CoreException {
  long start = -1;
  if (BasicSearchEngine.VERBOSE) {
    start = System.currentTimeMillis();
    System.out.println("Reporting match"); //$NON-NLS-1$
    System.out.println("\tResource: " + match.getResource());//$NON-NLS-1$
    System.out.println("\tPositions: [offset=" + match.getOffset() + ", length=" + match.getLength() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    try {
      if (this.parser != null && match.getOffset() > 0 && match.getLength() > 0 && !(match.getElement() instanceof BinaryMember)) {
        String selection = new String(this.parser.scanner.source, match.getOffset(), match.getLength());
        System.out.println("\tSelection: -->" + selection + "<--"); //$NON-NLS-1$ //$NON-NLS-2$
      }
    } catch (Exception e) {
      // it's just for debug purposes... ignore all exceptions in this area
    }
    try {
      JavaElement javaElement = (JavaElement)match.getElement();
      System.out.println("\tJava element: "+ javaElement.toStringWithAncestors()); //$NON-NLS-1$
      if (!javaElement.exists()) {
        System.out.println("\t\tWARNING: this element does NOT exist!"); //$NON-NLS-1$
      }
    } catch (Exception e) {
      // it's just for debug purposes... ignore all exceptions in this area
    }
    if (match instanceof TypeReferenceMatch) {
      try {
        TypeReferenceMatch typeRefMatch = (TypeReferenceMatch) match;
        JavaElement local = (JavaElement) typeRefMatch.getLocalElement();
        if (local != null) {
          System.out.println("\tLocal element: "+ local.toStringWithAncestors()); //$NON-NLS-1$
        }
        IJavaElement[] others = typeRefMatch.getOtherElements();
        if (others != null) {
          int length = others.length;
          if (length > 0) {
            System.out.println("\tOther elements:"); //$NON-NLS-1$
            for (int i=0; i<length; i++) {
              JavaElement other = (JavaElement) others[i];
              System.out.println("\t\t- "+ other.toStringWithAncestors()); //$NON-NLS-1$
            }
          }
        }
      } catch (Exception e) {
        // it's just for debug purposes... ignore all exceptions in this area
      }
    }
    System.out.println(match.getAccuracy() == SearchMatch.A_ACCURATE
      ? "\tAccuracy: EXACT_MATCH" //$NON-NLS-1$
      : "\tAccuracy: POTENTIAL_MATCH"); //$NON-NLS-1$
    System.out.print("\tRule: "); //$NON-NLS-1$
    if (match.isExact()) {
      System.out.print("EXACT"); //$NON-NLS-1$
    } else if (match.isEquivalent()) {
      System.out.print("EQUIVALENT"); //$NON-NLS-1$
    } else if (match.isErasure()) {
      System.out.print("ERASURE"); //$NON-NLS-1$
    } else {
      System.out.print("INVALID RULE"); //$NON-NLS-1$
    }
    if (match instanceof MethodReferenceMatch) {
      MethodReferenceMatch methodReferenceMatch = (MethodReferenceMatch) match;
      if (methodReferenceMatch.isSuperInvocation()) {
        System.out.print("+SUPER INVOCATION"); //$NON-NLS-1$
      }
      if (methodReferenceMatch.isImplicit()) {
        System.out.print("+IMPLICIT"); //$NON-NLS-1$
      }
      if (methodReferenceMatch.isSynthetic()) {
        System.out.print("+SYNTHETIC"); //$NON-NLS-1$
      }
    }
    System.out.println("\n\tRaw: "+match.isRaw()); //$NON-NLS-1$
  }
  this.requestor.acceptSearchMatch(match);
  if (BasicSearchEngine.VERBOSE)
    this.resultCollectorTime += System.currentTimeMillis()-start;
}
/**
* Finds the accurate positions of the sequence of tokens given by qualifiedName
* in the source and reports a reference to this this qualified name
* to the search requestor.
*/
protected void reportAccurateTypeReference(SearchMatch match, ASTNode typeRef, char[] name) throws CoreException {
  if (match.getRule() == 0) return;
  if (!encloses((IJavaElement)match.getElement())) return;
 
  // Compute source positions of the qualified reference
  int sourceStart = typeRef.sourceStart;
  int sourceEnd = typeRef.sourceEnd;
  Scanner scanner = this.parser.scanner;
  scanner.setSource(this.currentPossibleMatch.getContents());
  scanner.resetTo(sourceStart, sourceEnd);

  int token = -1;
  int currentPosition;
  do {
    currentPosition = scanner.currentPosition;
    try {
      token = scanner.getNextToken();
    } catch (InvalidInputException e) {
      // ignore
    }
    if (token == TerminalTokens.TokenNameIdentifier && this.pattern.matchesName(name, scanner.getCurrentTokenSource())) {
      int length = scanner.currentPosition-currentPosition;
      match.setOffset(currentPosition);
      match.setLength(length);
      report(match);
      return;
    }
  } while (token != TerminalTokens.TokenNameEOF);

  //  Report match
  match.setOffset(sourceStart);
  match.setLength(sourceEnd-sourceStart+1);
  report(match);
}

/**
* Finds the accurate positions of the sequence of tokens given by qualifiedName
* in the source and reports a reference to this parameterized type name
* to the search requestor.
* @since 3.1
*/
protected void reportAccurateParameterizedMethodReference(SearchMatch match, ASTNode statement, TypeReference[] typeArguments) throws CoreException {
  if (match.getRule() == 0) return;
  if (!encloses((IJavaElement)match.getElement())) return;

  // If there's type arguments, look for end (ie. char '>') of last one.
  int start = match.getOffset();
  if (typeArguments != null && typeArguments.length > 0) {
    boolean isErasureMatch= (pattern instanceof OrPattern) ? ((OrPattern)pattern).isErasureMatch() : ((JavaSearchPattern)pattern).isErasureMatch();
    if (!isErasureMatch) {
     
      // Initialize scanner
      Scanner scanner = this.parser.scanner;
      char[] source = this.currentPossibleMatch.getContents();
      scanner.setSource(source);

      // Search previous opening '<'
      start = typeArguments[0].sourceStart;
      int end = statement.sourceEnd;
      scanner.resetTo(start, end);
      int lineStart = start;
      try {
        linesUp: while (true) {
          while (scanner.source[scanner.currentPosition] != '\n') {
            scanner.currentPosition--;
            if (scanner.currentPosition == 0) break linesUp;
          }
          lineStart = scanner.currentPosition+1;
          scanner.resetTo(lineStart, end);
          while (!scanner.atEnd()) {
            if (scanner.getNextToken() == TerminalTokens.TokenNameLESS) {
              start = scanner.getCurrentTokenStartPosition();
              break linesUp;
            }
          }
          end = lineStart - 2;
          scanner.currentPosition = end;
        }
      }
      catch (InvalidInputException ex) {
        // give up
      }
     }
  }
 
  // Report match
  match.setOffset(start);
  match.setLength(statement.sourceEnd-start+1);
  report(match);
}

/**
* Finds the accurate positions of the sequence of tokens given by qualifiedName
* in the source and reports a reference to this parameterized type name
* to the search requestor.
* @since 3.1
*/
protected void reportAccurateParameterizedTypeReference(SearchMatch match, TypeReference typeRef, int index, TypeReference[] typeArguments) throws CoreException {
  if (match.getRule() == 0) return;
  if (!encloses((IJavaElement)match.getElement())) return;

  // If there's type arguments, look for end (ie. char '>') of last one.
  int end = typeRef.sourceEnd;
  if (typeArguments != null) {
    // Initialize scanner
    Scanner scanner = this.parser.scanner;
    char[] source = this.currentPossibleMatch.getContents();
    scanner.setSource(source);

    boolean shouldMatchErasure= (pattern instanceof OrPattern) ? ((OrPattern)pattern).isErasureMatch() : ((JavaSearchPattern)pattern).isErasureMatch();
    boolean hasSignatures = (pattern instanceof OrPattern) ? ((OrPattern)pattern).hasSignatures() : ((JavaSearchPattern)pattern).hasSignatures();
    if (shouldMatchErasure || !hasSignatures) {
      // if pattern is erasure only, then select the end of the reference
      if (typeRef instanceof QualifiedTypeReference && index >= 0) {
        long[] positions = ((QualifiedTypeReference) typeRef).sourcePositions;
        end = (int) positions[index];
      } else if (typeRef instanceof ArrayTypeReference) {
        end = ((ArrayTypeReference) typeRef).originalSourceEnd;
      }
    }  else {
      // Set scanner position at end of last type argument
      scanner.resetTo(end, source.length-1);
      int depth = 0;
      for (int i=typeArguments.length-1; i>=0; i--) {
        if (typeArguments[i] != null) {
          long lastTypeArgInfo = findLastTypeArgumentInfo(typeArguments[i]);
          depth = (int) (lastTypeArgInfo >>> 32)+1;
          scanner.resetTo(((int)lastTypeArgInfo)+1, scanner.eofPosition-1);
          break;
        }
      }
 
      // Now, scan to search next closing '>'
      while (depth-- > 0) {
        while (!scanner.atEnd()) {
          if (scanner.getNextChar() == '>') {
            end = scanner.currentPosition - 1;
            break;
          }
        }
      }
     }
  }
 
  // Report match
  match.setLength(end-match.getOffset()+1);
  report(match);
}
/**
* Finds the accurate positions of each valid token in the source and
* reports a reference to this token to the search requestor.
* A token is valid if it has an accuracy which is not -1.
*/
protected void reportAccurateEnumConstructorReference(SearchMatch match, FieldDeclaration field, AllocationExpression allocation) throws CoreException {
  // Verify that field declaration is really an enum constant
  if (allocation == null || allocation.enumConstant == null) {
    report(match);
    return;
  }

  // Get scan area
  int sourceStart = match.getOffset()+match.getLength();
  if (allocation.arguments != null && allocation.arguments.length > 0) {
    sourceStart = allocation.arguments[allocation.arguments.length-1].sourceEnd+1;
  }
  int sourceEnd = field.declarationSourceEnd;
  if (allocation instanceof QualifiedAllocationExpression) {
    QualifiedAllocationExpression qualifiedAllocation = (QualifiedAllocationExpression) allocation;
    if (qualifiedAllocation.anonymousType != null) {
      sourceEnd = qualifiedAllocation.anonymousType.sourceStart - 1;
    }
  }
 
  // Scan to find last closing parenthesis
  Scanner scanner = this.parser.scanner;
  scanner.setSource(this.currentPossibleMatch.getContents());
  scanner.resetTo(sourceStart, sourceEnd);
  try {
    int token = scanner.getNextToken();
    while (token != TerminalTokens.TokenNameEOF) {
      if (token == TerminalTokens.TokenNameRPAREN) {
        sourceEnd = scanner.getCurrentTokenEndPosition();
      }
      token = scanner.getNextToken();
    }
  }
  catch (InvalidInputException iie) {
    // give up
  }

  // Report match
  match.setLength(sourceEnd-match.getOffset()+1);
  report(match);
}
/**
* Finds the accurate positions of each valid token in the source and
* reports a reference to this token to the search requestor.
* A token is valid if it has an accuracy which is not -1.
*/
protected void reportAccurateFieldReference(SearchMatch[] matches, QualifiedNameReference qNameRef) throws CoreException {
  if (matches == null) return; // there's nothing to accurate in this case
  int matchesLength = matches.length;

  int sourceStart = qNameRef.sourceStart;
  int sourceEnd = qNameRef.sourceEnd;
  char[][] tokens = qNameRef.tokens;
 
  // compute source positions of the qualified reference
  Scanner scanner = this.parser.scanner;
  scanner.setSource(this.currentPossibleMatch.getContents());
  scanner.resetTo(sourceStart, sourceEnd);
  int sourceLength = sourceEnd-sourceStart+1;

  int refSourceStart = -1, refSourceEnd = -1;
  int length = tokens.length;
  int token = -1;
  int previousValid = -1;
  int i = 0;
  int index = 0;
  do {
    int currentPosition = scanner.currentPosition;
    // read token
    try {
      token = scanner.getNextToken();
    } catch (InvalidInputException e) {
      //ignore
    }
    if (token != TerminalTokens.TokenNameEOF) {
      char[] currentTokenSource = scanner.getCurrentTokenSource();
      boolean equals = false;
      while (i < length && !(equals = this.pattern.matchesName(tokens[i++], currentTokenSource))){/*empty*/}
      if (equals && (previousValid == -1 || previousValid == i - 2)) {
        previousValid = i - 1;
        if (refSourceStart == -1)
          refSourceStart = currentPosition;
        refSourceEnd = scanner.currentPosition - 1;
      } else {
        i = 0;
        refSourceStart = -1;
        previousValid = -1;
      }
      // read '.'
      try {
        token = scanner.getNextToken();
      } catch (InvalidInputException e) {
        // ignore
      }
    }
    SearchMatch match = matches[index];
    if (match != null && match.getRule() != 0) {
      if (!encloses((IJavaElement)match.getElement())) return;
      // accept reference
      if (refSourceStart != -1) {
        match.setOffset(refSourceStart);
        match.setLength(refSourceEnd-refSourceStart+1);
        report(match);
      } else {
        match.setOffset(sourceStart);
        match.setLength(sourceLength);
        report(match);
      }
      i = 0;
    }
    refSourceStart = -1;
    previousValid = -1;
    if (index < matchesLength - 1) {
      index++;
    }
  } while (token != TerminalTokens.TokenNameEOF);

}
protected void reportBinaryMemberDeclaration(IResource resource, IMember binaryMember, Binding binaryMemberBinding, IBinaryType info, int accuracy) throws CoreException {
  ClassFile classFile = (ClassFile) binaryMember.getClassFile();
  ISourceRange range = classFile.isOpen() ? binaryMember.getNameRange() : SourceMapper.UNKNOWN_RANGE;
  if (range.getOffset() == -1) {
    BinaryType type = (BinaryType) classFile.getType();
    String sourceFileName = type.sourceFileName(info);
    if (sourceFileName != null) {
      SourceMapper mapper = classFile.getSourceMapper();
      if (mapper != null) {
        char[] contents = mapper.findSource(type, sourceFileName);
        if (contents != null)
          range = mapper.mapSource(type, contents, info, binaryMember);
      }
    }
  }
  if (resource == null) resource =  this.currentPossibleMatch.resource;
  SearchMatch match = newDeclarationMatch(binaryMember, binaryMemberBinding, accuracy, range.getOffset(), range.getLength(), getParticipant(), resource);
  report(match);
}
/**
* Visit the given method declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
* Note that the method declaration has already been checked.
*/
protected void reportMatching(TypeDeclaration type, AbstractMethodDeclaration method, IJavaElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException {
  IJavaElement enclosingElement = null;
  if (accuracy > -1) {
    enclosingElement = createHandle(method, parent);
    if (enclosingElement != null) { // skip if unable to find method
      // compute source positions of the selector
      Scanner scanner = parser.scanner;
      int nameSourceStart = method.sourceStart;
      scanner.setSource(this.currentPossibleMatch.getContents());
      scanner.resetTo(nameSourceStart, method.sourceEnd);
      try {
        scanner.getNextToken();
      } catch (InvalidInputException e) {
        // ignore
      }
      if (encloses(enclosingElement)) {
        SearchMatch match = null;
        if (method.isDefaultConstructor()) {
          // Use type for match associated element as default constructor does not exist in source
          int offset = type.sourceStart;
          match = this.patternLocator.newDeclarationMatch(type, parent, type.binding, accuracy, type.sourceEnd-offset+1, this);
        } else {
          int length = scanner.currentPosition - nameSourceStart;
          match = this.patternLocator.newDeclarationMatch(method, enclosingElement, method.binding, accuracy, length, this);
        }
        if (match != null) {
          report(match);
        }
      }
    }
  }

  // handle nodes for the local type first
  if ((method.bits & ASTNode.HasLocalType) != 0) {
    if (enclosingElement == null)
      enclosingElement = createHandle(method, parent);
    LocalDeclarationVisitor localDeclarationVisitor = new LocalDeclarationVisitor(enclosingElement, method.binding, nodeSet);
    try {
      method.traverse(localDeclarationVisitor, (ClassScope) null);
    } catch (WrappedCoreException e) {
      throw e.coreException;
    }
  }

  // report the type parameters
  TypeParameter[] typeParameters = method.typeParameters();
  if (typeParameters != null) {
    if (enclosingElement == null) {
      enclosingElement = createHandle(method, parent);
    }
    reportMatching(typeParameters, enclosingElement, parent, method.binding, nodeSet);
  }

  // report annotations
  if (method.annotations != null) {
    if (enclosingElement == null) {
      enclosingElement = createHandle(method, parent);
    }
    reportMatching(method.annotations, enclosingElement, method.binding, nodeSet, true, true);
  }

  // references in this method
  if (typeInHierarchy) {
    ASTNode[] nodes = nodeSet.matchingNodes(method.declarationSourceStart, method.declarationSourceEnd);
    if (nodes != null) {
      if ((this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0) {
        if (enclosingElement == null)
          enclosingElement = createHandle(method, parent);
        if (encloses(enclosingElement)) {
          for (int i = 0, l = nodes.length; i < l; i++) {
            ASTNode node = nodes[i];
            Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
            this.patternLocator.matchReportReference(node, enclosingElement, method.binding, method.scope, level.intValue(), this);
          }
          return;
        }
      }
      for (int i = 0, l = nodes.length; i < l; i++)
        nodeSet.matchingNodes.removeKey(nodes[i]);
    }
  }
}
/**
* Report matching in annotations.
*/
protected void reportMatching(Annotation[] annotations, IJavaElement enclosingElement, Binding elementBinding, MatchingNodeSet nodeSet, boolean matchedContainer, boolean enclosesElement) throws CoreException {
  for (int i=0, al=annotations.length; i<al; i++) {
    Annotation annotationType = annotations[i];

    // Look for annotation type ref
    TypeReference typeRef = annotationType.type;
    Integer level = (Integer) nodeSet.matchingNodes.removeKey(typeRef);
    if (level != null && matchedContainer) {
      this.patternLocator.matchReportReference(typeRef, enclosingElement, elementBinding, level.intValue(), this);
    }
   
    // Look for attribute ref
    MemberValuePair[] pairs = annotationType.memberValuePairs();
    for (int j = 0, pl = pairs.length; j < pl; j++) {
      MemberValuePair pair = pairs[j];
      level = (Integer) nodeSet.matchingNodes.removeKey(pair);
      if (level != null && enclosesElement) {
        ASTNode reference = (annotationType instanceof SingleMemberAnnotation) ? (ASTNode) annotationType: pair;
        this.patternLocator.matchReportReference(reference, enclosingElement, pair.binding, level.intValue(), this);
      }
    }
   
    // Look for reference inside annotation
    ASTNode[] nodes = nodeSet.matchingNodes(annotationType.sourceStart, annotationType.declarationSourceEnd);
    if (nodes != null) {
      if (!matchedContainer) {
        for (int j = 0, nl = nodes.length; j < nl; j++) {
          nodeSet.matchingNodes.removeKey(nodes[j]);
        }
      } else {
        for (int j = 0, nl = nodes.length; j < nl; j++) {
          ASTNode node = nodes[j];
          level = (Integer) nodeSet.matchingNodes.removeKey(node);
          if (enclosesElement) {
            this.patternLocator.matchReportReference(node, enclosingElement, elementBinding, level.intValue(), this);
          }
        }
      }
    }
  }
}
/**
* Visit the given resolved parse tree and report the nodes that match the search pattern.
*/
protected void reportMatching(CompilationUnitDeclaration unit, boolean mustResolve) throws CoreException {
  MatchingNodeSet nodeSet = this.currentPossibleMatch.nodeSet;
  boolean locatorMustResolve = this.patternLocator.mustResolve;
  if (nodeSet.mustResolve) this.patternLocator.mustResolve = true;
  if (BasicSearchEngine.VERBOSE) {
    System.out.println("Report matching: "); //$NON-NLS-1$
    int size = nodeSet.matchingNodes==null ? 0 : nodeSet.matchingNodes.elementSize;
    System.out.print("  - node set: accurate="+ size); //$NON-NLS-1$
    size = nodeSet.possibleMatchingNodesSet==null ? 0 : nodeSet.possibleMatchingNodesSet.elementSize;
    System.out.println(", possible="+size); //$NON-NLS-1$
    System.out.print("  - must resolve: "+mustResolve); //$NON-NLS-1$
    System.out.print(" (locator: "+this.patternLocator.mustResolve); //$NON-NLS-1$
    System.out.println(", nodeSet: "+nodeSet.mustResolve+')'); //$NON-NLS-1$
  }
  if (mustResolve) {
    this.unitScope= unit.scope.compilationUnitScope();
    // move the possible matching nodes that exactly match the search pattern to the matching nodes set
    Object[] nodes = nodeSet.possibleMatchingNodesSet.values;
    for (int i = 0, l = nodes.length; i < l; i++) {
      ASTNode node = (ASTNode) nodes[i];
      if (node == null) continue;
      if (node instanceof ImportReference) {
        // special case for import refs: they don't know their binding
        // import ref cannot be in the hierarchy of a type
        if (this.hierarchyResolver != null) continue;

        ImportReference importRef = (ImportReference) node;
        Binding binding = (importRef.bits & ASTNode.OnDemand) != 0
          ? unitScope.getImport(CharOperation.subarray(importRef.tokens, 0, importRef.tokens.length), true, importRef.isStatic())
          : unitScope.getImport(importRef.tokens, false, importRef.isStatic());
        this.patternLocator.matchLevelAndReportImportRef(importRef, binding, this);
      }
      nodeSet.addMatch(node, this.patternLocator.resolveLevel(node));
    }
    nodeSet.possibleMatchingNodesSet = new SimpleSet(3);
    if (BasicSearchEngine.VERBOSE) {
      int size = nodeSet.matchingNodes==null ? 0 : nodeSet.matchingNodes.elementSize;
      System.out.print("  - node set: accurate="+size); //$NON-NLS-1$
      size = nodeSet.possibleMatchingNodesSet==null ? 0 : nodeSet.possibleMatchingNodesSet.elementSize;
      System.out.println(", possible="+size); //$NON-NLS-1$
    }
  } else {
    this.unitScope = null;
  }

  if (nodeSet.matchingNodes.elementSize == 0) return; // no matching nodes were found
  this.methodHandles = new HashSet();

  boolean matchedUnitContainer = (this.matchContainer & PatternLocator.COMPILATION_UNIT_CONTAINER) != 0;

  // report references in javadoc
  if (unit.javadoc != null) {
    ASTNode[] nodes = nodeSet.matchingNodes(unit.javadoc.sourceStart, unit.javadoc.sourceEnd);
    if (nodes != null) {
      if (!matchedUnitContainer) {
        for (int i = 0, l = nodes.length; i < l; i++)
          nodeSet.matchingNodes.removeKey(nodes[i]);
      } else {
        IJavaElement element = createPackageDeclarationHandle(unit);
        for (int i = 0, l = nodes.length; i < l; i++) {
          ASTNode node = nodes[i];
          Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
          if (encloses(element))
            this.patternLocator.matchReportReference(node, element, null/*no binding*/, level.intValue(), this);
        }
      }
    }
  }

  if (matchedUnitContainer) {
    ImportReference pkg = unit.currentPackage;
    if (pkg != null && pkg.annotations != null) {
      IJavaElement element = createPackageDeclarationHandle(unit);
      if (element != null) {
        reportMatching(pkg.annotations, element, null, nodeSet, true, encloses(element));
      }
    }

    ImportReference[] imports = unit.imports;
    if (imports != null) {
      for (int i = 0, l = imports.length; i < l; i++) {
        ImportReference importRef = imports[i];
        Integer level = (Integer) nodeSet.matchingNodes.removeKey(importRef);
        if (level != null)
          this.patternLocator.matchReportImportRef(importRef, null/*no binding*/, createImportHandle(importRef), level.intValue(), this);
      }
    }
  }

  TypeDeclaration[] types = unit.types;
  if (types != null) {
    for (int i = 0, l = types.length; i < l; i++) {
      if (nodeSet.matchingNodes.elementSize == 0) return; // reported all the matching nodes
      TypeDeclaration type = types[i];
      Integer level = (Integer) nodeSet.matchingNodes.removeKey(type);
      int accuracy = (level != null && matchedUnitContainer) ? level.intValue() : -1;
      reportMatching(type, null, accuracy, nodeSet, 1);
    }
  }
 
  // Clear handle cache
  this.methodHandles = null;
  this.bindings.removeKey(this.pattern);
  this.patternLocator.mustResolve = locatorMustResolve;
}
/**
* Visit the given field declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
*/
protected void reportMatching(FieldDeclaration field, FieldDeclaration[] otherFields, TypeDeclaration type, IJavaElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException {
  IJavaElement enclosingElement = null;
  if (accuracy > -1) {
    enclosingElement = createHandle(field, type, parent);
    if (encloses(enclosingElement)) {
      int offset = field.sourceStart;
      SearchMatch match = newDeclarationMatch(enclosingElement, field.binding, accuracy, offset, field.sourceEnd-offset+1);
      if (field.initialization instanceof AllocationExpression) {
        reportAccurateEnumConstructorReference(match, field, (AllocationExpression) field.initialization);
      } else {
        report(match);
      }
    }
  }

  // handle the nodes for the local type first
  if ((field.bits & ASTNode.HasLocalType) != 0) {
    if (enclosingElement == null)
      enclosingElement = createHandle(field, type, parent);
    LocalDeclarationVisitor localDeclarationVisitor = new LocalDeclarationVisitor(enclosingElement, field.binding, nodeSet);
    try {
      field.traverse(localDeclarationVisitor, null);
    } catch (WrappedCoreException e) {
      throw e.coreException;
    }
  }

  // report annotations
  if (field.annotations != null) {
    if (enclosingElement == null) {
      enclosingElement = createHandle(field, type, parent);
    }
    reportMatching(field.annotations, enclosingElement, field.binding, nodeSet, true, true);
  }

  if (typeInHierarchy) {
    // Look at field declaration
    if (field.endPart1Position != 0) { // not necessary if field is an initializer
      ASTNode[] nodes = nodeSet.matchingNodes(field.declarationSourceStart, field.endPart1Position);
      if (nodes != null) {
        if ((this.matchContainer & PatternLocator.FIELD_CONTAINER) == 0) {
          for (int i = 0, l = nodes.length; i < l; i++)
            nodeSet.matchingNodes.removeKey(nodes[i]);
        } else {
          if (enclosingElement == null)
            enclosingElement = createHandle(field, type, parent);
          if (encloses(enclosingElement)) {
            for (int i = 0, l = nodes.length; i < l; i++) {
              ASTNode node = nodes[i];
              Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
              IJavaElement[] otherElements = null;
              if (otherFields != null) {
                int length = otherFields.length;
                int size = 0;
                while (size<length && otherFields[size] != null) {
                  size++;
                }
                otherElements = new IJavaElement[size];
                for (int j=0; j<size; j++) {
                  otherElements[j] = createHandle(otherFields[j], type, parent);
                }
              }
              this.patternLocator.matchReportReference(node, enclosingElement, null, otherElements, field.binding, level.intValue(), this);
            }
          }
        }
      }
    }

    // Look in initializer
    int fieldEnd = field.endPart2Position == 0 ? field.declarationSourceEnd : field.endPart2Position;
    ASTNode[] nodes = nodeSet.matchingNodes(field.sourceStart, fieldEnd);
    if (nodes != null) {
      if ((this.matchContainer & PatternLocator.FIELD_CONTAINER) == 0) {
        for (int i = 0, l = nodes.length; i < l; i++)
          nodeSet.matchingNodes.removeKey(nodes[i]);
      } else {
        if (enclosingElement == null) {
          enclosingElement = createHandle(field, type, parent);
        }
        if (encloses(enclosingElement)) {
          for (int i = 0, l = nodes.length; i < l; i++) {
            ASTNode node = nodes[i];
            Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
            if (node instanceof TypeDeclaration) {
              // use field declaration to report match (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=88174)
              AllocationExpression allocation = ((TypeDeclaration)node).allocation;
              if (allocation != null && allocation.enumConstant != null) {
                node = field;
              }
            }
            // Set block scope for initializer in case there would have other local and other elements to report
            BlockScope blockScope = null;
            if (field.getKind() == AbstractVariableDeclaration.INITIALIZER) {
              Block block = ((Initializer)field).block;
              if (block != null) blockScope = block.scope;
            }
            this.patternLocator.matchReportReference(node, enclosingElement, field.binding, blockScope, level.intValue(), this);
          }
        }
      }
    }
  }
}
/**
* Visit the given type declaration and report the nodes that match exactly the
* search pattern (ie. the ones in the matching nodes set)
*/
protected void reportMatching(TypeDeclaration type, IJavaElement parent, int accuracy, MatchingNodeSet nodeSet, int occurrenceCount) throws CoreException {
  // create type handle
  IJavaElement enclosingElement = parent;
  if (enclosingElement == null) {
    enclosingElement = createTypeHandle(new String(type.name));
  } else if (enclosingElement instanceof IType) {
    enclosingElement = ((IType) parent).getType(new String(type.name));
  } else if (enclosingElement instanceof IMember) {
      IMember member = (IMember) parent;
    if (member.isBinary())  {
      enclosingElement = ((IClassFile)this.currentPossibleMatch.openable).getType();
    } else {
      enclosingElement = member.getType(new String(type.name), occurrenceCount);
    }
  }
  if (enclosingElement == null) return;
  boolean enclosesElement = encloses(enclosingElement);

  // report the type declaration
  if (accuracy > -1 && enclosesElement) {
    int offset = type.sourceStart;
    SearchMatch match = this.patternLocator.newDeclarationMatch(type, enclosingElement, type.binding, accuracy, type.sourceEnd-offset+1, this);
    report(match);
  }

  boolean matchedClassContainer = (this.matchContainer & PatternLocator.CLASS_CONTAINER) != 0;

  // report the type parameters
  if (type.typeParameters != null) {
    reportMatching(type.typeParameters, enclosingElement, parent, type.binding, nodeSet);
  }

  // report annotations
  if (type.annotations != null) {
    reportMatching(type.annotations, enclosingElement, type.binding, nodeSet, matchedClassContainer, enclosesElement);
  }

  // report references in javadoc
  if (type.javadoc != null) {
    ASTNode[] nodes = nodeSet.matchingNodes(type.declarationSourceStart, type.sourceStart);
    if (nodes != null) {
      if (!matchedClassContainer) {
        for (int i = 0, l = nodes.length; i < l; i++)
          nodeSet.matchingNodes.removeKey(nodes[i]);
      } else {
        for (int i = 0, l = nodes.length; i < l; i++) {
          ASTNode node = nodes[i];
          Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
          if (enclosesElement) {
            this.patternLocator.matchReportReference(node, enclosingElement, type.binding, level.intValue(), this);
          }
        }
      }
    }
  }
 
  // super types
  if ((type.bits & ASTNode.IsAnonymousType) != 0) {
    TypeReference superType =type.allocation.type;
    if (superType != null) {
      Integer level = (Integer) nodeSet.matchingNodes.removeKey(superType);
      if (level != null && matchedClassContainer)
        this.patternLocator.matchReportReference(superType, enclosingElement, type.binding, level.intValue(), this);
    }
  } else {
    TypeReference superClass = type.superclass;
    if (superClass != null) {
      reportMatchingSuper(superClass, enclosingElement, type.binding, nodeSet, matchedClassContainer);
    }
    TypeReference[] superInterfaces = type.superInterfaces;
    if (superInterfaces != null) {
      for (int i = 0, l = superInterfaces.length; i < l; i++) {
        reportMatchingSuper(superInterfaces[i], enclosingElement, type.binding, nodeSet, matchedClassContainer);
      }
    }
  }

  // filter out element not in hierarchy scope
  boolean typeInHierarchy = type.binding == null || typeInHierarchy(type.binding);
  matchedClassContainer = matchedClassContainer && typeInHierarchy;

  // Visit fields
  FieldDeclaration[] fields = type.fields;
  if (fields != null) {
    if (nodeSet.matchingNodes.elementSize == 0) return// end as all matching nodes were reported
    FieldDeclaration[] otherFields = null;
    int first = -1;
    int length = fields.length;
    for (int i = 0; i < length; i++) {
      FieldDeclaration field = fields[i];
      boolean last = field.endPart2Position == 0 || field.declarationEnd == field.endPart2Position;
      // Store first index of multiple field declaration
      if (!last) {
        if (first == -1) {
          first = i;
        }
      }
      if (first >= 0) {
        // Store all multiple fields but first one for other elements
        if (i > first) {
          if (otherFields == null) {
            otherFields = new FieldDeclaration[length-i];
          }
          otherFields[i-1-first] = field;
        }
        // On last field, report match with all other elements
        if (last) {
          for (int j=first; j<=i; j++) {
            Integer level = (Integer) nodeSet.matchingNodes.removeKey(fields[j]);
            int value = (level != null && matchedClassContainer) ? level.intValue() : -1;
            reportMatching(fields[j], otherFields, type, enclosingElement, value, typeInHierarchy, nodeSet);
          }
          first = -1;
          otherFields = null;
        }
      } else {
        // Single field, report normally
        Integer level = (Integer) nodeSet.matchingNodes.removeKey(field);
        int value = (level != null && matchedClassContainer) ? level.intValue() : -1;
        reportMatching(field, null, type, enclosingElement, value, typeInHierarchy, nodeSet);
      }
    }
  }

  // Visit methods
  AbstractMethodDeclaration[] methods = type.methods;
  if (methods != null) {
    if (nodeSet.matchingNodes.elementSize == 0) return// end as all matching nodes were reported
    for (int i = 0, l = methods.length; i < l; i++) {
      AbstractMethodDeclaration method = methods[i];
      Integer level = (Integer) nodeSet.matchingNodes.removeKey(method);
      int value = (level != null && matchedClassContainer) ? level.intValue() : -1;
      reportMatching(type, method, enclosingElement, value, typeInHierarchy, nodeSet);
    }
  }

  // Visit types
  TypeDeclaration[] memberTypes = type.memberTypes;
  if (memberTypes != null) {
    for (int i = 0, l = memberTypes.length; i < l; i++) {
      if (nodeSet.matchingNodes.elementSize == 0) return// end as all matching nodes were reported
      TypeDeclaration memberType = memberTypes[i];
      Integer level = (Integer) nodeSet.matchingNodes.removeKey(memberType);
      int value = (level != null && matchedClassContainer) ? level.intValue() : -1;
      reportMatching(memberType, enclosingElement, value, nodeSet, 1);
    }
  }
}
/**
* Report matches in type parameters.
*/
protected void reportMatching(TypeParameter[] typeParameters, IJavaElement enclosingElement, IJavaElement parent, Binding binding, MatchingNodeSet nodeSet) throws CoreException {
  if (typeParameters == null) return;
  for (int i=0, l=typeParameters.length; i<l; i++) {
    TypeParameter typeParameter = typeParameters[i];
    if (typeParameter != null) {
      Integer level = (Integer) nodeSet.matchingNodes.removeKey(typeParameter);
      if (level != null) {
        if (level.intValue() > -1 && encloses(enclosingElement)) {
          int offset = typeParameter.sourceStart;
          SearchMatch match = this.patternLocator.newDeclarationMatch(typeParameter, enclosingElement, binding, level.intValue(), typeParameter.sourceEnd-offset+1, this);
          report(match);
        }
      }
      if (typeParameter.type != null) {
        level = (Integer) nodeSet.matchingNodes.removeKey(typeParameter.type);
        if (level != null) {
          IJavaElement localElement = createHandle(typeParameter, enclosingElement);
          this.patternLocator.matchReportReference(typeParameter.type, enclosingElement, localElement, null, binding, level.intValue(), this);
        }
      }
      if (typeParameter.bounds != null) {
        for (int j=0, b=typeParameter.bounds.length; j<b; j++) {
          level = (Integer) nodeSet.matchingNodes.removeKey(typeParameter.bounds[j]);
          if (level != null) {
            IJavaElement localElement = createHandle(typeParameter, enclosingElement);
            this.patternLocator.matchReportReference(typeParameter.bounds[j], enclosingElement, localElement, null, binding, level.intValue(), this);
          }
        }
      }
    }
  }
}
protected void reportMatchingSuper(TypeReference superReference, IJavaElement enclosingElement, Binding elementBinding, MatchingNodeSet nodeSet, boolean matchedClassContainer) throws CoreException {
  ASTNode[] nodes = null;
  if (superReference instanceof ParameterizedSingleTypeReference || superReference instanceof ParameterizedQualifiedTypeReference) {
    long lastTypeArgumentInfo = findLastTypeArgumentInfo(superReference);
    nodes = nodeSet.matchingNodes(superReference.sourceStart, (int)lastTypeArgumentInfo);
  }
  if (nodes != null) {
    if ((this.matchContainer & PatternLocator.CLASS_CONTAINER) == 0) {
      for (int i = 0, l = nodes.length; i < l; i++)
        nodeSet.matchingNodes.removeKey(nodes[i]);
    } else {
      if (encloses(enclosingElement))
        for (int i = 0, l = nodes.length; i < l; i++) {
          ASTNode node = nodes[i];
          Integer level = (Integer) nodeSet.matchingNodes.removeKey(node);
          this.patternLocator.matchReportReference(node, enclosingElement, elementBinding, level.intValue(), this);
        }
    }
  } else {
    Integer level = (Integer) nodeSet.matchingNodes.removeKey(superReference);
    if (level != null && matchedClassContainer)
      this.patternLocator.matchReportReference(superReference, enclosingElement, elementBinding, level.intValue(), this);
  }
}
protected boolean typeInHierarchy(ReferenceBinding binding) {
  if (this.hierarchyResolver == null) return true; // not a hierarchy scope
  if (this.hierarchyResolver.subOrSuperOfFocus(binding)) return true;

  if (this.allSuperTypeNames != null) {
    char[][] compoundName = binding.compoundName;
    for (int i = 0, length = this.allSuperTypeNames.length; i < length; i++)
      if (CharOperation.equals(compoundName, this.allSuperTypeNames[i]))
        return true;
  }
  return false;
}
}
TOP

Related Classes of org.aspectj.org.eclipse.jdt.internal.core.search.matching.MatchLocator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.