KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > web > core > syntax > JSPHyperlinkProvider


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.web.core.syntax;
21
22
23 import java.awt.Toolkit JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28 import java.util.logging.Level JavaDoc;
29 import java.util.logging.Logger JavaDoc;
30 import javax.lang.model.element.TypeElement;
31 import javax.servlet.jsp.tagext.TagFileInfo JavaDoc;
32 import javax.servlet.jsp.tagext.TagInfo JavaDoc;
33 import javax.servlet.jsp.tagext.TagLibraryInfo JavaDoc;
34 import javax.swing.text.BadLocationException JavaDoc;
35 import javax.swing.text.Document JavaDoc;
36 import javax.swing.text.JTextComponent JavaDoc;
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 /**
65  *
66  * @author Petr Pisl
67  * @author Marek.Fukala@Sun.COM
68  * @author Tomasz.Slota@Sun.COM
69  */

70 public class JSPHyperlinkProvider implements HyperlinkProvider {
71     private static final Logger JavaDoc logger = Logger.getLogger(JSPHyperlinkProvider.class.getName());
72     /**
73      * Should determine whether there should be a hyperlink on the given offset
74      * in the given document. May be called any number of times for given parameters.
75      * <br>
76      * This method is called from event dispatch thread.
77      * It should run very fast as it is called very often.
78      *
79      * @param doc document on which to operate.
80      * @param offset &gt;=0 offset to test (it generally should be offset &lt; doc.getLength(), but
81      * the implementations should not depend on it)
82      * @return true if the provided offset should be in a hyperlink
83      * false otherwise
84      */

85     public boolean isHyperlinkPoint(Document JavaDoc doc, int offset){
86         if (!(doc instanceof BaseDocument))
87             return false;
88         
89         try {
90             BaseDocument bdoc = (BaseDocument) doc;
91             JTextComponent JavaDoc 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; //no token found
105
}
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                         // <%@include file="xxx"%> hyperlink usecase
113
if(((SyntaxElement.Directive)syntaxElement).getName().equals("include")) {
114                             //find attribute name
115
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                         //find attribute name
129
while (tokenSequence.movePrevious() && tokenSequence.token().id() != JspTokenId.TAG) {
130                             if(tokenSequence.token().id() == JspTokenId.ATTRIBUTE) {
131                                 String JavaDoc attributeName = tokenSequence.token().text().toString();
132                                 String JavaDoc tagName = ((SyntaxElement.Tag)syntaxElement).getName();
133                                 
134                                 if("jsp:include".equals(tagName) && "page".equals(attributeName)) {
135                                     //<jsp:include page="xxx"/> usecase
136
return true;
137                                 }
138                                 
139                                 if("jsp:useBean".equals(tagName) &&
140                                         ("type".equals(attributeName) || "class".equals(attributeName))) {
141                                     //<jsp:useBean class="xxx" type="yyy"/> usecase
142
return true;
143                                 }
144                             }
145                         }
146                     }
147                 }
148             }
149             
150             // is it a bean in EL?
151
tokenSequence.move(offset); //reset tokenSequence
152
if(!tokenSequence.moveNext()) {
153                 return false; //no token
154
}
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; //no token
161
}
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             // is the a reachable tag file?
175
return (getTagFile(tokenSequence, jspSup) != null);
176             
177         } catch (BadLocationException JavaDoc e) {
178             ErrorManager.getDefault().notify(e);
179             return false;
180         }
181     }
182     
183     /**
184      * Should determine the span of hyperlink on given offset. Generally, if
185      * isHyperlinkPoint returns true for a given parameters, this class should
186      * return a valid span, but it is not strictly required.
187      * <br>
188      * This method is called from event dispatch thread.
189      * This method should run very fast as it is called very often.
190      *
191      * @param doc document on which to operate.
192      * @param offset &gt;=0 offset to test (it generally should be offset &lt; doc.getLength(), but
193      * the implementations should not depend on it)
194      * @return a two member array which contains starting and ending offset of a hyperlink
195      * that should be on a given offset
196      */

197     public int[] getHyperlinkSpan(Document JavaDoc doc, int offset){
198         if (!(doc instanceof BaseDocument)) {
199             return null;
200         }
201         
202         BaseDocument bdoc = (BaseDocument) doc;
203         JTextComponent JavaDoc 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; //no token found
216
}
217         Token token = tokenSequence.token();
218         
219         if (getTagFile(tokenSequence, jspSup) != null){
220             // a reachable tag file.
221
return new int[]{token.offset(tokenHierarchy), token.offset(tokenHierarchy) + token.length()-1};
222         } else{
223             // is it a bean in EL ?
224
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; //no token
231
}
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             // dynamic or static include, forward.
241
// remove all white space between the value.
242
int tokenOffset = 0;
243             String JavaDoc 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     /**
253      * The implementor should perform an action
254      * corresponding to clicking on the hyperlink on the given offset. The
255      * nature of the action is given by the nature of given hyperlink, but
256      * generally should open some resource or move cursor
257      * to certain place in the current document.
258      *
259      * @param doc document on which to operate.
260      * @param offset &gt;=0 offset to test (it generally should be offset &lt; doc.getLength(), but
261      * the implementations should not depend on it)
262      */

263     public void performClickAction(Document JavaDoc doc, int offset){
264         try {
265             BaseDocument bdoc = (BaseDocument) doc;
266             JTextComponent JavaDoc 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; //no token found
279
}
280             Token token = tokenSequence.token();
281             
282             // is it a bean in EL
283
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 ;//not token
290
}
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             // is ti declaration of userBean?
307
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                 //we are in useBean
311
String JavaDoc 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 JavaDoc e){
321                     logger.log(Level.SEVERE, e.getMessage(), e);
322                 }
323             }
324             
325             tokenSequence.move(offset);//reset tokenSequence
326
if(!tokenSequence.moveNext()) {
327                 return ; //no token
328
}
329             
330             FileObject fObj = getTagFile(tokenSequence, jspSup);
331             if ( fObj != null)
332                 openInEditor(fObj);
333             else {
334                 String JavaDoc 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                     // when the file was not found.
343
String JavaDoc msg = NbBundle.getMessage(JSPHyperlinkProvider.class, "LBL_file_not_found", path); //NOI18N
344
StatusDisplayer.getDefault().setStatusText(msg);
345                 }
346                 
347             }
348         } catch (BadLocationException JavaDoc e) {
349             ErrorManager.getDefault().notify(e);
350         }
351     }
352     
353     
354     private FileObject getFileObject(Document JavaDoc doc, String JavaDoc path){
355         //Find out the file object from the document
356
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     /**
365      * This method finds the file object according the path or null if the file object doesn't exist.
366      * @param doc Document, where user perform the hyperlink action.
367      * @param file
368      * @param path
369      * @return
370      */

371     private FileObject getFileObject(Document JavaDoc doc,FileObject file, String JavaDoc path){
372         if (path == null) // it the path is null -> don't find it
373
return file;
374         path = path.trim();
375         FileObject find = file;
376         if (!file.isFolder()) // if the file is not folder, get the parent
377
find = file.getParent();
378         
379         if (path.charAt(0) == '/'){ // is the absolute path in the web module?
380
find = JspUtils.guessWebModuleRoot(doc, file); // find the folder, where the absolut path starts
381
if (find == null)
382                 return null; // we are not able to find out the webmodule root
383

384             path = path.substring(1); // if we have folder, where the webmodule starts, the path can me relative to this folder
385
}
386         // find relative path to the folder
387
StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(path, "/");
388         String JavaDoc token;
389         while (find != null && st.hasMoreTokens()) {
390             token = st.nextToken();
391             if ("..".equals(token)) // move to parent
392
find = find.getParent();
393             else if (!".".equals(token)) // if there is . - don't move
394
find = find.getFileObject(token);
395         }
396         return find;
397         
398     }
399     
400     private String JavaDoc getTagName(String JavaDoc 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 JavaDoc image = token.text().toString().trim();
429             if (!image.startsWith("jsp:") && image.indexOf(':') != -1){ // NOI18N
430
List JavaDoc l = jspSup.getTags(image);
431                 if (l.size() == 1){
432                     TagLibraryInfo JavaDoc libInfo = ((TagInfo JavaDoc)l.get(0)).getTagLibrary();
433                     if (libInfo != null){
434                         TagFileInfo JavaDoc 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     /* Move the cursor to the user bean definition.
445      */

446     private void navigateToUserBeanDef(Document JavaDoc doc, JspSyntaxSupport jspSup, JTextComponent JavaDoc target, String JavaDoc bean)
447             throws BadLocationException JavaDoc {
448         String JavaDoc 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; //no token found
457
}
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 JavaDoc 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 JavaDoc className;
501         
502         GoToTypeDefTask(String JavaDoc className){
503             this.className = className;
504         }
505         
506         public void run(CompilationController parameter) throws Exception JavaDoc {
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); //NOI18N
516
}
517         }
518         
519         public void cancel(){};
520     }
521 }
522
Popular Tags