blob: 2376a56e7fe306c8cf94273606d0f00f05216d7c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.click.eclipse.ui.editor;
import org.apache.click.eclipse.ClickPlugin;
import org.apache.click.eclipse.ClickUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.ui.JavaUI;
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.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.ui.ide.IDE;
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.utils.StringUtils;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* The <code>IHyperlinkDetector</code> implementation
* which provides hyperlink for classes and page path.
*
* @author Naoki Takezoe
*/
public class ClickXMLHyperlinkDetector implements IHyperlinkDetector {
public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
if (region == null || textViewer == null) {
return null;
}
IDocument document = textViewer.getDocument();
Node currentNode = getCurrentNode(document, region.getOffset());
if (currentNode != null) {
short nodeType = currentNode.getNodeType();
if (nodeType == Node.DOCUMENT_TYPE_NODE) {
// nothing to do
} else if (nodeType == Node.ELEMENT_NODE) {
// element nodes
Attr currentAttr = getCurrentAttrNode(currentNode, region.getOffset());
if (currentAttr != null){
IRegion hyperlinkRegion = getHyperlinkRegion(currentAttr);
IHyperlink hyperLink = createHyperlinkForClass(
currentAttr.getName(), currentAttr.getNodeValue(), hyperlinkRegion, document);
if (hyperLink != null) {
return new IHyperlink[] { hyperLink };
}
}
}
}
return null;
}
/**
* Returns the attribute node within node at offset
*/
private Attr getCurrentAttrNode(Node node, int offset) {
if ((node instanceof IndexedRegion)
&& ((IndexedRegion) node).contains(offset)
&& (node.hasAttributes())) {
NamedNodeMap attrs = node.getAttributes();
// go through each attribute in node and if attribute contains
// offset, return that attribute
for (int i = 0; i < attrs.getLength(); ++i) {
// assumption that if parent node is of type IndexedRegion,
// then its attributes will also be of type IndexedRegion
IndexedRegion attRegion = (IndexedRegion) attrs.item(i);
if (attRegion.contains(offset)) {
return (Attr) attrs.item(i);
}
}
}
return null;
}
/**
* 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);
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;
}
private IRegion getHyperlinkRegion(Node node) {
IRegion hyperRegion = null;
if (node != null) {
short nodeType = node.getNodeType();
if (nodeType == Node.DOCUMENT_TYPE_NODE
|| nodeType == Node.ELEMENT_NODE
|| nodeType == Node.TEXT_NODE) {
// handle doc type node
IDOMNode docNode = (IDOMNode) node;
hyperRegion = new Region(docNode.getStartOffset(),
docNode.getEndOffset() - docNode.getStartOffset());
} else if (nodeType == Node.ATTRIBUTE_NODE) {
// handle attribute nodes
IDOMAttr att = (IDOMAttr) node;
// do not include quotes in attribute value region
int regOffset = att.getValueRegionStartOffset();
int regLength = att.getValueRegionText().length();
String attValue = att.getValueRegionText();
if (StringUtils.isQuoted(attValue)) {
regOffset = regOffset + 1;
regLength = regLength - 2;
}
hyperRegion = new Region(regOffset, regLength);
}
}
return hyperRegion;
}
/**
* Create the appropriate hyperlink.
*/
private IHyperlink createHyperlinkForClass(String name, String target,
IRegion hyperlinkRegion, IDocument document) {
IHyperlink link = null;
if (name != null) {
if (ClickPlugin.ATTR_CLASSNAME.equals(name)) {
try {
IFile file = ClickUtils.getResource(document);
IJavaProject project = JavaCore.create(file.getProject());
IType type = project.findType(target);
if (type != null) {
link = new AttributeHyperlink(hyperlinkRegion, type);
}
} catch(Exception ex){
ClickPlugin.log(ex);
}
} else if(ClickPlugin.ATTR_PATH.equals(name)){
try {
IFile file = ClickUtils.getResource(document);
IProject project = file.getProject();
String root = ClickUtils.getWebAppRootFolder(project);
IFile targetFile = project.getFile(new Path(root).append(target));
if(targetFile.exists()){
link = new AttributeHyperlink(hyperlinkRegion, targetFile);
}
} catch(Exception ex){
ClickPlugin.log(ex);
}
}
}
return link;
}
/**
* IHyperlink implementation for the java class and other files.
*/
private class AttributeHyperlink implements IHyperlink {
private final IRegion region;
private final IJavaElement element;
private final IFile file;
/**
* Creates a new Java element hyperlink.
*/
public AttributeHyperlink(IRegion region, IJavaElement element) {
this.region = region;
this.element = element;
this.file = null;
}
/**
* Creates a new Java element hyperlink.
*/
public AttributeHyperlink(IRegion region, IFile file) {
this.region = region;
this.element = null;
this.file = file;
}
public IRegion getHyperlinkRegion() {
return this.region;
}
/**
* opens the standard Java Editor for the given IJavaElement
*/
public void open() {
if (this.element != null) {
try {
JavaUI.revealInEditor(JavaUI.openInEditor(element), element);
} catch (Exception e) {
}
}
if(this.file != null){
try {
IDE.openEditor(ClickUtils.getActivePage(), file);
} catch(Exception ex){
}
}
}
public String getTypeLabel() {
return null;
}
public String getHyperlinkText() {
return null;
}
}
}