/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.jdt.core.groovy.tests.search;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.groovy.tests.MockPossibleMatch;
import org.eclipse.jdt.core.groovy.tests.MockSearchRequestor;
import org.eclipse.jdt.core.groovy.tests.builder.BuilderTestSuite;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameRequestor;
import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.groovy.search.ITypeRequestor;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorFactory;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorWithRequestor;
import org.eclipse.jdt.groovy.search.TypeRequestorFactory;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.LocalVariable;
import org.junit.After;
import org.junit.Before;
public abstract class SearchTestSuite extends BuilderTestSuite {
protected static class MatchRegion {
final int offset;
final int length;
public MatchRegion(int offset, int length) {
this.offset = offset;
this.length = length;
}
@Override
public String toString() {
return "[ " + offset + " , " + length + " ]";
}
}
MockSearchRequestor searchRequestor;
protected IProject project;
protected static final TypeInferencingVisitorFactory factory = new TypeInferencingVisitorFactory();
/**
* Controls the file extension of the files that are created by this test class
*/
protected String defaultFileExtension = "groovy";
@Before
public final void setUpSearchTestCase() throws Exception {
project = createSimpleGroovyProject();
searchRequestor = new MockSearchRequestor();
}
@After
public final void tearDownSearchTestCase() throws Exception {
defaultFileExtension = "groovy";
}
protected IProject createSimpleGroovyProject() throws Exception {
IPath projectPath = env.addProject("Project");
env.addGroovyNature("Project");
env.addExternalJars(projectPath, Util.getJavaClassLibs());
env.addGroovyJars(projectPath);
// remove old package fragment root so that names don't collide
env.removePackageFragmentRoot(projectPath, "");
env.addPackageFragmentRoot(projectPath, "src");
env.setOutputFolder(projectPath, "bin");
IProject proj = env.getProject("Project");
IJavaProject javaProject = JavaCore.create(proj);
javaProject.setOption(CompilerOptions.OPTION_Compliance, "1.6");
javaProject.setOption(CompilerOptions.OPTION_Source, "1.6");
javaProject.setOption(CompilerOptions.OPTION_TargetPlatform, "1.6");
fullBuild(projectPath);
return proj;
}
protected GroovyCompilationUnit createUnit(String name, String contents) {
IPath path = env.addGroovyClassExtension(project.getFolder("src").getFullPath(), name, contents, defaultFileExtension);
fullBuild(project.getFullPath());
return (GroovyCompilationUnit) JavaCore.createCompilationUnitFrom(env.getWorkspace().getRoot().getFile(path));
}
protected ICompilationUnit createJavaUnit(String name, String contents) {
IPath path = env.addClass(project.getFolder("src").getFullPath(), name, contents);
return JavaCore.createCompilationUnitFrom(env.getWorkspace().getRoot().getFile(path));
}
protected ICompilationUnit createJavaUnit(String pack, String name, String contents) {
IPath path = env.addClass(project.getFolder("src").getFullPath(), pack, name, contents);
return JavaCore.createCompilationUnitFrom(env.getWorkspace().getRoot().getFile(path));
}
protected GroovyCompilationUnit createUnit(String pkg, String name, String contents) throws CoreException {
IFolder folder = project.getFolder("src").getFolder(new Path(pkg));
if (!folder.exists()) {
folder.create(true, true, null);
}
IPath path = env.addGroovyClassExtension(folder.getFullPath(), name, contents, defaultFileExtension);
return (GroovyCompilationUnit) JavaCore.createCompilationUnitFrom(env.getWorkspace().getRoot().getFile(path));
}
protected void assertLocation(SearchMatch match, int start, int length) {
assertEquals("Invalid match start for: " + MockPossibleMatch.printMatch(match), start, match.getOffset());
assertEquals("Invalid match length for: " + MockPossibleMatch.printMatch(match), length, match.getLength());
}
protected final static String FIRST_CONTENTS_CLASS = "class First {}";
protected final static String FIRST_CONTENTS_INTERFACE = "interface First {}";
protected final static String FIRST_CONTENTS_CLASS_FOR_FIELDS = "class First { def xxx }";
protected final static String FIRST_CONTENTS_CLASS_FOR_METHODS = "class First { def xxx() { } }";
protected final static String FIRST_CONTENTS_CLASS_FOR_METHODS2 = "class First { def xxx() { } \n def xxx(arg) { } }";
protected void doTestForTwoTypeReferences(String firstContents, String secondContents, boolean contentsIsScript, int offsetInParent) throws JavaModelException {
String firstClassName = "First";
String secondClassName = "Second";
GroovyCompilationUnit first = createUnit(firstClassName, firstContents);
IType firstType = findType(firstClassName, first);
SearchPattern pattern = SearchPattern.createPattern(firstType, IJavaSearchConstants.REFERENCES);
GroovyCompilationUnit second = createUnit(secondClassName, secondContents);
IJavaElement firstMatchEnclosingElement;
IJavaElement secondMatchEnclosingElement;
if (contentsIsScript) {
firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
} else {
// if not a script, then the first match is always enclosed in the type,
firstMatchEnclosingElement = findType(secondClassName, second);
}
// match is enclosed in run method (for script), or x method for class
secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
checkMatches(secondContents, firstClassName, pattern, second, firstMatchEnclosingElement, secondMatchEnclosingElement);
}
protected void doTestForTwoFieldReferences(String firstContents, String secondContents, boolean contentsIsScript, int offsetInParent, String matchName) throws JavaModelException {
doTestForTwoFieldReferences(firstContents, secondContents, contentsIsScript, offsetInParent, matchName, IJavaSearchConstants.REFERENCES);
}
protected void doTestForTwoFieldReferences(String firstContents, String secondContents, boolean contentsIsScript, int offsetInParent, String matchName, int searchFlags) throws JavaModelException {
String firstClassName = "First";
String secondClassName = "Second";
String matchedFieldName = "xxx";
GroovyCompilationUnit first = createUnit(firstClassName, firstContents);
IField firstField = findType(firstClassName, first).getField(matchedFieldName);
SearchPattern pattern = SearchPattern.createPattern(firstField, searchFlags);
GroovyCompilationUnit second = createUnit(secondClassName, secondContents);
IJavaElement firstMatchEnclosingElement;
IJavaElement secondMatchEnclosingElement;
if (contentsIsScript) {
firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
} else {
firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent+2];
}
// match is enclosed in run method (for script), or x method for class
checkMatches(secondContents, matchName, pattern, second,
firstMatchEnclosingElement, secondMatchEnclosingElement);
}
// as above, but enclosing element is always the first child of the enclosing type
protected void doTestForTwoFieldReferencesInGString(String firstContents, String secondContents, String matchName) throws JavaModelException {
String firstClassName = "First";
String secondClassName = "Second";
String matchedFieldName = "xxx";
GroovyCompilationUnit first = createUnit(firstClassName, firstContents);
IField firstField = findType(firstClassName, first).getField(matchedFieldName);
SearchPattern pattern = SearchPattern.createPattern(firstField, IJavaSearchConstants.REFERENCES);
GroovyCompilationUnit second = createUnit(secondClassName, secondContents);
IJavaElement firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[0];
IJavaElement secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[0];
checkMatches(secondContents, matchName, pattern, second,
firstMatchEnclosingElement, secondMatchEnclosingElement);
}
protected void doTestForTwoMethodReferences(String firstContents, String secondContents, boolean contentsIsScript, int offsetInParent, String matchName) throws JavaModelException {
String firstClassName = "First";
String secondClassName = "Second";
GroovyCompilationUnit first = createUnit(firstClassName, firstContents);
IMethod firstMethod = (IMethod) findType(firstClassName, first).getChildren()[0];
SearchPattern pattern = SearchPattern.createPattern(firstMethod, IJavaSearchConstants.REFERENCES);
GroovyCompilationUnit second = createUnit(secondClassName, secondContents);
env.fullBuild();
IJavaElement firstMatchEnclosingElement;
IJavaElement secondMatchEnclosingElement;
if (contentsIsScript) {
firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
} else {
firstMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent];
secondMatchEnclosingElement = findType(secondClassName, second).getChildren()[offsetInParent+2];
}
// match is enclosed in run method (for script), or x method for class
checkMatches(secondContents, matchName, pattern, second,
firstMatchEnclosingElement, secondMatchEnclosingElement);
}
protected List<SearchMatch> getAllMatches(String firstContents, String secondContents) throws Exception {
return getAllMatches(firstContents, secondContents, false);
}
protected List<SearchMatch> getAllMatches(String firstContents, String secondContents, boolean waitForIndexer) throws Exception {
return getAllMatches(firstContents, secondContents, "", "", waitForIndexer);
}
protected List<SearchMatch> getAllMatches(String firstContents, String secondContents, String firstPackage, String secondPackage, boolean waitForIndexer) throws Exception {
String firstClassName = "First";
String secondClassName = "Second";
GroovyCompilationUnit first = createUnit(firstPackage, firstClassName, firstContents);
IType firstType = findType(firstClassName, first);
SearchPattern pattern = SearchPattern.createPattern(firstType, IJavaSearchConstants.REFERENCES);
GroovyCompilationUnit second = createUnit(secondPackage, secondClassName, secondContents);
// saves time if we don't wait
// only need to do this if we are referencing inner classes
if (waitForIndexer) {
waitForIndexer();
}
// search the first
MockPossibleMatch match1 = new MockPossibleMatch(first);
ITypeRequestor typeRequestor1 = new TypeRequestorFactory().createRequestor(match1, pattern, searchRequestor);
TypeInferencingVisitorWithRequestor visitor1 = factory.createVisitor(match1);
visitor1.visitCompilationUnit(typeRequestor1);
// search the second
MockPossibleMatch match2 = new MockPossibleMatch(second);
ITypeRequestor typeRequestor2 = new TypeRequestorFactory().createRequestor(match2, pattern, searchRequestor);
TypeInferencingVisitorWithRequestor visitor2 = factory.createVisitor(match2);
visitor2.visitCompilationUnit(typeRequestor2);
return searchRequestor.getMatches();
}
private IType findType(String firstClassName, GroovyCompilationUnit first) {
IType type = first.getType(firstClassName);
if (! type.exists()) {
try {
IType[] allTypes = first.getAllTypes();
for (IType type2 : allTypes) {
if (type2.getElementName().equals(firstClassName)) {
return type2;
}
}
} catch (JavaModelException e) {
e.printStackTrace();
}
return null;
}
return type;
}
protected void doTestForVarReferences(String contents, int offsetInParent, String matchName, int start, MatchRegion[] matchLocations) throws JavaModelException {
String className = "First";
String matchedVarName = "xxx";
int until = start + matchedVarName.length() - 1;
GroovyCompilationUnit unit = createUnit(className, contents);
JavaElement parent = (JavaElement) findType(className, unit).getChildren()[offsetInParent];
ILocalVariable var = new LocalVariable(parent, matchedVarName, start, until, start, until, Signature.SIG_INT, null, 0, false);
SearchPattern pattern = SearchPattern.createPattern(var, IJavaSearchConstants.REFERENCES);
checkLocalVarMatches(contents, matchName, pattern, unit, matchLocations);
}
private void checkLocalVarMatches(String contents, String matchName, SearchPattern pattern, GroovyCompilationUnit unit, MatchRegion[] matchLocations) {
MockPossibleMatch match = new MockPossibleMatch(unit);
ITypeRequestor typeRequestor = new TypeRequestorFactory().createRequestor(match, pattern, searchRequestor);
TypeInferencingVisitorWithRequestor visitor = factory.createVisitor(match);
visitor.visitCompilationUnit(typeRequestor);
assertEquals("Should have found " + matchLocations.length + " matches, but found: " + searchRequestor.printMatches(), matchLocations.length, searchRequestor.getMatches().size());
for (int i = 0, n = matchLocations.length; i < n; i += 1) {
assertLocation(searchRequestor.getMatch(i), matchLocations[i].offset, matchLocations[i].length);
}
}
private void checkMatches(String secondContents, String matchText, SearchPattern pattern, GroovyCompilationUnit second, IJavaElement firstMatchEnclosingElement, IJavaElement secondMatchEnclosingElement) {
MockPossibleMatch match = new MockPossibleMatch(second);
ITypeRequestor typeRequestor = new TypeRequestorFactory().createRequestor(match, pattern, searchRequestor);
TypeInferencingVisitorWithRequestor visitor = factory.createVisitor(match);
visitor.visitCompilationUnit(typeRequestor);
assertEquals("Should have found 2 matches, but found: " + searchRequestor.printMatches(), 2, searchRequestor.getMatches().size());
assertEquals("Incorrect match in " + searchRequestor.printMatches(), firstMatchEnclosingElement, searchRequestor.getElementNumber(0));
assertLocation(searchRequestor.getMatch(0), secondContents.indexOf(matchText), matchText.length());
assertEquals("Incorrect match in " + searchRequestor.printMatches(), secondMatchEnclosingElement, searchRequestor.getElementNumber(1));
assertLocation(searchRequestor.getMatch(1), secondContents.lastIndexOf(matchText), matchText.length());
}
protected void waitForIndexer(IJavaElement... elements) throws Exception {
new SearchEngine().searchAllTypeNames(
null, 0,
"XXXXXXXXX".toCharArray(),
SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH,
IJavaSearchConstants.CLASS,
SearchEngine.createJavaSearchScope(elements),
new TypeNameRequestor() {},
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
null);
for (Job job : Job.getJobManager().find(null)) {
if (job.getName().contains("Java index")) {
job.join();
}
}
}
}