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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 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", "Tomcat", 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.beans.*;
  56. import java.io.*;
  57. import java.lang.reflect.*;
  58. import java.util.*;
  59. import org.xml.sax.*;
  60. import org.w3c.dom.*;
  61. import org.apache.tools.ant.taskdefs.*;
  62. import javax.xml.parsers.*;
  63. /**
  64. * Configures a Project (complete with Targets and Tasks) based on
  65. * a XML build file.
  66. *
  67. * @author duncan@x180.com
  68. */
  69. public class ProjectHelper {
  70. private static SAXParserFactory parserFactory = null;
  71. private org.xml.sax.Parser parser;
  72. private Project project;
  73. private File buildFile;
  74. private Locator locator;
  75. /**
  76. * Configures the Project with the contents of the specified XML file.
  77. */
  78. public static void configureProject(Project project, File buildFile) throws BuildException {
  79. new ProjectHelper(project, buildFile).parse();
  80. }
  81. /**
  82. * Constructs a new Ant parser for the specified XML file.
  83. */
  84. private ProjectHelper(Project project, File buildFile) {
  85. this.project = project;
  86. this.buildFile = buildFile;
  87. }
  88. /**
  89. * Parses the project file.
  90. */
  91. private void parse() throws BuildException {
  92. try {
  93. parser = getParserFactory().newSAXParser().getParser();
  94. parser.setDocumentHandler(new RootHandler());
  95. parser.parse(new InputSource(new FileReader(buildFile)));
  96. }
  97. catch(ParserConfigurationException exc) {
  98. throw new BuildException("Parser has not been configured correctly", exc);
  99. }
  100. catch(SAXParseException exc) {
  101. Location location =
  102. new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber());
  103. throw new BuildException(exc.getMessage(), exc.getException(), location);
  104. }
  105. catch(SAXException exc) {
  106. throw new BuildException(exc.getMessage(), exc.getException());
  107. }
  108. catch(FileNotFoundException exc) {
  109. throw new BuildException("File \"" + buildFile.toString() + "\" not found");
  110. }
  111. catch(IOException exc) {
  112. throw new BuildException("Error reading project file", exc);
  113. }
  114. }
  115. /**
  116. * The common superclass for all sax event handlers in Ant. Basically
  117. * throws an exception in each method, so subclasses should override
  118. * what they can handle.
  119. *
  120. * Each type of xml element (task, target, etc) in ant will
  121. * have its own subclass of AbstractHandler.
  122. *
  123. * In the constructor, this class takes over the handling of sax
  124. * events from the parent handler, and returns
  125. * control back to the parent in the endElement method.
  126. */
  127. private class AbstractHandler extends HandlerBase {
  128. protected DocumentHandler parentHandler;
  129. public AbstractHandler(DocumentHandler parentHandler) {
  130. this.parentHandler = parentHandler;
  131. // Start handling SAX events
  132. parser.setDocumentHandler(this);
  133. }
  134. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  135. throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
  136. }
  137. public void characters(char[] buf, int start, int end) throws SAXParseException {
  138. String s = new String(buf, start, end).trim();
  139. if (s.length() > 0) {
  140. throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
  141. }
  142. }
  143. public void endElement(String name) throws SAXException {
  144. // Let parent resume handling SAX events
  145. parser.setDocumentHandler(parentHandler);
  146. }
  147. }
  148. /**
  149. * Handler for the root element. It's only child must be the "project" element.
  150. */
  151. private class RootHandler extends HandlerBase {
  152. public void startElement(String tag, AttributeList attrs) throws SAXParseException {
  153. if (tag.equals("project")) {
  154. new ProjectHandler(this).init(tag, attrs);
  155. } else {
  156. throw new SAXParseException("Config file is not of expected XML type", locator);
  157. }
  158. }
  159. public void setDocumentLocator(Locator locator) {
  160. ProjectHelper.this.locator = locator;
  161. }
  162. }
  163. /**
  164. * Handler for the top level "project" element.
  165. */
  166. private class ProjectHandler extends AbstractHandler {
  167. public ProjectHandler(DocumentHandler parentHandler) {
  168. super(parentHandler);
  169. }
  170. public void init(String tag, AttributeList attrs) throws SAXParseException {
  171. String def = null;
  172. String name = null;
  173. String id = null;
  174. String baseDir = new File(buildFile.getAbsolutePath()).getParent();
  175. for (int i = 0; i < attrs.getLength(); i++) {
  176. String key = attrs.getName(i);
  177. String value = attrs.getValue(i);
  178. if (key.equals("default")) {
  179. def = value;
  180. } else if (key.equals("name")) {
  181. name = value;
  182. } else if (key.equals("id")) {
  183. id = value;
  184. } else if (key.equals("basedir")) {
  185. baseDir = value;
  186. } else {
  187. throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator);
  188. }
  189. }
  190. project.setDefaultTarget(def);
  191. project.setName(name);
  192. if (name != null) project.addReference(name, project);
  193. if (id != null) project.addReference(id, project);
  194. if (project.getProperty("basedir") != null) {
  195. project.setBasedir(project.getProperty("basedir"));
  196. } else {
  197. project.setBasedir(baseDir);
  198. }
  199. }
  200. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  201. if (name.equals("taskdef")) {
  202. handleTaskdef(name, attrs);
  203. } else if (name.equals("property")) {
  204. handleProperty(name, attrs);
  205. } else if (name.equals("target")) {
  206. handleTarget(name, attrs);
  207. } else {
  208. throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
  209. }
  210. }
  211. private void handleTaskdef(String name, AttributeList attrs) throws SAXParseException {
  212. new TaskHandler(this, null).init(name, attrs);
  213. }
  214. private void handleProperty(String name, AttributeList attrs) throws SAXParseException {
  215. new TaskHandler(this, null).init(name, attrs);
  216. }
  217. private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
  218. new TargetHandler(this).init(tag, attrs);
  219. }
  220. }
  221. /**
  222. * Handler for "target" elements.
  223. */
  224. private class TargetHandler extends AbstractHandler {
  225. private Target target;
  226. public TargetHandler(DocumentHandler parentHandler) {
  227. super(parentHandler);
  228. }
  229. public void init(String tag, AttributeList attrs) throws SAXParseException {
  230. String name = null;
  231. String depends = "";
  232. String cond = null;
  233. String id = null;
  234. for (int i = 0; i < attrs.getLength(); i++) {
  235. String key = attrs.getName(i);
  236. String value = attrs.getValue(i);
  237. if (key.equals("name")) {
  238. name = value;
  239. } else if (key.equals("depends")) {
  240. depends = value;
  241. } else if (key.equals("if")) {
  242. cond = value;
  243. } else if (key.equals("id")) {
  244. id = value;
  245. } else {
  246. throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
  247. }
  248. }
  249. if (name == null) {
  250. throw new SAXParseException("target element appears without a name attribute", locator);
  251. }
  252. target = new Target();
  253. target.setName(name);
  254. target.setCondition(cond);
  255. project.addTarget(name, target);
  256. if (id != null && !id.equals(""))
  257. project.addReference(id, target);
  258. // take care of dependencies
  259. if (depends.length() > 0) {
  260. StringTokenizer tok =
  261. new StringTokenizer(depends, ",", false);
  262. while (tok.hasMoreTokens()) {
  263. target.addDependency(tok.nextToken().trim());
  264. }
  265. }
  266. }
  267. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  268. new TaskHandler(this, target).init(name, attrs);
  269. }
  270. }
  271. /**
  272. * Handler for all task elements.
  273. */
  274. private class TaskHandler extends AbstractHandler {
  275. private Target target;
  276. private Task task;
  277. public TaskHandler(DocumentHandler parentHandler, Target target) {
  278. super(parentHandler);
  279. this.target = target;
  280. }
  281. public void init(String tag, AttributeList attrs) throws SAXParseException {
  282. task = project.createTask(tag);
  283. project.currentTask = task;
  284. configure(task, attrs);
  285. task.setLocation(new Location(buildFile.toString(), locator.getLineNumber(), locator.getColumnNumber()));
  286. task.init();
  287. project.currentTask = null;
  288. // Top level tasks don't have associated targets
  289. if (target != null) {
  290. task.setTarget(target);
  291. target.addTask(task);
  292. }
  293. }
  294. public void characters(char[] buf, int start, int end) throws SAXParseException {
  295. String text = new String(buf, start, end).trim();
  296. if (text.length() == 0) return;
  297. try {
  298. Method addProp = task.getClass().getMethod("addText", new Class[]{String.class});
  299. Object child = addProp.invoke(task, new Object[] {text});
  300. } catch(NoSuchMethodException exc) {
  301. throw new SAXParseException(task.getClass() + " does not support nested text elements", locator);
  302. } catch(InvocationTargetException exc) {
  303. throw new SAXParseException("Error invoking \"addText\" method", locator, exc);
  304. } catch(IllegalAccessException exc) {
  305. throw new SAXParseException("Unable to access \"addText\" method", locator, exc);
  306. }
  307. }
  308. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  309. new NestedElementHandler(this, task).init(name, attrs);
  310. }
  311. }
  312. /**
  313. * Handler for all nested properties.
  314. */
  315. private class NestedElementHandler extends AbstractHandler {
  316. private DocumentHandler parentHandler;
  317. private Object target;
  318. private Object child;
  319. public NestedElementHandler(DocumentHandler parentHandler, Object target) {
  320. super(parentHandler);
  321. this.target = target;
  322. }
  323. public void init(String propType, AttributeList attrs) throws SAXParseException {
  324. Class targetClass = target.getClass();
  325. String methodName = "create" + Character.toUpperCase(propType.charAt(0)) + propType.substring(1);
  326. try {
  327. Method addProp = targetClass.getMethod(methodName, new Class[]{});
  328. child = addProp.invoke(target, new Object[] {});
  329. configure(child, attrs);
  330. } catch(NoSuchMethodException exc) {
  331. throw new SAXParseException(targetClass + " does not support nested " + propType + " properties", locator);
  332. } catch(InvocationTargetException exc) {
  333. throw new SAXParseException(exc.getMessage(), locator);
  334. } catch(IllegalAccessException exc) {
  335. throw new SAXParseException(exc.getMessage(), locator);
  336. }
  337. }
  338. public void startElement(String name, AttributeList attrs) throws SAXParseException {
  339. new NestedElementHandler(this, child).init(name, attrs);
  340. }
  341. }
  342. private void configure(Object target, AttributeList attrs) throws BuildException {
  343. if( target instanceof TaskAdapter )
  344. target=((TaskAdapter)target).getProxy();
  345. // XXX
  346. // instead of doing this introspection each time around, I
  347. // should have a helper class to keep this info around for
  348. // each kind of class
  349. Hashtable propertySetters = new Hashtable();
  350. BeanInfo beanInfo;
  351. try {
  352. beanInfo = Introspector.getBeanInfo(target.getClass());
  353. } catch (IntrospectionException ie) {
  354. String msg = "Can't introspect class: " + target.getClass();
  355. throw new BuildException(msg);
  356. }
  357. PropertyDescriptor[] pda = beanInfo.getPropertyDescriptors();
  358. for (int i = 0; i < pda.length; i++) {
  359. PropertyDescriptor pd = pda[i];
  360. String property = pd.getName();
  361. Method setMethod = pd.getWriteMethod();
  362. if (setMethod != null) {
  363. // make sure that there's only 1 param and that it
  364. // takes a String object, all other setMethods need
  365. // to get screened out
  366. Class[] ma =setMethod.getParameterTypes();
  367. if (ma.length == 1) {
  368. Class c = ma[0];
  369. if (c.getName().equals("java.lang.String")) {
  370. propertySetters.put(property, setMethod);
  371. }
  372. }
  373. }
  374. }
  375. for (int i = 0; i < attrs.getLength(); i++) {
  376. // reflect these into the target
  377. Method setMethod = (Method)propertySetters.get(attrs.getName(i));
  378. if (setMethod == null) {
  379. if (attrs.getName(i).equals("id")) {
  380. project.addReference(attrs.getValue(i), target);
  381. continue;
  382. }
  383. String msg = "Class " + target.getClass() +
  384. " doesn't support the \"" + attrs.getName(i) + "\" property";
  385. throw new BuildException(msg);
  386. }
  387. String value=replaceProperties(attrs.getValue(i), project.getProperties() );
  388. try {
  389. setMethod.invoke(target, new String[] {value});
  390. } catch (IllegalAccessException iae) {
  391. String msg = "Error setting value for attrib: " +
  392. attrs.getName(i);
  393. iae.printStackTrace();
  394. throw new BuildException(msg);
  395. } catch (InvocationTargetException ie) {
  396. String msg = "Error setting value for attrib: " +
  397. attrs.getName(i) + " in " + target.getClass().getName();
  398. ie.printStackTrace();
  399. ie.getTargetException().printStackTrace();
  400. throw new BuildException(msg);
  401. }
  402. }
  403. }
  404. /** Replace ${NAME} with the property value
  405. */
  406. public static String replaceProperties( String value, Hashtable keys )
  407. throws BuildException
  408. {
  409. // XXX use Map instead of proj, it's too heavy
  410. // XXX need to replace this code with something better.
  411. StringBuffer sb=new StringBuffer();
  412. int i=0;
  413. int prev=0;
  414. // assert value!=nil
  415. int pos;
  416. while( (pos=value.indexOf( "$", prev )) >= 0 ) {
  417. if(pos>0) {
  418. sb.append( value.substring( prev, pos ) );
  419. }
  420. if( pos == (value.length() - 1)) {
  421. sb.append('$');
  422. prev = pos + 1;
  423. }
  424. else if (value.charAt( pos + 1 ) != '{' ) {
  425. sb.append( value.charAt( pos + 1 ) );
  426. prev=pos+2; // XXX
  427. } else {
  428. int endName=value.indexOf( '}', pos );
  429. if( endName < 0 ) {
  430. throw new BuildException("Syntax error in prop: " +
  431. value );
  432. }
  433. String n=value.substring( pos+2, endName );
  434. String v=(String) keys.get( n );
  435. //System.out.println("N: " + n + " " + " V:" + v);
  436. sb.append( v );
  437. prev=endName+1;
  438. }
  439. }
  440. if( prev < value.length() ) sb.append( value.substring( prev ) );
  441. // System.out.println("After replace: " + sb.toString());
  442. // System.out.println("Before replace: " + value);
  443. return sb.toString();
  444. }
  445. private static SAXParserFactory getParserFactory() {
  446. if (parserFactory == null) {
  447. parserFactory = SAXParserFactory.newInstance();
  448. }
  449. return parserFactory;
  450. }
  451. }