PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/IntrospectedInfo.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 818 lines | 547 code | 48 blank | 223 comment | 132 complexity | 44c21c4862a0e2dbbf6037cbb8a9c210 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.apache.tools.ant.module.api;
  45. import java.io.IOException;
  46. import java.io.InputStream;
  47. import java.util.Arrays;
  48. import java.util.Collections;
  49. import java.util.Enumeration;
  50. import java.util.HashSet;
  51. import java.util.Map;
  52. import java.util.Properties;
  53. import java.util.Set;
  54. import java.util.TreeMap;
  55. import java.util.logging.Level;
  56. import java.util.logging.Logger;
  57. import java.util.prefs.BackingStoreException;
  58. import java.util.prefs.Preferences;
  59. import java.util.regex.Matcher;
  60. import java.util.regex.Pattern;
  61. import javax.swing.event.ChangeEvent;
  62. import javax.swing.event.ChangeListener;
  63. import org.apache.tools.ant.module.AntModule;
  64. import org.apache.tools.ant.module.AntSettings;
  65. import org.apache.tools.ant.module.bridge.AntBridge;
  66. import org.apache.tools.ant.module.bridge.IntrospectionHelperProxy;
  67. import org.openide.ErrorManager;
  68. import org.openide.util.ChangeSupport;
  69. import org.openide.util.NbCollections;
  70. import org.openide.util.RequestProcessor;
  71. import org.openide.util.Utilities;
  72. import org.openide.util.WeakListeners;
  73. // XXX in order to support Ant 1.6 interface addition types, need to keep
  74. // track of which classes implement a given interface
  75. /** Represents Ant-style introspection info for a set of classes.
  76. * There should be one instance which is loaded automatically
  77. * from defaults.properties files, i.e. standard tasks/datatypes.
  78. * A second is loaded from settings and represents custom tasks/datatypes.
  79. * Uses Ant's IntrospectionHelper for the actual work, but manages the results
  80. * and makes them safely serializable (stores only classnames, etc.).
  81. * <p>
  82. * All task and type names may be namespace-qualified for use
  83. * in Ant 1.6: a name of the form <samp>nsuri:localname</samp> refers to
  84. * an XML element with namespace <samp>nsuri</samp> and local name <samp>localname</samp>.
  85. * Attribute names could also be similarly qualified, but in practice attributes
  86. * used in Ant never have a defined namespace. The prefix <samp>antlib:org.apache.tools.ant:</samp>
  87. * is implied, not expressed, on Ant core element names (for backwards compatibility).
  88. * Subelement names are *not* namespace-qualified here, even though in the script
  89. * they would be - because the namespace used in the script will actually vary
  90. * according to how an antlib is imported and used. An unqualified subelement name
  91. * should be understood to inherit a namespace from its parent element.
  92. * <em>(Namespace support since <code>org.apache.tools.ant.module/3 3.6</code>)</em>
  93. */
  94. public final class IntrospectedInfo {
  95. private static final Logger LOG = Logger.getLogger(IntrospectedInfo.class.getName());
  96. private static IntrospectedInfo defaults = null;
  97. private static boolean defaultsInited = false;
  98. private static boolean defaultsEverInited = false;
  99. /** Get default definitions specified by Ant's defaults.properties.
  100. * @return the singleton defaults
  101. */
  102. public static synchronized IntrospectedInfo getDefaults() {
  103. if (defaults == null) {
  104. defaults = new IntrospectedInfo();
  105. }
  106. return defaults;
  107. }
  108. private Map<String,IntrospectedClass> clazzes = Collections.synchronizedMap(new TreeMap<String,IntrospectedClass>());
  109. /** definitions first by kind then by name to class name */
  110. private final Map<String,Map<String,String>> namedefs = new TreeMap<String,Map<String,String>>();
  111. private final ChangeSupport cs = new ChangeSupport(this);
  112. private ChangeListener antBridgeListener = new ChangeListener() {
  113. public void stateChanged(ChangeEvent ev) {
  114. clearDefs();
  115. fireStateChanged();
  116. }
  117. };
  118. /** Make new empty set of info.
  119. */
  120. public IntrospectedInfo () {
  121. }
  122. private void init() {
  123. synchronized (IntrospectedInfo.class) {
  124. if (!defaultsInited && this == defaults) {
  125. AntModule.err.log("IntrospectedInfo.getDefaults: loading...");
  126. defaultsInited = true;
  127. loadDefaults(!defaultsEverInited);
  128. defaultsEverInited = true;
  129. }
  130. }
  131. }
  132. private void clearDefs() {
  133. clazzes.clear();
  134. namedefs.clear();
  135. defaultsInited = false;
  136. }
  137. private void loadDefaults(boolean listen) {
  138. ClassLoader cl = AntBridge.getMainClassLoader();
  139. InputStream taskDefaults = cl.getResourceAsStream("org/apache/tools/ant/taskdefs/defaults.properties");
  140. if (taskDefaults != null) {
  141. try {
  142. defaults.load(taskDefaults, "task", cl); // NOI18N
  143. } catch (IOException ioe) {
  144. AntModule.err.log("Could not load default taskdefs");
  145. AntModule.err.notify(ioe);
  146. }
  147. } else {
  148. AntModule.err.log("Could not open default taskdefs");
  149. }
  150. InputStream typeDefaults = cl.getResourceAsStream("org/apache/tools/ant/types/defaults.properties");
  151. if (typeDefaults != null) {
  152. try {
  153. defaults.load(typeDefaults, "type", cl); // NOI18N
  154. } catch (IOException ioe) {
  155. AntModule.err.log("Could not load default typedefs");
  156. AntModule.err.notify(ioe);
  157. }
  158. } else {
  159. AntModule.err.log("Could not open default typedefs");
  160. }
  161. defaults.loadNetBeansSpecificDefinitions();
  162. if (listen) {
  163. AntBridge.addChangeListener(WeakListeners.change(antBridgeListener, AntBridge.class));
  164. }
  165. if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
  166. AntModule.err.log("IntrospectedInfo.defaults=" + defaults);
  167. }
  168. }
  169. /** Add a listener to changes in the definition set.
  170. * @param l the listener to add
  171. * @since 2.6
  172. */
  173. public void addChangeListener(ChangeListener l) {
  174. cs.addChangeListener(l);
  175. }
  176. /** Remove a listener to changes in the definition set.
  177. * @param l the listener to remove
  178. * @since 2.6
  179. */
  180. public void removeChangeListener(ChangeListener l) {
  181. cs.removeChangeListener(l);
  182. }
  183. private class ChangeTask implements Runnable {
  184. public void run() {
  185. cs.fireChange();
  186. }
  187. }
  188. private static final RequestProcessor RP = new RequestProcessor(IntrospectedInfo.class);
  189. private void fireStateChanged() {
  190. if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
  191. AntModule.err.log("IntrospectedInfo.fireStateChanged");
  192. }
  193. RP.post(new ChangeTask());
  194. }
  195. /** Get definitions.
  196. * @param kind the kind of definition, e.g. <code>task</code>
  197. * @return an immutable map from definition names to class names
  198. */
  199. public Map<String,String> getDefs(String kind) {
  200. init();
  201. synchronized (namedefs) {
  202. Map<String,String> m = namedefs.get(kind);
  203. if (m != null) {
  204. return Collections.unmodifiableMap(m);
  205. } else {
  206. return Collections.emptyMap();
  207. }
  208. }
  209. }
  210. private IntrospectedClass getData (String clazz) throws IllegalArgumentException {
  211. IntrospectedClass data = clazzes.get(clazz);
  212. if (data == null) {
  213. throw new IllegalArgumentException("Unknown class: " + clazz); // NOI18N
  214. }
  215. return data;
  216. }
  217. /** Is anything known about this class?
  218. * @param clazz the class name
  219. * @return true if it is known, false if never encountered
  220. */
  221. public boolean isKnown (String clazz) {
  222. init();
  223. return clazzes.get (clazz) != null;
  224. }
  225. /** Does this class support inserting text data?
  226. * @param clazz the class name
  227. * @return true if so
  228. * @throws IllegalArgumentException if the class is unknown
  229. */
  230. public boolean supportsText (String clazz) throws IllegalArgumentException {
  231. init();
  232. return getData (clazz).supportsText;
  233. }
  234. /** Get all attributes supported by this class.
  235. * @param clazz the class name
  236. * @return an immutable map from attribute name to type (class name)
  237. * @throws IllegalArgumentException if the class is unknown
  238. */
  239. public Map<String,String> getAttributes(String clazz) throws IllegalArgumentException {
  240. init();
  241. Map<String,String> map = getData(clazz).attrs;
  242. if (map == null) {
  243. return Collections.emptyMap();
  244. } else {
  245. return Collections.unmodifiableMap (map);
  246. }
  247. }
  248. /** Get all subelements supported by this class.
  249. * @param clazz the class name
  250. * @return an immutable map from element name to type (class name)
  251. * @throws IllegalArgumentException if the class is unknown
  252. */
  253. public Map<String,String> getElements(String clazz) throws IllegalArgumentException {
  254. init();
  255. Map<String,String> map = getData(clazz).subs;
  256. if (map == null) {
  257. return Collections.emptyMap();
  258. } else {
  259. return Collections.unmodifiableMap (map);
  260. }
  261. }
  262. /**
  263. * Get tags represented by this class if it is an <code>EnumeratedAttribute</code>.
  264. * @param clazz the class name
  265. * @return a list of tag names, or null if the class is not a subclass of <code>EnumeratedAttribute</code>
  266. * @throws IllegalArgumentException if the class is unknown
  267. * @since org.apache.tools.ant.module/3 3.3
  268. */
  269. public String[] getTags(String clazz) throws IllegalArgumentException {
  270. init();
  271. return getData(clazz).enumTags;
  272. }
  273. /** Load defs from a properties file. */
  274. private void load (InputStream is, String kind, ClassLoader cl) throws IOException {
  275. Properties p = new Properties ();
  276. try {
  277. p.load (is);
  278. } finally {
  279. is.close ();
  280. }
  281. for (Map.Entry<String,String> entry : NbCollections.checkedMapByFilter(p, String.class, String.class, true).entrySet()) {
  282. String name = entry.getKey();
  283. if (kind.equals("type") && name.equals("description")) { // NOI18N
  284. // Not a real data type; handled specially.
  285. AntModule.err.log("Skipping pseudodef of <description>");
  286. continue;
  287. }
  288. String clazzname = entry.getValue();
  289. try {
  290. Class clazz = cl.loadClass (clazzname);
  291. register(name, clazz, kind, false);
  292. } catch (ClassNotFoundException cnfe) {
  293. // This is normal, e.g. Ant's taskdefs include optional tasks we don't have.
  294. AntModule.err.log ("IntrospectedInfo: skipping " + clazzname + ": " + cnfe);
  295. } catch (NoClassDefFoundError ncdfe) {
  296. // Normal for e.g. optional tasks which we cannot resolve against.
  297. AntModule.err.log ("IntrospectedInfo: skipping " + clazzname + ": " + ncdfe);
  298. } catch (LinkageError e) {
  299. // Not normal; if it is there it ought to be resolvable etc.
  300. throw (IOException) new IOException("Could not load class " + clazzname + ": " + e).initCause(e); // NOI18N
  301. } catch (RuntimeException e) {
  302. // SecurityException etc. Not normal.
  303. throw (IOException) new IOException("Could not load class " + clazzname + ": " + e).initCause(e); // NOI18N
  304. }
  305. }
  306. }
  307. private void loadNetBeansSpecificDefinitions() {
  308. loadNetBeansSpecificDefinitions0(AntBridge.getCustomDefsNoNamespace());
  309. if (AntBridge.getInterface().isAnt16()) {
  310. // Define both.
  311. loadNetBeansSpecificDefinitions0(AntBridge.getCustomDefsWithNamespace());
  312. }
  313. }
  314. private void loadNetBeansSpecificDefinitions0(Map<String,Map<String,Class>> defsByKind) {
  315. for (Map.Entry<String,Map<String,Class>> kindE : defsByKind.entrySet()) {
  316. for (Map.Entry<String,Class> defsE : kindE.getValue().entrySet()) {
  317. register(defsE.getKey(), defsE.getValue(), kindE.getKey());
  318. }
  319. }
  320. }
  321. /** Register a new definition.
  322. * May change the defined task/type for a given name, but
  323. * will not redefine structure if classes are modified.
  324. * Also any class definitions contained in the default map (if not this one)
  325. * are just ignored; you should refer to the default map for info on them.
  326. * Throws various errors if the class could not be resolved, e.g. NoClassDefFoundError.
  327. * @param name name of the task or type as it appears in scripts
  328. * @param clazz the implementing class
  329. * @param kind the kind of definition to register (<code>task</code> or <code>type</code> currently)
  330. * @since 2.4
  331. */
  332. public synchronized void register(String name, Class clazz, String kind) {
  333. register(name, clazz, kind, true);
  334. }
  335. private void register(String name, Class clazz, String kind, boolean fire) {
  336. init();
  337. synchronized (namedefs) {
  338. Map<String,String> m = namedefs.get(kind);
  339. if (m == null) {
  340. m = new TreeMap<String,String>();
  341. namedefs.put(kind, m);
  342. }
  343. m.put(name, clazz.getName());
  344. }
  345. boolean changed = analyze(clazz, null, false);
  346. if (changed && fire) {
  347. fireStateChanged();
  348. }
  349. }
  350. /** Unregister a definition.
  351. * Removes it from the definition mapping, though structural
  352. * information about the implementing class (and classes referenced
  353. * by that class) will not be removed.
  354. * If the definition was not registered before, does nothing.
  355. * @param name the definition name
  356. * @param kind the kind of definition (<code>task</code> etc.)
  357. * @since 2.4
  358. */
  359. public synchronized void unregister(String name, String kind) {
  360. init();
  361. synchronized (namedefs) {
  362. Map<String,String> m = namedefs.get(kind);
  363. if (m != null) {
  364. m.remove(name);
  365. }
  366. }
  367. fireStateChanged();
  368. }
  369. /**
  370. * Analyze a particular class and other classes recursively.
  371. * Will never try to redefine anything in the default IntrospectedInfo.
  372. * For custom IntrospectedInfo's, will never try to redefine anything
  373. * if skipReanalysis is null. If not null, will not redefine anything
  374. * in that set - so start recursion by passing an empty set, if you wish
  375. * to redefine anything you come across recursively that is not in the
  376. * default IntrospectedInfo, without causing loops.
  377. * Attribute classes are examined just in case they are EnumeratedAttribute
  378. * subclasses; they are not checked for subelements etc.
  379. * Does not itself fire changes - you should do this if the return value is true.
  380. * @param clazz the class to look at
  381. * @param skipReanalysis null to do not redefs, or a set of already redef'd classes
  382. * @param isAttrType false for an element class, true for an attribute class
  383. * @return true if something changed
  384. */
  385. private boolean analyze(Class clazz, Set<Class> skipReanalysis, boolean isAttrType) {
  386. String n = clazz.getName();
  387. /*
  388. if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
  389. AntModule.err.log("IntrospectedInfo.analyze: " + n + " skipping=" + skipReanalysis + " attrType=" + isAttrType);
  390. }
  391. */
  392. if (getDefaults().isKnown(n)) {
  393. // Never try to redefine anything in the default IntrospectedInfo.
  394. return false;
  395. }
  396. if ((skipReanalysis == null || !skipReanalysis.add(clazz)) && /* #23630 */isKnown(n)) {
  397. // Either we are not redefining anything; or we are, but this class
  398. // has already been in the list. Skip it. If we are continuing, make
  399. // sure to add this class to the skip list so we do not loop.
  400. return false;
  401. }
  402. //AntModule.err.log ("IntrospectedInfo.analyze: clazz=" + clazz.getName ());
  403. //boolean dbg = (clazz == org.apache.tools.ant.taskdefs.Taskdef.class);
  404. //if (! dbg && clazz.getName ().equals ("org.apache.tools.ant.taskdefs.Taskdef")) { // NOI18N
  405. // AntModule.err.log ("Classloader mismatch: cl1=" + clazz.getClassLoader () + " cl2=" + org.apache.tools.ant.taskdefs.Taskdef.class.getClassLoader ());
  406. //}
  407. //if (dbg) AntModule.err.log ("Analyzing <taskdef> attrs...");
  408. IntrospectedClass info = new IntrospectedClass ();
  409. if (isAttrType) {
  410. String[] enumTags = AntBridge.getInterface().getEnumeratedValues(clazz);
  411. if (enumTags != null) {
  412. info.enumTags = enumTags;
  413. return !info.equals(clazzes.put(clazz.getName(), info));
  414. } else {
  415. // Do not store attr clazzes unless they are interesting: EnumAttr.
  416. return clazzes.remove(clazz.getName()) != null;
  417. }
  418. // That's all we do - no subelements etc.
  419. }
  420. IntrospectionHelperProxy helper = AntBridge.getInterface().getIntrospectionHelper(clazz);
  421. info.supportsText = helper.supportsCharacters ();
  422. Enumeration<String> e = helper.getAttributes();
  423. Set<Class> nueAttrTypeClazzes = new HashSet<Class>();
  424. //if (dbg) AntModule.err.log ("Analyzing <taskdef> attrs...");
  425. if (e.hasMoreElements ()) {
  426. while (e.hasMoreElements ()) {
  427. String name = e.nextElement();
  428. //if (dbg) AntModule.err.log ("\tname=" + name);
  429. try {
  430. Class attrType = helper.getAttributeType(name);
  431. String type = attrType.getName();
  432. //if (dbg) AntModule.err.log ("\ttype=" + type);
  433. if (hasSuperclass(clazz, "org.apache.tools.ant.Task") && // NOI18N
  434. ((name.equals ("location") && type.equals ("org.apache.tools.ant.Location")) || // NOI18N
  435. (name.equals ("taskname") && type.equals ("java.lang.String")) || // NOI18N
  436. (name.equals ("description") && type.equals ("java.lang.String")))) { // NOI18N
  437. // IntrospectionHelper is supposed to exclude such things, but I guess not.
  438. // Or it excludes location & taskType.
  439. // description may be OK to actually show on nodes, but since it is common
  440. // to all tasks it should not be stored as such. Ditto taskname.
  441. continue;
  442. }
  443. // XXX also handle subclasses of DataType and its standard attrs
  444. // incl. creating nicely-named node props for description, refid, etc.
  445. if (info.attrs == null) {
  446. info.attrs = new TreeMap<String,String>();
  447. }
  448. info.attrs.put (name, type);
  449. nueAttrTypeClazzes.add(attrType);
  450. } catch (RuntimeException re) { // i.e. BuildException; but avoid loading this class
  451. AntModule.err.notify (ErrorManager.INFORMATIONAL, re);
  452. }
  453. }
  454. } else {
  455. info.attrs = null;
  456. }
  457. Set<Class> nueClazzes = new HashSet<Class>();
  458. e = helper.getNestedElements ();
  459. //if (dbg) AntModule.err.log ("Analyzing <taskdef> subels...");
  460. if (e.hasMoreElements ()) {
  461. while (e.hasMoreElements ()) {
  462. String name = e.nextElement();
  463. //if (dbg) AntModule.err.log ("\tname=" + name);
  464. try {
  465. Class subclazz = helper.getElementType (name);
  466. //if (dbg) AntModule.err.log ("\ttype=" + subclazz.getName ());
  467. if (info.subs == null) {
  468. info.subs = new TreeMap<String,String>();
  469. }
  470. info.subs.put (name, subclazz.getName ());
  471. nueClazzes.add (subclazz);
  472. } catch (RuntimeException re) { // i.e. BuildException; but avoid loading this class
  473. AntModule.err.notify (ErrorManager.INFORMATIONAL, re);
  474. }
  475. }
  476. } else {
  477. info.subs = null;
  478. }
  479. boolean changed = !info.equals(clazzes.put(clazz.getName(), info));
  480. // And recursively analyze reachable classes for subelements...
  481. // (usually these will already be known, and analyze will return at once)
  482. for (Class nueClazz : nueClazzes) {
  483. changed |= analyze(nueClazz, skipReanalysis, false);
  484. }
  485. for (Class nueClazz : nueAttrTypeClazzes) {
  486. changed |= analyze(nueClazz, skipReanalysis, true);
  487. }
  488. return changed;
  489. }
  490. private static boolean hasSuperclass(Class subclass, String superclass) {
  491. for (Class c = subclass; c != null; c = c.getSuperclass()) {
  492. if (c.getName().equals(superclass)) {
  493. return true;
  494. }
  495. }
  496. return false;
  497. }
  498. /**
  499. * Scan an existing (already-run) project to see if it has any new tasks/types.
  500. * Any new definitions found will automatically be added to the known list.
  501. * This will try to change existing definitions in the custom set, i.e.
  502. * if a task is defined to be implemented with a different class, or if a
  503. * class changes structure.
  504. * Will not try to define anything contained in the defaults list.
  505. * @param defs map from kinds to maps from names to classes
  506. */
  507. public void scanProject(Map<String,Map<String,Class>> defs) {
  508. init();
  509. Set<Class> skipReanalysis = new HashSet<Class>();
  510. boolean changed = false;
  511. for (Map.Entry<String,Map<String,Class>> e : defs.entrySet()) {
  512. changed |= scanMap(e.getValue(), e.getKey(), skipReanalysis);
  513. }
  514. if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
  515. AntModule.err.log("IntrospectedInfo.scanProject: " + this);
  516. }
  517. if (changed) {
  518. fireStateChanged();
  519. }
  520. }
  521. private boolean scanMap(Map<String,Class> m, String kind, Set<Class> skipReanalysis) {
  522. if (kind == null) throw new IllegalArgumentException();
  523. boolean changed = false;
  524. for (Map.Entry<String,Class> entry : m.entrySet()) {
  525. String name = entry.getKey();
  526. if (kind.equals("type") && name.equals("description")) { // NOI18N
  527. // Not a real data type; handled specially.
  528. AntModule.err.log("Skipping pseudodef of <description>");
  529. continue;
  530. }
  531. Class clazz = entry.getValue();
  532. if (clazz.getName().equals("org.apache.tools.ant.taskdefs.MacroInstance")) { // NOI18N
  533. continue;
  534. }
  535. Map<String,String> registry = namedefs.get(kind);
  536. if (registry == null) {
  537. registry = new TreeMap<String,String>();
  538. namedefs.put(kind, registry);
  539. }
  540. synchronized (this) {
  541. if (getDefaults().getDefs(kind).get(name) == null) {
  542. changed |= !clazz.getName().equals(registry.put(name, clazz.getName()));
  543. }
  544. if (! getDefaults ().isKnown (clazz.getName ())) {
  545. try {
  546. changed |= analyze(clazz, skipReanalysis, false);
  547. } catch (ThreadDeath td) {
  548. throw td;
  549. } catch (NoClassDefFoundError ncdfe) {
  550. // Reasonably normal.
  551. AntModule.err.log ("Skipping " + clazz.getName () + ": " + ncdfe);
  552. } catch (LinkageError e) {
  553. // Not so normal.
  554. AntModule.err.annotate (e, ErrorManager.INFORMATIONAL, "Cannot scan class " + clazz.getName (), null, null, null); // NOI18N
  555. AntModule.err.notify (ErrorManager.INFORMATIONAL, e);
  556. }
  557. }
  558. }
  559. }
  560. return changed;
  561. }
  562. @Override
  563. public String toString () {
  564. return "IntrospectedInfo[namedefs=" + namedefs + ",clazzes=" + clazzes + "]"; // NOI18N
  565. }
  566. private static final class IntrospectedClass {
  567. public boolean supportsText;
  568. public Map<String,String> attrs; // null or name -> class
  569. public Map<String,String> subs; // null or name -> class
  570. public String[] enumTags; // null or list of tags
  571. @Override
  572. public String toString () {
  573. return "IntrospectedClass[text=" + supportsText + ",attrs=" + attrs + ",subs=" + subs + ",enumTags=" + Arrays.toString(enumTags) + "]"; // NOI18N
  574. }
  575. @Override
  576. public int hashCode() {
  577. // XXX
  578. return 0;
  579. }
  580. @Override
  581. public boolean equals(Object o) {
  582. if (!(o instanceof IntrospectedClass)) {
  583. return false;
  584. }
  585. IntrospectedClass other = (IntrospectedClass)o;
  586. return supportsText == other.supportsText &&
  587. Utilities.compareObjects(attrs, other.attrs) &&
  588. Utilities.compareObjects(subs, other.subs) &&
  589. Utilities.compareObjects(enumTags, other.enumTags);
  590. }
  591. }
  592. // merging and including custom defs:
  593. /** only used to permit use of WeakListener */
  594. private ChangeListener holder;
  595. /**
  596. * Merge several IntrospectedInfo instances together.
  597. * Responds live to updates.
  598. */
  599. private static IntrospectedInfo merge(IntrospectedInfo[] proxied) {
  600. final IntrospectedInfo ii = new IntrospectedInfo();
  601. ChangeListener l = new ChangeListener() {
  602. public void stateChanged(ChangeEvent ev) {
  603. IntrospectedInfo ii2 = (IntrospectedInfo)ev.getSource();
  604. ii2.init();
  605. ii.clazzes.putAll(ii2.clazzes);
  606. for (Map.Entry<String,Map<String,String>> e : ii2.namedefs.entrySet()) {
  607. String kind = e.getKey();
  608. Map<String,String> entries = e.getValue();
  609. if (ii.namedefs.containsKey(kind)) {
  610. ii.namedefs.get(kind).putAll(entries);
  611. } else {
  612. ii.namedefs.put(kind, new TreeMap<String,String>(entries));
  613. }
  614. }
  615. ii.fireStateChanged();
  616. }
  617. };
  618. ii.holder = l;
  619. for (IntrospectedInfo info : proxied) {
  620. info.addChangeListener(WeakListeners.change(l, info));
  621. l.stateChanged(new ChangeEvent(info));
  622. }
  623. return ii;
  624. }
  625. /** defaults + custom defs */
  626. private static IntrospectedInfo merged;
  627. /**
  628. * Get all known introspected definitions.
  629. * Includes all those in {@link #getDefaults} plus custom definitions
  630. * encountered in actual build scripts (details unspecified).
  631. * @return a set of all known definitions, e.g. of tasks and types
  632. * @since 2.14
  633. */
  634. public static synchronized IntrospectedInfo getKnownInfo() {
  635. if (merged == null) {
  636. merged = merge(new IntrospectedInfo[] {
  637. getDefaults(),
  638. AntSettings.getCustomDefs(),
  639. });
  640. }
  641. return merged;
  642. }
  643. static {
  644. AntSettings.IntrospectedInfoSerializer.instance = new AntSettings.IntrospectedInfoSerializer() {
  645. /*
  646. Format quick key:
  647. Map<String,Map<String,String>> namedefs: task.echo=org.apache.tools.ant.taskdefs.Echo
  648. Map<String,IntrospectedClass> clazzes: class.org.apache.tools.ant.taskdefs.Echo.<...>
  649. boolean supportsText: .supportsText=true
  650. null | Map<String,String> attrs: .attrs.message=java.lang.String
  651. null | Map<String,String> subs: .subs.file=java.io.File
  652. null | String[] enumTags: .enumTags=whenempty,always,never
  653. */
  654. Pattern p = Pattern.compile("(.+)\\.(supportsText|attrs\\.(.+)|subs\\.(.+)|enumTags)");
  655. public IntrospectedInfo load(Preferences node) {
  656. IntrospectedInfo ii = new IntrospectedInfo();
  657. try {
  658. for (String k : node.keys()) {
  659. String v = node.get(k, null);
  660. assert v != null : k;
  661. String[] ss = k.split("\\.", 2);
  662. if (ss.length != 2) {
  663. LOG.log(Level.WARNING, "malformed key: {0}", k);
  664. continue;
  665. }
  666. if (ss[0].equals("class")) {
  667. Matcher m = p.matcher(ss[1]);
  668. boolean match = m.matches();
  669. if (!match) {
  670. LOG.log(Level.WARNING, "malformed key: {0}", k);
  671. continue;
  672. }
  673. String c = m.group(1);
  674. IntrospectedClass ic = assureDefined(ii, c);
  675. String tail = m.group(2);
  676. if (tail.equals("supportsText")) {
  677. assert v.equals("true") : k;
  678. ic.supportsText = true;
  679. } else if (tail.equals("enumTags")) {
  680. ic.enumTags = v.split(",");
  681. } else if (m.group(3) != null) {
  682. if (ic.attrs == null) {
  683. ic.attrs = new TreeMap<String,String>();
  684. }
  685. ic.attrs.put(m.group(3), v);
  686. //assureDefined(ii, v);
  687. } else {
  688. assert m.group(4) != null : k;
  689. if (ic.subs == null) {
  690. ic.subs = new TreeMap<String,String>();
  691. }
  692. ic.subs.put(m.group(4), v);
  693. //assureDefined(ii, v);
  694. }
  695. } else {
  696. Map<String,String> m = ii.namedefs.get(ss[0]);
  697. if (m == null) {
  698. m = new TreeMap<String,String>();
  699. ii.namedefs.put(ss[0], m);
  700. }
  701. m.put(ss[1], v);
  702. //assureDefined(ii, v);
  703. }
  704. }
  705. } catch (BackingStoreException x) {
  706. LOG.log(Level.WARNING, null, x);
  707. }
  708. for (String kind : new String[] {"task", "type"}) {
  709. if (!ii.namedefs.containsKey(kind)) {
  710. ii.namedefs.put(kind, new TreeMap<String,String>());
  711. }
  712. }
  713. return ii;
  714. }
  715. private IntrospectedClass assureDefined(IntrospectedInfo ii, String clazz) {
  716. IntrospectedClass ic = ii.clazzes.get(clazz);
  717. if (ic == null) {
  718. ic = new IntrospectedClass();
  719. ii.clazzes.put(clazz, ic);
  720. }
  721. return ic;
  722. }
  723. public void store(Preferences node, IntrospectedInfo info) {
  724. try {
  725. node.clear();
  726. } catch (BackingStoreException x) {
  727. LOG.log(Level.WARNING, null, x);
  728. return;
  729. }
  730. for (Map.Entry<String,Map<String,String>> kindEntries : info.namedefs.entrySet()) {
  731. for (Map.Entry<String,String> namedef : kindEntries.getValue().entrySet()) {
  732. node.put(kindEntries.getKey() + "." + namedef.getKey(), namedef.getValue());
  733. }
  734. }
  735. for (Map.Entry<String,IntrospectedClass> clazzPair : info.clazzes.entrySet()) {
  736. String c = "class." + clazzPair.getKey();
  737. IntrospectedClass ic = clazzPair.getValue();
  738. if (ic.supportsText) {
  739. node.putBoolean(c + ".supportsText", true);
  740. }
  741. if (ic.attrs != null) {
  742. for (Map.Entry<String,String> attr : ic.attrs.entrySet()) {
  743. node.put(c + ".attrs." + attr.getKey(), attr.getValue());
  744. }
  745. }
  746. if (ic.subs != null) {
  747. for (Map.Entry<String,String> sub : ic.subs.entrySet()) {
  748. node.put(c + ".subs." + sub.getKey(), sub.getValue());
  749. }
  750. }
  751. if (ic.enumTags != null) {
  752. StringBuilder b = new StringBuilder();
  753. for (String s : ic.enumTags) {
  754. if (b.length() > 0) {
  755. b.append(',');
  756. }
  757. b.append(s);
  758. }
  759. node.put(c + ".enumTags", b.toString());
  760. }
  761. }
  762. // Exact equivalence is unlikely to happen; there may be unanalyzed Java classes, etc.
  763. //assert equiv(info, load(node)) : info + " vs. " + load(node);
  764. }
  765. private boolean equiv(IntrospectedInfo ii1, IntrospectedInfo ii2) {
  766. return ii1.clazzes.equals(ii2.clazzes) && ii1.namedefs.equals(ii2.namedefs);
  767. }
  768. };
  769. }
  770. }