1 19 20 package org.netbeans.modules.ant.grammar; 21 22 import java.text.Collator ; 23 import java.util.*; 24 import javax.swing.Icon ; 25 26 import org.apache.tools.ant.module.api.IntrospectedInfo; 27 28 import org.w3c.dom.*; 29 30 import org.netbeans.modules.xml.api.model.*; 31 import org.netbeans.modules.xml.spi.dom.*; 32 33 39 class AntGrammar implements GrammarQuery { 40 41 private static Enumeration empty; 42 private static Enumeration empty () { 43 if (empty == null) { 44 empty = Collections.enumeration (Collections.EMPTY_LIST); 45 } 46 return empty; 47 } 48 49 53 public Enumeration queryEntities(String prefix) { 54 ArrayList list = new ArrayList (); 55 56 58 if ("lt".startsWith(prefix)) list.add(new MyEntityReference("lt")); 59 if ("gt".startsWith(prefix)) list.add(new MyEntityReference("gt")); 60 if ("apos".startsWith(prefix)) list.add(new MyEntityReference("apos")); 61 if ("quot".startsWith(prefix)) list.add(new MyEntityReference("quot")); 62 if ("amp".startsWith(prefix)) list.add(new MyEntityReference("amp")); 63 64 return java.util.Collections.enumeration (list); 65 } 66 67 78 79 private static IntrospectedInfo getAntGrammar() { 80 return IntrospectedInfo.getKnownInfo(); 81 } 82 83 84 static final String KIND_SPECIAL = "special"; 86 static final String KIND_TASK = "task"; 88 static final String KIND_TYPE = "type"; 90 static final String KIND_DATA = "data"; 92 static final String SPECIAL_PROJECT = "project"; 94 static final String SPECIAL_TARGET = "target"; 96 static final String SPECIAL_DESCRIPTION = "description"; 98 static final String SPECIAL_IMPORT = "import"; 100 109 static final String [] typeOf(Element e) { 110 String name = e.getNodeName(); 111 Node p = e.getParentNode(); 112 if (p == null) { 113 throw new IllegalArgumentException ("Detached node: " + e); } 115 if (p.getNodeType() == Node.DOCUMENT_NODE) { 116 if (name.equals("project")) { return new String [] {KIND_SPECIAL, SPECIAL_PROJECT}; 118 } else { 119 return null; 121 } 122 } else if (p.getNodeType() == Node.ELEMENT_NODE) { 123 String [] ptype = typeOf((Element)p); 125 if (ptype == null) { 126 return null; 128 } 129 if (ptype[0] == KIND_SPECIAL) { 130 if (ptype[1] == SPECIAL_PROJECT) { 131 if (name.equals("description")) { return new String [] {KIND_SPECIAL, SPECIAL_DESCRIPTION}; 134 } else if (name.equals("target")) { return new String [] {KIND_SPECIAL, SPECIAL_TARGET}; 136 } else if (name.equals("import")) { return new String [] {KIND_SPECIAL, SPECIAL_IMPORT}; 138 } else { 139 String taskClazz = (String )getAntGrammar().getDefs("task").get(name); if (taskClazz != null) { 141 return new String [] {KIND_TASK, taskClazz}; 142 } else { 143 String typeClazz = (String )getAntGrammar().getDefs("type").get(name); if (typeClazz != null) { 145 return new String [] {KIND_TYPE, typeClazz}; 146 } else { 147 return null; 148 } 149 } 150 } 151 } else if (ptype[1] == SPECIAL_TARGET) { 152 String taskClazz = (String )getAntGrammar().getDefs("task").get(name); if (taskClazz != null) { 155 return new String [] {KIND_TASK, taskClazz}; 156 } else { 157 String typeClazz = (String )getAntGrammar().getDefs("type").get(name); if (typeClazz != null) { 159 return new String [] {KIND_TYPE, typeClazz}; 160 } else { 161 return null; 162 } 163 } 164 } else if (ptype[1] == SPECIAL_DESCRIPTION) { 165 return null; 167 } else if (ptype[1] == SPECIAL_IMPORT) { 168 return null; 170 } else { 171 throw new IllegalStateException (ptype[1]); 172 } 173 } else { 174 String pclazz = ptype[1]; 176 String clazz = (String )getAntGrammar().getElements(pclazz).get(name); 177 if (clazz != null) { 178 return new String [] {KIND_DATA, clazz}; 179 } else { 180 return null; 182 } 183 } 184 } else { 185 throw new IllegalArgumentException ("Bad parent for " + e.toString() + ": " + p); } 187 } 188 189 197 public Enumeration queryAttributes(HintContext ctx) { 198 199 Element ownerElement = null; 200 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) { 202 ownerElement = ((Attr)ctx).getOwnerElement(); 203 } else if (ctx.getNodeType() == Node.ELEMENT_NODE) { 204 ownerElement = (Element) ctx; 205 } 206 if (ownerElement == null) return empty (); 207 208 NamedNodeMap existingAttributes = ownerElement.getAttributes(); 209 List possibleAttributes; 210 String [] typePair = typeOf(ownerElement); 211 if (typePair == null) { 212 return empty (); 213 } 214 String kind = typePair[0]; 215 String clazz = typePair[1]; 216 217 if (kind == KIND_SPECIAL && clazz == SPECIAL_PROJECT) { 218 possibleAttributes = new LinkedList(); 219 possibleAttributes.add("default"); 220 possibleAttributes.add("name"); 221 possibleAttributes.add("basedir"); 222 } else if (kind == KIND_SPECIAL && clazz == SPECIAL_TARGET) { 223 possibleAttributes = new LinkedList(); 224 possibleAttributes.add("name"); 225 possibleAttributes.add("depends"); 226 possibleAttributes.add("description"); 227 possibleAttributes.add("if"); 228 possibleAttributes.add("unless"); 229 } else if (kind == KIND_SPECIAL && clazz == SPECIAL_DESCRIPTION) { 230 return empty (); 231 } else if (kind == KIND_SPECIAL && clazz == SPECIAL_IMPORT) { 232 possibleAttributes = new LinkedList(); 233 possibleAttributes.add("file"); 234 possibleAttributes.add("optional"); 235 } else { 236 possibleAttributes = new LinkedList(); 238 if (kind == KIND_TYPE) { 239 possibleAttributes.add("id"); 240 } 241 if (getAntGrammar().isKnown(clazz)) { 242 possibleAttributes.addAll(new TreeSet(getAntGrammar().getAttributes(clazz).keySet())); 243 } 244 if (kind == KIND_TASK) { 245 possibleAttributes.add("id"); 247 possibleAttributes.add("description"); 251 possibleAttributes.add("taskname"); 253 } 254 } 255 256 String prefix = ctx.getCurrentPrefix(); 257 258 ArrayList list = new ArrayList (); 259 Iterator it = possibleAttributes.iterator(); 260 while ( it.hasNext()) { 261 String next = (String ) it.next(); 262 if (next.startsWith(prefix)) { 263 if (existingAttributes.getNamedItem(next) == null) { 264 list.add(new MyAttr(next)); 265 } 266 } 267 } 268 269 return Collections.enumeration (list); 270 } 271 272 281 public Enumeration queryElements(HintContext ctx) { 282 283 Node parent = ((Node)ctx).getParentNode(); 284 if (parent == null) return empty (); 285 if (parent.getNodeType() != Node.ELEMENT_NODE) { 286 return empty (); 287 } 288 289 List elements; 290 String [] typePair = typeOf((Element)parent); 291 if (typePair == null) { 292 return empty (); 293 } 294 String kind = typePair[0]; 295 String clazz = typePair[1]; 296 297 if (kind == KIND_SPECIAL && clazz == SPECIAL_PROJECT) { 298 elements = new LinkedList(); 299 elements.add("target"); 300 elements.add("import"); 301 elements.add("property"); 302 elements.add("description"); 303 SortedSet tasks = getSortedDefs("task"); 304 tasks.remove("property"); 305 tasks.remove("import"); 306 elements.addAll(tasks); elements.addAll(getSortedDefs("type")); 308 } else if (kind == KIND_SPECIAL && clazz == SPECIAL_TARGET) { 309 elements = new ArrayList(getSortedDefs("task")); 310 elements.addAll(getSortedDefs("type")); } else if (kind == KIND_SPECIAL && clazz == SPECIAL_DESCRIPTION) { 313 return empty (); 314 } else if (kind == KIND_SPECIAL && clazz == SPECIAL_IMPORT) { 315 return empty (); 316 } else { 317 if (getAntGrammar().isKnown(clazz)) { 319 elements = new ArrayList(new TreeSet(getAntGrammar().getElements(clazz).keySet())); 320 } else { 321 elements = Collections.EMPTY_LIST; 322 } 323 } 324 325 String prefix = ctx.getCurrentPrefix(); 326 327 ArrayList list = new ArrayList (); 328 Iterator it = elements.iterator(); 329 while ( it.hasNext()) { 330 String next = (String ) it.next(); 331 if (next.startsWith(prefix)) { 332 list.add (new MyElement(next)); 333 } 334 } 335 336 return Collections.enumeration (list); 337 } 338 339 private static SortedSet getSortedDefs(String kind) { 340 SortedSet defs = new TreeSet(Collator.getInstance()); 341 defs.addAll(getAntGrammar().getDefs(kind).keySet()); 342 return defs; 343 } 344 345 349 public Enumeration queryNotations(String prefix) { 350 return empty (); 351 } 352 353 public Enumeration queryValues(HintContext ctx) { 354 Attr ownerAttr; 357 if (canCompleteProperty(ctx.getCurrentPrefix())) { 358 return completeProperties(ctx); 359 } else if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) { 360 ownerAttr = (Attr)ctx; 361 } else { 362 return empty (); 363 } 364 Element ownerElement = ownerAttr.getOwnerElement(); 365 String attrName = ownerAttr.getName(); 366 String [] typePair = typeOf(ownerElement); 367 if (typePair == null) { 368 return empty (); 369 } 370 List choices = new ArrayList(); 371 372 if (typePair[0].equals(KIND_SPECIAL)) { 373 if (typePair[1].equals(SPECIAL_PROJECT)) { 374 if (attrName.equals("default")) { 375 } else if (attrName.equals("basedir")) { 377 } 379 } else if (typePair[1].equals(SPECIAL_TARGET)) { 381 if (attrName.equals("depends")) { 382 } else if (attrName.equals("if") || attrName.equals("unless")) { 384 choices.addAll(Arrays.asList(likelyPropertyNames(ctx))); 385 } 386 } else if (typePair[1].equals(SPECIAL_DESCRIPTION)) { 388 } else if (typePair[1].equals(SPECIAL_IMPORT)) { 390 if (attrName.equals("file")) { 391 } else if (attrName.equals("optional")) { 393 choices.add("true"); 394 choices.add("false"); 395 } 396 } else { 397 assert false : typePair[1]; 398 } 399 } else { 400 String elementClazz = typePair[1]; 401 if (getAntGrammar().isKnown(elementClazz)) { 402 String attrClazzName = (String )getAntGrammar().getAttributes(elementClazz).get(attrName); 403 if (attrClazzName != null) { 404 if (getAntGrammar().isKnown(attrClazzName)) { 405 String [] enumTags = getAntGrammar().getTags(attrClazzName); 406 if (enumTags != null) { 407 choices.addAll(Arrays.asList(enumTags)); 408 } 409 } 410 if (attrClazzName.equals("boolean")) { 411 choices.add("true"); 412 choices.add("false"); 413 } else if (attrClazzName.equals("org.apache.tools.ant.types.Reference")) { 414 } else if (attrClazzName.equals("org.apache.tools.ant.types.Path") || 416 attrClazzName.equals("java.io.File") 417 418 ) { 419 } else if (attrClazzName.equals("java.lang.String") && 421 Arrays.asList(PROPERTY_NAME_VALUED_PROPERTY_NAMES).contains(attrName)) { 422 choices.addAll(Arrays.asList(likelyPropertyNames(ctx))); 424 } 425 } 426 } 427 } 428 429 String prefix = ctx.getCurrentPrefix(); 431 ArrayList list = new ArrayList (); 432 Iterator it = choices.iterator(); 433 while (it.hasNext()) { 434 String next = (String )it.next(); 435 if (next.startsWith(prefix)) { 436 list.add (new MyText(next)); 437 } 438 } 439 return Collections.enumeration (list); 440 } 441 442 453 private static boolean canCompleteProperty(String content) { 454 content = deletedEscapedShells(content); 455 if (content.length() == 0) { 456 return false; 457 } 458 if (content.charAt(content.length() - 1) == '$') { 459 return true; 460 } 461 int idx = content.lastIndexOf("${"); 462 return idx != -1 && content.indexOf('}', idx) == -1; 463 } 464 465 private static Enumeration completeProperties(HintContext ctx) { 466 String content = ctx.getCurrentPrefix(); 467 assert content.length() > 0; 468 String header; 469 String propPrefix; 470 if (content.charAt(content.length() - 1) == '$') { 471 header = content + '{'; 472 propPrefix = ""; 473 } else { 474 int idx = content.lastIndexOf("${"); 475 assert idx != -1; 476 header = content.substring(0, idx + 2); 477 propPrefix = content.substring(idx + 2); 478 } 479 String [] props = likelyPropertyNames(ctx); 480 boolean shortHeader = ctx.getNodeType() == Node.TEXT_NODE; 483 ArrayList list = new ArrayList (); 484 for (int i = 0; i < props.length; i++) { 485 if (props[i].startsWith(propPrefix)) { 486 String text = header + props[i] + '}';; 487 if (shortHeader) { 488 assert text.startsWith(content) : "text=" + text + " content=" + content; 489 text = text.substring(content.length()); 490 } 491 list.add (new MyText(text)); 492 } 493 } 494 return Collections.enumeration (list); 495 } 496 497 500 private static final String [] STOCK_PROPERTY_NAMES = { 501 "ant.home", "basedir", "ant.file", "ant.project.name", "ant.java.version", "ant.version", "java.version", "java.vendor", "java.vendor.url", "java.home", "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", "java.vm.version", "java.vm.vendor", "java.vm.name", "java.specification.version", "java.specification.vendor", "java.specification.name", "java.class.version", "java.class.path", "java.library.path", "java.io.tmpdir", "java.compiler", "java.ext.dirs", "os.name", "os.arch", "os.version", "file.separator", "path.separator", "line.separator", "user.name", "user.home", "user.dir", }; 539 540 private static String [] likelyPropertyNames(HintContext ctx) { 541 Element parent; 544 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE) { 547 parent = ((Attr)ctx).getOwnerElement(); 548 } else if (ctx.getNodeType() == Node.TEXT_NODE) { 549 Node p = ctx.getParentNode(); 550 if (p != null && p.getNodeType() == Node.ELEMENT_NODE) { 551 parent = (Element)p; 552 } else { 553 System.err.println("strange parent of text node: " + p.getNodeType() + " " + p); 554 return new String [0]; 555 } 556 } else { 557 System.err.println("strange context type: " + ctx.getNodeType() + " " + ctx); 558 return new String [0]; 559 } 560 while (parent.getParentNode() != null && parent.getParentNode().getNodeType() == Node.ELEMENT_NODE) { 561 parent = (Element)parent.getParentNode(); 562 } 563 Set choices = new TreeSet(Arrays.asList(STOCK_PROPERTY_NAMES)); 565 visitForLikelyPropertyNames(parent, choices); 566 Iterator it = choices.iterator(); 567 while (it.hasNext()) { 568 String propname = (String )it.next(); 569 if (propname.indexOf("${") != -1) { 570 it.remove(); 573 } 574 } 575 return (String [])choices.toArray(new String [choices.size()]); 576 } 577 578 private static final String [] PROPERTY_NAME_VALUED_PROPERTY_NAMES = { 579 "if", 580 "unless", 581 "property", 583 "failureproperty", 584 "errorproperty", 585 "addproperty", 586 }; 587 588 private static void visitForLikelyPropertyNames(Node n, Set choices) { 589 int type = n.getNodeType(); 590 switch (type) { 591 case Node.ELEMENT_NODE: 592 Element el = (Element)n; 595 String tagname = el.getTagName(); 596 if (tagname.equals("property")) { 597 String propname = el.getAttribute("name"); 598 if (propname != null && propname.length() > 0) { 600 choices.add(propname); 601 } 602 } else if (tagname.equals("buildnumber")) { 604 choices.add("build.number"); 606 } else if (tagname.equals("tstamp")) { 607 choices.add("DSTAMP"); 610 choices.add("TSTAMP"); 611 choices.add("TODAY"); 612 } 613 for (int i = 0; i < PROPERTY_NAME_VALUED_PROPERTY_NAMES.length; i++) { 615 String propname = el.getAttribute(PROPERTY_NAME_VALUED_PROPERTY_NAMES[i]); 616 if (propname != null && propname.length() > 0) { 617 choices.add(propname); 618 } 619 } 620 break; 621 case Node.ATTRIBUTE_NODE: 622 case Node.TEXT_NODE: 623 String text = deletedEscapedShells(n.getNodeValue()); 625 int idx = 0; 626 while (true) { 627 int start = text.indexOf("${", idx); 628 if (start == -1) { 629 break; 630 } 631 int end = text.indexOf('}', start + 2); 632 if (end == -1) { 633 break; 634 } 635 String propname = text.substring(start + 2, end); 636 if (propname.length() > 0) { 637 choices.add(propname); 638 } 639 idx = end + 1; 640 } 641 break; 642 default: 643 break; 645 } 646 NodeList l = n.getChildNodes(); 647 for (int i = 0; i < l.getLength(); i++) { 648 visitForLikelyPropertyNames(l.item(i), choices); 649 } 650 NamedNodeMap m = n.getAttributes(); 652 if (m != null) { 653 for (int i = 0; i < m.getLength(); i++) { 654 visitForLikelyPropertyNames(m.item(i), choices); 655 } 656 } 657 } 658 659 663 private static String deletedEscapedShells(String text) { 664 return text.replaceAll("\\$\\$", ""); 666 } 667 668 public GrammarResult queryDefault(final HintContext ctx) { 670 return null; 671 } 672 673 public boolean isAllowed(Enumeration en) { 675 return true; 676 } 677 678 680 public java.awt.Component getCustomizer(HintContext ctx) { 681 return null; 682 } 683 684 public boolean hasCustomizer(HintContext ctx) { 685 return false; 686 } 687 688 public org.openide.nodes.Node.Property[] getProperties(HintContext ctx) { 689 return null; 690 } 691 692 693 695 private static abstract class AbstractResultNode extends AbstractNode implements GrammarResult { 696 697 public Icon getIcon(int kind) { 698 return null; 699 } 700 701 public String getDescription() { 702 return null; 703 } 704 705 public String getDisplayName() { 706 return null; 707 } 708 709 public boolean isEmptyElement() { 711 return false; 712 } 713 } 714 715 private static class MyEntityReference extends AbstractResultNode implements EntityReference { 716 717 private String name; 718 719 MyEntityReference(String name) { 720 this.name = name; 721 } 722 723 public short getNodeType() { 724 return Node.ENTITY_REFERENCE_NODE; 725 } 726 727 public String getNodeName() { 728 return name; 729 } 730 731 } 732 733 private static class MyElement extends AbstractResultNode implements Element { 734 735 private String name; 736 737 MyElement(String name) { 738 this.name = name; 739 } 740 741 public short getNodeType() { 742 return Node.ELEMENT_NODE; 743 } 744 745 public String getNodeName() { 746 return name; 747 } 748 749 public String getTagName() { 750 return name; 751 } 752 753 } 754 755 private static class MyAttr extends AbstractResultNode implements Attr { 756 757 private String name; 758 759 MyAttr(String name) { 760 this.name = name; 761 } 762 763 public short getNodeType() { 764 return Node.ATTRIBUTE_NODE; 765 } 766 767 public String getNodeName() { 768 return name; 769 } 770 771 public String getName() { 772 return name; 773 } 774 775 public String getValue() { 776 return null; } 778 779 780 } 781 782 private static class MyText extends AbstractResultNode implements Text { 783 784 private String data; 785 786 MyText(String data) { 787 this.data = data; 788 } 789 790 public short getNodeType() { 791 return Node.TEXT_NODE; 792 } 793 794 public String getNodeValue() { 795 return data; 796 } 797 798 public String getData() throws DOMException { 799 return data; 800 } 801 802 public int getLength() { 803 return data == null ? -1 : data.length(); 804 } 805 } 806 807 } 808 | Popular Tags |