/******************************************************************************* * 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 org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IType; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; import org.eclipse.wst.xml.core.internal.document.ElementImpl; import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.grails.ide.eclipse.editor.groovy.elements.GrailsWorkspaceCore; import org.grails.ide.eclipse.editor.gsp.model.GSPStructuredModel; import org.grails.ide.eclipse.editor.gsp.tags.AbstractGSPTag; /** * This class finds all references to a tag in the workspace * @author Andrew Eisenberg * @since 2.7.0 */ class FindTagReferences { /** can be a field or a local variable */ private IJavaElement tagField; private String tagName; /** * only check for tags if the java element states that * is a field in a taglib * @param elt * @return the tag name if the specification corresponds to a tag, or else null */ boolean shouldSearchForTagRefs(IJavaElement elt) { // strangely, if the referenced tag is from a class file, then the type is ILocalVariable if (elt.getElementType() == IJavaElement.LOCAL_VARIABLE) { elt = elt.getParent(); } if (elt.getElementType() == IJavaElement.FIELD) { ICompilationUnit unit = (ICompilationUnit) elt.getAncestor(IJavaElement.COMPILATION_UNIT); if (unit instanceof GroovyCompilationUnit) { if (GrailsWorkspaceCore.isTagLibClass((GroovyCompilationUnit) unit)) { tagField = elt; tagName = tagField.getElementName(); } } else { // could be a built in tag IType type = (IType) elt.getAncestor(IJavaElement.TYPE); if (type != null && type.isReadOnly() && type.getElementName().endsWith("TagLib")) { IPackageFragment frag = (IPackageFragment) elt.getAncestor(IJavaElement.PACKAGE_FRAGMENT); if (frag.getElementName().equals("org.codehaus.groovy.grails.plugins.web.taglib")) { tagField = elt; tagName = tagField.getElementName(); } } } } return tagField != null; } void findTags(IStructuredModel model, IFile file, IGSPSearchRequestor requestor) { if (model instanceof GSPStructuredModel) { GSPStructuredModel gspModel = (GSPStructuredModel) model; IDOMDocument dom = gspModel.getDocument(); visitDOMNode(dom, file, requestor); } } /** * @param domNode * @param matches */ private void visitDOMNode(Node domNode, IFile file, IGSPSearchRequestor requestor) { if (domNode instanceof ElementImpl) { CMElementDeclaration decl = getDeclaration((Element) domNode); if (decl instanceof AbstractGSPTag) { if (matchesTag((AbstractGSPTag) decl)) { ElementImpl impl = (ElementImpl) domNode; IStructuredDocumentRegion region = impl.getStartStructuredDocumentRegion(); if (region.getNumberOfRegions() > 2 && region.getRegions().get(1).getType() == DOMRegionContext.XML_TAG_NAME) { ITextRegion nameRegion = region.getRegions().get(1); // since the name may include a prefix, start from the end of the name region and work back requestor.acceptMatch(file, region.getStart() + nameRegion.getTextEnd() - tagName.length(), tagName.length()); } // now find the end region IStructuredDocumentRegion lastRegion = impl.getEndStructuredDocumentRegion(); if (lastRegion != null && region != lastRegion && region.getNumberOfRegions() > 2 && lastRegion.getRegions().get(1).getType() == DOMRegionContext.XML_TAG_NAME) { ITextRegion nameRegion = lastRegion.getRegions().get(1); // since the name may include a prefix, start from the end of the name region and work back requestor.acceptMatch(file, lastRegion.getStart() + nameRegion.getTextEnd() - tagName.length(), tagName.length()); } } } } NodeList children = domNode.getChildNodes(); if (children != null) { int length = children.getLength(); for (int i = 0; i < length; i++) { visitDOMNode(children.item(i), file, requestor); } } } /** * @param decl * @return */ private boolean matchesTag(AbstractGSPTag decl) { return decl.getTagDefinitionHandle() != null && decl.getTagDefinitionHandle().equals(tagField.getHandleIdentifier()); } public CMElementDeclaration getDeclaration(Element target) { Document doc = target.getOwnerDocument(); ModelQuery query = ModelQueryUtil.getModelQuery(doc); return query.getCMElementDeclaration(target); } }