/******************************************************************************* * Copyright (c) 2004, 2012 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.che.jdt.internal.core.search.matching; import org.eclipse.che.jdt.core.search.IJavaSearchConstants; import org.eclipse.che.jdt.core.search.IJavaSearchScope; import org.eclipse.che.jdt.core.search.SearchPattern; import org.eclipse.che.jdt.internal.core.ClasspathEntry; import org.eclipse.che.jdt.internal.core.JavaModelManager; import org.eclipse.che.jdt.internal.core.JavaProject; import org.eclipse.che.jdt.internal.core.PackageFragmentRoot; import org.eclipse.che.jdt.internal.core.builder.ClasspathDirectory; import org.eclipse.che.jdt.internal.core.builder.ClasspathJar; import org.eclipse.che.jdt.internal.core.builder.CodenvyClasspathLocation; import org.eclipse.che.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.che.jdt.internal.core.search.IRestrictedAccessConstructorRequestor; import org.eclipse.che.jdt.internal.core.search.IRestrictedAccessTypeRequestor; import org.eclipse.che.jdt.internal.core.search.indexing.IndexManager; import org.eclipse.core.resources.IContainer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.codeassist.ISearchRequestor; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; import org.eclipse.jdt.internal.core.NameLookup; import org.eclipse.jdt.internal.core.util.Util; import java.io.File; import java.util.HashMap; import java.util.zip.ZipFile; /* * A name environment based on the classpath of a Java project. */ public class JavaSearchNameEnvironment implements INameEnvironment, SuffixConstants, INameEnvironmentWithProgress { CodenvyClasspathLocation[] locations; /* * A map from the fully qualified slash-separated name of the main type (String) to the working copy */ HashMap workingCopies; private JavaProject javaProject; private org.eclipse.che.jdt.core.search.IJavaSearchScope searchScope; public JavaSearchNameEnvironment(JavaProject javaProject, org.eclipse.jdt.core.ICompilationUnit[] copies) { this.javaProject = javaProject; computeClasspathLocations(javaProject); try { int length = copies == null ? 0 : copies.length; this.workingCopies = new HashMap(length); if (copies != null) { for (int i = 0; i < length; i++) { org.eclipse.jdt.core.ICompilationUnit workingCopy = copies[i]; IPackageDeclaration[] pkgs = workingCopy.getPackageDeclarations(); String pkg = pkgs.length > 0 ? pkgs[0].getElementName() : ""; //$NON-NLS-1$ String cuName = workingCopy.getElementName(); String mainTypeName = Util.getNameWithoutJavaLikeExtension(cuName); String qualifiedMainTypeName = pkg.length() == 0 ? mainTypeName : pkg.replace('.', '/') + '/' + mainTypeName; this.workingCopies.put(qualifiedMainTypeName, workingCopy); } } } catch (JavaModelException e) { // working copy doesn't exist: cannot happen } } public void cleanup() { for (CodenvyClasspathLocation location : this.locations) { location.cleanup(); } } /** * reset only source locations */ public void reset() { for (CodenvyClasspathLocation location : this.locations) { if (location instanceof ClasspathSourceDirectory) location.cleanup(); } } private void computeClasspathLocations(JavaProject javaProject) { IPackageFragmentRoot[] roots = null; try { roots = javaProject.getAllPackageFragmentRoots(); } catch (JavaModelException e) { // project doesn't exist this.locations = new CodenvyClasspathLocation[0]; return; } int length = roots.length; CodenvyClasspathLocation[] cpLocations = new CodenvyClasspathLocation[length]; int index = 0; JavaModelManager manager = javaProject.getJavaModelManager(); for (int i = 0; i < length; i++) { PackageFragmentRoot root = (PackageFragmentRoot)roots[i]; IPath path = root.getPath(); try { if (root.isArchive()) { ZipFile zipFile = manager.getZipFile(path); cpLocations[index++] = new ClasspathJar(zipFile, ((ClasspathEntry)root.getRawClasspathEntry()).getAccessRuleSet()); } else { Object target = JavaModelManager.getTarget(path, true); if (target == null) { // target doesn't exist any longer // just resize cpLocations System.arraycopy(cpLocations, 0, cpLocations = new CodenvyClasspathLocation[cpLocations.length - 1], 0, index); } else if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { cpLocations[index++] = new ClasspathSourceDirectory((File)target, root.fullExclusionPatternChars(), root.fullInclusionPatternChars()); } else { cpLocations[index++] = new ClasspathDirectory((IContainer)target, false, ((ClasspathEntry)root.getRawClasspathEntry()).getAccessRuleSet()); } } } catch (CoreException e1) { // problem opening zip file or getting root kind // consider root corrupt and ignore // just resize cpLocations System.arraycopy(cpLocations, 0, cpLocations = new CodenvyClasspathLocation[cpLocations.length - 1], 0, index); } } this.locations = cpLocations; } private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) { String binaryFileName = null, qBinaryFileName = null, sourceFileName = null, qSourceFileName = null, qPackageName = null; NameEnvironmentAnswer suggestedAnswer = null; for (int i = 0, length = this.locations.length; i < length; i++) { CodenvyClasspathLocation location = this.locations[i]; NameEnvironmentAnswer answer; if (location instanceof ClasspathSourceDirectory) { if (sourceFileName == null) { qSourceFileName = qualifiedTypeName; // doesn't include the file extension sourceFileName = qSourceFileName; qPackageName = ""; //$NON-NLS-1$ if (qualifiedTypeName.length() > typeName.length) { int typeNameStart = qSourceFileName.length() - typeName.length; qPackageName = qSourceFileName.substring(0, typeNameStart - 1); sourceFileName = qSourceFileName.substring(typeNameStart); } } ICompilationUnit workingCopy = (ICompilationUnit)this.workingCopies.get(qualifiedTypeName); if (workingCopy != null) { answer = new NameEnvironmentAnswer(workingCopy, null /*no access restriction*/); } else { answer = location.findClass( sourceFileName, // doesn't include the file extension qPackageName, qSourceFileName); // doesn't include the file extension } } else { if (binaryFileName == null) { qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class; binaryFileName = qBinaryFileName; qPackageName = ""; //$NON-NLS-1$ if (qualifiedTypeName.length() > typeName.length) { int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class" qPackageName = qBinaryFileName.substring(0, typeNameStart - 1); binaryFileName = qBinaryFileName.substring(typeNameStart); } } answer = location.findClass( binaryFileName, qPackageName, qBinaryFileName); } if (answer != null) { if (!answer.ignoreIfBetter()) { if (answer.isBetter(suggestedAnswer)) return answer; } else if (answer.isBetter(suggestedAnswer)) // remember suggestion and keep looking suggestedAnswer = answer; } } if (suggestedAnswer != null) // no better answer was found return suggestedAnswer; return null; } public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { if (typeName != null) return findClass( new String(CharOperation.concatWith(packageName, typeName, '/')), typeName); return null; } public NameEnvironmentAnswer findType(char[][] compoundName) { if (compoundName != null) return findClass( new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1]); return null; } public boolean isPackage(char[][] compoundName, char[] packageName) { return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/'))); } public boolean isPackage(String qualifiedPackageName) { for (CodenvyClasspathLocation location : this.locations) if (location.isPackage(qualifiedPackageName)) return true; return false; } private static int convertSearchFilterToModelFilter(int searchFilter) { switch (searchFilter) { case org.eclipse.jdt.core.search.IJavaSearchConstants.CLASS: return NameLookup.ACCEPT_CLASSES; case org.eclipse.jdt.core.search.IJavaSearchConstants.INTERFACE: return NameLookup.ACCEPT_INTERFACES; case org.eclipse.jdt.core.search.IJavaSearchConstants.ENUM: return NameLookup.ACCEPT_ENUMS; case org.eclipse.jdt.core.search.IJavaSearchConstants.ANNOTATION_TYPE: return NameLookup.ACCEPT_ANNOTATIONS; case org.eclipse.jdt.core.search.IJavaSearchConstants.CLASS_AND_ENUM: return NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_ENUMS; case org.eclipse.jdt.core.search.IJavaSearchConstants.CLASS_AND_INTERFACE: return NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES; default: return NameLookup.ACCEPT_ALL; } } /** * Find the top-level types that are defined * in the current environment and whose name starts with the * given prefix. The prefix is a qualified name separated by periods * or a simple name (ex. java.util.V or V). * * The types found are passed to one of the following methods (if additional * information is known about the types): * ISearchRequestor.acceptType(char[][] packageName, char[] typeName) * ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers) * ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers) * * This method can not be used to find member types... member * types are found relative to their enclosing type. */ public void findTypes(char[] prefix, final boolean findMembers, boolean camelCaseMatch, int searchFor, final ISearchRequestor storage) { findTypes(prefix, findMembers, camelCaseMatch, searchFor, storage, null); } /** * Must be used only by CompletionEngine. * The progress monitor is used to be able to cancel completion operations * * Find the top-level types that are defined * in the current environment and whose name starts with the * given prefix. The prefix is a qualified name separated by periods * or a simple name (ex. java.util.V or V). * * The types found are passed to one of the following methods (if additional * information is known about the types): * ISearchRequestor.acceptType(char[][] packageName, char[] typeName) * ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers) * ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers) * * This method can not be used to find member types... member * types are found relative to their enclosing type. */ public void findTypes(char[] prefix, final boolean findMembers, boolean camelCaseMatch, int searchFor, final ISearchRequestor storage, IProgressMonitor monitor) { try { int lastDotIndex = CharOperation.lastIndexOf('.', prefix); char[] qualification, simpleName; if (lastDotIndex < 0) { qualification = null; if (camelCaseMatch) { simpleName = prefix; } else { simpleName = CharOperation.toLowerCase(prefix); } } else { qualification = CharOperation.subarray(prefix, 0, lastDotIndex); if (camelCaseMatch) { simpleName = CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length); } else { simpleName = CharOperation.toLowerCase( CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length)); } } IProgressMonitor progressMonitor = new IProgressMonitor() { boolean isCanceled = false; public void beginTask(String name, int totalWork) { // implements interface method } public void done() { // implements interface method } public void internalWorked(double work) { // implements interface method } public boolean isCanceled() { return this.isCanceled; } public void setCanceled(boolean value) { this.isCanceled = value; } public void setTaskName(String name) { // implements interface method } public void subTask(String name) { // implements interface method } public void worked(int work) { // implements interface method } }; IRestrictedAccessTypeRequestor typeRequestor = new IRestrictedAccessTypeRequestor() { public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) { // if (excludePath != null && excludePath.equals(path)) // return; if (!findMembers && enclosingTypeNames != null && enclosingTypeNames.length > 0) return; // accept only top level types storage.acceptType(packageName, simpleTypeName, enclosingTypeNames, modifiers, access); } }; int matchRule = SearchPattern.R_PREFIX_MATCH; if (camelCaseMatch) matchRule |= SearchPattern.R_CAMELCASE_MATCH; IndexManager indexManager = javaProject.getIndexManager(); if (monitor != null) { if (indexManager.awaitingJobsCount() == 0) { // indexes were already there, so perform an immediate search to avoid any index rebuilt new BasicSearchEngine(indexManager, javaProject).searchAllTypeNames( qualification, SearchPattern.R_EXACT_MATCH, simpleName, matchRule, // not case sensitive searchFor, getSearchScope(), typeRequestor, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, progressMonitor); } else { // indexes were not ready, give the indexing a chance to finish small jobs by sleeping 100ms... try { Thread.sleep(100); } catch (InterruptedException e) { // Do nothing } if (monitor.isCanceled()) { throw new OperationCanceledException(); } if (indexManager.awaitingJobsCount() == 0) { // indexes are now ready, so perform an immediate search to avoid any index rebuilt new BasicSearchEngine(indexManager, javaProject).searchAllTypeNames( qualification, SearchPattern.R_EXACT_MATCH, simpleName, matchRule, // not case sensitive searchFor, getSearchScope(), typeRequestor, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, progressMonitor); } //else { // Indexes are still not ready, so look for types in the model instead of a search request // findTypes( // new String(prefix), // storage, // convertSearchFilterToModelFilter(searchFor)); // } } } else { try { new BasicSearchEngine(indexManager, javaProject).searchAllTypeNames( qualification, SearchPattern.R_EXACT_MATCH, simpleName, matchRule, // not case sensitive searchFor, getSearchScope(), typeRequestor, IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, progressMonitor); } catch (OperationCanceledException e) { // findTypes( // new String(prefix), // storage, // convertSearchFilterToModelFilter(searchFor)); } } } catch (JavaModelException e) { // findTypes( // new String(prefix), // storage, // convertSearchFilterToModelFilter(searchFor)); } } /** * Must be used only by CompletionEngine. * The progress monitor is used to be able to cancel completion operations * * Find constructor declarations that are defined * in the current environment and whose name starts with the * given prefix. The prefix is a qualified name separated by periods * or a simple name (ex. java.util.V or V). * * The constructors found are passed to one of the following methods: * ISearchRequestor.acceptConstructor(...) */ public void findConstructorDeclarations(char[] prefix, boolean camelCaseMatch, final ISearchRequestor storage, IProgressMonitor monitor) { try { int lastDotIndex = CharOperation.lastIndexOf('.', prefix); char[] qualification, simpleName; if (lastDotIndex < 0) { qualification = null; if (camelCaseMatch) { simpleName = prefix; } else { simpleName = CharOperation.toLowerCase(prefix); } } else { qualification = CharOperation.subarray(prefix, 0, lastDotIndex); if (camelCaseMatch) { simpleName = CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length); } else { simpleName = CharOperation.toLowerCase( CharOperation.subarray(prefix, lastDotIndex + 1, prefix.length)); } } IProgressMonitor progressMonitor = new IProgressMonitor() { boolean isCanceled = false; public void beginTask(String name, int totalWork) { // implements interface method } public void done() { // implements interface method } public void internalWorked(double work) { // implements interface method } public boolean isCanceled() { return this.isCanceled; } public void setCanceled(boolean value) { this.isCanceled = value; } public void setTaskName(String name) { // implements interface method } public void subTask(String name) { // implements interface method } public void worked(int work) { // implements interface method } }; IRestrictedAccessConstructorRequestor constructorRequestor = new IRestrictedAccessConstructorRequestor() { public void acceptConstructor( int modifiers, char[] simpleTypeName, int parameterCount, char[] signature, char[][] parameterTypes, char[][] parameterNames, int typeModifiers, char[] packageName, int extraFlags, String path, AccessRestriction access) { storage.acceptConstructor( modifiers, simpleTypeName, parameterCount, signature, parameterTypes, parameterNames, typeModifiers, packageName, extraFlags, path, access); } }; int matchRule = SearchPattern.R_PREFIX_MATCH; if (camelCaseMatch) matchRule |= SearchPattern.R_CAMELCASE_MATCH; IndexManager indexManager = javaProject.getIndexManager(); if (monitor != null) { while (indexManager.awaitingJobsCount() > 0) { try { Thread.sleep(50); // indexes are not ready, sleep 50ms... } catch (InterruptedException e) { // Do nothing } if (monitor.isCanceled()) { throw new OperationCanceledException(); } } new BasicSearchEngine(indexManager, javaProject).searchAllConstructorDeclarations( qualification, simpleName, matchRule, getSearchScope(), constructorRequestor, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, progressMonitor); } else { try { new BasicSearchEngine(indexManager, javaProject).searchAllConstructorDeclarations( qualification, simpleName, matchRule, getSearchScope(), constructorRequestor, IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, progressMonitor); } catch (OperationCanceledException e) { // Do nothing } } } catch (JavaModelException e) { // Do nothing } } private IJavaSearchScope getSearchScope() { if (this.searchScope == null) { // // Create search scope with visible entry on the project's classpath // if(this.checkAccessRestrictions) { this.searchScope = BasicSearchEngine .createJavaSearchScope(new IJavaElement[]{this.javaProject}); // } else { // this.searchScope = BasicSearchEngine // .createJavaSearchScope(this.nameLookup.packageFragmentRoots); // } } return this.searchScope; } @Override public void setMonitor(IProgressMonitor monitor) { } /** * Find the top-level types that are defined * in the current environment and whose simple name matches the given name. * * The types found are passed to one of the following methods (if additional * information is known about the types): * ISearchRequestor.acceptType(char[][] packageName, char[] typeName) * ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers) * ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers) * * This method can not be used to find member types... member * types are found relative to their enclosing type. */ public void findExactTypes(char[] name, final boolean findMembers, int searchFor, final ISearchRequestor storage) { try { IProgressMonitor progressMonitor = new IProgressMonitor() { boolean isCanceled = false; public void beginTask(String n, int totalWork) { // implements interface method } public void done() { // implements interface method } public void internalWorked(double work) { // implements interface method } public boolean isCanceled() { return this.isCanceled; } public void setCanceled(boolean value) { this.isCanceled = value; } public void setTaskName(String n) { // implements interface method } public void subTask(String n) { // implements interface method } public void worked(int work) { // implements interface method } }; IRestrictedAccessTypeRequestor typeRequestor = new IRestrictedAccessTypeRequestor() { public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) { if (!findMembers && enclosingTypeNames != null && enclosingTypeNames.length > 0) return; // accept only top level types storage.acceptType(packageName, simpleTypeName, enclosingTypeNames, modifiers, access); } }; try { new BasicSearchEngine(javaProject.getIndexManager(), javaProject).searchAllTypeNames( null, SearchPattern.R_EXACT_MATCH, name, SearchPattern.R_EXACT_MATCH, searchFor, getSearchScope(), typeRequestor, IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, progressMonitor); } catch (OperationCanceledException e) { // findExactTypes( // new String(name), // storage, // convertSearchFilterToModelFilter(searchFor)); } } catch (JavaModelException e) { // findExactTypes( // new String(name), // storage, // convertSearchFilterToModelFilter(searchFor)); } } /** * Find the packages that start with the given prefix. * A valid prefix is a qualified name separated by periods * (ex. java.util). * The packages found are passed to: * ISearchRequestor.acceptPackage(char[][] packageName) */ public void findPackages(char[] prefix, ISearchRequestor requestor) { String name = new String(prefix); String[] splittedName = Util.splitOn('.', name, 0, name.length()); for (CodenvyClasspathLocation location : this.locations) { location.findPackages(splittedName, requestor); } } }