Skip to main content
summaryrefslogtreecommitdiffstats
blob: ae634a7451668ef2a6f4511e5265e11fa8d7e249 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package org.eclipse.jst.jsp.ui.internal.hyperlink;

import java.io.File;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.URLHyperlink;
import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace;
import org.eclipse.jst.jsp.core.taglib.ITLDRecord;
import org.eclipse.jst.jsp.core.taglib.ITaglibRecord;
import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
import org.eclipse.jst.jsp.core.text.IJSPPartitions;
import org.eclipse.jst.jsp.ui.internal.Logger;
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.IStructuredPartitioning;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
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.Element;
import org.w3c.dom.Node;

/**
 * Detects hyperlinks for taglibs.
 */
public class TaglibHyperlinkDetector implements IHyperlinkDetector {
	private final String HTTP_PROTOCOL = "http://";//$NON-NLS-1$

	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
		IHyperlink hyperlink = null;

		if (textViewer != null && region != null) {
			IDocument doc = textViewer.getDocument();
			if (doc != null) {
				try {
					// check if jsp tag/directive first
					ITypedRegion partition = TextUtilities.getPartition(doc, IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, region.getOffset(), false);
					if (partition != null && partition.getType() == IJSPPartitions.JSP_DIRECTIVE) {
						// check if jsp taglib directive
						Node currentNode = getCurrentNode(doc, region.getOffset());
						if (currentNode != null && currentNode.getNodeType() == Node.ELEMENT_NODE && JSP11Namespace.ElementName.DIRECTIVE_TAGLIB.equalsIgnoreCase(currentNode.getNodeName())) {
							// get the uri attribute
							Attr taglibNode = ((Element) currentNode).getAttributeNode(JSP11Namespace.ATTR_NAME_URI);
							ITaglibRecord reference = TaglibIndex.resolve(getBaseLocationForTaglib(doc), taglibNode.getValue(), false);
							if (reference != null) {
								// handle taglibs
								switch (reference.getRecordType()) {
									case (ITaglibRecord.TLD) : {
										ITLDRecord record = (ITLDRecord) reference;
										String uriString = record.getPath().toString();
										IRegion hyperlinkRegion = getHyperlinkRegion(taglibNode);
										hyperlink = createHyperlink(uriString, hyperlinkRegion, doc, taglibNode);
									}
										break;
									case (ITaglibRecord.JAR) :
									case (ITaglibRecord.URL) : {
										IRegion hyperlinkRegion = getHyperlinkRegion(taglibNode);
										hyperlink = new TaglibJarUriHyperlink(hyperlinkRegion, reference);
									}
								}
							}
						}
					}
				}
				catch (BadLocationException e) {
					Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
				}
			}
		}
		if (hyperlink != null)
			return new IHyperlink[]{hyperlink};
		return null;
	}

	/**
	 * Get the base location from the current model (if within workspace,
	 * location is relative to workspace, otherwise, file system path)
	 */
	private String getBaseLocationForTaglib(IDocument document) {
		String baseLoc = null;

		// get the base location from the current model
		IStructuredModel sModel = null;
		try {
			sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document);
			if (sModel != null) {
				baseLoc = sModel.getBaseLocation();
			}
		}
		finally {
			if (sModel != null) {
				sModel.releaseFromRead();
			}
		}
		return baseLoc;
	}

	// the below methods were copied from URIHyperlinkDetector

	private IRegion getHyperlinkRegion(Node node) {
		IRegion hyperRegion = null;

		if (node != null) {
			short nodeType = node.getNodeType();
			if (nodeType == Node.DOCUMENT_TYPE_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();
				ITextRegion valueRegion = att.getValueRegion();
				if (valueRegion != null) {
					int regLength = valueRegion.getTextLength();
					String attValue = att.getValueRegionText();
					if (StringUtils.isQuoted(attValue)) {
						++regOffset;
						regLength = regLength - 2;
					}
					hyperRegion = new Region(regOffset, regLength);
				}
			}
		}
		return hyperRegion;
	}

	/**
	 * Returns an IFile from the given uri.
	 * 
	 * @param fileString
	 *            workspace-relative path to an existing file in the workspace
	 * @return returns existing IFile
	 */
	private IFile getFile(String fileString) {
		/*
		 * Note at this point, fileString is already guaranteed to be pointing
		 * to an existing file in the workspace, so we can just call getFile.
		 */
		return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileString));
	}

	/**
	 * Create the appropriate hyperlink
	 * 
	 * @param uriString
	 * @param hyperlinkRegion
	 * @return IHyperlink
	 */
	private IHyperlink createHyperlink(String uriString, IRegion hyperlinkRegion, IDocument document, Node node) {
		IHyperlink link = null;

		if (uriString != null) {
			String temp = uriString.toLowerCase();
			if (temp.startsWith(HTTP_PROTOCOL)) {
				// this is a URLHyperlink since this is a web address
				link = new URLHyperlink(hyperlinkRegion, uriString);
			}

			// try to locate the file in the workspace
			IFile file = getFile(uriString);
			if (file != null) {
				// this is a WorkspaceFileHyperlink since file exists in
				// workspace
				link = new WorkspaceFileHyperlink(hyperlinkRegion, file);
			}
			else {
				// this is an ExternalFileHyperlink since file does not exist
				// in workspace
				File externalFile = new File(uriString);
				link = new ExternalFileHyperlink(hyperlinkRegion, externalFile);
			}
		}

		return link;
	}

	/**
	 * 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;
	}
}

Back to the top