/******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core.search.indexing; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.SearchDocument; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; import org.eclipse.jdt.internal.compiler.SourceElementParser; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.ISourceType; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; import org.eclipse.jdt.internal.core.JavaModel; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.jdom.CompilationUnit; import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment; import org.eclipse.jdt.internal.core.search.matching.MethodPattern; import org.eclipse.jdt.internal.core.search.processing.JobManager; /** * A SourceIndexer indexes java files using a java parser. The following items are indexed: * Declarations of:<br> * - Classes<br> * - Interfaces;<br> * - Methods;<br> * - Fields;<br> * - Lambda expressions;<br> * References to:<br> * - Methods (with number of arguments); <br> * - Fields;<br> * - Types;<br> * - Constructors. */ public class SourceIndexer extends AbstractIndexer implements ITypeRequestor, SuffixConstants { private LookupEnvironment lookupEnvironment; private CompilerOptions options; public ISourceElementRequestor requestor; private Parser basicParser; private CompilationUnit compilationUnit; private CompilationUnitDeclaration cud; private static final boolean DEBUG = false; public SourceIndexer(SearchDocument document) { super(document); this.requestor = new SourceIndexerRequestor(this); } public void indexDocument() { // Create a new Parser String documentPath = this.document.getPath(); SourceElementParser parser = this.document.getParser(); if (parser == null) { IPath path = new Path(documentPath); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)); parser = JavaModelManager.getJavaModelManager().indexManager.getSourceElementParser(JavaCore.create(project), this.requestor); } else { parser.setRequestor(this.requestor); } // Launch the parser char[] source = null; char[] name = null; try { source = this.document.getCharContents(); name = documentPath.toCharArray(); } catch(Exception e){ // ignore } if (source == null || name == null) return; // could not retrieve document info (e.g. resource was discarded) this.compilationUnit = new CompilationUnit(source, name); try { if (parser.parseCompilationUnit(this.compilationUnit, true, null).hasFunctionalTypes()) this.document.requireIndexingResolvedDocument(); } catch (Exception e) { if (JobManager.VERBOSE) { e.printStackTrace(); } } } public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { this.lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); } public void accept(ICompilationUnit unit, AccessRestriction accessRestriction) { CompilationResult unitResult = new CompilationResult(unit, 1, 1, this.options.maxProblemsPerUnit); CompilationUnitDeclaration parsedUnit = this.basicParser.dietParse(unit, unitResult); this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); this.lookupEnvironment.completeTypeBindings(parsedUnit, true); } public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { ISourceType sourceType = sourceTypes[0]; while (sourceType.getEnclosingType() != null) sourceType = sourceType.getEnclosingType(); SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) sourceType; IType type = elementInfo.getHandle(); ICompilationUnit sourceUnit = (ICompilationUnit) type.getCompilationUnit(); accept(sourceUnit, accessRestriction); } public void resolveDocument() { try { IPath path = new Path(this.document.getPath()); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)); JavaModel model = JavaModelManager.getJavaModelManager().getJavaModel(); JavaProject javaProject = (JavaProject) model.getJavaProject(project); this.options = new CompilerOptions(javaProject.getOptions(true)); ProblemReporter problemReporter = new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), this.options, new DefaultProblemFactory()); // Re-parse using normal parser, IndexingParser swallows several nodes, see comment above class. this.basicParser = new Parser(problemReporter, false); this.basicParser.reportOnlyOneSyntaxError = true; this.basicParser.scanner.taskTags = null; this.cud = this.basicParser.parse(this.compilationUnit, new CompilationResult(this.compilationUnit, 0, 0, this.options.maxProblemsPerUnit)); // Use a non model name environment to avoid locks, monitors and such. INameEnvironment nameEnvironment = new JavaSearchNameEnvironment(javaProject, JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true/*add primary WCs*/)); this.lookupEnvironment = new LookupEnvironment(this, this.options, problemReporter, nameEnvironment); reduceParseTree(this.cud); this.lookupEnvironment.buildTypeBindings(this.cud, null); this.lookupEnvironment.completeTypeBindings(); this.cud.scope.faultInTypes(); this.cud.resolve(); } catch (Exception e) { if (JobManager.VERBOSE) { e.printStackTrace(); } } } /** * Called prior to the unit being resolved. Reduce the parse tree where possible. */ private void reduceParseTree(CompilationUnitDeclaration unit) { // remove statements from methods that have no functional interface types. TypeDeclaration[] types = unit.types; for (int i = 0, l = types == null ? 0 : types.length; i < l; i++) purgeMethodStatements(types[i]); } private void purgeMethodStatements(TypeDeclaration type) { AbstractMethodDeclaration[] methods = type.methods; for (int j = 0, length = methods == null ? 0 : methods.length; j < length; j++) { AbstractMethodDeclaration method = methods[j]; if (method != null && (method.bits & ASTNode.HasFunctionalInterfaceTypes) == 0) { 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]); } public void indexResolvedDocument() { try { if (DEBUG) System.out.println(new String(this.cud.compilationResult.fileName) + ':'); for (int i = 0, length = this.cud.functionalExpressionsCount; i < length; i++) { FunctionalExpression expression = this.cud.functionalExpressions[i]; if (expression instanceof LambdaExpression) { LambdaExpression lambdaExpression = (LambdaExpression) expression; if (lambdaExpression.binding != null && lambdaExpression.binding.isValidBinding()) { final char[] superinterface = lambdaExpression.resolvedType.sourceName(); if (DEBUG) { System.out.println('\t' + new String(superinterface) + '.' + new String(lambdaExpression.descriptor.selector) + "-> {}"); //$NON-NLS-1$ } SourceIndexer.this.addIndexEntry(IIndexConstants.METHOD_DECL, MethodPattern.createIndexKey(lambdaExpression.descriptor.selector, lambdaExpression.descriptor.parameters.length)); addClassDeclaration(0, // most entries are blank, that is fine, since lambda type/method cannot be searched. CharOperation.NO_CHAR, // package name ONE_ZERO, ONE_ZERO_CHAR, // enclosing types. CharOperation.NO_CHAR, // super class new char[][] { superinterface }, CharOperation.NO_CHAR_CHAR, true); // not primary. } else { if (DEBUG) System.out.println("\tnull/bad binding in lambda"); //$NON-NLS-1$ } } else { ReferenceExpression referenceExpression = (ReferenceExpression) expression; if (referenceExpression.isArrayConstructorReference()) continue; MethodBinding binding = referenceExpression.getMethodBinding(); if (binding != null && binding.isValidBinding()) { if (DEBUG) { System.out.println('\t' + new String(referenceExpression.resolvedType.sourceName()) + "::" //$NON-NLS-1$ + new String(referenceExpression.descriptor.selector) + " == " + new String(binding.declaringClass.sourceName()) + '.' + //$NON-NLS-1$ new String(binding.selector)); } if (referenceExpression.isMethodReference()) SourceIndexer.this.addMethodReference(binding.selector, binding.parameters.length); else SourceIndexer.this.addConstructorReference(binding.declaringClass.sourceName(), binding.parameters.length); } else { if (DEBUG) System.out.println("\tnull/bad binding in reference expression"); //$NON-NLS-1$ } } } } catch (Exception e) { if (JobManager.VERBOSE) { e.printStackTrace(); } } } }