KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > text > completion > XMLCompletionQuery


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.xml.text.completion;
21
22 import java.util.*;
23 import java.util.Enumeration JavaDoc;
24 import javax.swing.text.JTextComponent JavaDoc;
25 import javax.swing.text.BadLocationException JavaDoc;
26 import javax.swing.text.Document JavaDoc;
27 import org.netbeans.modules.xml.text.api.XMLDefaultTokenContext;
28
29 import org.w3c.dom.*;
30
31 import org.netbeans.editor.*;
32 import org.netbeans.editor.ext.*;
33 import org.openide.ErrorManager;
34
35 import org.netbeans.modules.xml.text.syntax.*;
36 import org.netbeans.modules.xml.text.syntax.dom.*;
37 import org.netbeans.modules.xml.api.model.*;
38 import org.netbeans.modules.xml.spi.dom.UOException;
39 import org.netbeans.modules.xml.text.syntax.dom.SyntaxNode;
40
41 /**
42  * Consults grammar and presents list of possible choices
43  * in particular document context.
44  * <p>
45  * <b>Warning:</b> It is public for unit test purposes only!
46  *
47  * @author Petr Nejedly
48  * @author Sandeep Randhawa
49  * @author Petr Kuzel
50  * @author asgeir@dimonsoftware.com
51  * @version 1.01
52  */

53
54 public class XMLCompletionQuery implements CompletionQuery, XMLTokenIDs {
55     
56     // the name of a property indentifing cached query
57
public static final String JavaDoc DOCUMENT_GRAMMAR_BINDING_PROP = "doc-bind-query";
58     
59     /**
60      * Perform the query on the given component. The query usually
61      * gets the component's document, the caret position and searches back
62      * to examine surrounding context. Then it returns the result.
63      * <p>
64      * It is also called after every keystroke while opened completion
65      * popup. So some result cache could be used. It is not easy at
66      * this level because of BACKSPACE that can extend result.
67      *
68      * @param component the component to use in this query.
69      * @param offset position in the component's document to which the query will
70      * be performed. Usually it's a caret position.
71      * @param support syntax-support that will be used during resolving of the query.
72      * @return result of the query or null if there's no result.
73      */

74     public CompletionQuery.Result query(JTextComponent JavaDoc component, int offset, SyntaxSupport support) {
75         
76         // assert precondition, actually serial access required
77

78 // synchronized (this) {
79
// if (thread == null) {
80
// thread = new ThreadLocal();
81
// thread.set(thread);
82
// }
83
// if (thread != thread.get()) {
84
// // unfortunatelly RP can probably provide serialization even
85
// // if delegating to multiple threads
86
// // throw new IllegalStateException("Serial access required!"); //NOI18N
87
// }
88
// }
89

90         // perform query
91

92         BaseDocument doc = (BaseDocument)component.getDocument();
93         if (doc == null) return null;
94         XMLSyntaxSupport sup = (XMLSyntaxSupport)support.get(XMLSyntaxSupport.class);
95         if( sup == null ) return null;// No SyntaxSupport for us, no hint for user
96

97         try {
98             SyntaxQueryHelper helper = new SyntaxQueryHelper(sup, offset);
99             
100             //supposing that there wont be more cc items result sets in one moment hence
101
//using the static field, otherwise the substitute offset would have to be set to all CC items
102
XMLResultItem.substituteOffset = helper.getOffset() - helper.getEraseCount();
103             
104             // completion request originates from area covered by DOM,
105
if (helper.getCompletionType() != SyntaxQueryHelper.COMPLETION_TYPE_DTD) {
106                 List all = new ArrayList();
107                 List list = null;
108                 switch (helper.getCompletionType()) {
109                     case SyntaxQueryHelper.COMPLETION_TYPE_ATTRIBUTE:
110                         list = queryAttributes(helper, doc, sup);
111                         break;
112                     case SyntaxQueryHelper.COMPLETION_TYPE_VALUE:
113                         list = queryValues(helper, doc, sup);
114                         break;
115                     case SyntaxQueryHelper.COMPLETION_TYPE_ELEMENT:
116                         list = queryElements(helper, doc, sup);
117                         break;
118                     case SyntaxQueryHelper.COMPLETION_TYPE_ENTITY:
119                         list = queryEntities(helper, doc, sup);
120                         break;
121                     case SyntaxQueryHelper.COMPLETION_TYPE_NOTATION:
122                         list = queryNotations(helper, doc, sup);
123                     case SyntaxQueryHelper.COMPLETION_TYPE_UNKNOWN:
124                         return null; //do not show the CC
125
}
126                 
127                 if(list == null) {
128                     // broken document
129
return cannotSuggest(component, sup.requestedAutoCompletion());
130                 }
131
132                 if (helper.getCompletionType() == SyntaxQueryHelper.COMPLETION_TYPE_VALUE) {
133                     //might be the end tag autocompletion
134
if(helper.getToken().getTokenID() == XMLDefaultTokenContext.TAG) {
135                         SyntaxElement se = helper.getSyntaxElement();
136                         if(se instanceof StartTag) {
137                             String JavaDoc tagName = ((StartTag)se).getNodeName();
138                             list.add(new EndTagAutocompletionResultItem(tagName));
139                         }
140                     }
141                 }
142                 
143                 if (list.isEmpty() && helper.getPreText().startsWith("</")) { // NOI18N
144
List stlist = findStartTag((SyntaxNode)helper.getSyntaxElement(), !helper.getPreText().endsWith("/") ? "/" : "");
145                     if (stlist != null && !stlist.isEmpty()) {
146                         ElementResultItem item = (ElementResultItem)stlist.get(0); //we always get just one item
147
if(!item.getItemText().startsWith("/") || item.getItemText().startsWith(helper.getPreText().substring(1))) {
148                             String JavaDoc title = Util.THIS.getString("MSG_result", helper.getPreText());
149                             return new XMLCompletionResult(component, title,
150                                     stlist, helper.getOffset(), 0);
151                         }
152                     }
153                 }
154                 
155                 String JavaDoc debugMsg = Boolean.getBoolean("netbeans.debug.xml") ? " " + helper.getOffset() + "-" + helper.getEraseCount() : "";
156                 String JavaDoc title = Util.THIS.getString("MSG_result", helper.getPreText()) + debugMsg;
157                 
158                 // add to the list end tag if detected '<'
159
// unless following end tag is of the same name
160

161                 if (helper.getPreText().endsWith("<") && helper.getToken().getTokenID() == TEXT) { // NOI18N
162
List startTags = findStartTag((SyntaxNode)helper.getSyntaxElement(), "/"); // NOI18N
163

164                     boolean addEndTag = true;
165                     SyntaxNode ctx = (SyntaxNode)helper.getSyntaxElement();
166                     SyntaxElement nextElement = ctx != null ? ctx.getNext() : null;
167                     if (nextElement instanceof EndTag) {
168                         EndTag endtag = (EndTag) nextElement;
169                         String JavaDoc nodename = endtag.getNodeName();
170                         if (nodename != null && startTags.isEmpty() == false) {
171                             ElementResultItem item = (ElementResultItem)startTags.get(0);
172                             if (("/" + nodename).equals(item.getItemText())) { // NOI18N
173
addEndTag = false;
174                             }
175                         }
176                     }
177                     
178                     if (addEndTag) {
179                         //prevent autocompletion of the only CC item.
180
if(!list.isEmpty() || startTags.size() > 1) {
181                             all.addAll(startTags);
182                         }
183                     }
184                     
185                 }
186                 
187                 all.addAll(list);
188                 
189                 if(all.isEmpty()) {
190                     return noSuggestion(component, sup.requestedAutoCompletion());
191                 } else {
192                     return new XMLCompletionResult(
193                             component,
194                             title,
195                             all,
196                             helper.getOffset() - helper.getEraseCount(),
197                             helper.getEraseCount()
198                             );
199                 }
200                 
201             } else {
202                 // prolog, internal DTD no completition yet
203
if (helper.getToken().getTokenID() == PI_CONTENT) {
204                     if (helper.getPreText().endsWith("encoding=")) { // NOI18N
205
List encodings = new ArrayList(2);
206                         encodings.add(new XMLResultItem("\"UTF-8\"")); // NOI18N
207
encodings.add(new XMLResultItem("\"UTF-16\"")); // NOI18N
208
return new XMLCompletionResult(
209                                 component,
210                                 Util.THIS.getString("MSG_encoding_comp"),
211                                 encodings,
212                                 helper.getOffset(),
213                                 0
214                                 );
215                     }
216                 }
217                 return noSuggestion(component, sup.requestedAutoCompletion());
218             }
219             
220         } catch (BadLocationException JavaDoc e) {
221             Util.THIS.debug(e);
222         }
223         
224         // nobody knows what happened...
225
return noSuggestion(component, sup.requestedAutoCompletion());
226     }
227     
228     /**
229      * Contruct result indicating that grammar is not able to give
230      * a hint because document is too broken or invalid. Grammar
231      * knows that it's broken.
232      */

233     private static Result cannotSuggest(JTextComponent JavaDoc component, boolean auto) {
234         if (auto) return null;
235         return new XMLCompletionResult(
236                 component,
237                 Util.THIS.getString("BK0002"),
238                 Collections.EMPTY_LIST,
239                 0,
240                 0
241                 );
242     }
243     
244     /**
245      * Contruct result indicating that grammar is not able to give
246      * a hint because in given context is not nothing allowed what
247      * the grammar know of. May grammar is missing at all.
248      */

249     private static Result noSuggestion(JTextComponent JavaDoc component, boolean auto) {
250         if (auto) return null;
251         return new XMLCompletionResult(
252                 component,
253                 Util.THIS.getString("BK0003"),
254                 Collections.EMPTY_LIST,
255                 0,
256                 0
257                 );
258     }
259     
260     // Grammar binding ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
261

262     /**
263      * Obtain grammar manager, cache results in document property
264      * <code>PROP_DOCUMENT_QUERY</code>. It is always called from single
265      * thread.
266      */

267     public static GrammarQuery getPerformer(Document JavaDoc doc, XMLSyntaxSupport sup) {
268         
269         Object JavaDoc grammarBindingObj = doc.getProperty(DOCUMENT_GRAMMAR_BINDING_PROP);
270         
271         if (grammarBindingObj == null) {
272             grammarBindingObj = new GrammarManager(doc, sup);
273             doc.putProperty(DOCUMENT_GRAMMAR_BINDING_PROP, grammarBindingObj);
274         }
275         
276         return ((GrammarManager)grammarBindingObj).getGrammar();
277     }
278     
279     // Delegate queriing to performer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
280

281     private List queryEntities(SyntaxQueryHelper helper, Document JavaDoc doc, XMLSyntaxSupport sup) {
282         Enumeration JavaDoc res = getPerformer(doc, sup).queryEntities(helper.getContext().getCurrentPrefix());
283         return translateEntityRefs(res);
284     }
285     
286     private List queryElements(SyntaxQueryHelper helper, Document JavaDoc doc, XMLSyntaxSupport sup) {
287         try {
288             GrammarQuery performer = getPerformer(doc, sup);
289             HintContext ctx = helper.getContext();
290             //66607 hacky fix - see the issue comment
291
if(helper.getPreText().startsWith("</")) {
292                 return Collections.EMPTY_LIST;
293             }
294             String JavaDoc typedPrefix = ctx.getCurrentPrefix();
295             Enumeration JavaDoc res = performer.queryElements(ctx);
296             return translateElements(res, typedPrefix, performer);
297         } catch(UOException e){
298             ErrorManager.getDefault().notify(e);
299             return null;
300         }
301     }
302     
303     private List queryAttributes(SyntaxQueryHelper helper, Document JavaDoc doc, XMLSyntaxSupport sup) {
304         Enumeration JavaDoc res = getPerformer(doc, sup).queryAttributes(helper.getContext());
305         return translateAttributes(res, helper.isBoundary());
306     }
307     
308     private List queryValues(SyntaxQueryHelper helper, Document JavaDoc doc, XMLSyntaxSupport sup) {
309         Enumeration JavaDoc res = getPerformer(doc, sup).queryValues(helper.getContext());
310         return translateValues(res);
311     }
312     
313     private List queryNotations(SyntaxQueryHelper helper, Document JavaDoc doc, XMLSyntaxSupport sup) { //!!! to be implemented
314
Enumeration JavaDoc res = getPerformer(doc, sup).queryNotations(helper.getContext().getCurrentPrefix());
315         return null;
316     }
317     
318     // Translate general results to editor ones ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
319

320     private List translateEntityRefs(Enumeration JavaDoc refs ) {
321         List result = new ArrayList(133);
322         while ( refs.hasMoreElements() ) {
323             GrammarResult next = (GrammarResult) refs.nextElement();
324             EntityRefResultItem ref = new EntityRefResultItem(next);
325             result.add( ref );
326         }
327         return result;
328     }
329     
330     /** Translate results perfromer (DOM nodes) format to CompletionQuery.ResultItems format. */
331     private List translateElements(Enumeration JavaDoc els, String JavaDoc prefix, GrammarQuery perfomer) {
332         List result = new ArrayList(13);
333         while (els.hasMoreElements()) {
334             GrammarResult next = (GrammarResult) els.nextElement();
335             if (prefix.equals(next.getNodeName())) {
336 // XXX It's probably OK that perfomer has returned it, we just do not want to visualize it
337
// ErrorManager err =ErrorManager.getDefault();
338
// err.log(ErrorManager.WARNING, "Grammar " + perfomer.getClass().getName() + " result '" + prefix + "' eliminated to avoid #28224."); // NOi18N
339
continue;
340             }
341             ElementResultItem ei = new ElementResultItem(next);
342             result.add( ei );
343         }
344         return result;
345     }
346     
347     
348     private List translateAttributes(Enumeration JavaDoc attrs, boolean boundary) {
349         List result = new ArrayList(13);
350         while (attrs.hasMoreElements()) {
351             GrammarResult next = (GrammarResult) attrs.nextElement();
352             AttributeResultItem attr = new AttributeResultItem(next, false);
353             result.add( attr );
354         }
355         return result;
356     }
357     
358     private List translateValues(Enumeration JavaDoc values ) {
359         List result = new ArrayList(3);
360         while (values.hasMoreElements()) {
361             GrammarResult next = (GrammarResult) values.nextElement();
362             ValueResultItem val = new ValueResultItem(next);
363             result.add( val );
364         }
365         return result;
366     }
367     
368     
369     /**
370      * User just typed <sample>&lt;/</sample> so we must locate
371      * paing start tag.
372      * @param text pointer to starting context
373      * @param prefix that is prepended to created ElementResult e.g. '</'
374      * @return list with one ElementResult or empty.
375      */

376     private static List findStartTag(SyntaxNode text, String JavaDoc prefix) {
377         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("XMLCompletionQuery.findStartTag: text=" + text);
378         
379         Node parent = text.getParentNode();
380         if (parent == null) {
381             return Collections.EMPTY_LIST;
382         }
383         
384         String JavaDoc name = parent.getNodeName();
385         if ( Util.THIS.isLoggable() ) /* then */ {
386             Util.THIS.debug(" name=" + name);
387         }
388         if ( name == null ) {
389             return Collections.EMPTY_LIST;
390         }
391         
392         XMLResultItem res = new ElementResultItem(prefix + name);
393         if ( Util.THIS.isLoggable() ) /* then */ {
394             Util.THIS.debug(" result=" + res);
395         }
396         
397         List list = new ArrayList(1);
398         list.add(res);
399         
400         return list;
401     }
402     
403     private static List findStartTag(SyntaxNode text) {
404         return findStartTag(text, "");
405     }
406     
407     public static class XMLCompletionResult extends CompletionQuery.DefaultResult {
408         private int substituteOffset;
409         public XMLCompletionResult(JTextComponent JavaDoc component, String JavaDoc title, List data, int offset, int len ) {
410             super(component, title, data, offset, len);
411             substituteOffset = offset;
412         }
413         
414         public int getSubstituteOffset() {
415             return substituteOffset;
416         }
417     }
418     
419 }
420
Popular Tags