You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProjectHelper.java 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant;
  19. import java.io.File;
  20. import java.net.URL;
  21. import java.util.Hashtable;
  22. import java.util.Locale;
  23. import java.util.Vector;
  24. import org.apache.tools.ant.util.LoaderUtils;
  25. import org.xml.sax.AttributeList;
  26. /**
  27. * Configures a Project (complete with Targets and Tasks) based on
  28. * a build file. It'll rely on a plugin to do the actual processing
  29. * of the file.
  30. * <p>
  31. * This class also provide static wrappers for common introspection.
  32. */
  33. public class ProjectHelper {
  34. /** The URI for ant name space */
  35. public static final String ANT_CORE_URI = "antlib:org.apache.tools.ant";
  36. /** The URI for antlib current definitions */
  37. public static final String ANT_CURRENT_URI = "ant:current";
  38. /** The URI for defined types/tasks - the format is antlib:<package> */
  39. public static final String ANTLIB_URI = "antlib:";
  40. /** Polymorphic attribute */
  41. public static final String ANT_TYPE = "ant-type";
  42. /**
  43. * Name of JVM system property which provides the name of the
  44. * ProjectHelper class to use.
  45. */
  46. public static final String HELPER_PROPERTY = MagicNames.PROJECT_HELPER_CLASS;
  47. /**
  48. * The service identifier in jars which provide Project Helper
  49. * implementations.
  50. */
  51. public static final String SERVICE_ID = MagicNames.PROJECT_HELPER_SERVICE;
  52. /**
  53. * name of project helper reference that we add to a project
  54. */
  55. public static final String PROJECTHELPER_REFERENCE = MagicNames.REFID_PROJECT_HELPER;
  56. /**
  57. * Configures the project with the contents of the specified XML file.
  58. *
  59. * @param project The project to configure. Must not be <code>null</code>.
  60. * @param buildFile An XML file giving the project's configuration.
  61. * Must not be <code>null</code>.
  62. *
  63. * @exception BuildException if the configuration is invalid or cannot be read
  64. */
  65. public static void configureProject(Project project, File buildFile) throws BuildException {
  66. ProjectHelper helper = ProjectHelperRepository.getInstance().getProjectHelper(buildFile);
  67. project.addReference(PROJECTHELPER_REFERENCE, helper);
  68. helper.parse(project, buildFile);
  69. }
  70. /** Default constructor */
  71. public ProjectHelper() {
  72. }
  73. // -------------------- Common properties --------------------
  74. // The following properties are required by import ( and other tasks
  75. // that read build files using ProjectHelper ).
  76. private Vector importStack = new Vector();
  77. /**
  78. * Import stack.
  79. * Used to keep track of imported files. Error reporting should
  80. * display the import path.
  81. *
  82. * @return the stack of import source objects.
  83. */
  84. public Vector getImportStack() {
  85. return importStack;
  86. }
  87. private final static ThreadLocal targetPrefix = new ThreadLocal() {
  88. protected Object initialValue() {
  89. return (String) null;
  90. }
  91. };
  92. /**
  93. * The prefix to prepend to imported target names.
  94. *
  95. * <p>May be set by &lt;import&gt;'s as attribute.</p>
  96. *
  97. * @return the configured prefix or null
  98. *
  99. * @since Ant 1.8.0
  100. */
  101. public static String getCurrentTargetPrefix() {
  102. return (String) targetPrefix.get();
  103. }
  104. /**
  105. * Sets the prefix to prepend to imported target names.
  106. *
  107. * @since Ant 1.8.0
  108. */
  109. public static void setCurrentTargetPrefix(String prefix) {
  110. targetPrefix.set(prefix);
  111. }
  112. private final static ThreadLocal prefixSeparator = new ThreadLocal() {
  113. protected Object initialValue() {
  114. return ".";
  115. }
  116. };
  117. /**
  118. * The separator between the prefix and the target name.
  119. *
  120. * <p>May be set by &lt;import&gt;'s prefixSeperator attribute.</p>
  121. *
  122. * @since Ant 1.8.0
  123. */
  124. public static String getCurrentPrefixSeparator() {
  125. return (String) prefixSeparator.get();
  126. }
  127. /**
  128. * Sets the separator between the prefix and the target name.
  129. *
  130. * @since Ant 1.8.0
  131. */
  132. public static void setCurrentPrefixSeparator(String sep) {
  133. prefixSeparator.set(sep);
  134. }
  135. private final static ThreadLocal inIncludeMode = new ThreadLocal() {
  136. protected Object initialValue() {
  137. return Boolean.FALSE;
  138. }
  139. };
  140. /**
  141. * Whether the current file should be read in include as opposed
  142. * to import mode.
  143. *
  144. * <p>In include mode included targets are only known by their
  145. * prefixed names and their depends lists get rewritten so that
  146. * all dependencies get the prefix as well.</p>
  147. *
  148. * <p>In import mode imported targets are known by an adorned as
  149. * well as a prefixed name and the unadorned target may be
  150. * overwritten in the importing build file. The depends list of
  151. * the imported targets is not modified at all.</p>
  152. *
  153. * @since Ant 1.8.0
  154. */
  155. public static boolean isInIncludeMode() {
  156. return inIncludeMode.get() == Boolean.TRUE;
  157. }
  158. /**
  159. * Sets whether the current file should be read in include as
  160. * opposed to import mode.
  161. *
  162. * @since Ant 1.8.0
  163. */
  164. public static void setInIncludeMode(boolean includeMode) {
  165. inIncludeMode.set(includeMode ? Boolean.TRUE : Boolean.FALSE);
  166. }
  167. // -------------------- Parse method --------------------
  168. /**
  169. * Parses the project file, configuring the project as it goes.
  170. *
  171. * @param project The project for the resulting ProjectHelper to configure.
  172. * Must not be <code>null</code>.
  173. * @param source The source for XML configuration. A helper must support
  174. * at least File, for backward compatibility. Helpers may
  175. * support URL, InputStream, etc or specialized types.
  176. *
  177. * @since Ant1.5
  178. * @exception BuildException if the configuration is invalid or cannot
  179. * be read
  180. */
  181. public void parse(Project project, Object source) throws BuildException {
  182. throw new BuildException("ProjectHelper.parse() must be implemented "
  183. + "in a helper plugin " + this.getClass().getName());
  184. }
  185. /**
  186. * Get the first project helper found in the classpath
  187. *
  188. * @return an project helper, never <code>null</code>
  189. * @see #getHelpers()
  190. */
  191. public static ProjectHelper getProjectHelper() {
  192. return (ProjectHelper) ProjectHelperRepository.getInstance().getHelpers().next();
  193. }
  194. /**
  195. * JDK1.1 compatible access to the context class loader. Cut & paste from JAXP.
  196. *
  197. * @deprecated since 1.6.x.
  198. * Use LoaderUtils.getContextClassLoader()
  199. *
  200. * @return the current context class loader, or <code>null</code>
  201. * if the context class loader is unavailable.
  202. */
  203. public static ClassLoader getContextClassLoader() {
  204. return LoaderUtils.isContextLoaderAvailable() ? LoaderUtils.getContextClassLoader() : null;
  205. }
  206. // -------------------- Static utils, used by most helpers ----------------
  207. /**
  208. * Configures an object using an introspection handler.
  209. *
  210. * @param target The target object to be configured.
  211. * Must not be <code>null</code>.
  212. * @param attrs A list of attributes to configure within the target.
  213. * Must not be <code>null</code>.
  214. * @param project The project containing the target.
  215. * Must not be <code>null</code>.
  216. *
  217. * @deprecated since 1.6.x.
  218. * Use IntrospectionHelper for each property.
  219. *
  220. * @exception BuildException if any of the attributes can't be handled by
  221. * the target
  222. */
  223. public static void configure(Object target, AttributeList attrs,
  224. Project project) throws BuildException {
  225. if (target instanceof TypeAdapter) {
  226. target = ((TypeAdapter) target).getProxy();
  227. }
  228. IntrospectionHelper ih = IntrospectionHelper.getHelper(project, target.getClass());
  229. for (int i = 0, length = attrs.getLength(); i < length; i++) {
  230. // reflect these into the target
  231. String value = replaceProperties(project, attrs.getValue(i), project.getProperties());
  232. try {
  233. ih.setAttribute(project, target, attrs.getName(i).toLowerCase(Locale.US), value);
  234. } catch (BuildException be) {
  235. // id attribute must be set externally
  236. if (!attrs.getName(i).equals("id")) {
  237. throw be;
  238. }
  239. }
  240. }
  241. }
  242. /**
  243. * Adds the content of #PCDATA sections to an element.
  244. *
  245. * @param project The project containing the target.
  246. * Must not be <code>null</code>.
  247. * @param target The target object to be configured.
  248. * Must not be <code>null</code>.
  249. * @param buf A character array of the text within the element.
  250. * Will not be <code>null</code>.
  251. * @param start The start element in the array.
  252. * @param count The number of characters to read from the array.
  253. *
  254. * @exception BuildException if the target object doesn't accept text
  255. */
  256. public static void addText(Project project, Object target, char[] buf,
  257. int start, int count) throws BuildException {
  258. addText(project, target, new String(buf, start, count));
  259. }
  260. /**
  261. * Adds the content of #PCDATA sections to an element.
  262. *
  263. * @param project The project containing the target.
  264. * Must not be <code>null</code>.
  265. * @param target The target object to be configured.
  266. * Must not be <code>null</code>.
  267. * @param text Text to add to the target.
  268. * May be <code>null</code>, in which case this
  269. * method call is a no-op.
  270. *
  271. * @exception BuildException if the target object doesn't accept text
  272. */
  273. public static void addText(Project project, Object target, String text)
  274. throws BuildException {
  275. if (text == null) {
  276. return;
  277. }
  278. if (target instanceof TypeAdapter) {
  279. target = ((TypeAdapter) target).getProxy();
  280. }
  281. IntrospectionHelper.getHelper(project, target.getClass()).addText(project, target, text);
  282. }
  283. /**
  284. * Stores a configured child element within its parent object.
  285. *
  286. * @param project Project containing the objects.
  287. * May be <code>null</code>.
  288. * @param parent Parent object to add child to.
  289. * Must not be <code>null</code>.
  290. * @param child Child object to store in parent.
  291. * Should not be <code>null</code>.
  292. * @param tag Name of element which generated the child.
  293. * May be <code>null</code>, in which case
  294. * the child is not stored.
  295. */
  296. public static void storeChild(Project project, Object parent, Object child, String tag) {
  297. IntrospectionHelper ih = IntrospectionHelper.getHelper(project, parent.getClass());
  298. ih.storeElement(project, parent, child, tag);
  299. }
  300. /**
  301. * Replaces <code>${xxx}</code> style constructions in the given value with
  302. * the string value of the corresponding properties.
  303. *
  304. * @param project The project containing the properties to replace.
  305. * Must not be <code>null</code>.
  306. *
  307. * @param value The string to be scanned for property references.
  308. * May be <code>null</code>.
  309. *
  310. * @exception BuildException if the string contains an opening
  311. * <code>${</code> without a closing
  312. * <code>}</code>
  313. * @return the original string with the properties replaced, or
  314. * <code>null</code> if the original string is <code>null</code>.
  315. *
  316. * @deprecated since 1.6.x.
  317. * Use project.replaceProperties().
  318. * @since 1.5
  319. */
  320. public static String replaceProperties(Project project, String value) throws BuildException {
  321. // needed since project properties are not accessible
  322. return project.replaceProperties(value);
  323. }
  324. /**
  325. * Replaces <code>${xxx}</code> style constructions in the given value
  326. * with the string value of the corresponding data types.
  327. *
  328. * @param project The container project. This is used solely for
  329. * logging purposes. Must not be <code>null</code>.
  330. * @param value The string to be scanned for property references.
  331. * May be <code>null</code>, in which case this
  332. * method returns immediately with no effect.
  333. * @param keys Mapping (String to String) of property names to their
  334. * values. Must not be <code>null</code>.
  335. *
  336. * @exception BuildException if the string contains an opening
  337. * <code>${</code> without a closing
  338. * <code>}</code>
  339. * @return the original string with the properties replaced, or
  340. * <code>null</code> if the original string is <code>null</code>.
  341. * @deprecated since 1.6.x.
  342. * Use PropertyHelper.
  343. */
  344. public static String replaceProperties(Project project, String value, Hashtable keys)
  345. throws BuildException {
  346. PropertyHelper ph = PropertyHelper.getPropertyHelper(project);
  347. return ph.replaceProperties(null, value, keys);
  348. }
  349. /**
  350. * Parses a string containing <code>${xxx}</code> style property
  351. * references into two lists. The first list is a collection
  352. * of text fragments, while the other is a set of string property names.
  353. * <code>null</code> entries in the first list indicate a property
  354. * reference from the second list.
  355. *
  356. * <p>As of Ant 1.8.0 this method is never invoked by any code
  357. * inside of Ant itself.</p>
  358. *
  359. * @param value Text to parse. Must not be <code>null</code>.
  360. * @param fragments List to add text fragments to.
  361. * Must not be <code>null</code>.
  362. * @param propertyRefs List to add property names to.
  363. * Must not be <code>null</code>.
  364. *
  365. * @deprecated since 1.6.x.
  366. * Use PropertyHelper.
  367. * @exception BuildException if the string contains an opening
  368. * <code>${</code> without a closing <code>}</code>
  369. */
  370. public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs)
  371. throws BuildException {
  372. PropertyHelper.parsePropertyStringDefault(value, fragments, propertyRefs);
  373. }
  374. /**
  375. * Map a namespaced {uri,name} to an internal string format.
  376. * For BC purposes the names from the ant core uri will be
  377. * mapped to "name", other names will be mapped to
  378. * uri + ":" + name.
  379. * @param uri The namepace URI
  380. * @param name The localname
  381. * @return The stringified form of the ns name
  382. */
  383. public static String genComponentName(String uri, String name) {
  384. if (uri == null || uri.equals("") || uri.equals(ANT_CORE_URI)) {
  385. return name;
  386. }
  387. return uri + ":" + name;
  388. }
  389. /**
  390. * extract a uri from a component name
  391. *
  392. * @param componentName The stringified form for {uri, name}
  393. * @return The uri or "" if not present
  394. */
  395. public static String extractUriFromComponentName(String componentName) {
  396. if (componentName == null) {
  397. return "";
  398. }
  399. int index = componentName.lastIndexOf(':');
  400. if (index == -1) {
  401. return "";
  402. }
  403. return componentName.substring(0, index);
  404. }
  405. /**
  406. * extract the element name from a component name
  407. *
  408. * @param componentName The stringified form for {uri, name}
  409. * @return The element name of the component
  410. */
  411. public static String extractNameFromComponentName(String componentName) {
  412. int index = componentName.lastIndexOf(':');
  413. if (index == -1) {
  414. return componentName;
  415. }
  416. return componentName.substring(index + 1);
  417. }
  418. /**
  419. * Add location to build exception.
  420. * @param ex the build exception, if the build exception
  421. * does not include
  422. * @param newLocation the location of the calling task (may be null)
  423. * @return a new build exception based in the build exception with
  424. * location set to newLocation. If the original exception
  425. * did not have a location, just return the build exception
  426. */
  427. public static BuildException addLocationToBuildException(
  428. BuildException ex, Location newLocation) {
  429. if (ex.getLocation() == null || ex.getMessage() == null) {
  430. return ex;
  431. }
  432. String errorMessage
  433. = "The following error occurred while executing this line:"
  434. + System.getProperty("line.separator")
  435. + ex.getLocation().toString()
  436. + ex.getMessage();
  437. if (newLocation == null) {
  438. return new BuildException(errorMessage, ex);
  439. }
  440. return new BuildException(errorMessage, ex, newLocation);
  441. }
  442. /**
  443. * Whether this instance of ProjectHelper can parse an Antlib
  444. * descriptor given by the URL and return its content as an
  445. * UnknownElement ready to be turned into an Antlib task.
  446. *
  447. * <p>This method should not try to parse the content of the
  448. * descriptor, the URL is only given as an argument to allow
  449. * subclasses to decide whether they can support a given URL
  450. * scheme or not.</p>
  451. *
  452. * <p>Subclasses that return true in this method must also
  453. * override {@link #parseAntlibDescriptor
  454. * parseAntlibDescriptor}.</p>
  455. *
  456. * <p>This implementation returns false.</p>
  457. *
  458. * @since Ant 1.8.0
  459. */
  460. public boolean canParseAntlibDescriptor(URL url) {
  461. return false;
  462. }
  463. /**
  464. * Parse the given URL as an antlib descriptor and return the
  465. * content as something that can be turned into an Antlib task.
  466. *
  467. * @since ant 1.8.0
  468. */
  469. public UnknownElement parseAntlibDescriptor(Project containingProject,
  470. URL source) {
  471. throw new BuildException("can't parse antlib descriptors");
  472. }
  473. /**
  474. * Check if the helper supports the kind of file. Some basic check on the
  475. * extension's file should be done here.
  476. *
  477. * @param buildFile
  478. * the file expected to be parsed (never <code>null</code>)
  479. * @return true if the helper supports it
  480. * @since Ant 1.8.0
  481. */
  482. public boolean supportsBuildFile(File buildFile) {
  483. return true;
  484. }
  485. /**
  486. * The file name of the build script to be parsed if none specified on the command line
  487. *
  488. * @return the name of the default file (never <code>null</code>)
  489. * @since Ant 1.8.0
  490. */
  491. public String getDefaultBuildFile() {
  492. return Main.DEFAULT_BUILD_FILENAME;
  493. }
  494. }