1 19 20 package org.netbeans.modules.xml.text.completion; 21 22 import java.util.Enumeration ; 23 import javax.swing.text.BadLocationException ; 24 import javax.swing.text.Document ; 25 import javax.swing.event.DocumentListener ; 26 import javax.swing.event.DocumentEvent ; 27 28 import org.netbeans.editor.*; 29 import org.netbeans.editor.ext.*; 30 import org.netbeans.modules.xml.api.model.GrammarQuery; 31 import org.netbeans.modules.xml.api.model.HintContext; 32 import org.netbeans.modules.xml.text.syntax.*; 33 import org.netbeans.modules.xml.text.syntax.dom.*; 34 import org.netbeans.modules.xml.text.api.XMLDefaultTokenContext; 35 import org.w3c.dom.Element ; 36 import org.w3c.dom.NamedNodeMap ; 37 import org.w3c.dom.Node ; 38 import org.openide.util.WeakListeners; 39 import org.openide.ErrorManager; 40 41 48 final class SyntaxQueryHelper { 49 50 public final static int COMPLETION_TYPE_UNKNOWN = 0; 51 public final static int COMPLETION_TYPE_ATTRIBUTE = 1; 52 public final static int COMPLETION_TYPE_VALUE = 2; 53 public final static int COMPLETION_TYPE_ELEMENT = 3; 54 public final static int COMPLETION_TYPE_ENTITY = 4; 55 public final static int COMPLETION_TYPE_NOTATION = 5; 56 public final static int COMPLETION_TYPE_DTD = 6; 57 58 59 private TokenItem token = null; 60 61 private String preText = ""; 62 63 private int erase = 0; 64 65 private int tunedOffset = 0; 66 67 private SyntaxElement element; 68 69 private int completionType = 0; 70 71 private boolean tokenBoundary; 72 73 private DefaultContext ctx = new DefaultContext(); 74 75 76 public SyntaxQueryHelper(XMLSyntaxSupport sup, int offset) throws BadLocationException , IllegalStateException { 77 tunedOffset = offset; 78 token = sup.getPreviousToken( tunedOffset); 79 if( token != null ) { tokenBoundary = token.getOffset() + token.getImage().length() == tunedOffset; 81 } else { 82 throw new BadLocationException ("No token found at current position", offset); } 85 86 88 int itemOffset = token.getOffset(); 89 preText = ""; 90 erase = 0; 91 int eraseRight = 0; 92 int id = token.getTokenID().getNumericID(); 93 94 96 if ( tokenBoundary == false ) { 97 98 preText = token.getImage().substring( 0, tunedOffset - token.getOffset() ); 99 if ("".equals(preText)) throw new IllegalStateException ("Cannot get token prefix at " + tunedOffset); 100 101 104 if (sup.lastTypedChar() != '<' && sup.lastTypedChar() != '&') { 105 switch (id) { 106 107 case XMLDefaultTokenContext.TAG_ID: 108 case XMLDefaultTokenContext.CHARACTER_ID: 109 case XMLDefaultTokenContext.ARGUMENT_ID: 110 111 int i = token.getImage().length(); 112 int tail = i - (tunedOffset - itemOffset); 113 tunedOffset += tail; 114 eraseRight = tail; 115 break; 116 } 117 } 118 } else { 119 switch (id) { 120 case XMLDefaultTokenContext.TEXT_ID: 121 case XMLDefaultTokenContext.TAG_ID: 122 case XMLDefaultTokenContext.ARGUMENT_ID: 123 case XMLDefaultTokenContext.CHARACTER_ID: 124 case XMLCompletionQuery.PI_CONTENT_ID: 125 preText = token.getImage(); 126 break; 127 } 128 } 129 130 132 switch (id) { 133 case XMLDefaultTokenContext.TAG_ID: 134 erase = preText.length() - 1 + eraseRight; 136 break; 137 case XMLDefaultTokenContext.CHARACTER_ID: 138 erase = preText.length() + -1 + eraseRight; 140 String tokenImage = token.getImage().substring(1); if(tokenImage.endsWith(";")) tokenImage = tokenImage.substring(0, tokenImage.length() - 1); GrammarQuery gq = XMLCompletionQuery.getPerformer(sup.getDocument(), sup); 144 if(gq != null) { 145 Enumeration ents = gq.queryEntities(tokenImage); 146 if(!ents.hasMoreElements()) { 147 erase = preText.length() - 1; 148 tunedOffset -= token.getImage().length() - preText.length(); 149 } 150 } 151 break; 152 case XMLDefaultTokenContext.ARGUMENT_ID: 153 erase = preText.length() + eraseRight; 154 break; 155 case XMLDefaultTokenContext.VALUE_ID: 156 erase = preText.length(); 157 if (erase > 0 && (preText.charAt(0) == '\'' || preText.charAt(0) == '"')) { 158 erase--; 160 } else 161 break; 162 } 163 164 element = sup.getElementChain( tunedOffset); 165 166 if (element == null) throw new IllegalStateException ("There exists a token therefore a syntax element must exist at " + offset + ", too."); 167 168 if (element instanceof SyntaxNode && ((SyntaxNode)element).getNodeType() != Node.DOCUMENT_TYPE_NODE) { 170 completionType = initContext(); 171 } else { 172 completionType = COMPLETION_TYPE_DTD; 174 } 175 } 176 177 204 private int initContext() { 205 int id = token.getTokenID().getNumericID(); 206 SyntaxNode syntaxNode = (SyntaxNode)element; 207 208 switch ( id) { 209 case XMLDefaultTokenContext.TEXT_ID: 210 if ( preText.endsWith("<" )) { 211 ctx.init(syntaxNode, ""); 212 return COMPLETION_TYPE_ELEMENT; 213 } else if ( preText.startsWith("&")) { 214 ctx.init(syntaxNode, preText.substring(1)); 215 return COMPLETION_TYPE_ENTITY; 216 } else { 217 ctx.init(syntaxNode, preText); 220 return COMPLETION_TYPE_VALUE; 221 } 222 224 case XMLDefaultTokenContext.TAG_ID: 225 if ( StartTag.class.equals(syntaxNode.getClass()) 226 || EmptyTag.class.equals(syntaxNode.getClass())) { 227 if (preText.equals("")) { 228 if (token.getImage().endsWith(">")) { 230 ctx.init(syntaxNode, preText); 231 return COMPLETION_TYPE_VALUE; 232 } else { 233 ctx.init(syntaxNode, preText); 234 return COMPLETION_TYPE_ELEMENT; 235 } 236 } else if (preText.endsWith("/>")) { 237 ctx.init(syntaxNode, ""); 238 return COMPLETION_TYPE_VALUE; 239 } else if (preText.endsWith(">")) { 240 ctx.init(syntaxNode, ""); 241 return COMPLETION_TYPE_VALUE; 242 } else if (preText.startsWith("</")) { 243 ctx.init(syntaxNode, preText.substring(2)); 245 return COMPLETION_TYPE_ELEMENT; 246 } else if (preText.startsWith("<")) { 247 ctx.init(syntaxNode, preText.substring(1)); 248 return COMPLETION_TYPE_ELEMENT; 249 } 250 } else if(EndTag.class.equals(syntaxNode.getClass()) && preText.startsWith("</")){ 251 ctx.init(syntaxNode, preText.substring(2)); 253 return COMPLETION_TYPE_ELEMENT; 254 } else { 255 if ("".equals(preText) && token.getImage().endsWith(">")) { 257 ctx.init(syntaxNode, preText); 258 return COMPLETION_TYPE_VALUE; 259 } 260 } 261 break; 262 263 case XMLDefaultTokenContext.VALUE_ID: 264 if (preText.endsWith("&")) { 265 ctx.init(syntaxNode, ""); 266 return COMPLETION_TYPE_ENTITY; 267 } else if ("".equals(preText)) { String image = token.getImage(); 269 char ch = image.charAt(image.length()-1); 270 271 273 if (ch == '\'' || ch == '"') { 274 275 if (image.charAt(0) == ch && image.length() > 1) { 276 return COMPLETION_TYPE_UNKNOWN; 278 } 279 280 boolean closing = false; 281 TokenItem prev = token.getPrevious(); 282 283 while (prev != null) { 284 int tid = prev.getTokenID().getNumericID(); 285 if (tid == XMLDefaultTokenContext.VALUE_ID) { 286 closing = true; 287 break; 288 } else if (tid == XMLDefaultTokenContext.CHARACTER_ID) { 289 prev = prev.getPrevious(); 290 } else { 291 break; 292 } 293 } 294 if (closing == false) { 295 ctx.init(syntaxNode, preText); 296 return COMPLETION_TYPE_VALUE; 297 } 298 } else { 299 ctx.init(syntaxNode, preText); 300 return COMPLETION_TYPE_VALUE; 301 } 302 } else { 303 NamedNodeMap attrs = syntaxNode.getAttributes(); 306 int maxOffsetLessThanCurrent = -1; 307 Node curAttrNode = null; 308 for (int ind = 0; ind < attrs.getLength(); ind++) { 309 AttrImpl attr = (AttrImpl)attrs.item(ind); 310 int attrTokOffset = attr.getFirstToken().getOffset(); 311 if (attrTokOffset > maxOffsetLessThanCurrent && attrTokOffset < token.getOffset()) { 312 maxOffsetLessThanCurrent = attrTokOffset; 313 curAttrNode = attr; 314 } 315 } 316 317 if (preText.length() > 0) { 319 preText = preText.substring(1); 320 } 321 if (curAttrNode != null) { 322 ctx.init(curAttrNode, preText); 323 } else { 324 ctx.init(syntaxNode, preText); 325 } 326 return COMPLETION_TYPE_VALUE; 327 } 328 break; 329 330 case XMLDefaultTokenContext.OPERATOR_ID: 331 if ("".equals(preText)) { 332 if ("=".equals(token.getImage())) { 333 ctx.init(syntaxNode, ""); 334 return COMPLETION_TYPE_VALUE; 335 } 336 } 337 break; 338 339 case XMLDefaultTokenContext.WS_ID: 340 if ((StartTag.class.equals(syntaxNode.getClass()) 341 || EmptyTag.class.equals(syntaxNode.getClass())) 342 && !token.getImage().startsWith("/")) { 343 ctx.init((Element )syntaxNode, ""); return COMPLETION_TYPE_ATTRIBUTE; 345 } else { 346 return COMPLETION_TYPE_UNKNOWN; 348 } 349 351 case XMLDefaultTokenContext.ARGUMENT_ID: 352 if (StartTag.class.equals(syntaxNode.getClass()) 353 || EmptyTag.class.equals(syntaxNode.getClass())) { 354 Tag tag = (Tag)syntaxNode; 356 NamedNodeMap nnm = tag.getAttributes(); 357 for(int i = 0; i < nnm.getLength(); i++) { 358 AttrImpl attrNode = (AttrImpl)nnm.item(i); 359 if(attrNode.getFirstToken().getOffset() == token.getOffset()) { 360 ctx.init(attrNode, preText); 361 } 362 } 363 if(!ctx.isInitialized()) { 364 ctx.init((Element )syntaxNode, preText); } 366 return COMPLETION_TYPE_ATTRIBUTE; 367 } 368 break; 369 370 case XMLDefaultTokenContext.CHARACTER_ID: if (preText.startsWith("&#")) { 372 return COMPLETION_TYPE_UNKNOWN; 374 } else if (preText.endsWith(";")) { 375 ctx.init(syntaxNode, ""); 376 return COMPLETION_TYPE_VALUE; 377 } else if (preText.startsWith("&")) { 378 ctx.init(syntaxNode, preText.substring(1)); 379 return COMPLETION_TYPE_ENTITY; 380 } else if ("".equals(preText)) { 381 if (token.getImage().endsWith(";")) { 382 ctx.init(syntaxNode, preText); 383 return COMPLETION_TYPE_VALUE; 384 } 385 } 386 break; 387 388 default: 389 390 } 391 392 return COMPLETION_TYPE_UNKNOWN; 394 } 395 396 public HintContext getContext() { 397 if (completionType != COMPLETION_TYPE_UNKNOWN && completionType != COMPLETION_TYPE_DTD) { 398 return ctx; 399 } else { 400 return null; 401 } 402 } 403 404 405 public TokenItem getToken() { 406 return token; 407 } 408 409 public String getPreText() { 410 return preText; 411 } 412 413 public int getEraseCount() { 414 return erase; 415 } 416 417 public int getOffset() { 418 return tunedOffset; 419 } 420 421 public SyntaxElement getSyntaxElement() { 422 return element; 423 } 424 425 public int getCompletionType() { 426 return completionType; 427 } 428 429 430 public boolean isBoundary() { 431 return tokenBoundary; 432 } 433 434 435 } 436 | Popular Tags |