/*- * Copyright (C) 2011-2014 by Iwao AVE! * This program is made available under the terms of the MIT License. */ package org.eclipselabs.stlipse.hyperlink; import java.util.Map; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; import org.eclipse.wst.sse.core.utils.StringUtils; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; import org.eclipselabs.stlipse.Activator; import org.eclipselabs.stlipse.cache.BeanPropertyCache; import org.eclipselabs.stlipse.cache.EventProperty; import org.eclipselabs.stlipse.jspeditor.StripesTagUtil; import org.eclipselabs.stlipse.util.ProjectUtil; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * @author Iwao AVE! */ @SuppressWarnings("restriction") public class JspHyperlinkDetector extends AbstractHyperlinkDetector { public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { IHyperlink[] hyperlinks = null; if (textViewer != null && region != null) { IDocument document = textViewer.getDocument(); if (document != null) { Node currentNode = getCurrentNode(document, region.getOffset()); if (currentNode != null && currentNode.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element)currentNode; IStructuredDocumentRegion documentRegion = ((IStructuredDocument)document).getRegionAtCharacterOffset(region.getOffset()); ITextRegion textRegion = documentRegion.getRegionAtCharacterOffset(region.getOffset()); ITextRegion nameRegion = null; ITextRegion valueRegion = null; String tagName = element.getTagName(); String attrName = null; String attrValue = null; if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(textRegion.getType())) { ITextRegionList regions = documentRegion.getRegions(); /* * Could use 2, but there needs to be the tag open and name regions */ int index = regions.indexOf(textRegion); if (index >= 4) { nameRegion = regions.get(index - 2); valueRegion = textRegion; attrName = documentRegion.getText(nameRegion); attrValue = StringUtils.strip(documentRegion.getText(valueRegion)); } } if (attrName != null && attrValue != null) { if ("beanclass".equals(attrName)) { hyperlinks = createHyperlink(document, documentRegion, valueRegion, attrValue); } else if (StripesTagUtil.isEventAttribute(tagName, attrName)) { String beanclass = StripesTagUtil.getBeanclassAttribute(currentNode); hyperlinks = createHyperlink(document, documentRegion, valueRegion, beanclass, attrValue); } else { String beanclass = StripesTagUtil.getParentBeanclass(currentNode, "form", "url", "link"); if (beanclass != null) { hyperlinks = createHyperlink(document, documentRegion, valueRegion, beanclass, attrValue); } } } } } } return hyperlinks; } private IHyperlink[] createHyperlink(IDocument document, IStructuredDocumentRegion documentRegion, ITextRegion valueRegion, String beanclass) { return createHyperlink(document, documentRegion, valueRegion, beanclass, null); } private IHyperlink[] createHyperlink(IDocument document, IStructuredDocumentRegion documentRegion, ITextRegion valueRegion, String beanclass, String attrValue) { IHyperlink[] hyperlinks = null; try { IJavaProject project = ProjectUtil.getProjectFromDocument(document); if (project != null) { IType actionBean = project.findType(beanclass); if (actionBean != null) { int fqnOffset = documentRegion.getStartOffset() + valueRegion.getStart(); int fqnLength = valueRegion.getTextLength(); Map<String, EventProperty> events = BeanPropertyCache.getEventHandlers(project, beanclass); IJavaElement target = searchElement(actionBean, attrValue, events); hyperlinks = new IHyperlink[]{ new JspHyperlink(target, new Region(fqnOffset, fqnLength)) }; } } } catch (JavaModelException e) { Activator.log(Status.WARNING, "Failed to create a hyperlink for " + beanclass, e); } return hyperlinks; } private IJavaElement searchElement(IType actionBean, String attrValue, Map<String, EventProperty> eventHandlers) { IJavaElement target = null; if (attrValue != null) { String elementName = attrValue.replaceAll("[^a-zA-Z\\.\\[]", ""); if (elementName.startsWith("actionBean.")) elementName = elementName.substring(11); final int dotPos = elementName.indexOf('.'); final int bracePos = elementName.indexOf('['); if (dotPos > 0 && (bracePos == -1 || dotPos < bracePos)) elementName = elementName.substring(0, dotPos); else if (bracePos > 0 && (dotPos == -1 || bracePos < dotPos)) elementName = elementName.substring(0, bracePos); // Resolve event name EventProperty eventProperty = eventHandlers.get(elementName); if (eventProperty != null) { elementName = eventProperty.getMethodName(); } // Search method target = actionBean.getMethod(elementName, null); if (target == null || !target.exists()) { // Search field target = actionBean.getField(elementName); } } return target == null ? actionBean : target; } /** * Returns the node the cursor is currently on in the document. null if no node is selected * * @param offset * @return Node either element, doctype, text, or null */ private Node getCurrentNode(IDocument document, int offset) { // get the current node at the offset (returns either: element, // doctype, text) IndexedRegion inode = null; IStructuredModel sModel = null; try { sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document); if (sModel != null) { inode = sModel.getIndexedRegion(offset); if (inode == null) { inode = sModel.getIndexedRegion(offset - 1); } } } finally { if (sModel != null) sModel.releaseFromRead(); } if (inode instanceof Node) { return (Node)inode; } return null; } }