001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.types;
019:
020: import java.io.File;
021: import java.util.Stack;
022: import java.util.Vector;
023: import java.util.Iterator;
024: import java.util.ArrayList;
025:
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.BuildException;
028: import org.apache.tools.ant.taskdefs.Redirector;
029:
030: /**
031: * Element representation of a <code>Redirector</code>.
032: * @since Ant 1.6.2
033: */
034: public class RedirectorElement extends DataType {
035:
036: /**
037: * Whether the input mapper was set via <code>setOutput</code>.
038: */
039: private boolean usingInput = false;
040:
041: /**
042: * Whether the output mapper was set via <code>setOutput</code>.
043: */
044: private boolean usingOutput = false;
045:
046: /**
047: * Whether the error mapper was set via <code>setError</code>.
048: */
049: private boolean usingError = false;
050:
051: /**
052: * Indicates if standard error should be logged to Ant's log system
053: * rather than the output. This has no effect if standard error is
054: * redirected to a file or property.
055: */
056: private Boolean logError;
057:
058: /** The name of the property into which output is to be stored */
059: private String outputProperty;
060:
061: /** The name of the property into which error output is to be stored */
062: private String errorProperty;
063:
064: /** String from which input is taken */
065: private String inputString;
066:
067: /** Flag which indicates if error and output files are to be appended. */
068: private Boolean append;
069:
070: /** Flag which indicates that output should be always sent to the log */
071: private Boolean alwaysLog;
072:
073: /** Flag which indicates whether files should be created even if empty. */
074: private Boolean createEmptyFiles;
075:
076: /** Input file mapper. */
077: private Mapper inputMapper;
078:
079: /** Output file mapper. */
080: private Mapper outputMapper;
081:
082: /** Error file mapper. */
083: private Mapper errorMapper;
084:
085: /** input filter chains. */
086: private Vector inputFilterChains = new Vector();
087:
088: /** output filter chains. */
089: private Vector outputFilterChains = new Vector();
090:
091: /** error filter chains. */
092: private Vector errorFilterChains = new Vector();
093:
094: /** The output encoding */
095: private String outputEncoding;
096:
097: /** The error encoding */
098: private String errorEncoding;
099:
100: /** The input encoding */
101: private String inputEncoding;
102:
103: /** whether to log the inputstring */
104: private Boolean logInputString;
105:
106: /**
107: * Add the input file mapper.
108: * @param inputMapper <code>Mapper</code>.
109: */
110: public void addConfiguredInputMapper(Mapper inputMapper) {
111: if (isReference()) {
112: throw noChildrenAllowed();
113: }
114: if (this .inputMapper != null) {
115: if (usingInput) {
116: throw new BuildException("attribute \"input\""
117: + " cannot coexist with a nested <inputmapper>");
118: } else {
119: throw new BuildException(
120: "Cannot have > 1 <inputmapper>");
121: }
122: }
123: this .inputMapper = inputMapper;
124: }
125:
126: /**
127: * Add the output file mapper.
128: * @param outputMapper <code>Mapper</code>.
129: */
130: public void addConfiguredOutputMapper(Mapper outputMapper) {
131: if (isReference()) {
132: throw noChildrenAllowed();
133: }
134: if (this .outputMapper != null) {
135: if (usingOutput) {
136: throw new BuildException(
137: "attribute \"output\""
138: + " cannot coexist with a nested <outputmapper>");
139: } else {
140: throw new BuildException(
141: "Cannot have > 1 <outputmapper>");
142: }
143: }
144: this .outputMapper = outputMapper;
145: }
146:
147: /**
148: * Add the error file mapper.
149: * @param errorMapper <code>Mapper</code>.
150: */
151: public void addConfiguredErrorMapper(Mapper errorMapper) {
152: if (isReference()) {
153: throw noChildrenAllowed();
154: }
155: if (this .errorMapper != null) {
156: if (usingError) {
157: throw new BuildException("attribute \"error\""
158: + " cannot coexist with a nested <errormapper>");
159: } else {
160: throw new BuildException(
161: "Cannot have > 1 <errormapper>");
162: }
163: }
164: this .errorMapper = errorMapper;
165: }
166:
167: /**
168: * Make this instance in effect a reference to another instance.
169: *
170: * <p>You must not set another attribute or nest elements inside
171: * this element if you make it a reference.</p>
172: * @param r the reference to use.
173: * @throws BuildException on error.
174: */
175: public void setRefid(Reference r) throws BuildException {
176: if (usingInput || usingOutput || usingError
177: || inputString != null || logError != null
178: || append != null || createEmptyFiles != null
179: || inputEncoding != null || outputEncoding != null
180: || errorEncoding != null || outputProperty != null
181: || errorProperty != null || logInputString != null) {
182: throw tooManyAttributes();
183: }
184: super .setRefid(r);
185: }
186:
187: /**
188: * Set the input to use for the task.
189: * @param input the file from which input is read.
190: */
191: public void setInput(File input) {
192: if (isReference()) {
193: throw tooManyAttributes();
194: }
195: if (inputString != null) {
196: throw new BuildException(
197: "The \"input\" and \"inputstring\" "
198: + "attributes cannot both be specified");
199: }
200: usingInput = true;
201: inputMapper = createMergeMapper(input);
202: }
203:
204: /**
205: * Set the string to use as input
206: * @param inputString the string which is used as the input source
207: */
208: public void setInputString(String inputString) {
209: if (isReference()) {
210: throw tooManyAttributes();
211: }
212: if (usingInput) {
213: throw new BuildException(
214: "The \"input\" and \"inputstring\" "
215: + "attributes cannot both be specified");
216: }
217: this .inputString = inputString;
218: }
219:
220: /**
221: * Set whether to include the value of the input string in log messages.
222: * Defaults to true.
223: * @param logInputString true or false.
224: * @since Ant 1.7
225: */
226: public void setLogInputString(boolean logInputString) {
227: if (isReference()) {
228: throw tooManyAttributes();
229: }
230: this .logInputString = logInputString ? Boolean.TRUE
231: : Boolean.FALSE;
232: }
233:
234: /**
235: * File the output of the process is redirected to. If error is not
236: * redirected, it too will appear in the output.
237: *
238: * @param out the file to which output stream is written.
239: */
240: public void setOutput(File out) {
241: if (isReference()) {
242: throw tooManyAttributes();
243: }
244: if (out == null) {
245: throw new IllegalArgumentException(
246: "output file specified as null");
247: }
248: usingOutput = true;
249: outputMapper = createMergeMapper(out);
250: }
251:
252: /**
253: * Set the output encoding.
254: * @param outputEncoding <code>String</code>.
255: */
256: public void setOutputEncoding(String outputEncoding) {
257: if (isReference()) {
258: throw tooManyAttributes();
259: }
260: this .outputEncoding = outputEncoding;
261: }
262:
263: /**
264: * Set the error encoding.
265: *
266: * @param errorEncoding <code>String</code>.
267: */
268: public void setErrorEncoding(String errorEncoding) {
269: if (isReference()) {
270: throw tooManyAttributes();
271: }
272: this .errorEncoding = errorEncoding;
273: }
274:
275: /**
276: * Set the input encoding.
277: * @param inputEncoding <code>String</code>.
278: */
279: public void setInputEncoding(String inputEncoding) {
280: if (isReference()) {
281: throw tooManyAttributes();
282: }
283: this .inputEncoding = inputEncoding;
284: }
285:
286: /**
287: * Controls whether error output of exec is logged. This is only useful
288: * when output is being redirected and error output is desired in the
289: * Ant log.
290: * @param logError if true the standard error is sent to the Ant log system
291: * and not sent to output.
292: */
293: public void setLogError(boolean logError) {
294: if (isReference()) {
295: throw tooManyAttributes();
296: }
297: this .logError = ((logError) ? Boolean.TRUE : Boolean.FALSE);
298: }
299:
300: /**
301: * Set the file to which standard error is to be redirected.
302: * @param error the file to which error is to be written.
303: */
304: public void setError(File error) {
305: if (isReference()) {
306: throw tooManyAttributes();
307: }
308: if (error == null) {
309: throw new IllegalArgumentException(
310: "error file specified as null");
311: }
312: usingError = true;
313: errorMapper = createMergeMapper(error);
314: }
315:
316: /**
317: * Property name whose value should be set to the output of
318: * the process.
319: * @param outputProperty the name of the property to be set with the
320: * task's output.
321: */
322: public void setOutputProperty(String outputProperty) {
323: if (isReference()) {
324: throw tooManyAttributes();
325: }
326: this .outputProperty = outputProperty;
327: }
328:
329: /**
330: * Whether output should be appended to or overwrite an existing file.
331: * Defaults to false.
332: * @param append if true output and error streams are appended to their
333: * respective files, if specified.
334: */
335: public void setAppend(boolean append) {
336: if (isReference()) {
337: throw tooManyAttributes();
338: }
339: this .append = ((append) ? Boolean.TRUE : Boolean.FALSE);
340: }
341:
342: /**
343: * If true, (error and non-error) output will be "teed", redirected
344: * as specified while being sent to Ant's logging mechanism as if no
345: * redirection had taken place. Defaults to false.
346: * @param alwaysLog <code>boolean</code>
347: * @since Ant 1.6.3
348: */
349: public void setAlwaysLog(boolean alwaysLog) {
350: if (isReference()) {
351: throw tooManyAttributes();
352: }
353: this .alwaysLog = ((alwaysLog) ? Boolean.TRUE : Boolean.FALSE);
354: }
355:
356: /**
357: * Whether output and error files should be created even when empty.
358: * Defaults to true.
359: * @param createEmptyFiles <code>boolean</code>.
360: */
361: public void setCreateEmptyFiles(boolean createEmptyFiles) {
362: if (isReference()) {
363: throw tooManyAttributes();
364: }
365: this .createEmptyFiles = ((createEmptyFiles) ? Boolean.TRUE
366: : Boolean.FALSE);
367: }
368:
369: /**
370: * Property name whose value should be set to the error of
371: * the process.
372: * @param errorProperty the name of the property to be set
373: * with the error output.
374: */
375: public void setErrorProperty(String errorProperty) {
376: if (isReference()) {
377: throw tooManyAttributes();
378: }
379: this .errorProperty = errorProperty;
380: }
381:
382: /**
383: * Create a nested input <code>FilterChain</code>.
384: * @return <code>FilterChain</code>.
385: */
386: public FilterChain createInputFilterChain() {
387: if (isReference()) {
388: throw noChildrenAllowed();
389: }
390: FilterChain result = new FilterChain();
391: result.setProject(getProject());
392: inputFilterChains.add(result);
393: return result;
394: }
395:
396: /**
397: * Create a nested output <code>FilterChain</code>.
398: * @return <code>FilterChain</code>.
399: */
400: public FilterChain createOutputFilterChain() {
401: if (isReference()) {
402: throw noChildrenAllowed();
403: }
404: FilterChain result = new FilterChain();
405: result.setProject(getProject());
406: outputFilterChains.add(result);
407: return result;
408: }
409:
410: /**
411: * Create a nested error <code>FilterChain</code>.
412: * @return <code>FilterChain</code>.
413: */
414: public FilterChain createErrorFilterChain() {
415: if (isReference()) {
416: throw noChildrenAllowed();
417: }
418: FilterChain result = new FilterChain();
419: result.setProject(getProject());
420: errorFilterChains.add(result);
421: return result;
422: }
423:
424: /**
425: * Configure the specified <code>Redirector</code>.
426: * @param redirector <code>Redirector</code>.
427: */
428: public void configure(Redirector redirector) {
429: configure(redirector, null);
430: }
431:
432: /**
433: * Configure the specified <code>Redirector</code>
434: * for the specified sourcefile.
435: * @param redirector <code>Redirector</code>.
436: * @param sourcefile <code>String</code>.
437: */
438: public void configure(Redirector redirector, String sourcefile) {
439: if (isReference()) {
440: getRef().configure(redirector, sourcefile);
441: return;
442: }
443: if (alwaysLog != null) {
444: redirector.setAlwaysLog(alwaysLog.booleanValue());
445: }
446: if (logError != null) {
447: redirector.setLogError(logError.booleanValue());
448: }
449: if (append != null) {
450: redirector.setAppend(append.booleanValue());
451: }
452: if (createEmptyFiles != null) {
453: redirector.setCreateEmptyFiles(createEmptyFiles
454: .booleanValue());
455: }
456: if (outputProperty != null) {
457: redirector.setOutputProperty(outputProperty);
458: }
459: if (errorProperty != null) {
460: redirector.setErrorProperty(errorProperty);
461: }
462: if (inputString != null) {
463: redirector.setInputString(inputString);
464: }
465: if (logInputString != null) {
466: redirector.setLogInputString(logInputString.booleanValue());
467: }
468: if (inputMapper != null) {
469: String[] inputTargets = null;
470: try {
471: inputTargets = inputMapper.getImplementation()
472: .mapFileName(sourcefile);
473: } catch (NullPointerException enPeaEx) {
474: if (sourcefile != null) {
475: throw enPeaEx;
476: }
477: }
478: if (inputTargets != null && inputTargets.length > 0) {
479: redirector.setInput(toFileArray(inputTargets));
480: }
481: }
482: if (outputMapper != null) {
483: String[] outputTargets = null;
484: try {
485: outputTargets = outputMapper.getImplementation()
486: .mapFileName(sourcefile);
487: } catch (NullPointerException enPeaEx) {
488: if (sourcefile != null) {
489: throw enPeaEx;
490: }
491: }
492: if (outputTargets != null && outputTargets.length > 0) {
493: redirector.setOutput(toFileArray(outputTargets));
494: }
495: }
496: if (errorMapper != null) {
497: String[] errorTargets = null;
498: try {
499: errorTargets = errorMapper.getImplementation()
500: .mapFileName(sourcefile);
501: } catch (NullPointerException enPeaEx) {
502: if (sourcefile != null) {
503: throw enPeaEx;
504: }
505: }
506: if (errorTargets != null && errorTargets.length > 0) {
507: redirector.setError(toFileArray(errorTargets));
508: }
509: }
510: if (inputFilterChains.size() > 0) {
511: redirector.setInputFilterChains(inputFilterChains);
512: }
513: if (outputFilterChains.size() > 0) {
514: redirector.setOutputFilterChains(outputFilterChains);
515: }
516: if (errorFilterChains.size() > 0) {
517: redirector.setErrorFilterChains(errorFilterChains);
518: }
519: if (inputEncoding != null) {
520: redirector.setInputEncoding(inputEncoding);
521: }
522: if (outputEncoding != null) {
523: redirector.setOutputEncoding(outputEncoding);
524: }
525: if (errorEncoding != null) {
526: redirector.setErrorEncoding(errorEncoding);
527: }
528: }
529:
530: /**
531: * Create a merge mapper pointing to the specified destination file.
532: * @param destfile <code>File</code>
533: * @return <code>Mapper</code>.
534: */
535: protected Mapper createMergeMapper(File destfile) {
536: Mapper result = new Mapper(getProject());
537: result
538: .setClassname(org.apache.tools.ant.util.MergingMapper.class
539: .getName());
540: result.setTo(destfile.getAbsolutePath());
541: return result;
542: }
543:
544: /**
545: * Return a <code>File[]</code> from the specified set of filenames.
546: * @param name <code>String[]</code>
547: * @return <code>File[]</code>.
548: */
549: protected File[] toFileArray(String[] name) {
550: if (name == null) {
551: return null;
552: }
553: //remove any null elements
554: ArrayList list = new ArrayList(name.length);
555: for (int i = 0; i < name.length; i++) {
556: if (name[i] != null) {
557: list.add(getProject().resolveFile(name[i]));
558: }
559: }
560: return (File[]) (list.toArray(new File[list.size()]));
561: }
562:
563: /**
564: * Overrides the version of DataType to recurse on all DataType
565: * child elements that may have been added.
566: * @param stk the stack of data types to use (recursively).
567: * @param p the project to use to dereference the references.
568: * @throws BuildException on error.
569: */
570: protected void dieOnCircularReference(Stack stk, Project p)
571: throws BuildException {
572: if (isChecked()) {
573: return;
574: }
575: if (isReference()) {
576: super .dieOnCircularReference(stk, p);
577: } else {
578: Mapper[] m = new Mapper[] { inputMapper, outputMapper,
579: errorMapper };
580: for (int i = 0; i < m.length; i++) {
581: if (m[i] != null) {
582: stk.push(m[i]);
583: m[i].dieOnCircularReference(stk, p);
584: stk.pop();
585: }
586: }
587: Vector[] v = new Vector[] { inputFilterChains,
588: outputFilterChains, errorFilterChains };
589: for (int i = 0; i < v.length; i++) {
590: if (v[i] != null) {
591: for (Iterator fci = v[i].iterator(); fci.hasNext();) {
592: FilterChain fc = (FilterChain) fci.next();
593: stk.push(fc);
594: fc.dieOnCircularReference(stk, p);
595: stk.pop();
596: }
597: }
598: }
599: setChecked(true);
600: }
601: }
602:
603: /**
604: * Perform the check for circular references, returning the
605: * referenced RedirectorElement.
606: * @return the referenced RedirectorElement.
607: */
608: private RedirectorElement getRef() {
609: return (RedirectorElement) getCheckedRef();
610: }
611:
612: }
|