1 19 20 package org.apache.tools.ant.module.api; 21 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.util.Arrays ; 25 import java.util.Collections ; 26 import java.util.Enumeration ; 27 import java.util.HashSet ; 28 import java.util.Map ; 29 import java.util.Properties ; 30 import java.util.Set ; 31 import java.util.TreeMap ; 32 import java.util.logging.Level ; 33 import java.util.logging.Logger ; 34 import java.util.prefs.BackingStoreException ; 35 import java.util.prefs.Preferences ; 36 import java.util.regex.Matcher ; 37 import java.util.regex.Pattern ; 38 import javax.swing.event.ChangeEvent ; 39 import javax.swing.event.ChangeListener ; 40 import org.apache.tools.ant.module.AntModule; 41 import org.apache.tools.ant.module.AntSettings; 42 import org.apache.tools.ant.module.bridge.AntBridge; 43 import org.apache.tools.ant.module.bridge.IntrospectionHelperProxy; 44 import org.openide.ErrorManager; 45 import org.openide.util.NbCollections; 46 import org.openide.util.RequestProcessor; 47 import org.openide.util.Utilities; 48 import org.openide.util.WeakListeners; 49 50 53 72 public final class IntrospectedInfo { 73 74 private static final Logger LOG = Logger.getLogger(IntrospectedInfo.class.getName()); 75 76 private static IntrospectedInfo defaults = null; 77 private static boolean defaultsInited = false; 78 private static boolean defaultsEverInited = false; 79 80 83 public static synchronized IntrospectedInfo getDefaults() { 84 if (defaults == null) { 85 defaults = new IntrospectedInfo(); 86 } 87 return defaults; 88 } 89 90 private Map <String ,IntrospectedClass> clazzes = Collections.synchronizedMap(new TreeMap <String ,IntrospectedClass>()); 91 92 private Map <String ,Map <String ,String >> namedefs = new TreeMap <String ,Map <String ,String >>(); 93 94 private Set <ChangeListener > listeners = new HashSet <ChangeListener >(5); 95 private Set <ChangeListener > tonotify = new HashSet <ChangeListener >(5); 96 97 private ChangeListener antBridgeListener = new ChangeListener () { 98 public void stateChanged(ChangeEvent ev) { 99 clearDefs(); 100 fireStateChanged(); 101 } 102 }; 103 104 106 public IntrospectedInfo () { 107 } 108 109 private void init() { 110 synchronized (IntrospectedInfo.class) { 111 if (!defaultsInited && this == defaults) { 112 AntModule.err.log("IntrospectedInfo.getDefaults: loading..."); 113 defaultsInited = true; 114 loadDefaults(!defaultsEverInited); 115 defaultsEverInited = true; 116 } 117 } 118 } 119 120 private void clearDefs() { 121 clazzes.clear(); 122 namedefs.clear(); 123 defaultsInited = false; 124 } 125 126 private void loadDefaults(boolean listen) { 127 ClassLoader cl = AntBridge.getMainClassLoader(); 128 InputStream taskDefaults = cl.getResourceAsStream("org/apache/tools/ant/taskdefs/defaults.properties"); 129 if (taskDefaults != null) { 130 try { 131 defaults.load(taskDefaults, "task", cl); } catch (IOException ioe) { 133 AntModule.err.log("Could not load default taskdefs"); 134 AntModule.err.notify(ioe); 135 } 136 } else { 137 AntModule.err.log("Could not open default taskdefs"); 138 } 139 InputStream typeDefaults = cl.getResourceAsStream("org/apache/tools/ant/types/defaults.properties"); 140 if (typeDefaults != null) { 141 try { 142 defaults.load(typeDefaults, "type", cl); } catch (IOException ioe) { 144 AntModule.err.log("Could not load default typedefs"); 145 AntModule.err.notify(ioe); 146 } 147 } else { 148 AntModule.err.log("Could not open default typedefs"); 149 } 150 defaults.loadNetBeansSpecificDefinitions(); 151 if (listen) { 152 AntBridge.addChangeListener(WeakListeners.change(antBridgeListener, AntBridge.class)); 153 } 154 if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) { 155 AntModule.err.log("IntrospectedInfo.defaults=" + defaults); 156 } 157 } 158 159 163 public void addChangeListener(ChangeListener l) { 164 synchronized (listeners) { 165 listeners.add(l); 166 } 167 } 168 169 173 public void removeChangeListener(ChangeListener l) { 174 synchronized (listeners) { 175 listeners.remove(l); 176 } 177 } 178 179 private class ChangeTask implements Runnable { 180 public void run() { 181 ChangeListener [] listeners2; 182 synchronized (listeners) { 183 if (tonotify.isEmpty()) return; 184 listeners2 = tonotify.toArray(new ChangeListener [tonotify.size()]); 185 tonotify.clear(); 186 } 187 ChangeEvent ev = new ChangeEvent (IntrospectedInfo.this); 188 for (ChangeListener l : listeners2) { 189 l.stateChanged(ev); 190 } 191 } 192 } 193 private void fireStateChanged() { 194 if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) { 195 AntModule.err.log("IntrospectedInfo.fireStateChanged"); 196 } 197 synchronized (listeners) { 198 if (listeners.isEmpty()) return; 199 if (tonotify.isEmpty()) { 200 RequestProcessor.getDefault().post(new ChangeTask()); 201 } 202 tonotify.addAll(listeners); 203 } 204 } 205 206 210 public Map <String ,String > getDefs(String kind) { 211 init(); 212 synchronized (namedefs) { 213 Map <String ,String > m = namedefs.get(kind); 214 if (m != null) { 215 return Collections.unmodifiableMap(m); 216 } else { 217 return Collections.emptyMap(); 218 } 219 } 220 } 221 222 private IntrospectedClass getData (String clazz) throws IllegalArgumentException { 223 IntrospectedClass data = clazzes.get(clazz); 224 if (data == null) { 225 throw new IllegalArgumentException ("Unknown class: " + clazz); } 227 return data; 228 } 229 230 234 public boolean isKnown (String clazz) { 235 init(); 236 return clazzes.get (clazz) != null; 237 } 238 239 244 public boolean supportsText (String clazz) throws IllegalArgumentException { 245 init(); 246 return getData (clazz).supportsText; 247 } 248 249 254 public Map <String ,String > getAttributes(String clazz) throws IllegalArgumentException { 255 init(); 256 Map <String ,String > map = getData(clazz).attrs; 257 if (map == null) { 258 return Collections.emptyMap(); 259 } else { 260 return Collections.unmodifiableMap (map); 261 } 262 } 263 264 269 public Map <String ,String > getElements(String clazz) throws IllegalArgumentException { 270 init(); 271 Map <String ,String > map = getData(clazz).subs; 272 if (map == null) { 273 return Collections.emptyMap(); 274 } else { 275 return Collections.unmodifiableMap (map); 276 } 277 } 278 279 286 public String [] getTags(String clazz) throws IllegalArgumentException { 287 init(); 288 return getData(clazz).enumTags; 289 } 290 291 292 private void load (InputStream is, String kind, ClassLoader cl) throws IOException { 293 Properties p = new Properties (); 294 try { 295 p.load (is); 296 } finally { 297 is.close (); 298 } 299 for (Map.Entry <String ,String > entry : NbCollections.checkedMapByFilter(p, String .class, String .class, true).entrySet()) { 300 String name = entry.getKey(); 301 if (kind.equals("type") && name.equals("description")) { AntModule.err.log("Skipping pseudodef of <description>"); 304 continue; 305 } 306 String clazzname = entry.getValue(); 307 try { 308 Class clazz = cl.loadClass (clazzname); 309 register(name, clazz, kind, false); 310 } catch (ClassNotFoundException cnfe) { 311 AntModule.err.log ("IntrospectedInfo: skipping " + clazzname + ": " + cnfe); 313 } catch (NoClassDefFoundError ncdfe) { 314 AntModule.err.log ("IntrospectedInfo: skipping " + clazzname + ": " + ncdfe); 316 } catch (LinkageError e) { 317 throw (IOException ) new IOException ("Could not load class " + clazzname + ": " + e).initCause(e); } catch (RuntimeException e) { 320 throw (IOException ) new IOException ("Could not load class " + clazzname + ": " + e).initCause(e); } 323 } 324 } 325 326 private void loadNetBeansSpecificDefinitions() { 327 loadNetBeansSpecificDefinitions0(AntBridge.getCustomDefsNoNamespace()); 328 if (AntBridge.getInterface().isAnt16()) { 329 loadNetBeansSpecificDefinitions0(AntBridge.getCustomDefsWithNamespace()); 331 } 332 } 333 334 private void loadNetBeansSpecificDefinitions0(Map <String ,Map <String ,Class >> defsByKind) { 335 for (Map.Entry <String ,Map <String ,Class >> kindE : defsByKind.entrySet()) { 336 for (Map.Entry <String ,Class > defsE : kindE.getValue().entrySet()) { 337 register(defsE.getKey(), defsE.getValue(), kindE.getKey()); 338 } 339 } 340 } 341 342 353 public synchronized void register(String name, Class clazz, String kind) { 354 register(name, clazz, kind, true); 355 } 356 357 private void register(String name, Class clazz, String kind, boolean fire) { 358 init(); 359 synchronized (namedefs) { 360 Map <String ,String > m = namedefs.get(kind); 361 if (m == null) { 362 m = new TreeMap <String ,String >(); 363 namedefs.put(kind, m); 364 } 365 m.put(name, clazz.getName()); 366 } 367 boolean changed = analyze(clazz, null, false); 368 if (changed && fire) { 369 fireStateChanged(); 370 } 371 } 372 373 382 public synchronized void unregister(String name, String kind) { 383 init(); 384 synchronized (namedefs) { 385 Map <String ,String > m = namedefs.get(kind); 386 if (m != null) { 387 m.remove(name); 388 } 389 } 390 fireStateChanged(); 391 } 392 393 409 private boolean analyze(Class clazz, Set <Class > skipReanalysis, boolean isAttrType) { 410 String n = clazz.getName(); 411 416 if (getDefaults().isKnown(n)) { 417 return false; 419 } 420 if ((skipReanalysis == null || !skipReanalysis.add(clazz)) && isKnown(n)) { 421 return false; 425 } 426 IntrospectedClass info = new IntrospectedClass (); 433 if (isAttrType) { 434 String [] enumTags = AntBridge.getInterface().getEnumeratedValues(clazz); 435 if (enumTags != null) { 436 info.enumTags = enumTags; 437 return !info.equals(clazzes.put(clazz.getName(), info)); 438 } else { 439 return clazzes.remove(clazz.getName()) != null; 441 } 442 } 444 IntrospectionHelperProxy helper = AntBridge.getInterface().getIntrospectionHelper(clazz); 445 info.supportsText = helper.supportsCharacters (); 446 Enumeration <String > e = helper.getAttributes(); 447 Set <Class > nueAttrTypeClazzes = new HashSet <Class >(); 448 if (e.hasMoreElements ()) { 450 while (e.hasMoreElements ()) { 451 String name = e.nextElement(); 452 try { 454 Class attrType = helper.getAttributeType(name); 455 String type = attrType.getName(); 456 if (hasSuperclass(clazz, "org.apache.tools.ant.Task") && ((name.equals ("location") && type.equals ("org.apache.tools.ant.Location")) || (name.equals ("taskname") && type.equals ("java.lang.String")) || (name.equals ("description") && type.equals ("java.lang.String")))) { continue; 466 } 467 if (info.attrs == null) { 470 info.attrs = new TreeMap <String ,String >(); 471 } 472 info.attrs.put (name, type); 473 nueAttrTypeClazzes.add(attrType); 474 } catch (RuntimeException re) { AntModule.err.notify (ErrorManager.INFORMATIONAL, re); 476 } 477 } 478 } else { 479 info.attrs = null; 480 } 481 Set <Class > nueClazzes = new HashSet <Class >(); 482 e = helper.getNestedElements (); 483 if (e.hasMoreElements ()) { 485 while (e.hasMoreElements ()) { 486 String name = e.nextElement(); 487 try { 489 Class subclazz = helper.getElementType (name); 490 if (info.subs == null) { 492 info.subs = new TreeMap <String ,String >(); 493 } 494 info.subs.put (name, subclazz.getName ()); 495 nueClazzes.add (subclazz); 496 } catch (RuntimeException re) { AntModule.err.notify (ErrorManager.INFORMATIONAL, re); 498 } 499 } 500 } else { 501 info.subs = null; 502 } 503 boolean changed = !info.equals(clazzes.put(clazz.getName(), info)); 504 for (Class nueClazz : nueClazzes) { 507 changed |= analyze(nueClazz, skipReanalysis, false); 508 } 509 for (Class nueClazz : nueAttrTypeClazzes) { 510 changed |= analyze(nueClazz, skipReanalysis, true); 511 } 512 return changed; 513 } 514 515 private static boolean hasSuperclass(Class subclass, String superclass) { 516 for (Class c = subclass; c != null; c = c.getSuperclass()) { 517 if (c.getName().equals(superclass)) { 518 return true; 519 } 520 } 521 return false; 522 } 523 524 533 public void scanProject(Map <String ,Map <String ,Class >> defs) { 534 init(); 535 Set <Class > skipReanalysis = new HashSet <Class >(); 536 boolean changed = false; 537 for (Map.Entry <String ,Map <String ,Class >> e : defs.entrySet()) { 538 changed |= scanMap(e.getValue(), e.getKey(), skipReanalysis); 539 } 540 if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) { 541 AntModule.err.log("IntrospectedInfo.scanProject: " + this); 542 } 543 if (changed) { 544 fireStateChanged(); 545 } 546 } 547 548 private boolean scanMap(Map <String ,Class > m, String kind, Set <Class > skipReanalysis) { 549 if (kind == null) throw new IllegalArgumentException (); 550 boolean changed = false; 551 for (Map.Entry <String ,Class > entry : m.entrySet()) { 552 String name = entry.getKey(); 553 if (kind.equals("type") && name.equals("description")) { AntModule.err.log("Skipping pseudodef of <description>"); 556 continue; 557 } 558 Class clazz = entry.getValue(); 559 if (clazz.getName().equals("org.apache.tools.ant.taskdefs.MacroInstance")) { continue; 561 } 562 Map <String ,String > registry = namedefs.get(kind); 563 if (registry == null) { 564 registry = new TreeMap <String ,String >(); 565 namedefs.put(kind, registry); 566 } 567 synchronized (this) { 568 Map <String ,String > defaults = getDefaults().getDefs(kind); 569 if (defaults.get(name) == null) { 570 changed |= !clazz.getName().equals(registry.put(name, clazz.getName())); 571 } 572 if (! getDefaults ().isKnown (clazz.getName ())) { 573 try { 574 changed |= analyze(clazz, skipReanalysis, false); 575 } catch (ThreadDeath td) { 576 throw td; 577 } catch (NoClassDefFoundError ncdfe) { 578 AntModule.err.log ("Skipping " + clazz.getName () + ": " + ncdfe); 580 } catch (LinkageError e) { 581 AntModule.err.annotate (e, ErrorManager.INFORMATIONAL, "Cannot scan class " + clazz.getName (), null, null, null); AntModule.err.notify (ErrorManager.INFORMATIONAL, e); 584 } 585 } 586 } 587 } 588 return changed; 589 } 590 591 @Override  592 public String toString () { 593 return "IntrospectedInfo[namedefs=" + namedefs + ",clazzes=" + clazzes + "]"; } 595 596 private static final class IntrospectedClass { 597 598 public boolean supportsText; 599 public Map <String ,String > attrs; public Map <String ,String > subs; public String [] enumTags; 603 @Override  604 public String toString () { 605 return "IntrospectedClass[text=" + supportsText + ",attrs=" + attrs + ",subs=" + subs + ",enumTags=" + Arrays.toString(enumTags) + "]"; } 607 608 @Override  609 public int hashCode() { 610 return 0; 612 } 613 614 @Override  615 public boolean equals(Object o) { 616 if (!(o instanceof IntrospectedClass)) { 617 return false; 618 } 619 IntrospectedClass other = (IntrospectedClass)o; 620 return supportsText == other.supportsText && 621 Utilities.compareObjects(attrs, other.attrs) && 622 Utilities.compareObjects(subs, other.subs) && 623 Utilities.compareObjects(enumTags, other.enumTags); 624 } 625 626 } 627 628 630 631 private ChangeListener holder; 632 633 637 private static IntrospectedInfo merge(IntrospectedInfo[] proxied) { 638 final IntrospectedInfo ii = new IntrospectedInfo(); 639 ChangeListener l = new ChangeListener () { 640 public void stateChanged(ChangeEvent ev) { 641 IntrospectedInfo ii2 = (IntrospectedInfo)ev.getSource(); 642 ii2.init(); 643 ii.clazzes.putAll(ii2.clazzes); 644 for (Map.Entry <String ,Map <String ,String >> e : ii2.namedefs.entrySet()) { 645 String kind = e.getKey(); 646 Map <String ,String > entries = e.getValue(); 647 if (ii.namedefs.containsKey(kind)) { 648 ii.namedefs.get(kind).putAll(entries); 649 } else { 650 ii.namedefs.put(kind, new TreeMap <String ,String >(entries)); 651 } 652 } 653 ii.fireStateChanged(); 654 } 655 }; 656 ii.holder = l; 657 for (IntrospectedInfo info : proxied) { 658 info.addChangeListener(WeakListeners.change(l, info)); 659 l.stateChanged(new ChangeEvent (info)); 660 } 661 return ii; 662 } 663 664 665 private static IntrospectedInfo merged; 666 667 674 public static synchronized IntrospectedInfo getKnownInfo() { 675 if (merged == null) { 676 merged = merge(new IntrospectedInfo[] { 677 getDefaults(), 678 AntSettings.getCustomDefs(), 679 }); 680 } 681 return merged; 682 } 683 684 static { 685 AntSettings.IntrospectedInfoSerializer.instance = new AntSettings.IntrospectedInfoSerializer() { 686 695 Pattern p = Pattern.compile("(.+)\\.(supportsText|attrs\\.(.+)|subs\\.(.+)|enumTags)"); 696 public IntrospectedInfo load(Preferences node) { 697 IntrospectedInfo ii = new IntrospectedInfo(); 698 try { 699 for (String k : node.keys()) { 700 String v = node.get(k, null); 701 assert v != null : k; 702 String [] ss = k.split("\\.", 2); 703 if (ss[0].equals("class")) { 704 Matcher m = p.matcher(ss[1]); 705 boolean match = m.matches(); 706 if (!match) { 707 LOG.log(Level.WARNING, "malformed key: {0}", k); 708 continue; 709 } 710 String c = m.group(1); 711 IntrospectedClass ic = assureDefined(ii, c); 712 String tail = m.group(2); 713 if (tail.equals("supportsText")) { 714 assert v.equals("true") : k; 715 ic.supportsText = true; 716 } else if (tail.equals("enumTags")) { 717 ic.enumTags = v.split(","); 718 } else if (m.group(3) != null) { 719 if (ic.attrs == null) { 720 ic.attrs = new TreeMap <String ,String >(); 721 } 722 ic.attrs.put(m.group(3), v); 723 } else { 725 assert m.group(4) != null : k; 726 if (ic.subs == null) { 727 ic.subs = new TreeMap <String ,String >(); 728 } 729 ic.subs.put(m.group(4), v); 730 } 732 } else { 733 Map <String ,String > m = ii.namedefs.get(ss[0]); 734 if (m == null) { 735 m = new TreeMap <String ,String >(); 736 ii.namedefs.put(ss[0], m); 737 } 738 m.put(ss[1], v); 739 } 741 } 742 } catch (BackingStoreException x) { 743 LOG.log(Level.WARNING, null, x); 744 } 745 for (String kind : new String [] {"task", "type"}) { 746 if (!ii.namedefs.containsKey(kind)) { 747 ii.namedefs.put(kind, new TreeMap <String ,String >()); 748 } 749 } 750 return ii; 751 } 752 private IntrospectedClass assureDefined(IntrospectedInfo ii, String clazz) { 753 IntrospectedClass ic = ii.clazzes.get(clazz); 754 if (ic == null) { 755 ic = new IntrospectedClass(); 756 ii.clazzes.put(clazz, ic); 757 } 758 return ic; 759 } 760 public void store(Preferences node, IntrospectedInfo info) { 761 try { 762 node.clear(); 763 } catch (BackingStoreException x) { 764 LOG.log(Level.WARNING, null, x); 765 return; 766 } 767 for (Map.Entry <String ,Map <String ,String >> kindEntries : info.namedefs.entrySet()) { 768 for (Map.Entry <String ,String > namedef : kindEntries.getValue().entrySet()) { 769 node.put(kindEntries.getKey() + "." + namedef.getKey(), namedef.getValue()); 770 } 771 } 772 for (Map.Entry <String ,IntrospectedClass> clazzPair : info.clazzes.entrySet()) { 773 String c = "class." + clazzPair.getKey(); 774 IntrospectedClass ic = clazzPair.getValue(); 775 if (ic.supportsText) { 776 node.putBoolean(c + ".supportsText", true); 777 } 778 if (ic.attrs != null) { 779 for (Map.Entry <String ,String > attr : ic.attrs.entrySet()) { 780 node.put(c + ".attrs." + attr.getKey(), attr.getValue()); 781 } 782 } 783 if (ic.subs != null) { 784 for (Map.Entry <String ,String > sub : ic.subs.entrySet()) { 785 node.put(c + ".subs." + sub.getKey(), sub.getValue()); 786 } 787 } 788 if (ic.enumTags != null) { 789 StringBuilder b = new StringBuilder (); 790 for (String s : ic.enumTags) { 791 if (b.length() > 0) { 792 b.append(','); 793 } 794 b.append(s); 795 } 796 node.put(c + ".enumTags", b.toString()); 797 } 798 } 799 } 802 private boolean equiv(IntrospectedInfo ii1, IntrospectedInfo ii2) { 803 return ii1.clazzes.equals(ii2.clazzes) && ii1.namedefs.equals(ii2.namedefs); 804 } 805 }; 806 } 807 808 } 809 | Popular Tags |