1 19 20 package org.netbeans.modules.web.core.syntax; 21 22 23 import java.awt.Toolkit ; 24 import java.io.IOException ; 25 import java.util.Collections ; 26 import java.util.List ; 27 import java.util.StringTokenizer ; 28 import java.util.logging.Level ; 29 import java.util.logging.Logger ; 30 import javax.lang.model.element.TypeElement; 31 import javax.servlet.jsp.tagext.TagFileInfo ; 32 import javax.servlet.jsp.tagext.TagInfo ; 33 import javax.servlet.jsp.tagext.TagLibraryInfo ; 34 import javax.swing.text.BadLocationException ; 35 import javax.swing.text.Document ; 36 import javax.swing.text.JTextComponent ; 37 import org.netbeans.api.java.source.CancellableTask; 38 import org.netbeans.api.java.source.ClasspathInfo; 39 import org.netbeans.api.java.source.CompilationController; 40 import org.netbeans.api.java.source.CompilationInfo; 41 import org.netbeans.api.java.source.JavaSource; 42 import org.netbeans.api.java.source.JavaSource.Phase; 43 import org.netbeans.api.java.source.UiUtils; 44 import org.netbeans.api.jsp.lexer.JspTokenId; 45 import org.netbeans.api.lexer.Token; 46 import org.netbeans.api.lexer.TokenHierarchy; 47 import org.netbeans.api.lexer.TokenSequence; 48 import org.netbeans.editor.BaseDocument; 49 import org.netbeans.editor.SyntaxSupport; 50 import org.netbeans.editor.Utilities; 51 import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider; 52 import org.netbeans.modules.editor.NbEditorUtilities; 53 import org.netbeans.modules.el.lexer.api.ELTokenId; 54 import org.netbeans.modules.web.core.syntax.completion.ELExpression; 55 import org.openide.ErrorManager; 56 import org.openide.awt.StatusDisplayer; 57 import org.openide.cookies.EditCookie; 58 import org.openide.filesystems.FileObject; 59 import org.openide.loaders.DataObject; 60 import org.openide.loaders.DataObjectNotFoundException; 61 import org.openide.nodes.Node; 62 import org.openide.util.NbBundle; 63 64 70 public class JSPHyperlinkProvider implements HyperlinkProvider { 71 private static final Logger logger = Logger.getLogger(JSPHyperlinkProvider.class.getName()); 72 85 public boolean isHyperlinkPoint(Document doc, int offset){ 86 if (!(doc instanceof BaseDocument)) 87 return false; 88 89 try { 90 BaseDocument bdoc = (BaseDocument) doc; 91 JTextComponent target = Utilities.getFocusedComponent(); 92 93 if (target == null || target.getDocument() != bdoc) { 94 return false; 95 } 96 97 SyntaxSupport sup = bdoc.getSyntaxSupport(); 98 JspSyntaxSupport jspSup = (JspSyntaxSupport)sup.get(JspSyntaxSupport.class); 99 100 TokenHierarchy tokenHierarchy = TokenHierarchy.get(bdoc); 101 TokenSequence tokenSequence = tokenHierarchy.tokenSequence(); 102 tokenSequence.move(offset); 103 if (!tokenSequence.moveNext() && !tokenSequence.movePrevious()) { 104 return false; } 106 Token token = tokenSequence.token(); 107 108 if (token.id() == JspTokenId.ATTR_VALUE){ 109 SyntaxElement syntaxElement = jspSup.getElementChain(offset); 110 if(syntaxElement != null) { 111 if(syntaxElement.getCompletionContext() == JspSyntaxSupport.DIRECTIVE_COMPLETION_CONTEXT) { 112 if(((SyntaxElement.Directive)syntaxElement).getName().equals("include")) { 114 while (tokenSequence.movePrevious() && tokenSequence.token().id() != JspTokenId.TAG) { 116 if(tokenSequence.token().id() == JspTokenId.ATTRIBUTE) { 117 if(tokenSequence.token().text().toString().equals("file")) { 118 return true; 119 } else { 120 return false; 121 } 122 } 123 124 } 125 } 126 } 127 if(syntaxElement.getCompletionContext() == JspSyntaxSupport.TAG_COMPLETION_CONTEXT) { 128 while (tokenSequence.movePrevious() && tokenSequence.token().id() != JspTokenId.TAG) { 130 if(tokenSequence.token().id() == JspTokenId.ATTRIBUTE) { 131 String attributeName = tokenSequence.token().text().toString(); 132 String tagName = ((SyntaxElement.Tag)syntaxElement).getName(); 133 134 if("jsp:include".equals(tagName) && "page".equals(attributeName)) { 135 return true; 137 } 138 139 if("jsp:useBean".equals(tagName) && 140 ("type".equals(attributeName) || "class".equals(attributeName))) { 141 return true; 143 } 144 } 145 } 146 } 147 } 148 } 149 150 tokenSequence.move(offset); if(!tokenSequence.moveNext()) { 153 return false; } 155 TokenSequence elTokenSequence = tokenSequence.embedded(ELTokenId.language()); 156 if (elTokenSequence != null){ 157 ELExpression exp = new ELExpression((JspSyntaxSupport)bdoc.getSyntaxSupport()); 158 elTokenSequence.move(offset); 159 if(!elTokenSequence.moveNext()) { 160 return false; } 162 163 if (elTokenSequence.token().id() == ELTokenId.DOT){ 164 return false; 165 } 166 167 int endOfEL = elTokenSequence.offset() + elTokenSequence.token().length(); 168 int res = exp.parse(endOfEL); 169 if (res == ELExpression.EL_START) { 170 res = res = exp.parse(endOfEL + 1); 171 } 172 return res == ELExpression.EL_BEAN; 173 } 174 return (getTagFile(tokenSequence, jspSup) != null); 176 177 } catch (BadLocationException e) { 178 ErrorManager.getDefault().notify(e); 179 return false; 180 } 181 } 182 183 197 public int[] getHyperlinkSpan(Document doc, int offset){ 198 if (!(doc instanceof BaseDocument)) { 199 return null; 200 } 201 202 BaseDocument bdoc = (BaseDocument) doc; 203 JTextComponent target = Utilities.getFocusedComponent(); 204 205 if (target == null || target.getDocument() != bdoc) 206 return null; 207 208 SyntaxSupport sup = bdoc.getSyntaxSupport(); 209 JspSyntaxSupport jspSup = (JspSyntaxSupport)sup.get(JspSyntaxSupport.class); 210 211 TokenHierarchy tokenHierarchy = TokenHierarchy.get(bdoc); 212 TokenSequence tokenSequence = tokenHierarchy.tokenSequence(); 213 tokenSequence.move(offset); 214 if (!tokenSequence.moveNext() && !tokenSequence.movePrevious()) { 215 return null; } 217 Token token = tokenSequence.token(); 218 219 if (getTagFile(tokenSequence, jspSup) != null){ 220 return new int[]{token.offset(tokenHierarchy), token.offset(tokenHierarchy) + token.length()-1}; 222 } else{ 223 TokenSequence elTokenSequence = tokenSequence.embedded(ELTokenId.language()); 225 226 if (elTokenSequence != null){ 227 ELExpression exp = new ELExpression((JspSyntaxSupport)bdoc.getSyntaxSupport()); 228 elTokenSequence.move(offset); 229 if(!elTokenSequence.moveNext()) { 230 return null; } 232 233 int elEnd = elTokenSequence.offset() + elTokenSequence.token().length(); 234 int res = exp.parse(elEnd); 235 236 if (res == ELExpression.EL_BEAN || res == ELExpression.EL_START ){ 237 return new int[] {elTokenSequence.offset(), elEnd}; 238 } 239 } 240 int tokenOffset = 0; 243 String image = token.text().toString(); 244 while (tokenOffset < image.length() && bdoc.isWhitespace(image.charAt(tokenOffset))) 245 tokenOffset++; 246 tokenOffset++; 247 return new int[]{token.offset(tokenHierarchy)+tokenOffset, token.offset(tokenHierarchy) + token.length()-2}; 248 } 249 250 } 251 252 263 public void performClickAction(Document doc, int offset){ 264 try { 265 BaseDocument bdoc = (BaseDocument) doc; 266 JTextComponent target = Utilities.getFocusedComponent(); 267 268 if (target == null || target.getDocument() != bdoc) 269 return; 270 271 SyntaxSupport sup = bdoc.getSyntaxSupport(); 272 JspSyntaxSupport jspSup = (JspSyntaxSupport)sup.get(JspSyntaxSupport.class); 273 274 TokenHierarchy tokenHierarchy = TokenHierarchy.get(bdoc); 275 TokenSequence tokenSequence = tokenHierarchy.tokenSequence(); 276 tokenSequence.move(offset); 277 if (!tokenSequence.moveNext() && !tokenSequence.movePrevious()) { 278 return; } 280 Token token = tokenSequence.token(); 281 282 TokenSequence elTokenSequence = tokenSequence.embedded(ELTokenId.language()); 284 if (elTokenSequence != null){ 285 ELExpression exp = new ELExpression((JspSyntaxSupport)bdoc.getSyntaxSupport()); 286 287 elTokenSequence.move(offset); 288 if(!elTokenSequence.moveNext()) { 289 return ; } 291 292 int elEnd = elTokenSequence.offset() + elTokenSequence.token().length(); 293 int res = exp.parse(elEnd); 294 if (res == ELExpression.EL_START ){ 295 navigateToUserBeanDef(doc, jspSup, target, elTokenSequence.token().text().toString()); 296 return; 297 } 298 if (res == ELExpression.EL_BEAN){ 299 if (!exp.gotoPropertyDeclaration(exp.getObjectClass())){ 300 gotoSourceFailed(); 301 } 302 } 303 return; 304 } 305 306 while (tokenSequence.token().id() != JspTokenId.TAG && !"jsp:useBean".equals(tokenSequence.token().text().toString()) && tokenSequence.movePrevious()); 308 309 if (tokenSequence.index() != -1 && tokenSequence.token().id() == JspTokenId.TAG){ 310 String className = token.text().toString().substring(1, token.length()-1).trim(); 312 313 GoToTypeDefTask gotoTask = new GoToTypeDefTask(className); 314 315 ClasspathInfo cpInfo = ClasspathInfo.create(jspSup.getFileObject()); 316 JavaSource source = JavaSource.create(cpInfo, Collections.EMPTY_LIST); 317 318 try{ 319 source.runUserActionTask(gotoTask, true); 320 } catch (IOException e){ 321 logger.log(Level.SEVERE, e.getMessage(), e); 322 } 323 } 324 325 tokenSequence.move(offset); if(!tokenSequence.moveNext()) { 327 return ; } 329 330 FileObject fObj = getTagFile(tokenSequence, jspSup); 331 if ( fObj != null) 332 openInEditor(fObj); 333 else { 334 String path = token.text().toString(); 335 path = path.substring(path.indexOf('"') +1); 336 path = path.substring(0, path.indexOf('"')); 337 338 fObj = getFileObject(doc, path); 339 if (fObj != null) { 340 openInEditor(fObj); 341 } else { 342 String msg = NbBundle.getMessage(JSPHyperlinkProvider.class, "LBL_file_not_found", path); StatusDisplayer.getDefault().setStatusText(msg); 345 } 346 347 } 348 } catch (BadLocationException e) { 349 ErrorManager.getDefault().notify(e); 350 } 351 } 352 353 354 private FileObject getFileObject(Document doc, String path){ 355 DataObject dobj = NbEditorUtilities.getDataObject(doc); 357 FileObject fobj = (dobj != null) ? NbEditorUtilities.getDataObject(doc).getPrimaryFile(): null; 358 359 if (fobj != null){ 360 return getFileObject(doc, fobj, path); 361 } 362 return null; 363 } 364 371 private FileObject getFileObject(Document doc,FileObject file, String path){ 372 if (path == null) return file; 374 path = path.trim(); 375 FileObject find = file; 376 if (!file.isFolder()) find = file.getParent(); 378 379 if (path.charAt(0) == '/'){ find = JspUtils.guessWebModuleRoot(doc, file); if (find == null) 382 return null; 384 path = path.substring(1); } 386 StringTokenizer st = new StringTokenizer (path, "/"); 388 String token; 389 while (find != null && st.hasMoreTokens()) { 390 token = st.nextToken(); 391 if ("..".equals(token)) find = find.getParent(); 393 else if (!".".equals(token)) find = find.getFileObject(token); 395 } 396 return find; 397 398 } 399 400 private String getTagName(String tagwithprefix){ 401 int index = tagwithprefix.indexOf(':'); 402 if (index > 0) 403 return tagwithprefix.substring(index+1); 404 else 405 return tagwithprefix; 406 } 407 408 private void openInEditor(FileObject fObj){ 409 if (fObj != null){ 410 DataObject dobj = null; 411 try{ 412 dobj = DataObject.find(fObj); 413 } catch (DataObjectNotFoundException e){ 414 ErrorManager.getDefault().notify(e); 415 return; 416 } 417 if (dobj != null){ 418 Node.Cookie cookie = dobj.getCookie(EditCookie.class); 419 if (cookie != null) 420 ((EditCookie)cookie).edit(); 421 } 422 } 423 } 424 425 private FileObject getTagFile(TokenSequence tokenSequence, JspSyntaxSupport jspSup){ 426 Token token = tokenSequence.token(); 427 if(token.id() == JspTokenId.TAG) { 428 String image = token.text().toString().trim(); 429 if (!image.startsWith("jsp:") && image.indexOf(':') != -1){ List l = jspSup.getTags(image); 431 if (l.size() == 1){ 432 TagLibraryInfo libInfo = ((TagInfo )l.get(0)).getTagLibrary(); 433 if (libInfo != null){ 434 TagFileInfo fileInfo = libInfo.getTagFile(getTagName(image)); 435 if (fileInfo != null) 436 return getFileObject(jspSup.getDocument(), fileInfo.getPath()); 437 } 438 } 439 } 440 } 441 return null; 442 } 443 444 446 private void navigateToUserBeanDef(Document doc, JspSyntaxSupport jspSup, JTextComponent target, String bean) 447 throws BadLocationException { 448 String text = doc.getText(0, doc.getLength()); 449 int index = text.indexOf(bean); 450 TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc); 451 TokenSequence tokenSequence = tokenHierarchy.tokenSequence(); 452 453 while (index > 0){ 454 tokenSequence.move(index); 455 if (!tokenSequence.moveNext() && !tokenSequence.movePrevious()) { 456 return; } 458 Token token = tokenSequence.token(); 459 460 if (token.id() == JspTokenId.ATTR_VALUE ){ 461 462 while (!(token.id() == JspTokenId.ATTRIBUTE 463 && (token.text().toString().equals("class") || token.text().toString().equals("type"))) 464 && !(token.id() == JspTokenId.SYMBOL 465 && token.text().toString().equals("/>")) && tokenSequence.moveNext()) { 466 token = tokenSequence.token(); 467 } 468 469 if(tokenSequence.index() != -1 && token.id() == JspTokenId.SYMBOL) { 470 while (!(token.id() == JspTokenId.ATTRIBUTE 471 && (token.text().toString().equals("class") || token.text().toString().equals("type"))) 472 && !(token.id() != JspTokenId.SYMBOL 473 && token.text().toString().equals("<")) && tokenSequence.movePrevious()) { 474 token = tokenSequence.token(); 475 } 476 } 477 478 if (tokenSequence.index() != -1 && token.id() == JspTokenId.ATTRIBUTE){ 479 while (token.id() != JspTokenId.ATTR_VALUE && tokenSequence.moveNext()) { 480 token = tokenSequence.token(); 481 } 482 } 483 484 if (tokenSequence.index() != -1 && token.id() == JspTokenId.ATTR_VALUE){ 485 target.setCaretPosition(token.offset(tokenHierarchy)+1); 486 break; 487 } 488 } 489 index = text.indexOf(bean, index + bean.length()); 490 } 491 } 492 493 private void gotoSourceFailed(){ 494 String msg = NbBundle.getBundle(JSPHyperlinkProvider.class).getString("MSG_source_not_found"); 495 StatusDisplayer.getDefault().setStatusText(msg); 496 Toolkit.getDefaultToolkit().beep(); 497 } 498 499 private class GoToTypeDefTask implements CancellableTask<CompilationController>{ 500 private String className; 501 502 GoToTypeDefTask(String className){ 503 this.className = className; 504 } 505 506 public void run(CompilationController parameter) throws Exception { 507 parameter.toPhase(Phase.ELEMENTS_RESOLVED); 508 TypeElement type = parameter.getElements().getTypeElement(className); 509 510 if (type != null){ 511 if (!UiUtils.open(parameter.getClasspathInfo(), type)){ 512 gotoSourceFailed(); 513 } 514 } else{ 515 logger.fine("could not resolve " + className); } 517 } 518 519 public void cancel(){}; 520 } 521 } 522 | Popular Tags |