blob: 0f3755835e74a691a00d2c5aeac89ec2edc03102 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.types;
import java.io.File;
import java.util.Stack;
import java.util.Vector;
import java.util.Iterator;
import java.util.ArrayList;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.Redirector;
/**
* Element representation of a <code>Redirector</code>.
* @since Ant 1.6.2
*/
public class RedirectorElement extends DataType {
/**
* Whether the input mapper was set via <code>setOutput</code>.
*/
private boolean usingInput = false;
/**
* Whether the output mapper was set via <code>setOutput</code>.
*/
private boolean usingOutput = false;
/**
* Whether the error mapper was set via <code>setError</code>.
*/
private boolean usingError = false;
/**
* Indicates if standard error should be logged to Ant's log system
* rather than the output. This has no effect if standard error is
* redirected to a file or property.
*/
private Boolean logError;
/** The name of the property into which output is to be stored */
private String outputProperty;
/** The name of the property into which error output is to be stored */
private String errorProperty;
/** String from which input is taken */
private String inputString;
/** Flag which indicates if error and output files are to be appended. */
private Boolean append;
/** Flag which indicates that output should be always sent to the log */
private Boolean alwaysLog;
/** Flag which indicates whether files should be created even if empty. */
private Boolean createEmptyFiles;
/** Input file mapper. */
private Mapper inputMapper;
/** Output file mapper. */
private Mapper outputMapper;
/** Error file mapper. */
private Mapper errorMapper;
/** input filter chains. */
private Vector inputFilterChains = new Vector();
/** output filter chains. */
private Vector outputFilterChains = new Vector();
/** error filter chains. */
private Vector errorFilterChains = new Vector();
/** The output encoding */
private String outputEncoding;
/** The error encoding */
private String errorEncoding;
/** The input encoding */
private String inputEncoding;
/** whether to log the inputstring */
private Boolean logInputString;
/**
* Add the input file mapper.
* @param inputMapper <code>Mapper</code>.
*/
public void addConfiguredInputMapper(Mapper inputMapper) {
if (isReference()) {
throw noChildrenAllowed();
}
if (this.inputMapper != null) {
if (usingInput) {
throw new BuildException("attribute \"input\""
+ " cannot coexist with a nested <inputmapper>");
} else {
throw new BuildException("Cannot have > 1 <inputmapper>");
}
}
this.inputMapper = inputMapper;
}
/**
* Add the output file mapper.
* @param outputMapper <code>Mapper</code>.
*/
public void addConfiguredOutputMapper(Mapper outputMapper) {
if (isReference()) {
throw noChildrenAllowed();
}
if (this.outputMapper != null) {
if (usingOutput) {
throw new BuildException("attribute \"output\""
+ " cannot coexist with a nested <outputmapper>");
} else {
throw new BuildException("Cannot have > 1 <outputmapper>");
}
}
this.outputMapper = outputMapper;
}
/**
* Add the error file mapper.
* @param errorMapper <code>Mapper</code>.
*/
public void addConfiguredErrorMapper(Mapper errorMapper) {
if (isReference()) {
throw noChildrenAllowed();
}
if (this.errorMapper != null) {
if (usingError) {
throw new BuildException("attribute \"error\""
+ " cannot coexist with a nested <errormapper>");
} else {
throw new BuildException("Cannot have > 1 <errormapper>");
}
}
this.errorMapper = errorMapper;
}
/**
* Make this instance in effect a reference to another instance.
*
* <p>You must not set another attribute or nest elements inside
* this element if you make it a reference.</p>
* @param r the reference to use.
* @throws BuildException on error.
*/
public void setRefid(Reference r) throws BuildException {
if (usingInput
|| usingOutput
|| usingError
|| inputString != null
|| logError != null
|| append != null
|| createEmptyFiles != null
|| inputEncoding != null
|| outputEncoding != null
|| errorEncoding != null
|| outputProperty != null
|| errorProperty != null
|| logInputString != null) {
throw tooManyAttributes();
}
super.setRefid(r);
}
/**
* Set the input to use for the task.
* @param input the file from which input is read.
*/
public void setInput(File input) {
if (isReference()) {
throw tooManyAttributes();
}
if (inputString != null) {
throw new BuildException("The \"input\" and \"inputstring\" "
+ "attributes cannot both be specified");
}
usingInput = true;
inputMapper = createMergeMapper(input);
}
/**
* Set the string to use as input
* @param inputString the string which is used as the input source
*/
public void setInputString(String inputString) {
if (isReference()) {
throw tooManyAttributes();
}
if (usingInput) {
throw new BuildException("The \"input\" and \"inputstring\" "
+ "attributes cannot both be specified");
}
this.inputString = inputString;
}
/**
* Set whether to include the value of the input string in log messages.
* Defaults to true.
* @param logInputString true or false.
* @since Ant 1.7
*/
public void setLogInputString(boolean logInputString) {
if (isReference()) {
throw tooManyAttributes();
}
this.logInputString = logInputString ? Boolean.TRUE : Boolean.FALSE;
}
/**
* File the output of the process is redirected to. If error is not
* redirected, it too will appear in the output.
*
* @param out the file to which output stream is written.
*/
public void setOutput(File out) {
if (isReference()) {
throw tooManyAttributes();
}
if (out == null) {
throw new IllegalArgumentException("output file specified as null");
}
usingOutput = true;
outputMapper = createMergeMapper(out);
}
/**
* Set the output encoding.
* @param outputEncoding <code>String</code>.
*/
public void setOutputEncoding(String outputEncoding) {
if (isReference()) {
throw tooManyAttributes();
}
this.outputEncoding = outputEncoding;
}
/**
* Set the error encoding.
*
* @param errorEncoding <code>String</code>.
*/
public void setErrorEncoding(String errorEncoding) {
if (isReference()) {
throw tooManyAttributes();
}
this.errorEncoding = errorEncoding;
}
/**
* Set the input encoding.
* @param inputEncoding <code>String</code>.
*/
public void setInputEncoding(String inputEncoding) {
if (isReference()) {
throw tooManyAttributes();
}
this.inputEncoding = inputEncoding;
}
/**
* Controls whether error output of exec is logged. This is only useful
* when output is being redirected and error output is desired in the
* Ant log.
* @param logError if true the standard error is sent to the Ant log system
* and not sent to output.
*/
public void setLogError(boolean logError) {
if (isReference()) {
throw tooManyAttributes();
}
this.logError = ((logError) ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Set the file to which standard error is to be redirected.
* @param error the file to which error is to be written.
*/
public void setError(File error) {
if (isReference()) {
throw tooManyAttributes();
}
if (error == null) {
throw new IllegalArgumentException("error file specified as null");
}
usingError = true;
errorMapper = createMergeMapper(error);
}
/**
* Property name whose value should be set to the output of
* the process.
* @param outputProperty the name of the property to be set with the
* task's output.
*/
public void setOutputProperty(String outputProperty) {
if (isReference()) {
throw tooManyAttributes();
}
this.outputProperty = outputProperty;
}
/**
* Whether output should be appended to or overwrite an existing file.
* Defaults to false.
* @param append if true output and error streams are appended to their
* respective files, if specified.
*/
public void setAppend(boolean append) {
if (isReference()) {
throw tooManyAttributes();
}
this.append = ((append) ? Boolean.TRUE : Boolean.FALSE);
}
/**
* If true, (error and non-error) output will be "teed", redirected
* as specified while being sent to Ant's logging mechanism as if no
* redirection had taken place. Defaults to false.
* @param alwaysLog <code>boolean</code>
* @since Ant 1.6.3
*/
public void setAlwaysLog(boolean alwaysLog) {
if (isReference()) {
throw tooManyAttributes();
}
this.alwaysLog = ((alwaysLog) ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Whether output and error files should be created even when empty.
* Defaults to true.
* @param createEmptyFiles <code>boolean</code>.
*/
public void setCreateEmptyFiles(boolean createEmptyFiles) {
if (isReference()) {
throw tooManyAttributes();
}
this.createEmptyFiles = ((createEmptyFiles)
? Boolean.TRUE : Boolean.FALSE);
}
/**
* Property name whose value should be set to the error of
* the process.
* @param errorProperty the name of the property to be set
* with the error output.
*/
public void setErrorProperty(String errorProperty) {
if (isReference()) {
throw tooManyAttributes();
}
this.errorProperty = errorProperty;
}
/**
* Create a nested input <code>FilterChain</code>.
* @return <code>FilterChain</code>.
*/
public FilterChain createInputFilterChain() {
if (isReference()) {
throw noChildrenAllowed();
}
FilterChain result = new FilterChain();
result.setProject(getProject());
inputFilterChains.add(result);
return result;
}
/**
* Create a nested output <code>FilterChain</code>.
* @return <code>FilterChain</code>.
*/
public FilterChain createOutputFilterChain() {
if (isReference()) {
throw noChildrenAllowed();
}
FilterChain result = new FilterChain();
result.setProject(getProject());
outputFilterChains.add(result);
return result;
}
/**
* Create a nested error <code>FilterChain</code>.
* @return <code>FilterChain</code>.
*/
public FilterChain createErrorFilterChain() {
if (isReference()) {
throw noChildrenAllowed();
}
FilterChain result = new FilterChain();
result.setProject(getProject());
errorFilterChains.add(result);
return result;
}
/**
* Configure the specified <code>Redirector</code>.
* @param redirector <code>Redirector</code>.
*/
public void configure(Redirector redirector) {
configure(redirector, null);
}
/**
* Configure the specified <code>Redirector</code>
* for the specified sourcefile.
* @param redirector <code>Redirector</code>.
* @param sourcefile <code>String</code>.
*/
public void configure(Redirector redirector, String sourcefile) {
if (isReference()) {
getRef().configure(redirector, sourcefile);
return;
}
if (alwaysLog != null) {
redirector.setAlwaysLog(alwaysLog.booleanValue());
}
if (logError != null) {
redirector.setLogError(logError.booleanValue());
}
if (append != null) {
redirector.setAppend(append.booleanValue());
}
if (createEmptyFiles != null) {
redirector.setCreateEmptyFiles(createEmptyFiles.booleanValue());
}
if (outputProperty != null) {
redirector.setOutputProperty(outputProperty);
}
if (errorProperty != null) {
redirector.setErrorProperty(errorProperty);
}
if (inputString != null) {
redirector.setInputString(inputString);
}
if (logInputString != null) {
redirector.setLogInputString(logInputString.booleanValue());
}
if (inputMapper != null) {
String[] inputTargets = null;
try {
inputTargets =
inputMapper.getImplementation().mapFileName(sourcefile);
} catch (NullPointerException enPeaEx) {
if (sourcefile != null) {
throw enPeaEx;
}
}
if (inputTargets != null && inputTargets.length > 0) {
redirector.setInput(toFileArray(inputTargets));
}
}
if (outputMapper != null) {
String[] outputTargets = null;
try {
outputTargets =
outputMapper.getImplementation().mapFileName(sourcefile);
} catch (NullPointerException enPeaEx) {
if (sourcefile != null) {
throw enPeaEx;
}
}
if (outputTargets != null && outputTargets.length > 0) {
redirector.setOutput(toFileArray(outputTargets));
}
}
if (errorMapper != null) {
String[] errorTargets = null;
try {
errorTargets =
errorMapper.getImplementation().mapFileName(sourcefile);
} catch (NullPointerException enPeaEx) {
if (sourcefile != null) {
throw enPeaEx;
}
}
if (errorTargets != null && errorTargets.length > 0) {
redirector.setError(toFileArray(errorTargets));
}
}
if (inputFilterChains.size() > 0) {
redirector.setInputFilterChains(inputFilterChains);
}
if (outputFilterChains.size() > 0) {
redirector.setOutputFilterChains(outputFilterChains);
}
if (errorFilterChains.size() > 0) {
redirector.setErrorFilterChains(errorFilterChains);
}
if (inputEncoding != null) {
redirector.setInputEncoding(inputEncoding);
}
if (outputEncoding != null) {
redirector.setOutputEncoding(outputEncoding);
}
if (errorEncoding != null) {
redirector.setErrorEncoding(errorEncoding);
}
}
/**
* Create a merge mapper pointing to the specified destination file.
* @param destfile <code>File</code>
* @return <code>Mapper</code>.
*/
protected Mapper createMergeMapper(File destfile) {
Mapper result = new Mapper(getProject());
result.setClassname(
org.apache.tools.ant.util.MergingMapper.class.getName());
result.setTo(destfile.getAbsolutePath());
return result;
}
/**
* Return a <code>File[]</code> from the specified set of filenames.
* @param name <code>String[]</code>
* @return <code>File[]</code>.
*/
protected File[] toFileArray(String[] name) {
if (name == null) {
return null;
}
//remove any null elements
ArrayList list = new ArrayList(name.length);
for (int i = 0; i < name.length; i++) {
if (name[i] != null) {
list.add(getProject().resolveFile(name[i]));
}
}
return (File[]) (list.toArray(new File[list.size()]));
}
/**
* Overrides the version of DataType to recurse on all DataType
* child elements that may have been added.
* @param stk the stack of data types to use (recursively).
* @param p the project to use to dereference the references.
* @throws BuildException on error.
*/
protected void dieOnCircularReference(Stack stk, Project p)
throws BuildException {
if (isChecked()) {
return;
}
if (isReference()) {
super.dieOnCircularReference(stk, p);
} else {
Mapper[] m = new Mapper[] {inputMapper, outputMapper, errorMapper};
for (int i = 0; i < m.length; i++) {
if (m[i] != null) {
stk.push(m[i]);
m[i].dieOnCircularReference(stk, p);
stk.pop();
}
}
Vector[] v = new Vector[]
{inputFilterChains, outputFilterChains, errorFilterChains};
for (int i = 0; i < v.length; i++) {
if (v[i] != null) {
for (Iterator fci = v[i].iterator(); fci.hasNext();) {
FilterChain fc = (FilterChain) fci.next();
stk.push(fc);
fc.dieOnCircularReference(stk, p);
stk.pop();
}
}
}
setChecked(true);
}
}
/**
* Perform the check for circular references, returning the
* referenced RedirectorElement.
* @return the referenced RedirectorElement.
*/
private RedirectorElement getRef() {
return (RedirectorElement) getCheckedRef();
}
}