| /* |
| * 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(); |
| } |
| |
| } |