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 23 kB

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