| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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 |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contribution for |
| * Bug 377883 - NPE on open Call Hierarchy |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.search.matching; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.zip.ZipFile; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IAnnotatable; |
| import org.eclipse.jdt.core.IAnnotation; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaModelStatusConstants; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IOpenable; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.compiler.*; |
| import org.eclipse.jdt.core.search.*; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; |
| import org.eclipse.jdt.internal.compiler.env.*; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.compiler.parser.*; |
| import org.eclipse.jdt.internal.compiler.problem.*; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfIntValues; |
| import org.eclipse.jdt.internal.compiler.util.Messages; |
| import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.compiler.util.SimpleSet; |
| import org.eclipse.jdt.internal.compiler.util.SuffixConstants; |
| import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver; |
| import org.eclipse.jdt.internal.core.BinaryMember; |
| import org.eclipse.jdt.internal.core.BinaryMethod; |
| import org.eclipse.jdt.internal.core.BinaryType; |
| import org.eclipse.jdt.internal.core.ClassFile; |
| import org.eclipse.jdt.internal.core.CompilationUnit; |
| import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; |
| import org.eclipse.jdt.internal.core.JavaElement; |
| import org.eclipse.jdt.internal.core.JavaModelManager; |
| import org.eclipse.jdt.internal.core.JavaProject; |
| import org.eclipse.jdt.internal.core.LambdaFactory; |
| import org.eclipse.jdt.internal.core.LocalVariable; |
| import org.eclipse.jdt.internal.core.NameLookup; |
| import org.eclipse.jdt.internal.core.Openable; |
| import org.eclipse.jdt.internal.core.PackageFragment; |
| import org.eclipse.jdt.internal.core.PackageFragmentRoot; |
| import org.eclipse.jdt.internal.core.SearchableEnvironment; |
| import org.eclipse.jdt.internal.core.SourceMapper; |
| import org.eclipse.jdt.internal.core.SourceMethod; |
| import org.eclipse.jdt.internal.core.SourceType; |
| import org.eclipse.jdt.internal.core.SourceTypeElementInfo; |
| import org.eclipse.jdt.internal.core.index.Index; |
| import org.eclipse.jdt.internal.core.search.*; |
| import org.eclipse.jdt.internal.core.util.ASTNodeFinder; |
| import org.eclipse.jdt.internal.core.util.HandleFactory; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| 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.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; |
| |
| HashtableOfIntValues inTypeOccurrencesCounts = new HashtableOfIntValues(); |
| // Cache for method handles |
| HashSet methodHandles; |
| private TypeBinding unitScopeTypeBinding = null; // cached |
| |
| private final boolean searchPackageDeclaration; |
| private int sourceStartOfMethodToRetain; |
| private int sourceEndOfMethodToRetain; |
| |
| public static class WorkingCopyDocument extends JavaSearchDocument { |
| public org.eclipse.jdt.core.ICompilationUnit workingCopy; |
| WorkingCopyDocument(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(SearchPattern pattern, SearchDocument[] indexMatches, org.eclipse.jdt.core.ICompilationUnit[] copies, SearchParticipant participant) { |
| if (copies == null) return indexMatches; |
| // working copies take precedence over corresponding compilation units |
| HashMap workingCopyDocuments = workingCopiesThatCanSeeFocus(copies, pattern, participant); |
| if (workingCopyDocuments.size() == 0) return indexMatches; |
| 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(SearchPattern pattern, IJavaElement focus) { |
| pattern.focus = focus; |
| } |
| |
| /* |
| * Returns the working copies that can see the given focus. |
| */ |
| private static HashMap workingCopiesThatCanSeeFocus(org.eclipse.jdt.core.ICompilationUnit[] copies, SearchPattern pattern, SearchParticipant participant) { |
| if (copies == null) return new HashMap(); |
| HashMap result = new HashMap(); |
| for (int i=0, length = copies.length; i<length; i++) { |
| org.eclipse.jdt.core.ICompilationUnit workingCopy = copies[i]; |
| IPath projectOrJar = MatchLocator.getProjectOrJar(workingCopy).getPath(); |
| if (pattern.focus == null || IndexSelector.canSeeFocus(pattern, projectOrJar) != IndexSelector.PROJECT_CAN_NOT_SEE_FOCUS) { |
| result.put( |
| workingCopy.getPath().toString(), |
| new WorkingCopyDocument(workingCopy, participant) |
| ); |
| } |
| } |
| return result; |
| } |
| |
| public static IBinaryType classFileReader(IType type) { |
| IClassFile classFile = type.getClassFile(); |
| JavaModelManager manager = JavaModelManager.getJavaModelManager(); |
| if (classFile.isOpen()) |
| return (IBinaryType)manager.getInfo(type); |
| |
| PackageFragment pkg = (PackageFragment) type.getPackageFragment(); |
| IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent(); |
| try { |
| if (!root.isArchive()) |
| return Util.newClassFileReader(((JavaElement) type).resource()); |
| |
| String rootPath = root.getPath().toOSString(); |
| if (org.eclipse.jdt.internal.compiler.util.Util.isJrt(rootPath)) { |
| String classFileName = classFile.getElementName(); |
| String path = Util.concatWith(pkg.names, classFileName, '/'); |
| return ClassFileReader.readFromJrt(new File(rootPath), path, null); |
| } else { |
| 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 | CoreException | IOException e) { |
| // invalid class file: return null |
| } |
| return null; |
| } |
| |
| /** |
| * Query a given index for matching entries. Assumes the sender has opened the index and will close when finished. |
| */ |
| public static void findIndexMatches(SearchPattern 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(SearchPattern 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 == null ? 0 : this.patternLocator.matchContainer(); |
| this.requestor = requestor; |
| this.scope = scope; |
| this.progressMonitor = progressMonitor; |
| if (pattern instanceof PackageDeclarationPattern) { |
| this.searchPackageDeclaration = true; |
| } else if (pattern instanceof OrPattern) { |
| this.searchPackageDeclaration = ((OrPattern)pattern).hasPackageDeclaration(); |
| } else { |
| this.searchPackageDeclaration = false; |
| } |
| if (pattern instanceof MethodPattern) { |
| IType type = ((MethodPattern) pattern).declaringType; |
| if (type != null && !type.isBinary()) { |
| SourceType sourceType = (SourceType) type; |
| IMember local = sourceType.getOuterMostLocalContext(); |
| if (local instanceof IMethod) { // remember this method's range so we don't purge its statements. |
| try { |
| ISourceRange range = local.getSourceRange(); |
| this.sourceStartOfMethodToRetain = range.getOffset(); |
| this.sourceEndOfMethodToRetain = this.sourceStartOfMethodToRetain + range.getLength() - 1; // offset is 0 based. |
| } catch (JavaModelException e) { |
| // drop silently. |
| } |
| } |
| } |
| } |
| } |
| /** |
| * 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.resource()); |
| } 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 lambda declaration and type. |
| */ |
| protected IJavaElement createHandle(LambdaExpression lambdaExpression, IJavaElement parent) { |
| org.eclipse.jdt.internal.core.LambdaExpression lambdaElement = LambdaFactory.createLambdaExpression((JavaElement) parent, lambdaExpression); |
| IMethod lambdaMethodElement = lambdaElement.getMethod(); |
| this.methodHandles.add(lambdaMethodElement); |
| return lambdaMethodElement; |
| } |
| /** |
| * 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 |
| IBinaryType reader = classFileReader(type); |
| if (reader != 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 |
| IMethod binaryMethod = createBinaryMethodHandle(type, method.selector, argumentTypeNames); |
| if (binaryMethod == null) { |
| // when first attempt fails, try with similar matches if any... |
| PossibleMatch similarMatch = this.currentPossibleMatch.getSimilarMatch(); |
| while (similarMatch != null) { |
| type = ((ClassFile)similarMatch.openable).getType(); |
| binaryMethod = createBinaryMethodHandle(type, method.selector, argumentTypeNames); |
| if (binaryMethod != null) { |
| return binaryMethod; |
| } |
| similarMatch = similarMatch.getSimilarMatch(); |
| } |
| } |
| return binaryMethod; |
| } |
| if (BasicSearchEngine.VERBOSE) { |
| System.out.println("Not able to createHandle for the method " + //$NON-NLS-1$ |
| CharOperation.charToString(method.selector) + " May miss some results"); //$NON-NLS-1$ |
| } |
| 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) { |
| IBinaryType 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 occurrences 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 occurrence count of the given initializer in its type declaration |
| int occurrenceCount = 0; |
| FieldDeclaration[] fields = typeDeclaration.fields; |
| int length = fields == null ? 0 : fields.length; |
| for (int i = 0; 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 declaration (may be a local variable or type parameter). |
| */ |
| protected IJavaElement createHandle(AbstractVariableDeclaration variableDeclaration, IJavaElement parent) { |
| boolean isParameter = true; |
| switch (variableDeclaration.getKind()) { |
| case AbstractVariableDeclaration.LOCAL_VARIABLE: |
| isParameter = false; |
| //$FALL-THROUGH$ |
| case AbstractVariableDeclaration.PARAMETER: |
| if (variableDeclaration.type.resolvedType != null) { |
| return new LocalVariable((JavaElement)parent, |
| new String(variableDeclaration.name), |
| variableDeclaration.declarationSourceStart, |
| variableDeclaration.declarationSourceEnd, |
| variableDeclaration.sourceStart, |
| variableDeclaration.sourceEnd, |
| new String(variableDeclaration.type.resolvedType.signature()), |
| variableDeclaration.annotations, |
| variableDeclaration.modifiers, |
| isParameter, |
| variableDeclaration.type.getAnnotationsOnDimensions() |
| ); |
| } |
| break; |
| case AbstractVariableDeclaration.TYPE_PARAMETER: |
| return new org.eclipse.jdt.internal.core.TypeParameter((JavaElement)parent, new String(variableDeclaration.name)); |
| } |
| return null; |
| } |
| /** |
| * Create an handle for a local variable declaration (may be a local variable or type parameter). |
| */ |
| protected IJavaElement createHandle(Annotation annotation, IAnnotatable parent) { |
| if (parent == null) return null; |
| TypeReference typeRef = annotation.type; |
| char[][] typeName = typeRef.getTypeName(); |
| String name = new String(typeName[typeName.length-1]); |
| try { |
| IAnnotation[] annotations = parent.getAnnotations(); |
| int length = annotations == null ? 0 : annotations.length; |
| for (int i=0; i<length; i++) { |
| if (annotations[i].getElementName().equals(name)) { |
| return annotations[i]; |
| } |
| } |
| if (parent instanceof LocalVariable) { |
| LocalVariable localVariable = (LocalVariable) parent; |
| IAnnotation[][] annotationsOnDimensions = localVariable.annotationsOnDimensions; |
| int noOfDimensions = annotationsOnDimensions == null ? 0 : annotationsOnDimensions.length; |
| for (int i = 0; i < noOfDimensions; ++i) { |
| IAnnotation[] dimAnnotations = annotationsOnDimensions[i]; |
| int noOfAnnotations = dimAnnotations.length; |
| for (int j = 0; j < noOfAnnotations; ++j) { |
| if (dimAnnotations[j].getElementName().equals(name)) |
| return dimAnnotations[j]; |
| } |
| } |
| } |
| } |
| catch (JavaModelException jme) { |
| // skip |
| } |
| return null; |
| } |
| /* |
| * Create handles for a list of fields |
| */ |
| private IJavaElement[] createHandles(FieldDeclaration[] fields, TypeDeclaration type, IJavaElement parent) { |
| IJavaElement[] otherElements = null; |
| if (fields != null) { |
| int length = fields.length; |
| int size = 0; |
| while (size<length && fields[size] != null) { |
| size++; |
| } |
| otherElements = new IJavaElement[size]; |
| for (int j=0; j<size; j++) { |
| otherElements[j] = createHandle(fields[j], type, parent); |
| } |
| } |
| return otherElements; |
| } |
| /* |
| * 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) { |
| if (element != null) { |
| if (this.scope instanceof HierarchyScope) |
| return ((HierarchyScope)this.scope).encloses(element, this.progressMonitor); |
| else |
| return this.scope.encloses(element); |
| } |
| return false; |
| } |
| private boolean filterEnum(SearchMatch match) { |
| |
| // filter org.apache.commons.lang.enum package for projects above 1.5 |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=317264 |
| IJavaElement element = (IJavaElement)match.getElement(); |
| PackageFragment pkg = (PackageFragment)element.getAncestor(IJavaElement.PACKAGE_FRAGMENT); |
| if (pkg != null) { |
| // enum was found in org.apache.commons.lang.enum at index 5 |
| if (pkg.names.length == 5 && pkg.names[4].equals("enum")) { //$NON-NLS-1$ |
| if (this.options == null) { |
| IJavaProject proj = (IJavaProject)pkg.getAncestor(IJavaElement.JAVA_PROJECT); |
| String complianceStr = proj.getOption(CompilerOptions.OPTION_Source, true); |
| if (CompilerOptions.versionToJdkLevel(complianceStr) >= ClassFileConstants.JDK1_5) |
| return true; |
| } else if (this.options.sourceLevel >= ClassFileConstants.JDK1_5) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /* (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.unitScopeTypeBinding = typeBinding; //cache. |
| if (typeBinding == null || !typeBinding.isValidBinding()) { |
| typeBinding = this.lookupEnvironment.getType(compoundName, this.unitScope.module()); |
| } |
| this.bindings.put(typeKey, typeBinding); |
| return typeBinding != null && typeBinding.isValidBinding() ? typeBinding : null; |
| } |
| public MethodBinding getMethodBinding(MethodPattern methodPattern) { |
| this.unitScopeTypeBinding = null; |
| MethodBinding methodBinding = getMethodBinding0(methodPattern); |
| if (methodBinding != null) |
| return methodBinding; // known to be valid. |
| // special handling for methods of anonymous/local types. Since these cannot be looked up in the environment the usual way ... |
| if (methodPattern.focus instanceof SourceMethod) { |
| char[] typeName = PatternLocator.qualifiedPattern(methodPattern.declaringSimpleName, methodPattern.declaringQualification); |
| if (typeName != null) { |
| IType type = methodPattern.declaringType; |
| IType enclosingType = type.getDeclaringType(); |
| while (enclosingType != null) { |
| type = enclosingType; |
| enclosingType = type.getDeclaringType(); |
| } |
| typeName = type.getFullyQualifiedName().toCharArray(); |
| TypeBinding declaringTypeBinding = getType(typeName, typeName); |
| if (declaringTypeBinding instanceof SourceTypeBinding) { |
| SourceTypeBinding sourceTypeBinding = ((SourceTypeBinding) declaringTypeBinding); |
| ClassScope skope = sourceTypeBinding.scope; |
| if (skope != null) { |
| CompilationUnitDeclaration unit = skope.referenceCompilationUnit(); |
| if (unit != null) { |
| AbstractMethodDeclaration amd = new ASTNodeFinder(unit).findMethod((IMethod) methodPattern.focus); |
| if (amd != null && amd.binding != null && amd.binding.isValidBinding()) { |
| this.bindings.put(methodPattern, amd.binding); |
| return amd.binding; |
| } |
| } |
| } |
| } |
| } |
| } else if (methodPattern.focus instanceof BinaryMethod && |
| methodPattern.declaringType instanceof BinaryType && |
| this.unitScopeTypeBinding instanceof ProblemReferenceBinding) {//Get binding from unit scope for non-visible member of binary type |
| char[] typeName = PatternLocator.qualifiedPattern(methodPattern.declaringSimpleName, methodPattern.declaringQualification); |
| if (typeName != null) { |
| IType type = methodPattern.declaringType; |
| IType enclosingType = type.getDeclaringType(); |
| while (enclosingType != null) { |
| type = enclosingType; |
| enclosingType = type.getDeclaringType(); |
| } |
| typeName = type.getFullyQualifiedName().toCharArray(); |
| TypeBinding typeBinding = this.unitScopeTypeBinding; |
| if (typeBinding instanceof ProblemReferenceBinding) { |
| ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) this.unitScopeTypeBinding; |
| ReferenceBinding closestMatch = (problemReferenceBinding.problemId() == ProblemReasons.NotVisible) ? |
| problemReferenceBinding.closestReferenceMatch() : null; |
| return closestMatch != null ? getMethodBinding(methodPattern, closestMatch) : null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private List<String> getInverseFullName(char[] qualifier, char[] simpleName) { |
| List <String> result = new ArrayList<String>(); |
| if (qualifier != null && qualifier.length > 0) { |
| result.addAll(Arrays.asList(new String(qualifier).split("\\.")));//$NON-NLS-1$ |
| Collections.reverse(result); |
| } |
| if (simpleName != null) result.add(0, new String(simpleName)); |
| return result; |
| } |
| |
| /** returns the row index which has the highest column entry. |
| * TODO: rewrite this code with list when (if) we move to 1.8 [with FP constructs]. |
| */ |
| private int getMaxResult(int[][] resultsMap) { |
| int rows = resultsMap.length; |
| int cols = resultsMap[0].length; |
| List <Integer> candidates = new ArrayList<Integer>(); |
| candidates.add(0); //default row |
| |
| for (int j = 0; j < cols; ++j) { |
| int current = resultsMap[0][j]; |
| for (int i = 1; i < rows; ++i) { |
| int tmp = resultsMap[i][j]; |
| if (tmp < current) continue; |
| if (tmp > current) { |
| current = tmp; |
| candidates.clear(); |
| } |
| candidates.add(i);// there is atleast one element always. |
| } |
| if (candidates.size() <= 1) break; // found |
| } |
| return candidates.get(0); |
| } |
| |
| /** apply the function to map the parameter full name to an index |
| */ |
| private int mapParameter(List <String> patternParameterFullName, List <String> methodParameterFullName) { |
| int patternLen = patternParameterFullName.size(); |
| int methodLen = methodParameterFullName.size(); |
| int size = patternLen < methodLen ? patternLen : methodLen; |
| int result = -1; |
| for (int i = 0; i < size; i++) { |
| if (!patternParameterFullName.get(i).equals(methodParameterFullName.get(i))) break; |
| ++result; |
| } |
| return patternLen == methodLen && result + 1 == patternLen ? Integer.MAX_VALUE : result; |
| } |
| /** |
| * returns an array of integers whose elements are matching indices. |
| * As a special case, full match would have max value as the index. |
| */ |
| private int[] getResultMap(Map<Integer, List<String>> patternMap, Map<Integer, List<String>> methodMap) { |
| int paramLength = methodMap.size(); |
| int[] result = new int[paramLength]; |
| for (int p = 0; p < paramLength; p++) { |
| result[p] = mapParameter(patternMap.get(p), methodMap.get(p)); |
| } |
| return result; |
| } |
| |
| private Map<Integer, List<String>> getSplitNames(char[][] qualifiedNames, char[][] simpleNames) { |
| int paramLength = simpleNames.length; |
| Map <Integer, List<String>> result = new HashMap<Integer, List<String>>(); |
| for (int p = 0; p < paramLength; p++) result.put(p, getInverseFullName(qualifiedNames[p], simpleNames[p])); |
| return result; |
| } |
| |
| private Map<Integer, List<String>> getSplitNames(MethodBinding method) { |
| TypeBinding[] methodParameters = method.parameters; |
| int paramLength = methodParameters == null ? 0 : methodParameters.length; |
| Map <Integer, List<String>> result = new HashMap<Integer, List<String>>(); |
| for (int p = 0; p < paramLength; p++) result.put(p, getInverseFullName(methodParameters[p].qualifiedSourceName(), null)); // source is part of qualifiedSourceName here); |
| return result; |
| } |
| |
| /** |
| * Selects the most applicable method (though similar but not to be confused with its namesake in jls) |
| * All this machinery for that elusive uncommon case referred in bug 431357. |
| */ |
| private MethodBinding getMostApplicableMethod(List<MethodBinding> possibleMethods, MethodPattern methodPattern) { |
| int size = possibleMethods.size(); |
| MethodBinding result = size != 0 ? possibleMethods.get(0) : null; |
| if (size > 1) { |
| // can cache but may not be worth since this is not a common case |
| Map<Integer, List<String>> methodPatternReverseNames = getSplitNames(methodPattern.parameterQualifications, methodPattern.parameterSimpleNames); |
| int len = possibleMethods.size(); |
| int[][] resultMaps = new int[len][]; |
| for (int i = 0; i < len; ++i) resultMaps[i] = getResultMap(methodPatternReverseNames, getSplitNames(possibleMethods.get(i))); |
| result = possibleMethods.get(getMaxResult(resultMaps)); |
| } |
| return result; |
| } |
| |
| private MethodBinding getMethodBinding0(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; |
| } |
| // 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); |
| MethodBinding result = null; |
| if (declaringTypeBinding != null) { |
| if (declaringTypeBinding.isArrayType()) { |
| declaringTypeBinding = declaringTypeBinding.leafComponentType(); |
| } |
| if (!declaringTypeBinding.isBaseType()) { |
| result = getMethodBinding(methodPattern, declaringTypeBinding); |
| } |
| } |
| this.bindings.put(methodPattern, result != null ? result : new ProblemMethodBinding(methodPattern.selector, null, ProblemReasons.NotFound)); |
| return result; |
| } |
| |
| private MethodBinding getMethodBinding(MethodPattern methodPattern, TypeBinding declaringTypeBinding) { |
| MethodBinding result; |
| 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; |
| List <MethodBinding> possibleMethods = new ArrayList<MethodBinding>(methodsLength); |
| 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) { |
| possibleMethods.add(methods[i]); |
| } |
| } |
| result = getMostApplicableMethod(possibleMethods, methodPattern); |
| return result; |
| } |
| 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(); |
| this.unitScope = null; // don't leak a reference to the cleaned-up name environment |
| } |
| |
| SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(this.workingCopies); |
| |
| List<IJavaProject> projects = new ArrayList<>(); |
| projects.add(project); |
| if (this.pattern.focus != null) { |
| IJavaProject focusProject = this.pattern.focus.getJavaProject(); |
| if (focusProject != project) { |
| projects.add(focusProject); |
| } |
| } |
| this.nameEnvironment = IndexBasedJavaSearchEnvironment.create(projects, this.workingCopies); |
| |
| // create lookup environment |
| Map map = project.getOptions(true); |
| map.put(CompilerOptions.OPTION_TaskTags, org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING); |
| 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.lookupEnvironment.mayTolerateMissingType = true; |
| this.parser = MatchLocatorParser.createParser(problemReporter, this); |
| this.bindings = new SimpleLookupTable(); // For every LE |
| |
| // 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]; |
| |
| this.lookupEnvironment.addResolutionListener(this.patternLocator); |
| } |
| 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 = 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.hasSimilarMatch()) { |
| // If there is similar match, then also process it |
| // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=211872 |
| possibleMatches[i] = possibleMatch.getSimilarMatch(); |
| i--; |
| } |
| 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 inaccurate 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 inaccurate 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 { |
| if (this.patternLocator == null) return; |
| int docsLength = searchDocuments.length; |
| int progressLength = docsLength; |
| 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$ |
| } |
| IJavaProject[] javaModelProjects = null; |
| if (this.searchPackageDeclaration) { |
| javaModelProjects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects(); |
| progressLength += javaModelProjects.length; |
| } |
| |
| // init infos for progress increasing |
| int n = progressLength<1000 ? Math.min(Math.max(progressLength/200+1, 2),4) : 5 *(progressLength/1000); |
| this.progressStep = progressLength < n ? 1 : progressLength / 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.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(this); |
| |
| // 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 (i.e. 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; |
| SearchParticipant searchParticipant = 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]; |
| if (searchParticipant == null) { |
| searchParticipant = searchDocument.getParticipant(); |
| } |
| 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.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 or external folder |
| 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,this.pattern.mustResolve)); |
| } |
| |
| // last project |
| if (previousJavaProject != null) { |
| try { |
| locateMatches(previousJavaProject, matchSet, docsLength-displayed); |
| } catch (JavaModelException e) { |
| // problem with classpath in last project -> ignore |
| } |
| } |
| |
| if (this.searchPackageDeclaration) { |
| locatePackageDeclarations(searchParticipant, javaModelProjects); |
| } |
| |
| } finally { |
| if (this.progressMonitor != null) |
| this.progressMonitor.done(); |
| if (this.nameEnvironment != null) |
| this.nameEnvironment.cleanup(); |
| this.unitScope = null; |
| manager.flushZipFiles(this); |
| this.bindings = null; |
| } |
| } |
| /** |
| * Locates the package declarations corresponding to this locator's pattern. |
| */ |
| protected void locatePackageDeclarations(SearchParticipant participant, IJavaProject[] projects) throws CoreException { |
| locatePackageDeclarations(this.pattern, participant, projects); |
| } |
| /** |
| * Locates the package declarations corresponding to the search pattern. |
| */ |
| protected void locatePackageDeclarations(SearchPattern searchPattern, SearchParticipant participant, IJavaProject[] projects) throws CoreException { |
| if (this.progressMonitor != null && this.progressMonitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| if (searchPattern instanceof OrPattern) { |
| SearchPattern[] patterns = ((OrPattern) searchPattern).patterns; |
| for (int i = 0, length = patterns.length; i < length; i++) { |
| locatePackageDeclarations(patterns[i], participant, projects); |
| } |
| } else if (searchPattern instanceof PackageDeclarationPattern) { |
| IJavaElement focus = 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; |
| SimpleSet packages = new SimpleSet(); |
| for (int i = 0, length = projects.length; i < length; i++) { |
| IJavaProject javaProject = projects[i]; |
| if (this.progressMonitor != null) { |
| if (this.progressMonitor.isCanceled()) throw new OperationCanceledException(); |
| this.progressWorked++; |
| if ((this.progressWorked%this.progressStep)==0) this.progressMonitor.worked(this.progressStep); |
| } |
| // 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 || !typeBinding.isValidBinding()) 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, false, 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 FieldReferenceMatch newFieldReferenceMatch( |
| IJavaElement enclosingElement, |
| IJavaElement localElement, |
| Binding enclosingBinding, |
| int accuracy, |
| int offset, |
| int length, ASTNode reference) { |
| int bits = reference.bits; |
| boolean isCompoundAssigned = (bits & ASTNode.IsCompoundAssigned) != 0; |
| boolean isReadAccess = isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0; |
| boolean isWriteAccess = isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0; |
| if (isWriteAccess) { |
| if (reference instanceof QualifiedNameReference) { |
| char[][] tokens = ((QualifiedNameReference)reference).tokens; |
| char[] lastToken = tokens[tokens.length-1]; |
| if (this.pattern instanceof OrPattern) { |
| SearchPattern[] patterns = ((OrPattern) this.pattern).patterns; |
| for (int i = 0, pLength = patterns.length; i < pLength; i++) { |
| if (!this.patternLocator.matchesName(((VariablePattern)patterns[i]).name, lastToken)) { |
| isWriteAccess = false; |
| isReadAccess = true; |
| } |
| } |
| } else if (!this.patternLocator.matchesName(((VariablePattern)this.pattern).name, lastToken)) { |
| isWriteAccess = false; |
| isReadAccess = true; |
| } |
| } |
| } |
| boolean insideDocComment = (bits & ASTNode.InsideJavadoc) != 0; |
| SearchParticipant participant = getParticipant(); |
| IResource resource = this.currentPossibleMatch.resource; |
| if (enclosingBinding != null) { |
| enclosingElement = ((JavaElement) enclosingElement).resolved(enclosingBinding); |
| } |
| FieldReferenceMatch match = new FieldReferenceMatch(enclosingElement, accuracy, offset, length, isReadAccess, isWriteAccess, insideDocComment, participant, resource); |
| match.setLocalElement(localElement); |
| return match; |
| } |
| |
| public SearchMatch newLocalVariableReferenceMatch( |
| IJavaElement enclosingElement, |
| int accuracy, |
| int offset, |
| int length, |
| ASTNode reference) { |
| int bits = reference.bits; |
| boolean isCompoundAssigned = (bits & ASTNode.IsCompoundAssigned) != 0; |
| boolean isReadAccess = isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) == 0; |
| boolean isWriteAccess = isCompoundAssigned || (bits & ASTNode.IsStrictlyAssigned) != 0; |
| if (isWriteAccess) { |
| if (reference instanceof QualifiedNameReference) { |
| char[][] tokens = ((QualifiedNameReference)reference).tokens; |
| char[] lastToken = tokens[tokens.length-1]; |
| if (this.pattern instanceof OrPattern) { |
| SearchPattern[] patterns = ((OrPattern) this.pattern).patterns; |
| for (int i = 0, pLength = patterns.length; i < pLength; i++) { |
| if (!this.patternLocator.matchesName(((VariablePattern)patterns[i]).name, lastToken)) { |
| isWriteAccess = false; |
| isReadAccess = true; |
| } |
| } |
| } else if (!this.patternLocator.matchesName(((VariablePattern)this.pattern).name, lastToken)) { |
| isWriteAccess = false; |
| isReadAccess = true; |
| } |
| } |
| } |
| 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 MethodReferenceMatch 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 PackageReferenceMatch 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 = null; |
| try { |
| info = getBinaryInfo(classFile, classFile.resource()); |
| } |
| catch (CoreException ce) { |
| // Do nothing |
| } |
| if (info != null) { |
| boolean mayBeGeneric = this.patternLocator.mayBeGeneric; |
| this.patternLocator.mayBeGeneric = false; // there's no longer generic 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 = (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) { |
| if (BasicSearchEngine.VERBOSE) { |
| System.out.println("AbortCompilation while resolving unit " + String.valueOf(unit.getFileName())); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| // 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)) { |
| if (this.sourceStartOfMethodToRetain != method.declarationSourceStart || this.sourceEndOfMethodToRetain != method.declarationSourceEnd) { // approximate, but no big deal |
| method.statements = null; |
| method.javadoc = null; |
| } |
| } |
| } |
| } else { |
| for (int j = 0, length = methods.length; j < length; j++) { |
| AbstractMethodDeclaration method = methods[j]; |
| if (this.sourceStartOfMethodToRetain != method.declarationSourceStart || this.sourceEndOfMethodToRetain != method.declarationSourceEnd) { // approximate, but no big deal |
| method.statements = null; |
| method.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 { |
| if (match == null) { |
| if (BasicSearchEngine.VERBOSE) { |
| System.out.println("Cannot report a null match!!!"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| if (filterEnum(match)){ |
| if (BasicSearchEngine.VERBOSE) { |
| System.out.println("Filtered package with name enum"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| 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 ReferenceMatch) { |
| try { |
| ReferenceMatch refMatch = (ReferenceMatch) match; |
| JavaElement local = (JavaElement) refMatch.getLocalElement(); |
| if (local != null) { |
| System.out.println("\tLocal element: "+ local.toStringWithAncestors()); //$NON-NLS-1$ |
| } |
| if (match instanceof TypeReferenceMatch) { |
| IJavaElement[] others = ((TypeReferenceMatch) refMatch).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; |
| |
| int sourceStart = typeRef.sourceStart; |
| int sourceEnd = typeRef.sourceEnd; |
| |
| // Compute source positions of the qualified reference |
| if (name != null) { |
| 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 (i.e. char '>') of last one. |
| int start = match.getOffset(); |
| if (typeArguments != null && typeArguments.length > 0) { |
| boolean isErasureMatch= (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).isErasureMatch() : ((JavaSearchPattern)this.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 (i.e. char '>') of last one. |
| int end = typeRef.sourceEnd; |
| if (typeArguments != null) { |
| |
| boolean shouldMatchErasure= (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).isErasureMatch() : ((JavaSearchPattern)this.pattern).isErasureMatch(); |
| boolean hasSignatures = (this.pattern instanceof OrPattern) ? ((OrPattern)this.pattern).hasSignatures() : ((JavaSearchPattern)this.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 { |
| // Initialize scanner |
| Scanner scanner = this.parser.scanner; |
| char[] source = this.currentPossibleMatch.getContents(); |
| scanner.setSource(source); |
| |
| // 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); |
| } |
| |
| protected void reportMatching(LambdaExpression lambdaExpression, IJavaElement parent, int accuracy, MatchingNodeSet nodeSet, boolean typeInHierarchy) throws CoreException { |
| IJavaElement enclosingElement = null; |
| // Report the lambda declaration itself. |
| if (accuracy > -1) { |
| enclosingElement = createHandle(lambdaExpression, parent); |
| if (enclosingElement != null) { // skip if unable to find method |
| // compute source positions of the selector |
| int nameSourceStart = lambdaExpression.sourceStart; |
| if (encloses(enclosingElement)) { |
| SearchMatch match = null; |
| int length = lambdaExpression.arrowPosition() + 1 - nameSourceStart; |
| match = this.patternLocator.newDeclarationMatch(lambdaExpression, enclosingElement, null, accuracy, length, this); |
| if (match != null) { |
| report(match); |
| } |
| } |
| } |
| } |
| if (enclosingElement == null) { |
| enclosingElement = createHandle(lambdaExpression, parent); |
| } |
| // Traverse the lambda declaration to report matches inside, these matches if any should see the present lambda as the parent model element. |
| ASTNode[] nodes = typeInHierarchy ? nodeSet.matchingNodes(lambdaExpression.sourceStart, lambdaExpression.sourceEnd) : null; |
| boolean report = (this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0 && encloses(enclosingElement); |
| MemberDeclarationVisitor declarationVisitor = new MemberDeclarationVisitor(enclosingElement, report ? nodes : null, nodeSet, this, typeInHierarchy); |
| |
| if (lambdaExpression.arguments != null) { |
| int argumentsLength = lambdaExpression.arguments.length; |
| for (int i = 0; i < argumentsLength; i++) |
| lambdaExpression.arguments[i].traverse(declarationVisitor, (BlockScope) null); |
| } |
| |
| if (lambdaExpression.body != null) { |
| lambdaExpression.body.traverse(declarationVisitor, (BlockScope) null); |
| } |
| |
| // Report all nodes and remove them |
| if (nodes != null) { |
| int length = nodes.length; |
| for (int i = 0; i < length; i++) { |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(nodes[i]); |
| if (report && level != null) { |
| this.patternLocator.matchReportReference(nodes[i], enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), lambdaExpression.binding, level.intValue(), this); |
| } |
| } |
| } |
| } |
| /** |
| * Visit the given method declaration and report the nodes that match exactly the |
| * search pattern (i.e. the ones in the matching nodes set) |
| * Note that the method declaration has already been checked. |
| */ |
| protected void reportMatching(AbstractMethodDeclaration method, TypeDeclaration type, IJavaElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException { |
| IJavaElement enclosingElement = null; |
| |
| // report method declaration itself |
| if (accuracy > -1) { |
| enclosingElement = createHandle(method, parent); |
| if (enclosingElement != null) { // skip if unable to find method |
| // compute source positions of the selector |
| Scanner scanner = this.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); |
| } |
| if (enclosingElement != null) { |
| // Traverse method declaration to report matches both in local types declaration |
| // and in local variables declaration |
| ASTNode[] nodes = typeInHierarchy ? nodeSet.matchingNodes(method.declarationSourceStart, method.declarationSourceEnd) : null; |
| boolean report = (this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0 && encloses(enclosingElement); |
| MemberDeclarationVisitor declarationVisitor = new MemberDeclarationVisitor(enclosingElement, report ? nodes : null, nodeSet, this, typeInHierarchy); |
| try { |
| method.traverse(declarationVisitor, (ClassScope) null); |
| } catch (WrappedCoreException e) { |
| throw e.coreException; |
| } |
| // Report all nodes and remove them |
| if (nodes != null) { |
| int length = nodes.length; |
| for (int i = 0; i < length; i++) { |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(nodes[i]); |
| if (report && level != null) { |
| this.patternLocator.matchReportReference(nodes[i], enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), method.binding, level.intValue(), this); |
| } |
| } |
| } |
| } |
| } |
| |
| // report the type parameters |
| TypeParameter[] typeParameters = method.typeParameters(); |
| if (typeParameters != null) { |
| if (enclosingElement == null) { |
| enclosingElement = createHandle(method, parent); |
| } |
| if (enclosingElement != null) { |
| reportMatching(typeParameters, enclosingElement, parent, method.binding, nodeSet); |
| } |
| } |
| |
| // report annotations |
| if (method.annotations != null) { |
| if (enclosingElement == null) { |
| enclosingElement = createHandle(method, parent); |
| } |
| if (enclosingElement != null) { |
| reportMatching(method.annotations, enclosingElement, null, 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)) { |
| if (this.pattern.mustResolve) { |
| // Visit only if the pattern must resolve |
| MemberDeclarationVisitor declarationVisitor = new MemberDeclarationVisitor(enclosingElement, nodes, nodeSet, this, typeInHierarchy); |
| method.traverse(declarationVisitor, (ClassScope) null); |
| int length = nodes.length; |
| for (int i = 0; i < length; i++) { |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(nodes[i]); |
| if (level != null) { // ensure that the reference has not been already reported while visiting |
| this.patternLocator.matchReportReference(nodes[i], enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), method.binding, level.intValue(), this); |
| } |
| } |
| } else { |
| for (int i = 0, l = nodes.length; i < l; i++) { |
| ASTNode node = nodes[i]; |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(node); |
| if (level != null) { // ensure that the reference has not been already reported while visiting |
| this.patternLocator.matchReportReference(node, enclosingElement, null, null, method.binding, level.intValue(), this); |
| } |
| } |
| } |
| return; |
| } |
| } |
| // Remove all remaining nodes |
| for (int i = 0, l = nodes.length; i < l; i++) { |
| nodeSet.matchingNodes.removeKey(nodes[i]); |
| } |
| } |
| } |
| } |
| /** |
| * Report matching in annotations. |
| * @param otherElements TODO |
| */ |
| protected void reportMatching(Annotation[] annotations, IJavaElement enclosingElement, IJavaElement[] otherElements, Binding elementBinding, MatchingNodeSet nodeSet, boolean matchedContainer, boolean enclosesElement) throws CoreException { |
| if (annotations == null) |
| return; |
| for (int i=0, al=annotations.length; i<al; i++) { |
| Annotation annotationType = annotations[i]; |
| IJavaElement localAnnotation = null; |
| IJavaElement[] otherAnnotations = null; |
| int length = otherElements == null ? 0 : otherElements.length; |
| boolean handlesCreated = false; |
| |
| // Look for annotation type ref |
| TypeReference typeRef = annotationType.type; |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(typeRef); |
| if (level != null && enclosesElement && matchedContainer) { |
| localAnnotation = createHandle(annotationType, (IAnnotatable) enclosingElement); |
| if (length > 0) { |
| otherAnnotations = new IJavaElement[length]; |
| for (int o=0; o<length; o++) { |
| otherAnnotations[o] = createHandle(annotationType, (IAnnotatable) otherElements[o]); |
| } |
| } |
| handlesCreated = true; |
| this.patternLocator.matchReportReference(typeRef, enclosingElement, localAnnotation, otherAnnotations, 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; |
| if (!handlesCreated) { |
| localAnnotation = createHandle(annotationType, (IAnnotatable) enclosingElement); |
| if (length > 0) { |
| otherAnnotations = new IJavaElement[length]; |
| for (int o=0; o<length; o++) { |
| otherAnnotations[o] = createHandle(annotationType, (IAnnotatable) otherElements[o]); |
| } |
| } |
| handlesCreated = true; |
| } |
| this.patternLocator.matchReportReference(reference, enclosingElement, localAnnotation, otherAnnotations, 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) { |
| if (!handlesCreated) { |
| localAnnotation = createHandle(annotationType, (IAnnotatable) enclosingElement); |
| if (length > 0) { |
| otherAnnotations = new IJavaElement[length]; |
| for (int o=0; o<length; o++) { |
| otherAnnotations[o] = createHandle(annotationType, (IAnnotatable) otherElements[o]); |
| } |
| } |
| handlesCreated = true; |
| } |
| this.patternLocator.matchReportReference(node, enclosingElement, localAnnotation, otherAnnotations, elementBinding, level.intValue(), this); |
| } |
| } |
| } |
| } |
| } |
| } |
| private void reportMatching(Annotation[][] annotationsList, IJavaElement enclosingElement, Binding binding, |
| MatchingNodeSet nodeSet, boolean matchedClassContainer) throws CoreException { |
| if (annotationsList != null) { |
| for (int i = 0, length = annotationsList.length; i < length; ++i) { |
| Annotation[] annotations = annotationsList[i]; |
| if (annotations != null) |
| reportMatching(annotations, enclosingElement, null, binding, nodeSet, matchedClassContainer, encloses(enclosingElement)); |
| } |
| } |
| } |
| /** |
| * 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$ |
| System.out.println(" - fine grain flags="+ JavaSearchPattern.getFineGrainFlagString(this.patternLocator.fineGrain())); //$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 |
| ? this.unitScope.getImport(CharOperation.subarray(importRef.tokens, 0, importRef.tokens.length), true, importRef.isStatic()) |
| : this.unitScope.getImport(importRef.tokens, false, importRef.isStatic()); |
| this.patternLocator.matchLevelAndReportImportRef(importRef, binding, this); |
| } else { |
| 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, null, 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, 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; |
| this.inTypeOccurrencesCounts = new HashtableOfIntValues(); |
| 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 (i.e. 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); |
| } |
| // Traverse field declaration(s) to report matches both in local types declaration |
| // and in local variables declaration |
| int fieldEnd = field.endPart2Position == 0 ? field.declarationSourceEnd : field.endPart2Position; |
| ASTNode[] nodes = typeInHierarchy ? nodeSet.matchingNodes(field.sourceStart, fieldEnd) : null; |
| boolean report = (this.matchContainer & PatternLocator.FIELD_CONTAINER) != 0 && encloses(enclosingElement); |
| MemberDeclarationVisitor declarationVisitor = new MemberDeclarationVisitor(enclosingElement, report ? nodes : null, nodeSet, this, typeInHierarchy); |
| try { |
| field.traverse(declarationVisitor, (MethodScope) null); |
| } catch (WrappedCoreException e) { |
| throw e.coreException; |
| } |
| // Report all nodes and remove them |
| if (nodes != null) { |
| int length = nodes.length; |
| for (int i = 0; i < length; i++) { |
| ASTNode node = nodes[i]; |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(node); |
| if (report && level != null) { |
| 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; |
| } |
| } |
| this.patternLocator.matchReportReference(node, enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), field.binding, level.intValue(), this); |
| } |
| } |
| } |
| } |
| |
| // report annotations |
| IJavaElement[] otherElements = null; |
| if (field.annotations != null) { |
| if (enclosingElement == null) { |
| enclosingElement = createHandle(field, type, parent); |
| } |
| if (otherFields != null) { |
| otherElements = createHandles(otherFields, type, parent); |
| } |
| reportMatching(field.annotations, enclosingElement, otherElements, 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); |
| if (otherFields != null && otherElements == null) { |
| otherElements = createHandles(otherFields, 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)) { |
| MemberDeclarationVisitor declarationVisitor = new MemberDeclarationVisitor(enclosingElement, nodes, nodeSet, this, typeInHierarchy); |
| field.traverse(declarationVisitor, (MethodScope) null); |
| int length = nodes.length; |
| for (int i = 0; i < length; i++) { |
| ASTNode node = nodes[i]; |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(node); |
| if (level != null) { // ensure that the reference has not been already reported while visiting |
| 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; |
| } |
| } |
| this.patternLocator.matchReportReference(node, enclosingElement, declarationVisitor.getLocalElement(i), declarationVisitor.getOtherElements(i), field.binding, level.intValue(), this); |
| } |
| } |
| return; |
| } |
| } |
| } |
| } |
| } |
| /** |
| * Visit the given type declaration and report the nodes that match exactly the |
| * search pattern (i.e. 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()) { |
| IOpenable openable = enclosingElement.getOpenable(); |
| IJavaElement anonType = null; |
| if (openable instanceof ClassFile) { |
| BinaryType binaryType = (BinaryType)((ClassFile) openable).getType(); |
| String fileName = binaryType.getPath().toOSString(); |
| if ((type.bits & ASTNode.IsAnonymousType) != 0) { |
| if (fileName != null) { |
| if (fileName.endsWith("jar") || fileName.endsWith(SuffixConstants.SUFFIX_STRING_class)) { //$NON-NLS-1$ |
| IClassFile classFile= binaryType.getPackageFragment().getClassFile(binaryType.getTypeQualifiedName() + |
| "$" + Integer.toString(occurrenceCount) + SuffixConstants.SUFFIX_STRING_class);//$NON-NLS-1$ |
| anonType = classFile.getType(); |
| } |
| } else { |
| // TODO: JAVA 9 - JIMAGE to be included later - currently assuming that only .class files will be dealt here. |
| } |
| } |
| } |
| enclosingElement = anonType != null ? anonType : ((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, null, 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, null, null, 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, null, null, type.binding, level.intValue(), this); |
| } |
| } else { |
| TypeReference superClass = type.superclass; |
| if (superClass != null) { |
| reportMatchingSuper(superClass, enclosingElement, type.binding, nodeSet, matchedClassContainer); |
| for (int i = 0, length = superClass.annotations == null ? 0 : superClass.annotations.length; i < length; i++) { |
| Annotation[] annotations = superClass.annotations[i]; |
| if (annotations == null) continue; |
| reportMatching(annotations, enclosingElement, null, type.binding, nodeSet, matchedClassContainer, enclosesElement); |
| } |
| } |
| 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); |
| TypeReference typeReference = type.superInterfaces[i]; |
| Annotation[][] annotations = typeReference != null ? typeReference.annotations : null; |
| if (annotations != null) { |
| for (int j = 0, length = annotations.length; j < length; j++) { |
| if (annotations[j] == null) continue; |
| reportMatching(annotations[j], enclosingElement, null, type.binding, nodeSet, matchedClassContainer, enclosesElement); |
| } |
| } |
| } |
| } |
| } |
| |
| // 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(method, type, 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); |
| } |
| } |
| boolean matchedClassContainer = (this.matchContainer & PatternLocator.ALL_CONTAINER) != 0; |
| if (typeParameter.annotations != null) { |
| reportMatching(typeParameter.annotations, enclosingElement, null, typeParameter.binding, nodeSet, matchedClassContainer, encloses(enclosingElement)); |
| } |
| if (typeParameter.type != null) { |
| reportMatching(typeParameter.type.annotations, enclosingElement, typeParameter.binding, nodeSet, matchedClassContainer); |
| 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.type instanceof ParameterizedSingleTypeReference) { |
| ParameterizedSingleTypeReference paramSTR = (ParameterizedSingleTypeReference) typeParameter.type; |
| if (paramSTR.typeArguments != null) { |
| int length = paramSTR.typeArguments.length; |
| for (int k=0; k<length; k++) { |
| TypeReference typeArgument = paramSTR.typeArguments[k]; |
| reportMatching(typeArgument.annotations, enclosingElement, typeArgument.resolvedType, nodeSet, matchedClassContainer); |
| level = (Integer) nodeSet.matchingNodes.removeKey(typeArgument); |
| if (level != null) { |
| IJavaElement localElement = createHandle(typeParameter, enclosingElement); |
| this.patternLocator.matchReportReference(typeArgument, enclosingElement, localElement, null, binding, level.intValue(), this); |
| } |
| if (typeArgument instanceof Wildcard) { |
| TypeReference wildcardBound = ((Wildcard) typeArgument).bound; |
| if (wildcardBound != null) { |
| reportMatching(wildcardBound.annotations, enclosingElement, wildcardBound.resolvedType, nodeSet, matchedClassContainer); |
| level = (Integer) nodeSet.matchingNodes.removeKey(wildcardBound); |
| if (level != null) { |
| IJavaElement localElement = createHandle(typeParameter, enclosingElement); |
| this.patternLocator.matchReportReference(wildcardBound, enclosingElement, localElement, null, binding, level.intValue(), this); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| if (typeParameter.bounds != null) { |
| for (int j=0, b=typeParameter.bounds.length; j<b; j++) { |
| TypeReference typeParameterBound = typeParameter.bounds[j]; |
| if (typeParameterBound.annotations != null) { |
| reportMatching(typeParameterBound.annotations, enclosingElement, binding,nodeSet, matchedClassContainer); |
| } |
| level = (Integer) nodeSet.matchingNodes.removeKey(typeParameterBound); |
| if (level != null) { |
| IJavaElement localElement = createHandle(typeParameter, enclosingElement); |
| this.patternLocator.matchReportReference(typeParameterBound, enclosingElement, localElement, null, binding, level.intValue(), this); |
| } |
| if (typeParameterBound instanceof ParameterizedSingleTypeReference) { |
| ParameterizedSingleTypeReference paramSTR = (ParameterizedSingleTypeReference) typeParameterBound; |
| if (paramSTR.typeArguments != null) { |
| int length = paramSTR.typeArguments.length; |
| for (int k=0; k<length; k++) { |
| TypeReference typeArgument = paramSTR.typeArguments[k]; |
| if (typeArgument.annotations != null) { |
| reportMatching(typeArgument.annotations, enclosingElement, binding,nodeSet, matchedClassContainer); |
| } |
| level = (Integer) nodeSet.matchingNodes.removeKey(typeArgument); |
| if (level != null) { |
| IJavaElement localElement = createHandle(typeParameter, enclosingElement); |
| this.patternLocator.matchReportReference(typeArgument, enclosingElement, localElement, null, binding, level.intValue(), this); |
| } |
| if (typeArgument instanceof Wildcard) { |
| TypeReference wildcardBound = ((Wildcard) typeArgument).bound; |
| if (wildcardBound != null) { |
| if (wildcardBound.annotations != null) { |
| reportMatching(wildcardBound.annotations, enclosingElement, binding,nodeSet, matchedClassContainer); |
| } |
| level = (Integer) nodeSet.matchingNodes.removeKey(wildcardBound); |
| if (level != null) { |
| IJavaElement localElement = createHandle(typeParameter, enclosingElement); |
| this.patternLocator.matchReportReference(wildcardBound, 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, null, null, elementBinding, level.intValue(), this); |
| } |
| } |
| } else if (encloses(enclosingElement)) { |
| Integer level = (Integer) nodeSet.matchingNodes.removeKey(superReference); |
| if (level != null && matchedClassContainer) |
| this.patternLocator.matchReportReference(superReference, enclosingElement, null, null, 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; |
| } |
| } |