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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant;
  55. import java.io.File;
  56. import java.io.FileInputStream;
  57. import java.io.FileNotFoundException;
  58. import java.io.IOException;
  59. import java.io.InputStream;
  60. import java.io.BufferedReader;
  61. import java.io.InputStreamReader;
  62. import java.util.Hashtable;
  63. import java.util.Vector;
  64. import java.util.Enumeration;
  65. import java.util.Locale;
  66. import java.lang.reflect.InvocationTargetException;
  67. import java.lang.reflect.Method;
  68. import org.xml.sax.AttributeList;
  69. import org.apache.tools.ant.helper.ProjectHelperImpl;
  70. /**
  71. * Configures a Project (complete with Targets and Tasks) based on
  72. * a XML build file. It'll rely on a plugin to do the actual processing
  73. * of the xml file.
  74. *
  75. * This class also provide static wrappers for common introspection.
  76. *
  77. * All helper plugins must provide backward compatiblity with the
  78. * original ant patterns, unless a different behavior is explicitely
  79. * specified. For example, if namespace is used on the <project> tag
  80. * the helper can expect the entire build file to be namespace-enabled.
  81. * Namespaces or helper-specific tags can provide meta-information to
  82. * the helper, allowing it to use new ( or different policies ).
  83. *
  84. * However, if no namespace is used the behavior should be exactly
  85. * identical with the default helper.
  86. *
  87. * @author duncan@x180.com
  88. */
  89. public class ProjectHelper {
  90. /**
  91. * Configures the project with the contents of the specified XML file.
  92. *
  93. * @param project The project to configure. Must not be <code>null</code>.
  94. * @param buildFile An XML file giving the project's configuration.
  95. * Must not be <code>null</code>.
  96. *
  97. * @exception BuildException if the configuration is invalid or cannot
  98. * be read
  99. */
  100. public static void configureProject(Project project, File buildFile) throws BuildException {
  101. ProjectHelper helper=ProjectHelper.getProjectHelper();
  102. helper.parse(project, buildFile);
  103. }
  104. public ProjectHelper() {
  105. }
  106. /**
  107. * Parses the project file, configuring the project as it goes.
  108. *
  109. * @param project The project for the resulting ProjectHelper to configure.
  110. * Must not be <code>null</code>.
  111. * @param source The source for XML configuration. A helper must support
  112. * at least File, for backward compatibility. Helpers may
  113. * support URL, InputStream, etc or specialized types.
  114. *
  115. * @since Ant1.5
  116. * @exception BuildException if the configuration is invalid or cannot
  117. * be read
  118. */
  119. public void parse(Project project, Object source) throws BuildException {
  120. throw new BuildException("ProjectHelper.parse() must be implemented in a helper plugin "
  121. + this.getClass().getName());
  122. }
  123. /* -------------------- Helper discovery -------------------- */
  124. public static final String HELPER_PROPERTY =
  125. "org.apache.tools.ant.ProjectHelper";
  126. public static final String SERVICE_ID =
  127. "/META-INF/services/org.apache.tools.ant.ProjectHelper";
  128. /** Discover a project helper instance. Uses the same patterns
  129. * as JAXP, commons-logging, etc: a system property, a JDK1.3
  130. * service discovery, default.
  131. */
  132. public static ProjectHelper getProjectHelper()
  133. throws BuildException
  134. {
  135. // Identify the class loader we will be using. Ant may be
  136. // in a webapp or embeded in a different app
  137. ProjectHelper helper=null;
  138. // First, try the system property
  139. try {
  140. String helperClass = System.getProperty(HELPER_PROPERTY);
  141. if (helperClass != null) {
  142. helper = newHelper(helperClass);
  143. }
  144. } catch (SecurityException e) {
  145. // It's ok, we'll try next option
  146. ;
  147. }
  148. // A JDK1.3 'service' ( like in JAXP ). That will plug a helper
  149. // automatically if in CLASSPATH, with the right META-INF/services.
  150. if( helper==null ) {
  151. try {
  152. ClassLoader classLoader=getContextClassLoader();
  153. InputStream is=null;
  154. if (classLoader != null) {
  155. is=classLoader.getResourceAsStream( SERVICE_ID );
  156. }
  157. if( is==null ) {
  158. is=ClassLoader.getSystemResourceAsStream( SERVICE_ID );
  159. }
  160. if( is != null ) {
  161. // This code is needed by EBCDIC and other strange systems.
  162. // It's a fix for bugs reported in xerces
  163. BufferedReader rd;
  164. try {
  165. rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  166. } catch (java.io.UnsupportedEncodingException e) {
  167. rd = new BufferedReader(new InputStreamReader(is));
  168. }
  169. String helperClassName = rd.readLine();
  170. rd.close();
  171. if (helperClassName != null &&
  172. ! "".equals(helperClassName)) {
  173. helper= newHelper( helperClassName );
  174. }
  175. }
  176. } catch( Exception ex ) {
  177. ;
  178. }
  179. }
  180. // Default
  181. return new ProjectHelperImpl();
  182. }
  183. /** Create a new helper. It'll first try the thread class loader,
  184. * then Class.forName() will load from the same loader that
  185. * loaded this class.
  186. */
  187. private static ProjectHelper newHelper(String helperClass)
  188. throws BuildException
  189. {
  190. ClassLoader classLoader = getContextClassLoader();
  191. try {
  192. Class clazz = null;
  193. if (classLoader != null) {
  194. try {
  195. clazz = classLoader.loadClass(helperClass);
  196. } catch( ClassNotFoundException ex ) {
  197. // try next method
  198. }
  199. }
  200. if( clazz==null ) {
  201. clazz = Class.forName(helperClass);
  202. }
  203. return ((ProjectHelper) clazz.newInstance());
  204. } catch (Exception e) {
  205. throw new BuildException(e);
  206. }
  207. }
  208. /**
  209. * JDK1.1 compatible access to the context class loader.
  210. * Cut&paste from Jaxp.
  211. */
  212. public static ClassLoader getContextClassLoader()
  213. throws BuildException
  214. {
  215. // Are we running on a JDK 1.2 or later system?
  216. Method method = null;
  217. try {
  218. method = Thread.class.getMethod("getContextClassLoader", null);
  219. } catch (NoSuchMethodException e) {
  220. // we are running on JDK 1.1
  221. return null;
  222. }
  223. // Get the thread context class loader (if there is one)
  224. ClassLoader classLoader = null;
  225. try {
  226. classLoader = (ClassLoader)
  227. method.invoke(Thread.currentThread(), null);
  228. } catch (IllegalAccessException e) {
  229. throw new BuildException
  230. ("Unexpected IllegalAccessException", e);
  231. } catch (InvocationTargetException e) {
  232. throw new BuildException
  233. ("Unexpected InvocationTargetException", e);
  234. }
  235. // Return the selected class loader
  236. return (classLoader);
  237. }
  238. // -------------------- Static utils, used by most helpers --------------------
  239. /**
  240. * Configures an object using an introspection handler.
  241. *
  242. * @param target The target object to be configured.
  243. * Must not be <code>null</code>.
  244. * @param attrs A list of attributes to configure within the target.
  245. * Must not be <code>null</code>.
  246. * @param project The project containing the target.
  247. * Must not be <code>null</code>.
  248. *
  249. * @exception BuildException if any of the attributes can't be handled by
  250. * the target
  251. */
  252. public static void configure(Object target, AttributeList attrs,
  253. Project project) throws BuildException {
  254. if( target instanceof TaskAdapter ) {
  255. target=((TaskAdapter)target).getProxy();
  256. }
  257. IntrospectionHelper ih =
  258. IntrospectionHelper.getHelper(target.getClass());
  259. project.addBuildListener(ih);
  260. for (int i = 0; i < attrs.getLength(); i++) {
  261. // reflect these into the target
  262. String value=replaceProperties(project, attrs.getValue(i),
  263. project.getProperties() );
  264. try {
  265. ih.setAttribute(project, target,
  266. attrs.getName(i).toLowerCase(Locale.US), value);
  267. } catch (BuildException be) {
  268. // id attribute must be set externally
  269. if (!attrs.getName(i).equals("id")) {
  270. throw be;
  271. }
  272. }
  273. }
  274. }
  275. /**
  276. * Adds the content of #PCDATA sections to an element.
  277. *
  278. * @param project The project containing the target.
  279. * Must not be <code>null</code>.
  280. * @param target The target object to be configured.
  281. * Must not be <code>null</code>.
  282. * @param buf A character array of the text within the element.
  283. * Will not be <code>null</code>.
  284. * @param start The start element in the array.
  285. * @param count The number of characters to read from the array.
  286. *
  287. * @exception BuildException if the target object doesn't accept text
  288. */
  289. public static void addText(Project project, Object target, char[] buf, int start, int count)
  290. throws BuildException {
  291. addText(project, target, new String(buf, start, count));
  292. }
  293. /**
  294. * Adds the content of #PCDATA sections to an element.
  295. *
  296. * @param project The project containing the target.
  297. * Must not be <code>null</code>.
  298. * @param target The target object to be configured.
  299. * Must not be <code>null</code>.
  300. * @param text Text to add to the target.
  301. * May be <code>null</code>, in which case this
  302. * method call is a no-op.
  303. *
  304. * @exception BuildException if the target object doesn't accept text
  305. */
  306. public static void addText(Project project, Object target, String text)
  307. throws BuildException {
  308. if (text == null ) {
  309. return;
  310. }
  311. if(target instanceof TaskAdapter) {
  312. target = ((TaskAdapter) target).getProxy();
  313. }
  314. IntrospectionHelper.getHelper(target.getClass()).addText(project, target, text);
  315. }
  316. /**
  317. * Stores a configured child element within its parent object.
  318. *
  319. * @param project Project containing the objects.
  320. * May be <code>null</code>.
  321. * @param parent Parent object to add child to.
  322. * Must not be <code>null</code>.
  323. * @param child Child object to store in parent.
  324. * Should not be <code>null</code>.
  325. * @param tag Name of element which generated the child.
  326. * May be <code>null</code>, in which case
  327. * the child is not stored.
  328. */
  329. public static void storeChild(Project project, Object parent, Object child, String tag) {
  330. IntrospectionHelper ih = IntrospectionHelper.getHelper(parent.getClass());
  331. ih.storeElement(project, parent, child, tag);
  332. }
  333. /**
  334. * Replaces <code>${xxx}</code> style constructions in the given value with
  335. * the string value of the corresponding properties.
  336. *
  337. * @param value The string to be scanned for property references.
  338. * May be <code>null</code>.
  339. *
  340. * @exception BuildException if the string contains an opening
  341. * <code>${</code> without a closing
  342. * <code>}</code>
  343. * @return the original string with the properties replaced, or
  344. * <code>null</code> if the original string is <code>null</code>.
  345. *
  346. * @since 1.5
  347. */
  348. public static String replaceProperties(Project project, String value)
  349. throws BuildException {
  350. return project.replaceProperties(value);
  351. }
  352. /**
  353. * Replaces <code>${xxx}</code> style constructions in the given value
  354. * with the string value of the corresponding data types.
  355. *
  356. * @param project The container project. This is used solely for
  357. * logging purposes. Must not be <code>null</code>.
  358. * @param value The string to be scanned for property references.
  359. * May be <code>null</code>, in which case this
  360. * method returns immediately with no effect.
  361. * @param keys Mapping (String to String) of property names to their
  362. * values. Must not be <code>null</code>.
  363. *
  364. * @exception BuildException if the string contains an opening
  365. * <code>${</code> without a closing
  366. * <code>}</code>
  367. * @return the original string with the properties replaced, or
  368. * <code>null</code> if the original string is <code>null</code>.
  369. */
  370. public static String replaceProperties(Project project, String value, Hashtable keys)
  371. throws BuildException {
  372. if (value == null) {
  373. return null;
  374. }
  375. Vector fragments = new Vector();
  376. Vector propertyRefs = new Vector();
  377. parsePropertyString(value, fragments, propertyRefs);
  378. StringBuffer sb = new StringBuffer();
  379. Enumeration i = fragments.elements();
  380. Enumeration j = propertyRefs.elements();
  381. while (i.hasMoreElements()) {
  382. String fragment = (String)i.nextElement();
  383. if (fragment == null) {
  384. String propertyName = (String)j.nextElement();
  385. if (!keys.containsKey(propertyName)) {
  386. project.log("Property ${" + propertyName + "} has not been set", Project.MSG_VERBOSE);
  387. }
  388. fragment = (keys.containsKey(propertyName)) ? (String) keys.get(propertyName)
  389. : "${" + propertyName + "}";
  390. }
  391. sb.append(fragment);
  392. }
  393. return sb.toString();
  394. }
  395. /**
  396. * Parses a string containing <code>${xxx}</code> style property
  397. * references into two lists. The first list is a collection
  398. * of text fragments, while the other is a set of string property names.
  399. * <code>null</code> entries in the first list indicate a property
  400. * reference from the second list.
  401. *
  402. * @param value Text to parse. Must not be <code>null</code>.
  403. * @param fragments List to add text fragments to.
  404. * Must not be <code>null</code>.
  405. * @param propertyRefs List to add property names to.
  406. * Must not be <code>null</code>.
  407. *
  408. * @exception BuildException if the string contains an opening
  409. * <code>${</code> without a closing
  410. * <code>}</code>
  411. */
  412. public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs)
  413. throws BuildException {
  414. int prev = 0;
  415. int pos;
  416. while ((pos = value.indexOf("$", prev)) >= 0) {
  417. if (pos > 0) {
  418. fragments.addElement(value.substring(prev, pos));
  419. }
  420. if( pos == (value.length() - 1)) {
  421. fragments.addElement("$");
  422. prev = pos + 1;
  423. }
  424. else if (value.charAt(pos + 1) != '{' ) {
  425. fragments.addElement(value.substring(pos + 1, pos + 2));
  426. prev = pos + 2;
  427. } else {
  428. int endName = value.indexOf('}', pos);
  429. if (endName < 0) {
  430. throw new BuildException("Syntax error in property: "
  431. + value );
  432. }
  433. String propertyName = value.substring(pos + 2, endName);
  434. fragments.addElement(null);
  435. propertyRefs.addElement(propertyName);
  436. prev = endName + 1;
  437. }
  438. }
  439. if (prev < value.length()) {
  440. fragments.addElement(value.substring(prev));
  441. }
  442. }
  443. }