/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.grails.ide.eclipse.editor.gsp.search;
import java.util.List;
import org.codehaus.groovy.eclipse.GroovyLogManager;
import org.codehaus.groovy.eclipse.TraceCategory;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
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.core.search.JavaSearchDocument;
import org.eclipse.jdt.internal.core.search.JavaSearchParticipant;
import org.eclipse.jdt.internal.core.search.matching.PossibleMatch;
import org.eclipse.jst.jsp.core.internal.java.IJSPTranslation;
import org.eclipse.jst.jsp.core.internal.java.JSPTranslation;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.editor.gsp.translation.GSPTranslationAdapter;
import org.grails.ide.eclipse.editor.gsp.translation.GSPTranslationExtension;
/**
* Some utilities for helping to search through GSPs
* @author Andrew Eisenberg
* @since 2.7.0
*/
public class SearchInGSPs {
private class MockPossibleMatch extends PossibleMatch {
public MockPossibleMatch(GroovyCompilationUnit unit) {
super(null, unit.getResource(), unit, new JavaSearchDocument(unit.getResource().getFullPath().toPortableString(), new JavaSearchParticipant()), false);
}
}
private class GspMatchRequestor extends SearchRequestor {
private final GSPTranslationExtension translation;
private final IFile file;
private final IGSPSearchRequestor requestor;
public GspMatchRequestor(GSPTranslationExtension translation, IFile file, IGSPSearchRequestor requestor) {
this.translation = translation;
this.file = file;
this.requestor = requestor;
}
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
int jspOffset = translation.getJspOffset(match.getOffset());
if (jspOffset>=0) {
requestor.acceptMatch(file, jspOffset, match.getLength());
}
}
}
public void performSearch(IGSPSearchRequestor gspRequestor, IProgressMonitor monitor)
throws CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
List<IFile> toSearch = gspRequestor.getGSPsToSearch();
if (toSearch.size() > 0) {
// check to see if we should also search for tag references
FindTagReferences findTagReferences = new FindTagReferences();
boolean shouldSearchForTags = gspRequestor.searchForTags() && findTagReferences.shouldSearchForTagRefs(gspRequestor.elementToSearchFor());
// for each gsp, get the translator, and do a search through the translated CU
for (IFile file : toSearch) {
monitor.subTask("Searching in " + file.getName());
IStructuredModel model = getModel(file);
if (model == null) {
continue;
}
if (shouldSearchForTags) {
findTagReferences.findTags(model, file, gspRequestor);
}
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
GSPTranslationAdapter translationAdapter = getTranslation(model);
try {
if (translationAdapter == null) {
continue;
}
JSPTranslation jsptranslation = translationAdapter.getJSPTranslation();
GSPTranslationExtension translation;
if (jsptranslation instanceof GSPTranslationExtension) {
translation = (GSPTranslationExtension) jsptranslation;
} else {
translation = null;
}
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
if (translation != null) {
ICompilationUnit unit = translation.getCompilationUnit();
if (unit instanceof GroovyCompilationUnit) {
TypeInferencingVisitorFactory factory = new TypeInferencingVisitorFactory();
TypeInferencingVisitorWithRequestor visitor = factory.createVisitor((GroovyCompilationUnit) unit);
GspMatchRequestor gspInferencingRequestor = new GspMatchRequestor(translation, file, gspRequestor);
ITypeRequestor requestor = createRequestor(gspRequestor, (GroovyCompilationUnit) unit, gspInferencingRequestor);
// requestor might be null since we don't handle all kinds of searches
if (requestor != null) {
visitor.visitCompilationUnit(requestor);
}
}
}
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
} catch (Exception e) {
GrailsCoreActivator.log(e);
} finally {
try {
// don't release the adapter. That is handled by the factory
// translationAdapter.release();
translationAdapter.getXMLModel().releaseFromRead();
} catch (Exception e) {
GrailsCoreActivator.log(e);
}
}
}
}
}
private ITypeRequestor createRequestor(IGSPSearchRequestor gspRequestor, GroovyCompilationUnit unit, SearchRequestor requestor) {
// don't know what the match rule should be, so make it exact match
SearchPattern pattern = SearchPattern.createPattern(gspRequestor.elementToSearchFor(), gspRequestor.limitTo(), SearchPattern.R_CASE_SENSITIVE);
TypeRequestorFactory factory = new TypeRequestorFactory();
return factory.createRequestor(new MockPossibleMatch(unit), pattern, requestor);
}
private GSPTranslationAdapter getTranslation(IStructuredModel model) {
if (model == null || ! (model instanceof IDOMModel)) {
return null;
}
IDOMModel jspModel = (IDOMModel) model;
IDOMDocument xmlDoc = jspModel.getDocument();
return (GSPTranslationAdapter) xmlDoc.getAdapterFor(IJSPTranslation.class);
}
private IStructuredModel getModel(IFile file) {
GroovyLogManager.manager.log(TraceCategory.REFACTORING, "ENTER getModel("+file.getFullPath()+")");
try {
return StructuredModelManager.getModelManager().getModelForRead(file);
} catch (Exception e) {
GrailsCoreActivator.log("Error reading GSP file '"+file.getFullPath()+"'",e);
return null;
}
finally {
GroovyLogManager.manager.log(TraceCategory.REFACTORING, "EXIT getModel("+file.getFullPath()+")");
}
}
}