1 19 20 package org.netbeans.modules.xml.text.completion; 21 22 import java.util.*; 23 import java.util.Enumeration ; 24 import javax.swing.text.JTextComponent ; 25 import javax.swing.text.BadLocationException ; 26 import javax.swing.text.Document ; 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 53 54 public class XMLCompletionQuery implements CompletionQuery, XMLTokenIDs { 55 56 public static final String DOCUMENT_GRAMMAR_BINDING_PROP = "doc-bind-query"; 58 59 74 public CompletionQuery.Result query(JTextComponent component, int offset, SyntaxSupport support) { 75 76 78 90 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; 97 try { 98 SyntaxQueryHelper helper = new SyntaxQueryHelper(sup, offset); 99 100 XMLResultItem.substituteOffset = helper.getOffset() - helper.getEraseCount(); 103 104 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; } 126 127 if(list == null) { 128 return cannotSuggest(component, sup.requestedAutoCompletion()); 130 } 131 132 if (helper.getCompletionType() == SyntaxQueryHelper.COMPLETION_TYPE_VALUE) { 133 if(helper.getToken().getTokenID() == XMLDefaultTokenContext.TAG) { 135 SyntaxElement se = helper.getSyntaxElement(); 136 if(se instanceof StartTag) { 137 String tagName = ((StartTag)se).getNodeName(); 138 list.add(new EndTagAutocompletionResultItem(tagName)); 139 } 140 } 141 } 142 143 if (list.isEmpty() && helper.getPreText().startsWith("</")) { List stlist = findStartTag((SyntaxNode)helper.getSyntaxElement(), !helper.getPreText().endsWith("/") ? "/" : ""); 145 if (stlist != null && !stlist.isEmpty()) { 146 ElementResultItem item = (ElementResultItem)stlist.get(0); if(!item.getItemText().startsWith("/") || item.getItemText().startsWith(helper.getPreText().substring(1))) { 148 String 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 debugMsg = Boolean.getBoolean("netbeans.debug.xml") ? " " + helper.getOffset() + "-" + helper.getEraseCount() : ""; 156 String title = Util.THIS.getString("MSG_result", helper.getPreText()) + debugMsg; 157 158 161 if (helper.getPreText().endsWith("<") && helper.getToken().getTokenID() == TEXT) { List startTags = findStartTag((SyntaxNode)helper.getSyntaxElement(), "/"); 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 nodename = endtag.getNodeName(); 170 if (nodename != null && startTags.isEmpty() == false) { 171 ElementResultItem item = (ElementResultItem)startTags.get(0); 172 if (("/" + nodename).equals(item.getItemText())) { addEndTag = false; 174 } 175 } 176 } 177 178 if (addEndTag) { 179 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 if (helper.getToken().getTokenID() == PI_CONTENT) { 204 if (helper.getPreText().endsWith("encoding=")) { List encodings = new ArrayList(2); 206 encodings.add(new XMLResultItem("\"UTF-8\"")); encodings.add(new XMLResultItem("\"UTF-16\"")); 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 e) { 221 Util.THIS.debug(e); 222 } 223 224 return noSuggestion(component, sup.requestedAutoCompletion()); 226 } 227 228 233 private static Result cannotSuggest(JTextComponent 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 249 private static Result noSuggestion(JTextComponent 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 262 267 public static GrammarQuery getPerformer(Document doc, XMLSyntaxSupport sup) { 268 269 Object 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 281 private List queryEntities(SyntaxQueryHelper helper, Document doc, XMLSyntaxSupport sup) { 282 Enumeration res = getPerformer(doc, sup).queryEntities(helper.getContext().getCurrentPrefix()); 283 return translateEntityRefs(res); 284 } 285 286 private List queryElements(SyntaxQueryHelper helper, Document doc, XMLSyntaxSupport sup) { 287 try { 288 GrammarQuery performer = getPerformer(doc, sup); 289 HintContext ctx = helper.getContext(); 290 if(helper.getPreText().startsWith("</")) { 292 return Collections.EMPTY_LIST; 293 } 294 String typedPrefix = ctx.getCurrentPrefix(); 295 Enumeration 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 doc, XMLSyntaxSupport sup) { 304 Enumeration res = getPerformer(doc, sup).queryAttributes(helper.getContext()); 305 return translateAttributes(res, helper.isBoundary()); 306 } 307 308 private List queryValues(SyntaxQueryHelper helper, Document doc, XMLSyntaxSupport sup) { 309 Enumeration res = getPerformer(doc, sup).queryValues(helper.getContext()); 310 return translateValues(res); 311 } 312 313 private List queryNotations(SyntaxQueryHelper helper, Document doc, XMLSyntaxSupport sup) { Enumeration res = getPerformer(doc, sup).queryNotations(helper.getContext().getCurrentPrefix()); 315 return null; 316 } 317 318 320 private List translateEntityRefs(Enumeration 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 331 private List translateElements(Enumeration els, String 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 continue; 340 } 341 ElementResultItem ei = new ElementResultItem(next); 342 result.add( ei ); 343 } 344 return result; 345 } 346 347 348 private List translateAttributes(Enumeration 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 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 376 private static List findStartTag(SyntaxNode text, String prefix) { 377 if ( Util.THIS.isLoggable() ) 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 name = parent.getNodeName(); 385 if ( Util.THIS.isLoggable() ) { 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() ) { 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 component, String 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 |