1 19 package org.netbeans.modules.xml.schema.completion.util; 20 21 import java.net.URI ; 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.HashMap ; 25 import java.util.List ; 26 import java.util.Stack ; 27 import javax.xml.XMLConstants ; 28 import javax.xml.namespace.QName ; 29 import org.w3c.dom.Attr ; 30 import org.w3c.dom.NamedNodeMap ; 31 32 import org.netbeans.editor.BaseDocument; 33 import org.netbeans.editor.TokenItem; 34 import org.openide.filesystems.FileObject; 35 import org.netbeans.modules.xml.text.syntax.dom.StartTag; 36 import org.netbeans.modules.xml.text.syntax.SyntaxElement; 37 import org.netbeans.modules.xml.text.syntax.XMLSyntaxSupport; 38 import org.netbeans.modules.xml.text.api.XMLDefaultTokenContext; 39 import org.netbeans.modules.xml.schema.completion.spi.CompletionContext; 40 import org.netbeans.modules.xml.schema.completion.spi.CompletionContext.CompletionType; 41 import org.netbeans.modules.xml.schema.completion.spi.CompletionModelProvider; 42 import org.netbeans.modules.xml.schema.completion.spi.CompletionModelProvider.CompletionModel; 43 import org.netbeans.modules.xml.text.syntax.dom.EmptyTag; 44 import org.netbeans.modules.xml.text.syntax.dom.EndTag; 45 import org.openide.util.Lookup; 46 47 52 public class CompletionContextImpl extends CompletionContext { 53 54 57 public CompletionContextImpl(FileObject primaryFile, 58 XMLSyntaxSupport support, int offset) { 59 try { 60 this.primaryFile = primaryFile; 61 this.document = support.getDocument(); 62 this.element = support.getElementChain(offset); 63 this.token = support.getPreviousToken(offset); 64 this.docRoot = CompletionUtil.getRoot(element); 65 this.lastTypedChar = support.lastTypedChar(); 66 populateNamespaces(); 67 } catch(Exception ex) { 68 } 71 } 72 73 public CompletionType getCompletionType() { 75 return completionType; 76 } 77 78 public String getDefaultNamespace() { 79 return defaultNamespace; 80 } 81 82 public List <QName > getPathFromRoot() { 83 return pathFromRoot; 84 } 85 86 public FileObject getPrimaryFile() { 87 return primaryFile; 88 } 89 90 public BaseDocument getBaseDocument() { 91 return document; 92 } 93 94 public HashMap <String , String > getDeclaredNamespaces() { 95 return declaredNamespaces; 96 } 97 98 public String getTypedChars() { 99 return typedChars; 100 } 101 102 public boolean isSchemaAwareCompletion() { 103 return schemaLocation != null; 104 } 105 106 public List <URI > getSchemas() { 107 List <URI > uris = new ArrayList <URI >(); 108 if(schemaLocation != null) 109 CompletionUtil.loadSchemaURIs(schemaLocation, uris, false); 110 if(noNamespaceSchemaLocation != null) 111 CompletionUtil.loadSchemaURIs(noNamespaceSchemaLocation, uris, true); 112 return uris; 113 } 114 116 126 private void populateNamespaces() { 127 if(docRoot == null) 128 return; 129 String tagName = docRoot.getTagName(); 132 String defNS = XMLConstants.XMLNS_ATTRIBUTE; 133 String temp = CompletionUtil.getPrefixFromTag(tagName); 134 if(temp != null) defNS = defNS+":"+temp; NamedNodeMap attributes = docRoot.getAttributes(); 136 for(int index=0; index<attributes.getLength(); index++) { 137 Attr attr = (Attr )attributes.item(index); 138 String attrName = attr.getName(); 139 if(CompletionUtil.getLocalNameFromTag(attrName). 140 equals(XSI_SCHEMALOCATION)) { 141 schemaLocation = attr.getValue().trim(); 142 continue; 143 } 144 if(CompletionUtil.getLocalNameFromTag(attrName). 145 equals(XSI_NONS_SCHEMALOCATION)) { 146 noNamespaceSchemaLocation = attr.getValue().trim(); 147 continue; 148 } 149 if(!attr.getName().startsWith(XMLConstants.XMLNS_ATTRIBUTE)) 150 continue; 151 if(attr.getName().equals(defNS)) 152 this.defaultNamespace = attr.getValue(); 153 declaredNamespaces.put(attr.getName(), attr.getValue()); 154 } 155 } 156 157 162 public boolean initContext() { 163 try { 164 fromNoNamespace = false; 165 noNamespaceModel = null; 166 int id = token.getTokenID().getNumericID(); 167 switch ( id) { 168 case XMLDefaultTokenContext.TEXT_ID: 170 String chars = token.getImage().trim(); 171 if(chars != null && chars.equals("") && 172 token.getPrevious().getImage().trim().equals(">")) { 173 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 174 break; 175 } 176 if(chars != null && !chars.equals("<") && 177 token.getPrevious().getImage().trim().equals(">")) { 178 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 179 break; 180 } 181 completionType = CompletionType.COMPLETION_TYPE_ELEMENT; 182 pathFromRoot = getPathFromRoot(element); 183 break; 184 185 case XMLDefaultTokenContext.TAG_ID: 187 if(lastTypedChar == '>') { 188 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 189 break; 190 } 191 if(element instanceof EmptyTag) { 192 completionType = CompletionType.COMPLETION_TYPE_ATTRIBUTE; 193 pathFromRoot = getPathFromRoot(element); 194 break; 195 } 196 197 if(element instanceof StartTag) { 198 StartTag tag = (StartTag)element; 199 typedChars = tag.getTagName(); 200 } 201 completionType = CompletionType.COMPLETION_TYPE_ELEMENT; 202 pathFromRoot = getPathFromRoot(element.getPrevious()); 203 break; 204 205 case XMLDefaultTokenContext.ARGUMENT_ID: 207 completionType = CompletionType.COMPLETION_TYPE_ATTRIBUTE; 208 typedChars = token.getImage(); 209 pathFromRoot = getPathFromRoot(element); 210 break; 211 212 case XMLDefaultTokenContext.CHARACTER_ID: 214 case XMLDefaultTokenContext.OPERATOR_ID: 216 case XMLDefaultTokenContext.VALUE_ID: 218 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 219 break; 220 221 case XMLDefaultTokenContext.WS_ID: 223 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 224 TokenItem prev = token.getPrevious(); 225 while( prev != null && 226 (prev.getTokenID().getNumericID() == XMLDefaultTokenContext.WS_ID) ) { 227 prev = prev.getPrevious(); 228 } 229 if( (prev.getTokenID().getNumericID() == XMLDefaultTokenContext.VALUE_ID) || 230 (prev.getTokenID().getNumericID() == XMLDefaultTokenContext.TAG_ID) ) { 231 completionType = CompletionType.COMPLETION_TYPE_ATTRIBUTE; 232 pathFromRoot = getPathFromRoot(element); 233 } 234 break; 235 236 default: 237 completionType = CompletionType.COMPLETION_TYPE_UNKNOWN; 238 pathFromRoot = getPathFromRoot(element); 239 break; 240 } 241 } catch (Exception ex) { 242 return false; 243 } 244 245 return true; 246 } 247 248 public NamedNodeMap getDocRootAttributes() { 249 return docRoot.getAttributes(); 250 } 251 252 public StartTag getDocRoot() { 253 return docRoot; 254 } 255 256 private List <QName > getPathFromRoot(SyntaxElement se) { 257 assert(se != null); 258 Stack stack = new Stack (); 259 while( se != null) { 260 if( (se instanceof EndTag) || 261 (se instanceof EmptyTag && stack.isEmpty()) || 262 (se instanceof StartTag && stack.isEmpty()) ) { 263 stack.push(se); 264 if( defaultNamespace == null && 265 (se instanceof StartTag || se instanceof EmptyTag) ) { 266 String tagName = (se instanceof StartTag)? 267 ((StartTag)se).getTagName():((EmptyTag)se).getTagName(); 268 if(isRootInNoNSModels(tagName)) 269 break; 270 } 271 se = se.getPrevious(); 272 continue; 273 } 274 if(se instanceof StartTag) { 275 StartTag start = (StartTag)se; 276 if(stack.peek() instanceof EndTag) { 277 EndTag end = (EndTag)stack.peek(); 278 if(end.getTagName().equals(start.getTagName())) { 279 stack.pop(); 280 } 281 } else { 282 SyntaxElement e = (SyntaxElement)stack.peek(); 283 String tagAtTop = (e instanceof StartTag)? 284 ((StartTag)e).getTagName():((EmptyTag)e).getTagName(); 285 if(isRoot(tagAtTop)) 286 break; 287 stack.push(se); 288 } 289 } 290 se = se.getPrevious(); 291 } 292 293 return createPath(stack); 294 } 295 296 private boolean fromSameNamespace(StartTag current, StartTag previous) { 297 String prevPrefix = CompletionUtil.getPrefixFromTag(previous.getTagName()); 298 String thisPrefix = CompletionUtil.getPrefixFromTag(current.getTagName()); 299 String thisNS = (thisPrefix == null) ? declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE) : 300 declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE+":"+thisPrefix); 301 String prevNS = (prevPrefix == null) ? declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE) : 302 declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE+":"+prevPrefix); 303 304 return (thisNS == null && prevNS == null) || 305 (thisNS != null && thisNS.equals(prevNS)) || 306 (prevNS != null && prevNS.equals(thisNS)); 307 } 308 309 private ArrayList <QName > createPath(Stack stack) { 310 ArrayList <QName > path = new ArrayList <QName >(); 311 while(!stack.isEmpty()) { 312 Object top = stack.pop(); 313 String tagName = (top instanceof StartTag)? 314 ((StartTag)top).getTagName():((EmptyTag)top).getTagName(); 315 String prefix = CompletionUtil.getPrefixFromTag(tagName); 316 String lName = CompletionUtil.getLocalNameFromTag(tagName); 317 if(fromNoNamespace) { 318 path.add(new QName (lName)); 319 continue; 320 } 321 322 QName qname = (prefix == null)? 323 new QName (declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE), lName) : 324 new QName (declaredNamespaces.get(XMLConstants.XMLNS_ATTRIBUTE+":"+prefix), lName, prefix); path.add(qname); 326 } 327 return path; 329 } 330 331 private boolean isRoot(String tag) { 332 if(defaultNamespace == null) { 334 if(isRootInNoNSModels(tag)) 335 return true; 336 } 337 String prefix = CompletionUtil.getPrefixFromTag(tag); 339 if(prefix == null) { 340 CompletionModel cm = getCompletionModelMap().get(getDefaultNamespace()); 342 if(CompletionUtil.isRoot(tag, cm)) 343 return true; 344 if(isRootInNoNSModels(tag)) 345 return true; 346 return false; 347 } 348 String tns = getDeclaredNamespaces(). 349 get(XMLConstants.XMLNS_ATTRIBUTE+":"+prefix); 350 CompletionModel cm = getCompletionModelMap().get(tns); 351 return CompletionUtil.isRoot(tag, cm); 352 } 353 354 private boolean isRootInNoNSModels(String tag) { 355 for(CompletionModel m : noNSModels) { 356 if(CompletionUtil.isRoot(tag, m)) { 357 fromNoNamespace = true; 358 noNamespaceModel = m; 359 return true; 360 } 361 } 362 return false; 363 } 364 365 368 public CompletionModel getActiveNoNSModel() { 369 return noNamespaceModel; 370 } 371 372 376 public HashMap <String , CompletionModel> getCompletionModelMap() { 377 return nsModelMap; 378 } 379 380 383 public List <CompletionModel> getNoNamespaceModels() { 384 return noNSModels; 385 } 386 387 390 public List <CompletionModel> getCompletionModels() { 391 List <CompletionModel> models = new ArrayList <CompletionModel>(); 392 models.addAll(nsModelMap.values()); 393 models.addAll(noNSModels); 394 return models; 395 } 396 397 401 public boolean initModels() { 402 Lookup.Template templ = new Lookup.Template(CompletionModelProvider.class); 403 Lookup.Result result = Lookup.getDefault().lookup(templ); 404 Collection impls = result.allInstances(); 405 if(impls == null || impls.size() == 0) 406 return false; 407 for(Object obj: impls) { 408 CompletionModelProvider modelProvider = (CompletionModelProvider)obj; 409 List <CompletionModel> models = modelProvider.getModels(this); 410 if(models == null || models.size() == 0) 411 continue; 412 for(CompletionModel m: models) { 413 String tns = m.getSchemaModel().getSchema().getTargetNamespace(); 414 if(tns == null) { 415 noNSModels.add(m); continue; 417 } 418 nsModelMap.put(tns, m); 420 } 421 } 422 423 return !(nsModelMap.size() == 0 && noNSModels.size() == 0); 424 } 425 426 430 String suggestPrefix(String tns) { 431 if(tns == null) 432 return null; 433 for(String key : getDeclaredNamespaces().keySet()) { 436 String ns = getDeclaredNamespaces().get(key); 437 if(ns.equals(tns)) 438 return key; 439 } 440 441 int index = suggestedNamespaces.size() + 1; 442 String prefix = PREFIX + index; 443 String nsDecl = XMLConstants.XMLNS_ATTRIBUTE+":"+prefix; 444 while(getDeclaredNamespaces().get(nsDecl) != null) { 445 prefix = PREFIX + index++; 446 nsDecl = XMLConstants.XMLNS_ATTRIBUTE+":" + prefix; 447 } 448 suggestedNamespaces.put(prefix, tns); 449 return prefix; 450 } 451 452 public boolean isPrefixBeingUsed(String prefix) { 453 return getDeclaredNamespaces(). 454 get(XMLConstants.XMLNS_ATTRIBUTE+":"+prefix) != null; 455 } 456 457 460 public String getTargetNamespaceByPrefix(String prefix) { 461 for(CompletionModel cm : getCompletionModelMap().values()) { 462 if(prefix.equals(cm.getSuggestedPrefix())) 463 return cm.getTargetNamespace(); 464 } 465 466 return null; 467 } 468 469 private FileObject primaryFile; 470 private String typedChars; 471 private TokenItem token; 472 private SyntaxElement element; 473 private StartTag docRoot; 474 private char lastTypedChar; 475 private CompletionType completionType; 476 private List <QName > pathFromRoot; 477 private String schemaLocation; 478 private String noNamespaceSchemaLocation; 479 private String defaultNamespace; 480 private BaseDocument document; 481 private HashMap <String , CompletionModel> nsModelMap = 482 new HashMap <String , CompletionModel>(); 483 private List <CompletionModel> noNSModels = 484 new ArrayList <CompletionModel>(); 485 private HashMap <String , String > declaredNamespaces = 486 new HashMap <String , String >(); 487 private HashMap <String , String > suggestedNamespaces = 488 new HashMap <String , String >(); 489 private boolean fromNoNamespace = false; 490 private CompletionModel noNamespaceModel; 491 492 public static final String PREFIX = "ns"; public static final String XSI_SCHEMALOCATION = "schemaLocation"; public static final String XSI_NONS_SCHEMALOCATION = "noNamespaceSchemaLocation"; } 496 | Popular Tags |