KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.*;
23 import javax.swing.text.JTextComponent JavaDoc;
24 import javax.swing.text.BadLocationException JavaDoc;
25 import javax.servlet.jsp.tagext.TagInfo JavaDoc;
26 import javax.servlet.jsp.tagext.TagAttributeInfo JavaDoc;
27 import org.netbeans.editor.*;
28 import org.netbeans.editor.ext.*;
29 import org.netbeans.editor.ext.html.HTMLCompletionQuery;
30 import org.netbeans.modules.editor.NbEditorUtilities;
31 import org.netbeans.modules.web.core.syntax.deprecated.JspTagTokenContext;
32 import org.openide.loaders.DataObject;
33 import org.openide.util.NbBundle;
34 import org.netbeans.modules.web.core.syntax.*;
35 import org.netbeans.modules.web.jsps.parserapi.PageInfo.BeanData;
36 import org.openide.loaders.DataObject;
37 import org.netbeans.spi.editor.completion.CompletionItem;
38
39
40 /**
41  * JSP completion support finder
42  *
43  * @author Petr Nejedly
44  * @author Tomasz.Slota@Sun.COM
45  */

46
47 public class JspCompletionQuery implements CompletionQuery {
48     
49     /**
50      * @see filterNonStandardXMLEntities(List, List)
51      **/

52     private static final Set stdXMLEntities = new TreeSet();
53     
54     static{
55         stdXMLEntities.add("<");
56         stdXMLEntities.add(">");
57         stdXMLEntities.add("'");
58         stdXMLEntities.add(""");
59         stdXMLEntities.add("&");
60     }
61     
62     protected CompletionQuery contentQuery;
63     
64     public JspCompletionQuery(CompletionQuery contentQuery) {
65         super();
66         this.contentQuery = contentQuery;
67     }
68     
69     /** Perform the query on the given component. The query usually
70      * gets the component's document, the caret position and searches back
71      * to find the last command start. Then it inspects the text up to the caret
72      * position and returns the result.
73      * @param component the component to use in this query.
74      * @param offset position in the component's document to which the query will
75      * be performed. Usually it's a caret position.
76      * @param support syntax-support that will be used during resolving of the query.
77      * @return result of the query or null if there's no result.
78      */

79     public CompletionQuery.Result query(JTextComponent JavaDoc component, int offset, SyntaxSupport support) {
80         BaseDocument doc = (BaseDocument)component.getDocument();
81         JspSyntaxSupport sup = (JspSyntaxSupport)support.get(JspSyntaxSupport.class);
82         
83         try {
84             SyntaxElement elem = sup.getElementChain( offset );
85             if (elem == null)
86                 // this is a legal option, when I don't have anything to say just return null
87
return null;
88             
89             CompletionData jspData;
90             switch (elem.getCompletionContext()) {
91                 // TAG COMPLETION
92
case JspSyntaxSupport.TAG_COMPLETION_CONTEXT :
93                 return queryJspTag(component, offset, sup,
94                         (SyntaxElement.Tag)elem);
95                 
96                 // ENDTAG COMPLETION
97
case JspSyntaxSupport.ENDTAG_COMPLETION_CONTEXT :
98                 jspData = queryJspEndTag(offset, sup,
99                         (SyntaxElement.EndTag)elem, doc);
100                 return result(component, offset, jspData);
101                 
102                 //DIRECTIVE COMPLETION IN JSP SCRIPTLET (<%| should offer <%@taglib etc.)
103
case JspSyntaxSupport.SCRIPTINGL_COMPLETION_CONTEXT:
104                 return queryJspDirectiveInScriptlet(component, offset, sup, elem, doc);
105                 
106                 // DIRECTIVE COMPLETION
107
case JspSyntaxSupport.DIRECTIVE_COMPLETION_CONTEXT :
108                 return queryJspDirective(component, offset, sup,
109                         (SyntaxElement.Directive)elem, doc);
110                 
111                 // EXPRESSION LANGUAGE
112
case JspSyntaxSupport.EL_COMPLETION_CONTEXT:
113                 return queryEL(component, offset, sup, elem, doc);
114                 
115                 // CONTENT LANGUAGE
116
case JspSyntaxSupport.CONTENTL_COMPLETION_CONTEXT :
117                 // html results
118
CompletionQuery.Result contentLResult = (contentQuery == null) ?
119                     null :
120                     contentQuery.query(component, offset, support);
121                 
122                 // JSP tags results
123
jspData = queryJspTagInContent(offset, sup, doc);
124                 
125                 // JSP directive results
126
CompletionQuery.Result jspDirec = queryJspDirectiveInContent(component, offset, sup, doc);
127                 
128                 //return null (do not popup completion) if there are no items in any of the completions
129
if(jspData.completionItems.isEmpty() && jspDirec.getData().isEmpty() && (contentLResult == null || contentLResult.getData().isEmpty()))
130                     return null;
131                 
132                 CompletionQuery.Result jspRes = result(component, offset, jspData);
133                 
134                 //merge result items
135
ArrayList all = new ArrayList();
136                 all.addAll(jspDirec.getData());
137                 all.addAll(jspRes.getData());
138                 if(contentLResult != null){
139                     DataObject dobj = NbEditorUtilities.getDataObject(doc);
140                     
141                     if(dobj != null && JspUtils.getJSPColoringData(doc, dobj.getPrimaryFile()).isXMLSyntax()){
142                         filterNonStandardXMLEntities(all, contentLResult.getData());
143                     } else{
144                         all.addAll(contentLResult.getData());
145                     }
146                 }
147                 
148                 int htmlAnchorOffset = contentLResult == null || contentLResult.getData().isEmpty() ? - 1 : ((HTMLCompletionQuery.HTMLCompletionResult)contentLResult).getSubstituteOffset();
149                 
150                 CompletionQuery.Result result = new JspCompletionResult(component,
151                         NbBundle.getMessage(JSPKit.class, "CTL_JSP_Completion_Title"), all,
152                         offset, jspData.removeLength, htmlAnchorOffset);
153                 
154                 return result;
155             }
156             
157         } catch (BadLocationException JavaDoc e) {
158             e.printStackTrace();
159         }
160         return null;
161     }
162     
163     /**
164      * A hack-fix for #70366
165      */

166     private void filterNonStandardXMLEntities(List completionItemsRep, List htmlSuggestions) {
167         Iterator it = htmlSuggestions.iterator();
168         
169         while (it.hasNext()){
170             CompletionQuery.ResultItem item = (CompletionQuery.ResultItem) it.next();
171             
172             String JavaDoc itemText = item.getItemText();
173             boolean filterOut = false;
174             
175             // check if entity is suggested
176
if (itemText.startsWith("&") && itemText.endsWith(";")){
177                 // only allow well known XML entities
178
if (!stdXMLEntities.contains(itemText)){
179                     filterOut = true;
180                 }
181             }
182             
183             if (!filterOut){
184                 completionItemsRep.add(item);
185             }
186         }
187     }
188     
189     /** a new CC api hack - the result item needs to know its offset, formerly got from result - now cannot be used. */
190     private void setResultItemsOffset(List/*<CompletionItem>*/ items, int removeLength, int ccoffset) {
191         Iterator i = items.iterator();
192         while(i.hasNext()) {
193             Object JavaDoc obj = i.next();
194             if(obj instanceof org.netbeans.modules.web.core.syntax.completion.ResultItem)
195                 ((org.netbeans.modules.web.core.syntax.completion.ResultItem)obj).setSubstituteOffset(ccoffset - removeLength);
196         }
197     }
198     
199     private void setResultItemsOffset(CompletionData cd, int ccoffset) {
200         setResultItemsOffset(cd.completionItems, cd.removeLength, ccoffset);
201     }
202     
203     /** Gets a list of completion items for JSP tags.
204      * @param component editor component
205      * @param offset position of caret
206      * @param sup JSP syntax support
207      * @param elem syntax element representing the current JSP tag
208      * @return list of completion items
209      */

210     protected CompletionQuery.Result queryJspTag(JTextComponent JavaDoc component, int offset,
211             JspSyntaxSupport sup, SyntaxElement.Tag elem) throws BadLocationException JavaDoc {
212         BaseDocument doc = (BaseDocument)component.getDocument();
213         // find the current item
214
List compItems = new ArrayList();
215         int removeLength = 0;
216         
217         TokenItem item = sup.getItemAtOrBefore(offset);
218         
219         if (item == null) {
220             return result(component, offset, new CompletionData(compItems, 0));
221         }
222         
223         TokenID id = item.getTokenID();
224         String JavaDoc tokenPart = item.getImage().substring(0, offset - item.getOffset());
225         String JavaDoc token = item.getImage().trim();
226         
227         // SYMBOL
228
if (id == JspTagTokenContext.SYMBOL) {
229             if (tokenPart.equals("<")) { // NOI18N
230
// just after the beginning of the tag
231
removeLength = 0;
232                 addTagPrefixItems(sup, compItems, sup.getTagPrefixes("")); // NOI18N
233
}
234             if (tokenPart.endsWith("\"")) { // NOI18N
235
// try an attribute value
236
String JavaDoc attrName = findAttributeForValue(sup, item);
237                 if (attrName != null) {
238                     AttributeValueSupport attSup =
239                             AttributeValueSupport.getSupport(true, elem.getName(), attrName);
240                     if (attSup != null) {
241                         return attSup.getResult(component, offset, sup, elem, ""); // NOI18N
242
}
243                 }
244             }
245             if(tokenPart.endsWith(">") && !tokenPart.endsWith("/>")) {
246                 compItems = sup.getAutocompletedEndTag(offset);
247             }
248             
249             
250         }
251         
252         // TAG
253
if (id == JspTagTokenContext.TAG
254                 || id == JspTagTokenContext.WHITESPACE
255                 || id == JspTagTokenContext.EOL) {
256             // inside a JSP tag name
257
if (isBlank(tokenPart.charAt(tokenPart.length() - 1))
258                     || tokenPart.equals("\n")) {
259                 // blank character - do attribute completion
260
removeLength = 0;
261                 addAttributeItems(sup, compItems, elem, sup.getTagAttributes(elem.getName(), ""), null); // NOI18N
262
} else {
263                 int colonIndex = tokenPart.indexOf(":"); // NOI18N
264
if (colonIndex == -1) {
265                     removeLength = tokenPart.length();
266                     addTagPrefixItems(sup, compItems, sup.getTagPrefixes(tokenPart));
267                 } else {
268                     String JavaDoc prefix = tokenPart.substring(0, colonIndex);
269                     removeLength = tokenPart.length();
270                     addTagPrefixItems(sup, compItems, prefix, sup.getTags(tokenPart), elem);
271                 }
272             }
273         }
274         
275         // ATTRIBUTE
276
if (id == JspTagTokenContext.ATTRIBUTE) {
277             // inside or after an attribute
278
if (isBlank(tokenPart.charAt(tokenPart.length() - 1))) {
279                 // blank character - do attribute completion
280
removeLength = 0;
281                 addAttributeItems(sup, compItems, elem, sup.getTagAttributes(elem.getName(), ""), null); // NOI18N
282
} else {
283                 removeLength = tokenPart.length();
284                 addAttributeItems(sup, compItems, elem, sup.getTagAttributes(elem.getName(), tokenPart), token);
285             }
286         }
287         
288         // ATTRIBUTE VALUE
289
if (id == JspTagTokenContext.ATTR_VALUE) {
290             // inside or after an attribute
291
String JavaDoc valuePart = tokenPart.trim();
292             //return empty completion if the CC is not invoked inside a quotations
293
if(valuePart.length() == 0) return result(component, offset, new CompletionData(compItems, 0));
294             
295             item = item.getPrevious();
296             while ((item != null) && (item.getTokenID() == JspTagTokenContext.ATTR_VALUE)) {
297                 valuePart = item.getImage() + valuePart;
298                 item = item.getPrevious();
299             }
300             // get rid of the first quote
301
valuePart = valuePart.substring(1);
302             removeLength = valuePart.length();
303             String JavaDoc attrName = findAttributeForValue(sup, item);
304             if (attrName != null) {
305                 AttributeValueSupport attSup =
306                         AttributeValueSupport.getSupport(true, elem.getName(), attrName);
307                 if (attSup != null) {
308                     CompletionQuery.Result result = attSup.getResult(component, offset, sup, elem, valuePart);
309                     if(!(attSup instanceof AttrSupports.FilenameSupport))
310                         setResultItemsOffset(result.getData(), valuePart.length(), offset);
311                     return result;
312                 }
313             }
314             
315         }
316         
317         return /*List<JspResultItem.PrefixTag>*/ result(component, offset, new CompletionData(compItems, removeLength));
318     }
319     
320     /** Gets a list of completion items for JSP tags.
321      * @param offset position of caret
322      * @param sup JSP syntax support
323      * @param elem syntax element representing the current JSP tag
324      * @return list of completion items
325      */

326     protected CompletionData queryJspEndTag(int offset, JspSyntaxSupport sup,
327             SyntaxElement.EndTag elem, BaseDocument doc) throws BadLocationException JavaDoc {
328         // find the current item
329
List compItems = new ArrayList();
330         int removeLength = 0;
331         
332         TokenItem item = sup.getItemAtOrBefore(offset);
333         if (item == null) {
334             return new CompletionData(compItems, 0);
335         }
336         
337         TokenID id = item.getTokenID();
338         String JavaDoc tokenPart = item.getImage().substring(0, offset - item.getOffset());
339         
340         removeLength = tokenPart.length();
341         return new CompletionData(sup.getPossibleEndTags(offset, tokenPart), removeLength);
342     }
343     
344     /** Gets a list of completion items for EL */
345     protected CompletionQuery.Result queryEL(JTextComponent JavaDoc component, int offset, JspSyntaxSupport sup,
346             SyntaxElement elem, BaseDocument doc) throws BadLocationException JavaDoc {
347         ELExpression elExpr = new ELExpression(sup);
348         ArrayList complItems = new ArrayList();
349         
350         switch (elExpr.parse(offset)){
351         case ELExpression.EL_START:
352             // implicit objects
353
for (ELImplicitObjects.ELImplicitObject implOb : ELImplicitObjects.getELImplicitObjects(elExpr.getReplace())) {
354                 complItems.add(new JspCompletionItem.ELImplicitObject(implOb.getName(), implOb.getType()));
355             }
356             
357             // defined beans on the page
358
BeanData[] beans = sup.getBeanData();
359             if (beans != null){
360                 for (int i = 0; i < beans.length; i++) {
361                     if (beans[i].getId().startsWith(elExpr.getReplace()))
362                         complItems.add(new JspCompletionItem.ELBean(beans[i].getId(), beans[i].getClassName()));
363                 }
364             }
365             //Functions
366
List functions = ELFunctions.getFunctions(sup, elExpr.getReplace());
367             Iterator iter = functions.iterator();
368             while (iter.hasNext()) {
369                 ELFunctions.Function fun = (ELFunctions.Function) iter.next();
370                 complItems.add(new JspCompletionItem.ELFunction(
371                         fun.getPrefix(),
372                         fun.getName(),
373                         fun.getReturnType(),
374                         fun.getParameters()));
375             }
376             break;
377         case ELExpression.EL_BEAN:
378         case ELExpression.EL_IMPLICIT:
379             
380             List<CompletionItem> items = elExpr.getPropertyCompletionItems(elExpr.getObjectClass());
381             complItems.addAll(items);
382             
383             break;
384         }
385         
386         return result(component, offset, new CompletionData(complItems, elExpr.getReplace().length()));
387     }
388     
389     /** Gets a list of JSP directives which can be completed just after <% in java scriptlet context */
390     protected CompletionQuery.Result queryJspDirectiveInScriptlet(JTextComponent JavaDoc component, int offset, JspSyntaxSupport sup,
391             SyntaxElement elem, BaseDocument doc) throws BadLocationException JavaDoc {
392         
393         List compItems = new ArrayList();
394         
395         TokenItem item = sup.getItemAtOrBefore(offset);
396         if (item == null) {
397             return result(component, offset, new CompletionData(compItems, 0));
398         }
399         
400         TokenID id = item.getTokenID();
401         String JavaDoc tokenPart = item.getImage().substring(0, offset - item.getOffset());
402         
403         if(id == JspTagTokenContext.SYMBOL2 && tokenPart.equals("<%"))
404             addDirectiveItems(sup, compItems, sup.getDirectives("")); // NOI18N
405

406         return result(component, offset, new CompletionData(compItems, 1 /*removeLength*/));
407     }
408     
409     
410     /** Gets a list of completion items for JSP directives.
411      * @param component editor component
412      * @param offset position of caret
413      * @param sup JSP syntax support
414      * @param elem syntax element representing the current JSP tag
415      * @return list of completion items
416      */

417     protected CompletionQuery.Result queryJspDirective(JTextComponent JavaDoc component, int offset, JspSyntaxSupport sup,
418             SyntaxElement.Directive elem, BaseDocument doc) throws BadLocationException JavaDoc {
419         // find the current item
420
List compItems = new ArrayList();
421         int removeLength = 0;
422         
423         TokenItem item = sup.getItemAtOrBefore(offset);
424         if (item == null) {
425             return result(component, offset, new CompletionData(compItems, 0));
426         }
427         
428         TokenID id = item.getTokenID();
429         String JavaDoc tokenPart = item.getImage().substring(0, offset - item.getOffset());
430         String JavaDoc token = item.getImage().trim();
431         
432         // SYMBOL
433
if (id.getNumericID() == JspTagTokenContext.SYMBOL_ID) {
434             if (tokenPart.startsWith("<")) { // NOI18N
435
//calculate a position of the potential replacement
436
removeLength = tokenPart.length() - 1;
437                 addDirectiveItems(sup, compItems, sup.getDirectives("")); // NOI18N
438
}
439             if (tokenPart.endsWith("\"")) { // NOI18N
440
// try an attribute value
441
String JavaDoc attrName = findAttributeForValue(sup, item);
442                 if (attrName != null) {
443                     AttributeValueSupport attSup =
444                             AttributeValueSupport.getSupport(false, elem.getName(), attrName);
445                     if (attSup != null) {
446                         return attSup.getResult(component, offset, sup, elem, ""); // NOI18N
447
}
448                 }
449             }
450         }
451         
452         // DIRECTIVE
453
if (id.getNumericID() == JspTagTokenContext.TAG_ID
454                 || id.getNumericID() == JspTagTokenContext.WHITESPACE_ID
455                 || id.getNumericID() == JspTagTokenContext.EOL_ID) {
456             // inside a JSP directive name or after a whitespace
457
if (isBlank(tokenPart.charAt(tokenPart.length() - 1))
458                     || tokenPart.equals("\n")) {
459                 TokenItem prevItem = item.getPrevious();
460                 TokenID prevId = prevItem.getTokenID();
461                 String JavaDoc prevToken = prevItem.getImage().trim();
462                 if (prevId.getNumericID() == JspTagTokenContext.TAG_ID
463                         || prevId.getNumericID() == JspTagTokenContext.ATTR_VALUE_ID
464                         || prevId.getNumericID() == JspTagTokenContext.WHITESPACE_ID
465                         || prevId.getNumericID() == JspTagTokenContext.EOL_ID) {
466                     // blank character - do attribute completion
467
removeLength = 0;
468                     addAttributeItems(sup, compItems, elem, sup.getDirectiveAttributes(elem.getName(), ""), null); // NOI18N
469
} else if (prevId.getNumericID() == JspTagTokenContext.SYMBOL_ID && prevToken.equals("<%@")) { // NOI18N
470
// just after the beginning of the directive
471
removeLength = tokenPart.length() + 2;
472                     addDirectiveItems(sup, compItems, sup.getDirectives("")); // NOI18N
473
}
474             } else {
475                 boolean add = true;
476                 //I need to get the whitespace token length before the tag name
477
int whitespaceLength = 0;
478                 TokenItem prevItem = item.getPrevious();
479                 TokenID prevId = prevItem.getTokenID();
480                 //test whether there is a space before the currently completed tagname
481
if(prevId.getNumericID() == JspTagTokenContext.TAG_ID && "".equals(prevItem.getImage().trim())) //try to trim the token image - just for sure since I am not absolutely sure if the TAG_ID is only for whitespaces in this case.
482
whitespaceLength = prevItem.getImage().length();
483                 
484                 
485                 List list = sup.getDirectives(tokenPart);
486                 if (list.size() == 1){
487                     Object JavaDoc directive = list.get(0);
488                     //is the cc invoce just after the directive?
489
if (directive instanceof TagInfo JavaDoc && ((TagInfo JavaDoc)directive).getTagName().equalsIgnoreCase(tokenPart))
490                         add = false;
491                 }
492                 if (add){
493                     removeLength = whitespaceLength + tokenPart.length() + 2;
494                     addDirectiveItems(sup, compItems, list);
495                 }
496             }
497         }
498         
499         // ATTRIBUTE
500
if (id.getNumericID() == JspTagTokenContext.ATTRIBUTE_ID) {
501             // inside or after an attribute
502
if (isBlank(tokenPart.charAt(tokenPart.length() - 1))) {
503                 // blank character - do attribute completion
504
removeLength = 0;
505                 addAttributeItems(sup, compItems, elem, sup.getDirectiveAttributes(elem.getName(), ""), null); // NOI18N
506
} else {
507                 removeLength = tokenPart.length();
508                 addAttributeItems(sup, compItems, elem, sup.getDirectiveAttributes(elem.getName(), tokenPart), token);
509             }
510         }
511         
512         // ATTRIBUTE VALUE
513
if (id.getNumericID() == JspTagTokenContext.ATTR_VALUE_ID) {
514             // inside or after an attribute
515
String JavaDoc valuePart = tokenPart;
516             item = item.getPrevious();
517             while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.ATTR_VALUE_ID)) {
518                 valuePart = item.getImage() + valuePart;
519                 item = item.getPrevious();
520             }
521             // get rid of the first quote
522
valuePart = valuePart.substring(1);
523             removeLength = valuePart.length();
524             String JavaDoc attrName = findAttributeForValue(sup, item);
525             if (attrName != null) {
526                 AttributeValueSupport attSup =
527                         AttributeValueSupport.getSupport(false, elem.getName(), attrName);
528                 //we cannot set substitute offset for file cc items
529
if (attSup != null) {
530                     CompletionQuery.Result result = attSup.getResult(component, offset, sup, elem, valuePart); // NOI18N
531
if(!(attSup instanceof AttrSupports.FilenameSupport))
532                         setResultItemsOffset(result.getData(), valuePart.length(), offset);
533                     return result;
534                 }
535             }
536             
537         }
538         
539         return result(component, offset, new CompletionData(compItems, removeLength));
540     }
541     
542     
543     protected CompletionData queryJspTagInContent(int offset, JspSyntaxSupport sup, BaseDocument doc) throws BadLocationException JavaDoc {
544         // find the current item
545
List compItems = new ArrayList();
546         int removeLength = 0;
547         
548         TokenItem item = sup.getItemAtOrBefore(offset);
549         if (item == null) {
550             return new CompletionData(compItems, 0);
551         }
552         
553         String JavaDoc tokenPart = item.getImage().substring(0,
554                 (offset - item.getOffset()) >= item.getImage().length() ? item.getImage().length() : offset - item.getOffset());
555         int ltIndex = tokenPart.lastIndexOf('<');
556         if (ltIndex != -1) {
557             tokenPart = tokenPart.substring(ltIndex + 1);
558         }
559         while (ltIndex == -1) {
560             item = item.getPrevious();
561             if (item == null) {
562                 return new CompletionData(compItems, 0);
563             }
564             String JavaDoc newImage = item.getImage();
565             ltIndex = newImage.lastIndexOf('<');
566             if (ltIndex != -1)
567                 tokenPart = newImage.substring(ltIndex + 1) + tokenPart;
568             else {
569                 tokenPart = newImage + tokenPart;
570             }
571             if (tokenPart.length() > 20) {
572                 return new CompletionData(compItems, 0);
573             }
574         }
575         // we found ltIndex, tokenPart is either the part of the token we are looking for
576
// or '/' + what we are looking for
577
if (tokenPart.startsWith("/")) { // NOI18N
578
tokenPart = tokenPart.substring(1);
579             compItems = sup.getPossibleEndTags(offset, tokenPart, true); //get only first end tag
580
} else {
581             addTagPrefixItems(sup, compItems, sup.getTagPrefixes(tokenPart));
582         }
583         removeLength = tokenPart.length();
584         return new CompletionData(compItems, removeLength);
585     }
586     
587     protected CompletionQuery.Result queryJspDirectiveInContent(JTextComponent JavaDoc component, int offset, JspSyntaxSupport sup, BaseDocument doc) throws BadLocationException JavaDoc {
588         // find the current item
589
List compItems = new ArrayList();
590         int removeLength = 0;
591         
592         TokenItem item = sup.getItemAtOrBefore(offset);
593         if (item == null) {
594             //return empty completion result
595
return result(component, offset, new CompletionData(compItems, 0));
596         }
597         
598         String JavaDoc tokenPart = item.getImage().substring(0,
599                 (offset - item.getOffset()) >= item.getImage().length() ? item.getImage().length() : offset - item.getOffset());
600         
601         //if (tokenPart.lastIndexOf('<') == -1 || !tokenPart.equals("<")) -- the condition is strange - the some should be !tokenPart.equals("<")
602
if(!tokenPart.equals("<") && !tokenPart.equals("<%")) // NOI18N
603
//return empty completion result
604
return result(component, offset, new CompletionData(compItems, 0));
605         
606         //the removeLenght has to be set 0 if the CC is invoked right after <, 1 for <%
607
if("<%".equals(tokenPart)) removeLength = 1; else removeLength = 0; // NOI18N
608

609         addDirectiveItems(sup, compItems, sup.getDirectives("")); // NOI18N
610

611         return result(component, offset, new CompletionData(compItems, removeLength));
612     }
613     
614     private boolean isBlank(char c) {
615         return c == ' ';
616     }
617     
618     /** Finds an attribute name, assuming that the item is either
619      * SYMBOL after the attribute name or ATTR_VALUE after this attribute name.
620      * May return null if nothing found.
621      */

622     protected String JavaDoc findAttributeForValue(JspSyntaxSupport sup, TokenItem item) {
623         // get before any ATTR_VALUE
624
while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.ATTR_VALUE_ID))
625             item = item.getPrevious();
626         // now collect the symbols
627
String JavaDoc symbols = ""; // NOI18N
628
while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID)) {
629             symbols = item.getImage() + symbols;
630             item = item.getPrevious();
631         }
632         // two quotes at the end are not allowed
633
if (!sup.isValueBeginning(symbols))
634             return null;
635         String JavaDoc attributeName = ""; // NOI18N
636
//there may be a whitespace before the equals sign - trace over the whitespace
637
//due to a bug in jsp tag syntax parser the whitespace has tag-directive tokenID
638
//so I need to use the token image to recognize whether it is a whitespace
639
while ((item != null) && (item.getImage().trim().length() == 0)) {
640             item = item.getPrevious();
641         }
642         //now there should be either tag name or attribute name
643
while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.ATTRIBUTE_ID)) {
644             attributeName = item.getImage() + attributeName;
645             item = item.getPrevious();
646         }
647         if (attributeName.trim().length() > 0)
648             return attributeName.trim();
649         return null;
650     }
651     
652     /** Adds to the list of items <code>compItemList</code> new TagPrefix items with prefix
653      * <code>prefix</code> for list of tag names <code>tagStringItems</code>.
654      * @param set - <code>SyntaxElement.Tag</code>
655      */

656     private void addTagPrefixItems(JspSyntaxSupport sup, List compItemList, String JavaDoc prefix, List tagStringItems, SyntaxElement.Tag set) {
657         for (int i = 0; i < tagStringItems.size(); i++) {
658             Object JavaDoc item = tagStringItems.get(i);
659             if (item instanceof TagInfo JavaDoc)
660                 compItemList.add(new JspCompletionItem.PrefixTag(prefix , (TagInfo JavaDoc)item, set));
661             else
662                 compItemList.add(new JspCompletionItem.PrefixTag(prefix + ":" + (String JavaDoc)item)); // NOI18N
663
}
664     }
665     
666     /** Adds to the list of items <code>compItemList</code> new TagPrefix items for prefix list
667      * <code>prefixStringItems</code>, followed by all possible tags for the given prefixes.
668      */

669     private void addTagPrefixItems(JspSyntaxSupport sup, List compItemList, List prefixStringItems) {
670         for (int i = 0; i < prefixStringItems.size(); i++) {
671             String JavaDoc prefix = (String JavaDoc)prefixStringItems.get(i);
672             // now get tags for this prefix
673
List tags = sup.getTags(prefix, ""); // NOI18N
674
for (int j = 0; j < tags.size(); j++) {
675                 Object JavaDoc item = tags.get(j);
676                 if (item instanceof TagInfo JavaDoc)
677                     compItemList.add(new JspCompletionItem.PrefixTag(prefix , (TagInfo JavaDoc)item));
678                 else
679                     compItemList.add(new JspCompletionItem.PrefixTag(prefix + ":" + (String JavaDoc)item)); // NOI18N
680
}
681         }
682     }
683     
684     /** Adds to the list of items <code>compItemList</code> new TagPrefix items with prefix
685      * <code>prefix</code> for list of tag names <code>tagStringItems</code>.
686      */

687     private void addDirectiveItems(JspSyntaxSupport sup, List compItemList, List directiveStringItems) {
688         for (int i = 0; i < directiveStringItems.size(); i++) {
689             Object JavaDoc item = directiveStringItems.get(i);
690             if(item instanceof TagInfo JavaDoc){
691                 TagInfo JavaDoc ti = (TagInfo JavaDoc) item;
692                 compItemList.add(new JspCompletionItem.Directive( ti.getTagName(), ti));
693             } else
694                 compItemList.add(new JspCompletionItem.Directive( (String JavaDoc)item));
695         }
696     }
697     
698     /** Adds to the list of items <code>compItemList</code> new Attribute items.
699      * Only those which are not already in tagDir
700      * @param sup the syntax support
701      * @param compItemList list to add to
702      * @param tagDir tag or directive element
703      * @param attributeItems list of strings containing suitable values (String or TagAttributeInfo)
704      * @param currentAttr current attribute, may be null
705      */

706     private void addAttributeItems(JspSyntaxSupport sup, List compItemList,
707             SyntaxElement.TagDirective tagDir, List attributeItems, String JavaDoc currentAttr) {
708         for (int i = 0; i < attributeItems.size(); i++) {
709             Object JavaDoc item = attributeItems.get(i);
710             String JavaDoc attr;
711             if (item instanceof TagAttributeInfo JavaDoc)
712                 attr = ((TagAttributeInfo JavaDoc)item).getName();
713             else
714                 attr = (String JavaDoc)item;
715             boolean isThere = tagDir.getAttributes().keySet().contains(attr);
716             if (!isThere || attr.equalsIgnoreCase(currentAttr) ||
717                     (currentAttr != null && attr.startsWith(currentAttr) && attr.length()>currentAttr.length() && !isThere)) {
718                 if (item instanceof TagAttributeInfo JavaDoc)
719                     //XXX This is hack for fixing issue #45302 - CC is to aggressive.
720
//The definition of the tag and declaration doesn't allow
721
//define something like "prefix [uri | tagdir]". In the future
722
//it should be rewritten definition of declaration, which allow
723
//to do it.
724
if ("taglib".equalsIgnoreCase(tagDir.getName())){ //NOI18N
725
if (attr.equalsIgnoreCase("prefix") //NOI18N
726
|| (attr.equalsIgnoreCase("uri") && !tagDir.getAttributes().keySet().contains("tagdir")) //NOI18N
727
|| (attr.equalsIgnoreCase("tagdir") && !tagDir.getAttributes().keySet().contains("uri"))) //NOI18N
728
compItemList.add(new JspCompletionItem.Attribute((TagAttributeInfo JavaDoc)item));
729                     } else {
730                     compItemList.add(new JspCompletionItem.Attribute((TagAttributeInfo JavaDoc)item));
731                     } else
732                         compItemList.add(new JspCompletionItem.Attribute((String JavaDoc)item));
733             }
734         }
735     }
736     
737     private CompletionQuery.Result result(JTextComponent JavaDoc component, int offset, CompletionData complData) {
738         setResultItemsOffset(complData, offset);
739         return new JspCompletionResult(component,
740                 NbBundle.getMessage(JSPKit.class, "CTL_JSP_Completion_Title"), complData.completionItems,
741                 offset, complData.removeLength, -1);
742     }
743     
744     /** Class which encapsulates a list of completion items and length of
745      * the part which should be replaced. */

746     public static class CompletionData {
747         
748         public List completionItems;
749         public int removeLength;
750         
751         public CompletionData(List items, int length) {
752             this.completionItems = items;
753             this.removeLength = length;
754         }
755         
756         public String JavaDoc toString() {
757             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
758             sb.append("------ completion items, remove " + removeLength + " : ----------\n"); // NOI18N
759
for (int i = 0; i < completionItems.size(); i++) {
760                 CompletionQuery.ResultItem item =
761                         (CompletionQuery.DefaultResultItem)completionItems.get(i);
762                 sb.append(item.getItemText());
763                 sb.append("\n"); // NOI18N
764
}
765             return sb.toString();
766         }
767         
768         
769     }
770     
771     static interface SubstituteOffsetProvider {
772         public int getSubstituteOffset();
773     }
774     
775     public static class JspCompletionResult extends CompletionQuery.DefaultResult implements SubstituteOffsetProvider {
776         private int substituteOffset;
777         public JspCompletionResult(JTextComponent JavaDoc component, String JavaDoc title, List data, int offset, int len, int htmlAnchorOffset ) {
778             super(component, title, data, offset, len);
779             substituteOffset = htmlAnchorOffset == -1 ? offset - len : htmlAnchorOffset;
780         }
781         
782         public int getSubstituteOffset() {
783             return substituteOffset;
784         }
785     }
786 }
787
Popular Tags