1. /*
  2. * Copyright 2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.types;
  18. import java.io.File;
  19. import java.util.Vector;
  20. import java.util.ArrayList;
  21. import org.apache.tools.ant.Task;
  22. import org.apache.tools.ant.Project;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.taskdefs.Redirector;
  25. import org.apache.tools.ant.types.DataType;
  26. /**
  27. * Element representation of a <CODE>Redirector</CODE>.
  28. */
  29. public class RedirectorElement extends DataType {
  30. /**
  31. * Whether the input mapper was set via <CODE>setOutput</CODE>.
  32. */
  33. private boolean usingInput = false;
  34. /**
  35. * Whether the output mapper was set via <CODE>setOutput</CODE>.
  36. */
  37. private boolean usingOutput = false;
  38. /**
  39. * Whether the error mapper was set via <CODE>setError</CODE>.
  40. */
  41. private boolean usingError = false;
  42. /**
  43. * Indicates if standard error should be logged to Ant's log system
  44. * rather than the output. This has no effect if standard error is
  45. * redirected to a file or property.
  46. */
  47. private Boolean logError;
  48. /** The name of the property into which output is to be stored */
  49. private String outputProperty;
  50. /** The name of the property into which error output is to be stored */
  51. private String errorProperty;
  52. /** String from which input is taken */
  53. private String inputString;
  54. /** Flag which indicates if error and output files are to be appended. */
  55. private Boolean append;
  56. /** Flag which indicates whether files should be created even if empty. */
  57. private Boolean createEmptyFiles;
  58. /** Input file mapper. */
  59. private Mapper inputMapper;
  60. /** Output file mapper. */
  61. private Mapper outputMapper;
  62. /** Error file mapper. */
  63. private Mapper errorMapper;
  64. /** input filter chains. */
  65. private Vector inputFilterChains = new Vector();
  66. /** output filter chains. */
  67. private Vector outputFilterChains = new Vector();
  68. /** error filter chains. */
  69. private Vector errorFilterChains = new Vector();
  70. /** The output encoding */
  71. private String outputEncoding;
  72. /** The error encoding */
  73. private String errorEncoding;
  74. /** The input encoding */
  75. private String inputEncoding;
  76. /**
  77. * Add the input file mapper.
  78. * @param inputMapper <CODE>Mapper</CODE>.
  79. */
  80. public void addConfiguredInputMapper(Mapper inputMapper) {
  81. if (isReference()) {
  82. throw noChildrenAllowed();
  83. }
  84. if (this.inputMapper != null) {
  85. if (usingInput) {
  86. throw new BuildException("attribute \"input\""
  87. + " cannot coexist with a nested <inputmapper>");
  88. } else {
  89. throw new BuildException("Cannot have > 1 <inputmapper>");
  90. }
  91. }
  92. this.inputMapper = inputMapper;
  93. }
  94. /**
  95. * Add the output file mapper.
  96. * @param outputMapper <CODE>Mapper</CODE>.
  97. */
  98. public void addConfiguredOutputMapper(Mapper outputMapper) {
  99. if (isReference()) {
  100. throw noChildrenAllowed();
  101. }
  102. if (this.outputMapper != null) {
  103. if (usingOutput) {
  104. throw new BuildException("attribute \"output\""
  105. + " cannot coexist with a nested <outputmapper>");
  106. } else {
  107. throw new BuildException("Cannot have > 1 <outputmapper>");
  108. }
  109. }
  110. this.outputMapper = outputMapper;
  111. }
  112. /**
  113. * Add the error file mapper.
  114. * @param errorMapper <CODE>Mapper</CODE>.
  115. */
  116. public void addConfiguredErrorMapper(Mapper errorMapper) {
  117. if (isReference()) {
  118. throw noChildrenAllowed();
  119. }
  120. if (this.errorMapper != null) {
  121. if (usingError) {
  122. throw new BuildException("attribute \"error\""
  123. + " cannot coexist with a nested <errormapper>");
  124. } else {
  125. throw new BuildException("Cannot have > 1 <errormapper>");
  126. }
  127. }
  128. this.errorMapper = errorMapper;
  129. }
  130. /**
  131. * Makes this instance in effect a reference to another instance.
  132. *
  133. * <p>You must not set another attribute or nest elements inside
  134. * this element if you make it a reference.</p>
  135. */
  136. public void setRefid(Reference r) throws BuildException {
  137. if (usingInput
  138. || usingOutput
  139. || usingError
  140. || inputString != null
  141. || logError != null
  142. || append != null
  143. || createEmptyFiles != null
  144. || inputEncoding != null
  145. || outputEncoding != null
  146. || errorEncoding != null
  147. || outputProperty != null
  148. || errorProperty != null) {
  149. throw tooManyAttributes();
  150. }
  151. super.setRefid(r);
  152. }
  153. /**
  154. * Set the input to use for the task
  155. * @param input the file from which input is read.
  156. */
  157. public void setInput(File input) {
  158. if (isReference()) {
  159. throw tooManyAttributes();
  160. }
  161. if (inputString != null) {
  162. throw new BuildException("The \"input\" and \"inputstring\" "
  163. + "attributes cannot both be specified");
  164. }
  165. usingInput = true;
  166. inputMapper = createMergeMapper(input);
  167. }
  168. /**
  169. * Set the string to use as input
  170. * @param inputString the string which is used as the input source
  171. */
  172. public void setInputString(String inputString) {
  173. if (isReference()) {
  174. throw tooManyAttributes();
  175. }
  176. if (usingInput) {
  177. throw new BuildException("The \"input\" and \"inputstring\" "
  178. + "attributes cannot both be specified");
  179. }
  180. this.inputString = inputString;
  181. }
  182. /**
  183. * File the output of the process is redirected to. If error is not
  184. * redirected, it too will appear in the output
  185. *
  186. * @param out the file to which output stream is written
  187. */
  188. public void setOutput(File out) {
  189. if (isReference()) {
  190. throw tooManyAttributes();
  191. }
  192. if (out == null) {
  193. throw new IllegalArgumentException("output file specified as null");
  194. }
  195. usingOutput = true;
  196. outputMapper = createMergeMapper(out);
  197. }
  198. /**
  199. * Set the output encoding.
  200. * @param outputEncoding <CODE>String</CODE>.
  201. */
  202. public void setOutputEncoding(String outputEncoding) {
  203. if (isReference()) {
  204. throw tooManyAttributes();
  205. }
  206. this.outputEncoding = outputEncoding;
  207. }
  208. /**
  209. * Set the error encoding.
  210. *
  211. * @param errorEncoding <CODE>String</CODE>.
  212. */
  213. public void setErrorEncoding(String errorEncoding) {
  214. if (isReference()) {
  215. throw tooManyAttributes();
  216. }
  217. this.errorEncoding = errorEncoding;
  218. }
  219. /**
  220. * Set the input encoding.
  221. * @param inputEncoding <CODE>String</CODE>.
  222. */
  223. public void setInputEncoding(String inputEncoding) {
  224. if (isReference()) {
  225. throw tooManyAttributes();
  226. }
  227. this.inputEncoding = inputEncoding;
  228. }
  229. /**
  230. * Controls whether error output of exec is logged. This is only useful
  231. * when output is being redirected and error output is desired in the
  232. * Ant log
  233. * @param logError if true the standard error is sent to the Ant log system
  234. * and not sent to output.
  235. */
  236. public void setLogError(boolean logError) {
  237. if (isReference()) {
  238. throw tooManyAttributes();
  239. }
  240. //pre JDK 1.4 compatible
  241. this.logError = ((logError) ? Boolean.TRUE : Boolean.FALSE);
  242. }
  243. /**
  244. * Set the file to which standard error is to be redirected.
  245. * @param error the file to which error is to be written
  246. */
  247. public void setError(File error) {
  248. if (isReference()) {
  249. throw tooManyAttributes();
  250. }
  251. if (error == null) {
  252. throw new IllegalArgumentException("error file specified as null");
  253. }
  254. usingError = true;
  255. errorMapper = createMergeMapper(error);
  256. }
  257. /**
  258. * Property name whose value should be set to the output of
  259. * the process.
  260. * @param outputProperty the name of the property to be set with the
  261. * task's output.
  262. */
  263. public void setOutputProperty(String outputProperty) {
  264. if (isReference()) {
  265. throw tooManyAttributes();
  266. }
  267. this.outputProperty = outputProperty;
  268. }
  269. /**
  270. * Whether output should be appended to or overwrite an existing file.
  271. * Defaults to false.
  272. * @param append if true output and error streams are appended to their
  273. * respective files, if specified.
  274. */
  275. public void setAppend(boolean append) {
  276. if (isReference()) {
  277. throw tooManyAttributes();
  278. }
  279. //pre JDK 1.4 compatible
  280. this.append = ((append) ? Boolean.TRUE : Boolean.FALSE);
  281. }
  282. /**
  283. * Whether output and error files should be created even when empty.
  284. * Defaults to true.
  285. * @param createEmptyFiles <CODE>boolean</CODE>.
  286. */
  287. public void setCreateEmptyFiles(boolean createEmptyFiles) {
  288. if (isReference()) {
  289. throw tooManyAttributes();
  290. }
  291. //pre JDK 1.4 compatible
  292. this.createEmptyFiles = ((createEmptyFiles)
  293. ? Boolean.TRUE : Boolean.FALSE);
  294. }
  295. /**
  296. * Property name whose value should be set to the error of
  297. * the process.
  298. * @param errorProperty the name of the property to be set
  299. * with the error output.
  300. */
  301. public void setErrorProperty(String errorProperty) {
  302. if (isReference()) {
  303. throw tooManyAttributes();
  304. }
  305. this.errorProperty = errorProperty;
  306. }
  307. /**
  308. * Create a nested input <CODE>FilterChain</CODE>.
  309. * @return <CODE>FilterChain</CODE>.
  310. */
  311. public FilterChain createInputFilterChain() {
  312. if (isReference()) {
  313. throw noChildrenAllowed();
  314. }
  315. FilterChain result = new FilterChain();
  316. result.setProject(getProject());
  317. inputFilterChains.add(result);
  318. return result;
  319. }
  320. /**
  321. * Create a nested output <CODE>FilterChain</CODE>.
  322. * @return <CODE>FilterChain</CODE>.
  323. */
  324. public FilterChain createOutputFilterChain() {
  325. if (isReference()) {
  326. throw noChildrenAllowed();
  327. }
  328. FilterChain result = new FilterChain();
  329. result.setProject(getProject());
  330. outputFilterChains.add(result);
  331. return result;
  332. }
  333. /**
  334. * Create a nested error <CODE>FilterChain</CODE>.
  335. * @return <CODE>FilterChain</CODE>.
  336. */
  337. public FilterChain createErrorFilterChain() {
  338. if (isReference()) {
  339. throw noChildrenAllowed();
  340. }
  341. FilterChain result = new FilterChain();
  342. result.setProject(getProject());
  343. errorFilterChains.add(result);
  344. return result;
  345. }
  346. /**
  347. * Configure the specified <CODE>Redirector</CODE>.
  348. * @param redirector <CODE>Redirector</CODE>.
  349. */
  350. public void configure(Redirector redirector) {
  351. configure(redirector, null);
  352. }
  353. /**
  354. * Configure the specified <CODE>Redirector</CODE>
  355. * for the specified sourcefile.
  356. * @param redirector <CODE>Redirector</CODE>.
  357. * @param sourcefile <CODE>String</CODE>.
  358. */
  359. public void configure(Redirector redirector, String sourcefile) {
  360. if (logError != null) {
  361. redirector.setLogError(logError.booleanValue());
  362. }
  363. if (append != null) {
  364. redirector.setAppend(append.booleanValue());
  365. }
  366. if (createEmptyFiles != null) {
  367. redirector.setCreateEmptyFiles(createEmptyFiles.booleanValue());
  368. }
  369. if (outputProperty != null) {
  370. redirector.setOutputProperty(outputProperty);
  371. }
  372. if (errorProperty != null) {
  373. redirector.setErrorProperty(errorProperty);
  374. }
  375. if (inputString != null) {
  376. redirector.setInputString(inputString);
  377. }
  378. if (inputMapper != null) {
  379. String[] inputTargets = null;
  380. try {
  381. inputTargets =
  382. inputMapper.getImplementation().mapFileName(sourcefile);
  383. } catch (NullPointerException enPeaEx) {
  384. if (sourcefile != null) {
  385. throw enPeaEx;
  386. }
  387. }
  388. if (inputTargets != null && inputTargets.length > 0) {
  389. redirector.setInput(toFileArray(inputTargets));
  390. }
  391. }
  392. if (outputMapper != null) {
  393. String[] outputTargets = null;
  394. try {
  395. outputTargets =
  396. outputMapper.getImplementation().mapFileName(sourcefile);
  397. } catch (NullPointerException enPeaEx) {
  398. if (sourcefile != null) {
  399. throw enPeaEx;
  400. }
  401. }
  402. if (outputTargets != null && outputTargets.length > 0) {
  403. redirector.setOutput(toFileArray(outputTargets));
  404. }
  405. }
  406. if (errorMapper != null) {
  407. String[] errorTargets = null;
  408. try {
  409. errorTargets =
  410. errorMapper.getImplementation().mapFileName(sourcefile);
  411. } catch (NullPointerException enPeaEx) {
  412. if (sourcefile != null) {
  413. throw enPeaEx;
  414. }
  415. }
  416. if (errorTargets != null && errorTargets.length > 0) {
  417. redirector.setError(toFileArray(errorTargets));
  418. }
  419. }
  420. if (inputFilterChains.size() > 0) {
  421. redirector.setInputFilterChains(inputFilterChains);
  422. }
  423. if (outputFilterChains.size() > 0) {
  424. redirector.setOutputFilterChains(outputFilterChains);
  425. }
  426. if (errorFilterChains.size() > 0) {
  427. redirector.setErrorFilterChains(errorFilterChains);
  428. }
  429. if (inputEncoding != null) {
  430. redirector.setInputEncoding(inputEncoding);
  431. }
  432. if (outputEncoding != null) {
  433. redirector.setOutputEncoding(outputEncoding);
  434. }
  435. if (errorEncoding != null) {
  436. redirector.setErrorEncoding(errorEncoding);
  437. }
  438. }
  439. /**
  440. * Create a merge mapper pointing to the specified destination file.
  441. * @param destfile <CODE>File</CODE>
  442. * @return <CODE>Mapper</CODE>.
  443. */
  444. protected Mapper createMergeMapper(File destfile) {
  445. Mapper result = new Mapper(getProject());
  446. result.setClassname(
  447. org.apache.tools.ant.util.MergingMapper.class.getName());
  448. result.setTo(destfile.getAbsolutePath());
  449. return result;
  450. }
  451. /**
  452. * Return a <CODE>File[]</CODE> from the specified set of filenames.
  453. * @param name <CODE>String[]</CODE>
  454. * @return <CODE>File[]</CODE>.
  455. */
  456. protected File[] toFileArray(String[] name) {
  457. if (name == null) {
  458. return null;
  459. }
  460. //remove any null elements
  461. ArrayList list = new ArrayList(name.length);
  462. for (int i = 0; i < name.length; i++) {
  463. if (name[i] != null) {
  464. list.add(getProject().resolveFile(name[i]));
  465. }
  466. }
  467. return (File[])(list.toArray(new File[list.size()]));
  468. }
  469. }