KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > web > core > syntax > completion > ELExpression


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.completion;
21
22 import java.io.IOException JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28 import javax.lang.model.element.ExecutableElement;
29 import javax.lang.model.element.Modifier;
30 import javax.lang.model.element.TypeElement;
31 import javax.lang.model.type.TypeKind;
32 import javax.lang.model.type.TypeMirror;
33 import javax.lang.model.util.ElementFilter;
34 import org.netbeans.api.java.source.CancellableTask;
35 import org.netbeans.api.java.source.ClasspathInfo;
36 import org.netbeans.api.java.source.CompilationController;
37 import org.netbeans.api.java.source.CompilationInfo;
38 import org.netbeans.api.java.source.JavaSource;
39 import org.netbeans.api.java.source.JavaSource.Phase;
40 import org.netbeans.api.java.source.UiUtils;
41 import org.netbeans.api.jsp.lexer.JspTokenId;
42 import org.netbeans.api.lexer.Token;
43 import org.netbeans.api.lexer.TokenHierarchy;
44 import org.netbeans.api.lexer.TokenSequence;
45 import org.netbeans.editor.BaseDocument;
46 import org.netbeans.modules.el.lexer.api.ELTokenId;
47 import org.netbeans.modules.web.core.syntax.JspSyntaxSupport;
48 import org.netbeans.modules.web.jsps.parserapi.PageInfo.BeanData;
49 import org.netbeans.spi.editor.completion.CompletionItem;
50
51 /**
52  *
53  * @author Petr Pisl
54  * @author Marek.Fukala@Sun.COM
55  * @author Tomasz.Slota@Sun.COM
56  */

57
58
59 /**
60  * This is a helper class for parsing and obtaining items for code completion of expression
61  * language.
62  */

63 public class ELExpression {
64     private static final Logger JavaDoc logger = Logger.getLogger(ELExpression.class.getName());
65     
66     /** it is not Expession Language */
67     public static final int NOT_EL = 0;
68     /** This is start of an EL expression */
69     public static final int EL_START = 1;
70     /** The expression is bean */
71     public static final int EL_BEAN = 2;
72     /** The expression is implicit language */
73     public static final int EL_IMPLICIT =3;
74     /** The expression is EL function */
75     public static final int EL_FUNCTION = 4;
76     /** It is EL but we are not able to recognize it */
77     public static final int EL_UNKNOWN = 5;
78     
79     /** The expression - result of the parsing */
80     private String JavaDoc expression;
81     
82     protected JspSyntaxSupport sup;
83     private String JavaDoc replace;
84     private boolean isDefferedExecution = false;
85     
86     public ELExpression(JspSyntaxSupport sup) {
87         this.sup = sup;
88         this.replace = "";
89     }
90     
91     /** Parses text before offset in the document. Doesn't parse after offset.
92      * It doesn't parse whole EL expression until ${ or #{, but just simple expression.
93      * For example ${ 2 < bean.start }. If the offset is after bean.start, then only bean.start
94      * is parsed.
95      */

96     public int parse(int offset){
97         String JavaDoc value = null;
98         int result = NOT_EL;
99         boolean middle;
100         
101         BaseDocument document = sup.getDocument();
102         document.readLock();
103         try {
104             TokenHierarchy hi = TokenHierarchy.get(document);
105             TokenSequence ts = JspSyntaxSupport.tokenSequence(hi, ELTokenId.language(), offset);
106             if(ts == null) {
107                 //no EL token sequence
108
return EL_UNKNOWN;
109             }
110             
111             TokenSequence jspTokenSequence = hi.tokenSequence();
112             jspTokenSequence.move(offset);
113             if(!jspTokenSequence.moveNext()) {
114                 return NOT_EL; //no token
115
}
116             
117             Token jspToken = jspTokenSequence.token();
118             isDefferedExecution = jspToken.text().toString().startsWith("#{"); //NOI18N
119

120             if (jspToken.id() == JspTokenId.EL){
121                 if (offset == jspToken.offset(hi) + "${".length()){ //NOI18N
122
return EL_START;
123                 }
124             }
125             
126             ts.move(offset);
127             if (!ts.moveNext() && !ts.movePrevious()) {
128                 return NOT_EL;
129             }
130             
131             if (ts.offset() == offset){
132                 ts.movePrevious();
133             }
134             
135             // Find the start of the expression. It doesn't have to be an EL delimiter (${ #{)
136
// it can be start of the function or start of a simple expression.
137
Token<ELTokenId> token = ts.token();
138             while (token.id() != ELTokenId.LPAREN
139                     && token.id() != ELTokenId.WHITESPACE
140                     && (!token.id().language().nonPrimaryTokenCategories(token.id()).contains(ELTokenId.ELTokenCategories.KEYWORDS.name())
141                     || token.id().language().nonPrimaryTokenCategories(token.id()).contains(ELTokenId.ELTokenCategories.NUMERIC_LITERALS.name()))) {
142                 token = ts.token();
143                 if (value == null){
144                     value = token.text().toString();
145                     if (token.id() == ELTokenId.DOT){
146                         replace="";
147                         middle = true;
148                     } else if (token.text().length() >= (offset-token.offset(hi))){
149                         if (token.offset(hi) <= offset){
150                             value = value.substring(0, offset-token.offset(hi));
151                             replace = value;
152                         } else {
153                             // cc invoked within EL delimiter
154
return NOT_EL;
155                         }
156                     }
157                 } else {
158                     value = token.text().toString() + value;
159                     if (token.id() == ELTokenId.TAG_LIB_PREFIX)
160                         replace = value;
161                 }
162                 if(!ts.movePrevious()) {
163                     break; //break the loop, we are on the beginning of the EL token sequence
164
}
165             }
166             if (token.id() != ELTokenId.IDENTIFIER && token.id() != ELTokenId.TAG_LIB_PREFIX ) {
167                 value = null;
168             } else
169                 if (token.id() == ELTokenId.WHITESPACE || token.id() == ELTokenId.LPAREN) {
170                     result = EL_START;
171                 } else
172                     if (value != null){
173                         result = findContext(value);
174                     }
175         } finally {
176             document.readUnlock();
177         }
178         expression = value;
179         return result;
180     }
181     
182     public List JavaDoc<CompletionItem> getPropertyCompletionItems(String JavaDoc beanType){
183         PropertyCompletionItemsTask task = new PropertyCompletionItemsTask(beanType);
184         runTask(task);
185         
186         return task.getCompletionItems();
187     }
188     
189     public boolean gotoPropertyDeclaration(String JavaDoc beanType){
190         GoToSourceTask task = new GoToSourceTask(beanType);
191         runTask(task);
192         return task.wasSuccessful();
193     }
194     
195     /**
196      * @return the class of the top-level object used in the expression
197      */

198     public String JavaDoc getObjectClass(){
199         String JavaDoc beanName = extractBeanName();
200         
201         BeanData[] allBeans = sup.getBeanData();
202         for (BeanData beanData : allBeans) {
203             if (beanData.getId().equals(beanName)){
204                 return beanData.getClassName();
205             }
206         }
207         
208         // not found within declared beans, try implicit objects
209
ELImplicitObjects.ELImplicitObject implObj = ELImplicitObjects.getELImplicitObject(beanName);
210         
211         if (implObj != null){
212             return implObj.getClazz();
213         }
214         
215         return null;
216     }
217     
218     private void runTask(CancellableTask task){
219         ClasspathInfo cpInfo = ClasspathInfo.create(sup.getFileObject());
220         JavaSource source = JavaSource.create(cpInfo, Collections.EMPTY_LIST);
221         
222         try{
223             source.runUserActionTask(task, true);
224         } catch (IOException JavaDoc e){
225             logger.log(Level.SEVERE, e.getMessage(), e);
226         }
227     }
228     
229     public String JavaDoc extractBeanName(){
230         String JavaDoc elExp = getExpression();
231         
232         if (elExp != null && !elExp.equals("")){
233             if (elExp.indexOf('.')> -1){
234                 String JavaDoc beanName = elExp.substring(0,elExp.indexOf('.'));
235                 return beanName;
236             }
237         }
238         
239         return null;
240     }
241     
242     public boolean isDefferedExecution(){
243         return isDefferedExecution;
244     }
245     
246     private String JavaDoc getPropertyBeingTypedName(){
247         String JavaDoc elExp = getExpression();
248         int dotPos = elExp.lastIndexOf('.');
249         
250         return dotPos == -1 ? null : elExp.substring(dotPos + 1);
251     }
252     
253     private abstract class BaseELTaskClass{
254         protected String JavaDoc beanType;
255         
256         BaseELTaskClass(String JavaDoc beanType){
257             this.beanType = beanType;
258         }
259         
260         /**
261          * bean.prop2... propN.propertyBeingTyped| - returns the type of propN
262          */

263         protected TypeElement getTypePreceedingCaret(CompilationInfo parameter){
264             if (beanType == null){
265                 return null;
266             }
267             
268             TypeElement lastKnownType = parameter.getElements().getTypeElement(beanType);
269             
270             String JavaDoc parts[] = getExpression().split("\\.");
271             // part[0] - the bean
272
// part[parts.length - 1] - the property being typed (if not empty)
273

274             int limit = parts.length - 1;
275             
276             if (getPropertyBeingTypedName().length() == 0){
277                 limit += 1;
278             }
279             
280             for (int i = 1; i < limit; i ++){
281                 if (lastKnownType == null){
282                     logger.fine("EL CC: Could not resolve type for property " //NOI18N
283
+ parts[i] + " in " + getExpression()); //NOI18N
284

285                     return null;
286                 }
287                 
288                 String JavaDoc accessorName = getAccessorName(parts[i]);
289                 List JavaDoc<ExecutableElement> allMethods = ElementFilter.methodsIn(lastKnownType.getEnclosedElements());
290                 lastKnownType = null;
291                 
292                 for (ExecutableElement method : allMethods){
293                     if (accessorName.equals(method.getSimpleName().toString())){
294                         TypeMirror returnType = method.getReturnType();
295                         
296                         if (returnType.getKind() == TypeKind.DECLARED){ // should always be true
297
lastKnownType = (TypeElement) parameter.getTypes().asElement(returnType);
298                             break;
299                         }
300                     }
301                     
302                 }
303             }
304             
305             return lastKnownType;
306         }
307         
308         protected String JavaDoc getAccessorName(String JavaDoc propertyName){
309             // we do not have to handle "is" type accessors here
310
return "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
311         }
312         
313         /**
314          * @return property name is <code>accessorMethod<code> is property accessor, otherwise null
315          */

316         protected String JavaDoc getExpressionSuffix(ExecutableElement method){
317             
318             if (method.getModifiers().contains(Modifier.PUBLIC)
319                     && method.getParameters().size() == 0){
320                 String JavaDoc methodName = method.getSimpleName().toString();
321                 
322                 if (methodName.startsWith("get")){ //NOI18N
323
return Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
324                 }
325                 
326                 if (methodName.startsWith("is")){ //NOI18N
327
return Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
328                 }
329                 
330                 if (isDefferedExecution()){
331                     // also return values for method expressions
332

333                     if ("java.lang.String".equals(method.getReturnType().toString())){ //NOI18N
334
return methodName;
335                     }
336                 }
337             }
338             
339             return null; // not a property accessor
340
}
341         
342         public void cancel() {}
343     }
344     
345     /**
346      * Go to the java source code of expression
347      * - a getter in case of
348      */

349     private class GoToSourceTask extends BaseELTaskClass implements CancellableTask<CompilationController>{
350         private boolean success = false;
351         
352         GoToSourceTask(String JavaDoc beanType){
353             super(beanType);
354         }
355         
356         public void run(CompilationController parameter) throws Exception JavaDoc {
357             parameter.toPhase(Phase.ELEMENTS_RESOLVED);
358             TypeElement bean = getTypePreceedingCaret(parameter);
359             
360             if (bean != null){
361                 String JavaDoc suffix = getPropertyBeingTypedName();
362                 
363                 for (ExecutableElement method : ElementFilter.methodsIn(bean.getEnclosedElements())){
364                     String JavaDoc propertyName = getExpressionSuffix(method);
365                     
366                     if (propertyName != null && propertyName.equals(suffix)){
367                         success = UiUtils.open(parameter.getClasspathInfo(), method);
368                         break;
369                     }
370                 }
371             }
372         }
373         
374         public boolean wasSuccessful(){
375             return success;
376         }
377     }
378     
379     private class PropertyCompletionItemsTask extends BaseELTaskClass implements CancellableTask<CompilationController>{
380         
381         private List JavaDoc<CompletionItem> completionItems = new ArrayList JavaDoc<CompletionItem>();
382         
383         PropertyCompletionItemsTask(String JavaDoc beanType){
384             super(beanType);
385         }
386         
387         public void run(CompilationController parameter) throws Exception JavaDoc {
388             parameter.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
389             
390             TypeElement bean = getTypePreceedingCaret(parameter);
391             
392             if (bean != null){
393                 String JavaDoc prefix = getPropertyBeingTypedName();
394                 
395                 for (ExecutableElement method : ElementFilter.methodsIn(bean.getEnclosedElements())){
396                     String JavaDoc propertyName = getExpressionSuffix(method);
397                     
398                     if (propertyName != null && propertyName.startsWith(prefix)){
399                         boolean isMethod = propertyName.equals(method.getSimpleName().toString());
400                         String JavaDoc type = isMethod ? "" : method.getReturnType().toString(); //NOI18N
401

402                         CompletionItem item = new JspCompletionItem.ELProperty(
403                                 propertyName, type);
404                         
405                         completionItems.add(item);
406                     }
407                 }
408             }
409         }
410         
411         public List JavaDoc<CompletionItem> getCompletionItems(){
412             return completionItems;
413         }
414     }
415     
416     /** Return context, whether the expression is about a bean, implicit object or
417      * function.
418      */

419     protected int findContext(String JavaDoc expr){
420         int dotIndex = expr.indexOf('.');
421         int bracketIndex = expr.indexOf('[');
422         int value = EL_UNKNOWN;
423         
424         if (bracketIndex == -1 && dotIndex > -1){
425             String JavaDoc first = expr.substring(0, dotIndex);
426             BeanData[] beans = sup.getBeanData();
427             if (beans != null) {
428                 for (int i = 0; i < beans.length; i++)
429                     if (beans[i].getId().equals(first)){
430                         value = EL_BEAN;
431                         continue;
432                     }
433             }
434             if (value == EL_UNKNOWN && ELImplicitObjects.getELImplicitObjects(first).size()>0)
435                 value = EL_IMPLICIT;
436         } else if (bracketIndex == -1 && dotIndex == -1)
437             value = EL_START;
438         return value;
439     }
440     
441     
442     public String JavaDoc getExpression() {
443         return expression;
444     }
445     
446     public void setExpression(String JavaDoc expression) {
447         this.expression = expression;
448     }
449     
450     public String JavaDoc getReplace() {
451         return replace;
452     }
453     
454 }
455
Popular Tags