amywu | 547e8e1 | 2007-03-14 20:16:31 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2006, 2007 IBM Corporation and others. |
| 3 | * All rights reserved. This program and the accompanying materials |
| 4 | * are made available under the terms of the Eclipse Public License v1.0 |
| 5 | * which accompanies this distribution, and is available at |
| 6 | * http://www.eclipse.org/legal/epl-v10.html |
| 7 | * |
| 8 | * Contributors: |
| 9 | * IBM Corporation - initial API and implementation |
| 10 | *******************************************************************************/ |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 11 | package org.eclipse.jst.jsp.ui.internal.hyperlink; |
| 12 | |
| 13 | import java.util.ArrayList; |
| 14 | import java.util.List; |
| 15 | |
| 16 | import org.eclipse.core.resources.IFile; |
| 17 | import org.eclipse.core.resources.ResourcesPlugin; |
| 18 | import org.eclipse.core.runtime.Path; |
| 19 | import org.eclipse.jdt.core.IField; |
| 20 | import org.eclipse.jdt.core.IJavaElement; |
| 21 | import org.eclipse.jdt.core.ILocalVariable; |
| 22 | import org.eclipse.jdt.core.IMethod; |
| 23 | import org.eclipse.jdt.core.ISourceRange; |
| 24 | import org.eclipse.jdt.core.ISourceReference; |
| 25 | import org.eclipse.jdt.core.JavaModelException; |
| 26 | import org.eclipse.jface.text.BadLocationException; |
| 27 | import org.eclipse.jface.text.IDocument; |
| 28 | import org.eclipse.jface.text.IRegion; |
| 29 | import org.eclipse.jface.text.ITextViewer; |
| 30 | import org.eclipse.jface.text.Region; |
amywu | 547e8e1 | 2007-03-14 20:16:31 +0000 | [diff] [blame] | 31 | import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 32 | import org.eclipse.jface.text.hyperlink.IHyperlink; |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 33 | import org.eclipse.jst.jsp.core.internal.java.IJSPTranslation; |
| 34 | import org.eclipse.jst.jsp.core.internal.java.JSPTranslation; |
| 35 | import org.eclipse.jst.jsp.core.internal.java.JSPTranslationAdapter; |
| 36 | import org.eclipse.jst.jsp.ui.internal.Logger; |
david_williams | b5d0563 | 2006-02-27 09:24:00 +0000 | [diff] [blame] | 37 | import org.eclipse.wst.sse.core.StructuredModelManager; |
david_williams | 4ad020f | 2005-04-18 08:00:30 +0000 | [diff] [blame] | 38 | import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
nitind | d7870fd | 2006-09-13 01:57:38 +0000 | [diff] [blame] | 39 | import org.eclipse.wst.sse.core.internal.util.URIResolver; |
david_williams | 4ad020f | 2005-04-18 08:00:30 +0000 | [diff] [blame] | 40 | import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| 41 | import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 42 | |
| 43 | /** |
| 44 | * Detects hyperlinks in JSP Java content |
| 45 | */ |
amywu | 547e8e1 | 2007-03-14 20:16:31 +0000 | [diff] [blame] | 46 | public class JSPJavaHyperlinkDetector extends AbstractHyperlinkDetector { |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 47 | |
| 48 | private IHyperlink createHyperlink(IJavaElement element, IRegion region, IDocument document) { |
| 49 | IHyperlink link = null; |
| 50 | if (region != null) { |
| 51 | // open local variable in the JSP file... |
| 52 | if (element instanceof ISourceReference) { |
| 53 | IFile file = null; |
| 54 | int jspOffset = 0; |
| 55 | IStructuredModel sModel = null; |
| 56 | |
| 57 | // try to locate the file in the workspace |
| 58 | try { |
| 59 | sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document); |
| 60 | if (sModel != null) { |
nitind | d7870fd | 2006-09-13 01:57:38 +0000 | [diff] [blame] | 61 | URIResolver resolver = sModel.getResolver(); |
amywu | 547e8e1 | 2007-03-14 20:16:31 +0000 | [diff] [blame] | 62 | if (resolver != null) { |
nitind | d7870fd | 2006-09-13 01:57:38 +0000 | [diff] [blame] | 63 | String uriString = resolver.getFileBaseLocation(); |
| 64 | file = getFile(uriString); |
| 65 | } |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 66 | } |
| 67 | } |
| 68 | finally { |
| 69 | if (sModel != null) |
| 70 | sModel.releaseFromRead(); |
| 71 | } |
| 72 | |
| 73 | // get Java range, translate coordinate to JSP |
| 74 | |
| 75 | try { |
| 76 | ISourceRange range = null; |
| 77 | IJSPTranslation jspTranslation = getJSPTranslation(document); |
| 78 | if (jspTranslation != null) { |
| 79 | // link to local variable definitions |
| 80 | if (element instanceof ILocalVariable) { |
| 81 | range = ((ILocalVariable) element).getNameRange(); |
| 82 | } |
| 83 | // linking to fields of the same compilation unit |
| 84 | else if (element.getElementType() == IJavaElement.FIELD) { |
| 85 | Object cu = ((IField) element).getCompilationUnit(); |
| 86 | if (cu != null && cu.equals(jspTranslation.getCompilationUnit())) |
| 87 | range = ((ISourceReference) element).getSourceRange(); |
| 88 | } |
| 89 | // linking to methods of the same compilation unit |
| 90 | else if (element.getElementType() == IJavaElement.METHOD) { |
| 91 | Object cu = ((IMethod) element).getCompilationUnit(); |
| 92 | if (cu != null && cu.equals(jspTranslation.getCompilationUnit())) |
| 93 | range = ((ISourceReference) element).getSourceRange(); |
| 94 | } |
| 95 | } |
| 96 | |
amywu | 65ab20a | 2006-04-28 21:41:03 +0000 | [diff] [blame] | 97 | if (range != null && file != null) { |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 98 | jspOffset = jspTranslation.getJspOffset(range.getOffset()); |
| 99 | if (jspOffset >= 0) { |
| 100 | link = new WorkspaceFileHyperlink(region, file, new Region(jspOffset, range.getLength())); |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | catch (JavaModelException jme) { |
| 105 | Logger.log(Logger.WARNING_DEBUG, jme.getMessage(), jme); |
| 106 | } |
| 107 | } |
| 108 | if (link == null) { |
| 109 | link = new JSPJavaHyperlink(region, element); |
| 110 | } |
| 111 | } |
| 112 | return link; |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | * (non-Javadoc) |
| 117 | * |
| 118 | * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, |
| 119 | * org.eclipse.jface.text.IRegion, boolean) |
| 120 | */ |
| 121 | public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { |
| 122 | List hyperlinks = new ArrayList(0); |
| 123 | |
| 124 | if (region != null && textViewer != null) { |
| 125 | IDocument document = textViewer.getDocument(); |
| 126 | |
| 127 | // check and make sure this is a valid Java type |
| 128 | JSPTranslation jspTranslation = getJSPTranslation(document); |
| 129 | if (jspTranslation != null) { |
| 130 | // check if we are in JSP Java content |
| 131 | int javaOffset = jspTranslation.getJavaOffset(region.getOffset()); |
| 132 | if (javaOffset > -1) { |
| 133 | // check that we are not in indirect Java content (like |
| 134 | // included files) |
| 135 | if (!jspTranslation.isIndirect(javaOffset)) { |
| 136 | // get Java elements |
| 137 | IJavaElement[] elements = jspTranslation.getElementsFromJspRange(region.getOffset(), region.getOffset() + region.getLength()); |
| 138 | if (elements != null && elements.length > 0) { |
| 139 | // create a JSPJavaHyperlink for each Java element |
| 140 | for (int i = 0; i < elements.length; ++i) { |
| 141 | IJavaElement element = elements[i]; |
| 142 | |
| 143 | // find hyperlink range for Java element |
| 144 | IRegion hyperlinkRegion = selectWord(document, region.getOffset()); |
| 145 | IHyperlink link = createHyperlink(element, hyperlinkRegion, document); |
| 146 | if (link != null) { |
| 147 | hyperlinks.add(link); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | if (hyperlinks.size() == 0) |
| 157 | return null; |
| 158 | return (IHyperlink[]) hyperlinks.toArray(new IHyperlink[0]); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Returns an IFile from the given uri if possible, null if cannot find |
| 163 | * file from uri. |
| 164 | * |
| 165 | * @param fileString |
| 166 | * file system path |
| 167 | * @return returns IFile if fileString exists in the workspace |
| 168 | */ |
| 169 | private IFile getFile(String fileString) { |
| 170 | IFile file = null; |
| 171 | |
| 172 | if (fileString != null) { |
nitind | a9d7a59 | 2007-09-06 11:36:55 +0000 | [diff] [blame] | 173 | Path filePath = new Path(fileString); |
| 174 | if (filePath.segmentCount() > 1 && ResourcesPlugin.getWorkspace().getRoot().getFile(filePath).exists()) { |
| 175 | return ResourcesPlugin.getWorkspace().getRoot().getFile(filePath); |
| 176 | } |
| 177 | IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(filePath); |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 178 | for (int i = 0; i < files.length && file == null; i++) |
| 179 | if (files[i].exists()) |
| 180 | file = files[i]; |
| 181 | } |
| 182 | |
| 183 | return file; |
| 184 | } |
amywu | 547e8e1 | 2007-03-14 20:16:31 +0000 | [diff] [blame] | 185 | |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 186 | /** |
| 187 | * Get JSP translation object |
| 188 | * |
| 189 | * @return JSPTranslation if one exists, null otherwise |
| 190 | */ |
| 191 | private JSPTranslation getJSPTranslation(IDocument document) { |
| 192 | JSPTranslation translation = null; |
| 193 | |
david_williams | c39caaf | 2005-04-05 06:07:16 +0000 | [diff] [blame] | 194 | IDOMModel xmlModel = null; |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 195 | try { |
david_williams | c39caaf | 2005-04-05 06:07:16 +0000 | [diff] [blame] | 196 | xmlModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(document); |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 197 | if (xmlModel != null) { |
david_williams | c39caaf | 2005-04-05 06:07:16 +0000 | [diff] [blame] | 198 | IDOMDocument xmlDoc = xmlModel.getDocument(); |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 199 | JSPTranslationAdapter adapter = (JSPTranslationAdapter) xmlDoc.getAdapterFor(IJSPTranslation.class); |
| 200 | if (adapter != null) { |
pavery | 9e8f240 | 2005-11-10 04:42:03 +0000 | [diff] [blame] | 201 | translation = adapter.getJSPTranslation(); |
nitind | d4f81c0 | 2005-02-07 23:11:25 +0000 | [diff] [blame] | 202 | } |
| 203 | } |
| 204 | } |
| 205 | finally { |
| 206 | if (xmlModel != null) |
| 207 | xmlModel.releaseFromRead(); |
| 208 | } |
| 209 | return translation; |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Java always selects word when defining region |
| 214 | * |
| 215 | * @param document |
| 216 | * @param anchor |
| 217 | * @return IRegion |
| 218 | */ |
| 219 | private IRegion selectWord(IDocument document, int anchor) { |
| 220 | |
| 221 | try { |
| 222 | int offset = anchor; |
| 223 | char c; |
| 224 | |
| 225 | while (offset >= 0) { |
| 226 | c = document.getChar(offset); |
| 227 | if (!Character.isJavaIdentifierPart(c)) |
| 228 | break; |
| 229 | --offset; |
| 230 | } |
| 231 | |
| 232 | int start = offset; |
| 233 | |
| 234 | offset = anchor; |
| 235 | int length = document.getLength(); |
| 236 | |
| 237 | while (offset < length) { |
| 238 | c = document.getChar(offset); |
| 239 | if (!Character.isJavaIdentifierPart(c)) |
| 240 | break; |
| 241 | ++offset; |
| 242 | } |
| 243 | |
| 244 | int end = offset; |
| 245 | |
| 246 | if (start == end) |
| 247 | return new Region(start, 0); |
| 248 | |
| 249 | return new Region(start + 1, end - start - 1); |
| 250 | |
| 251 | } |
| 252 | catch (BadLocationException x) { |
| 253 | return null; |
| 254 | } |
| 255 | } |
| 256 | } |