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

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