/*******************************************************************************
* Copyright (c) 2012 VMWare, Inc.
* 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:
* VMWare, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.search.test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.ui.search.ISearchRequestor;
import org.eclipse.jdt.ui.search.QuerySpecification;
import org.eclipse.search.ui.text.Match;
import org.grails.ide.eclipse.search.AbstractGrailsSearch;
import org.grails.ide.eclipse.search.AbstractQueryParticipant;
import org.grails.ide.eclipse.test.util.GrailsTest;
public class AbstractGrailsSearchParticipantTest extends GrailsTest {
public final String TEST_PROJECT_NAME = AbstractGrailsSearchParticipantTest.class.getSimpleName();
public final String PACKAGE_NAME = TEST_PROJECT_NAME.toLowerCase();
/**
* This class is just like {@link Match} but it implements hashCode and equals so we can
* put it into a hash set and compare expected matches easily to actual matches.
*
* @author Kris De Volder
*
* @since 2.9
*/
public static class MatchInfo {
public final Object element;
public int start;
public int len;
public MatchInfo(Object element, int start, int len) {
this.element = element;
this.start = start;
this.len = len;
}
@Override
public String toString() {
return "MatchInfo [element=" + toStr(element) + ", start=" + start
+ ", len=" + len + "]";
}
private String toStr(Object e) {
if (e instanceof IJavaElement) {
return ((IJavaElement) e).getHandleIdentifier();
} else {
return e.toString();
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((element == null) ? 0 : element.hashCode());
result = prime * result + len;
result = prime * result + start;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MatchInfo other = (MatchInfo) obj;
if (element == null) {
if (other.element != null)
return false;
} else if (!element.equals(other.element))
return false;
if (len != other.len)
return false;
if (start != other.start)
return false;
return true;
}
/**
* Change start and len as needed to avoid including surrounding quotes.
*/
public MatchInfo adjustForQuotes(String findString) {
if (findString.startsWith("\"")) {
start++;
len--;
}
if (findString.endsWith("\"")) {
len--;
}
return this;
}
}
public static class MockSearchRequestor implements ISearchRequestor {
Set<MatchInfo> expectedMatches = new HashSet<MatchInfo>();
Set<MatchInfo> actualMatches = new HashSet<MatchInfo>();
public MockSearchRequestor(MatchInfo[] expectedMatches) {
for (MatchInfo matchInfo : expectedMatches) {
this.expectedMatches.add(matchInfo);
}
}
public void reportMatch(Match match) {
actualMatches.add(new MatchInfo(match.getElement(), match.getOffset(), match.getLength()));
}
public void assertMatches() {
StringBuilder errorMessage = new StringBuilder();
for (MatchInfo actualMatch : actualMatches) {
if (!expectedMatches.contains(actualMatch)) {
errorMessage.append("Unexpected: "+actualMatch+"\n");
}
expectedMatches.remove(actualMatch);
}
//any matches that were seen have now been removed from expectedMatches... this means that any left over
//where missing from the actual result.
for (MatchInfo leftOverMatch : expectedMatches) {
errorMessage.append("Expected but not found: "+leftOverMatch+"\n");
}
String msg = errorMessage.toString();
if (msg.length()>0) {
fail(errorMessage.toString());
}
}
}
public static AbstractGrailsSearchParticipantTest.MatchInfo methodMatch(IJavaProject javaProject, String packageName, String className,
String methodName, String findString) throws JavaModelException {
IType type = javaProject.findType(packageName+"."+className);
IMethod method = null;
for (IMethod m : type.getMethods()) {
if (m.getElementName().equals(methodName)) {
assertNull("Ambiguous: there are multple methods with the name '"+methodName+"' in the class '"+type.getFullyQualifiedName(), method);
method = m;
}
}
CompilationUnit cu = (CompilationUnit)method.getCompilationUnit();
int methodStart = method.getSourceRange().getOffset();
int methodLen = method.getSourceRange().getLength();
int start = new String(cu.getContents()).indexOf(findString, methodStart);
int len = findString.length();
assertTrue(methodStart>0);
assertTrue(methodLen>0);
assertTrue(start>=methodStart);
assertTrue(start+len <= methodStart+methodLen);
return new AbstractGrailsSearchParticipantTest.MatchInfo(method, start, len).adjustForQuotes(findString);
}
public static AbstractGrailsSearchParticipantTest.MatchInfo fieldMatch(IJavaProject javaProject, String packageName, String className,
String fieldName, String findString) throws JavaModelException {
IField field = field(javaProject, packageName, className, fieldName);
CompilationUnit cu = (CompilationUnit)field.getCompilationUnit();
int fieldStart = field.getSourceRange().getOffset();
int fieldLen = field.getSourceRange().getLength();
int start = new String(cu.getContents()).indexOf(findString, fieldStart);
int len = findString.length();
assertTrue(fieldStart>0);
assertTrue(fieldLen>0);
assertTrue(start>=fieldStart);
assertTrue(start+len <= fieldStart+fieldLen);
return new AbstractGrailsSearchParticipantTest.MatchInfo(field, start, len).adjustForQuotes(findString);
}
public static IField field(IJavaProject javaProject, String packageName,
String className, String fieldName) throws JavaModelException {
IType type = javaProject.findType(packageName==null? className : packageName+"."+className);
IField field = type.getField(fieldName);
assertNotNull(field);
return field;
}
protected void assertMatches(AbstractQueryParticipant searchParticipant, QuerySpecification query, AbstractGrailsSearchParticipantTest.MatchInfo... expectedMatches)
throws CoreException {
MockSearchRequestor requestor = new MockSearchRequestor(expectedMatches);
searchParticipant.search(requestor, query, new NullProgressMonitor());
requestor.assertMatches();
}
protected void assertMatches(AbstractGrailsSearch search, MatchInfo[] expectedMatches) {
MockSearchRequestor requestor = new MockSearchRequestor(expectedMatches);
search.perform(requestor);
requestor.assertMatches();
}
protected MatchInfo[] determineExpectedMatches(IField withinElement, String template,
String expectSnippet) throws JavaModelException {
int searchStart = withinElement.getSourceRange().getOffset();
int searchEnd = searchStart + withinElement.getSourceRange().getLength();
return expectedMatchesHelper(withinElement, searchStart, searchEnd, template, expectSnippet);
}
protected MatchInfo[] determineExpectedMatches(IFile gspFile, String template, String expectSnippet) {
long fileSize = gspFile.getLocation().toFile().length();
assertTrue("File '"+gspFile+"' doesn't exist or has lenght zero", fileSize>0);
return expectedMatchesHelper(gspFile, 0, (int) fileSize, template, expectSnippet);
}
private MatchInfo[] expectedMatchesHelper(Object withinElement,
int searchStart, int searchEnd, String template, String expectSnippet) {
String expectTemplate = template.replace("***", expectSnippet);
ArrayList<MatchInfo> expectedMatches = new ArrayList<AbstractGrailsSearchParticipantTest.MatchInfo>();
int searchPos = searchStart;
while (searchPos>=0 && searchPos<searchEnd) {
searchPos = expectTemplate.indexOf('#', searchPos);
if (searchPos>=0 && searchPos<searchEnd) {
int start = searchPos;
int end = start;
while (expectTemplate.charAt(end)=='#') {
end++;
}
expectedMatches.add(new MatchInfo(withinElement, start, end-start));
searchPos = end;
}
}
return expectedMatches.toArray(new MatchInfo[expectedMatches.size()]);
}
}