001/*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2005 Grzegorz Lukasik
005 *
006 * Note: This file is dual licensed under the GPL and the Apache
007 * Source License (so that it can be used from both the main
008 * Cobertura classes and the ant tasks).
009 *
010 * Cobertura is free software; you can redistribute it and/or modify
011 * it under the terms of the GNU General Public License as published
012 * by the Free Software Foundation; either version 2 of the License,
013 * or (at your option) any later version.
014 *
015 * Cobertura is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of
017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018 * General Public License for more details.
019 *
020 * You should have received a copy of the GNU General Public License
021 * along with Cobertura; if not, write to the Free Software
022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
023 * USA
024 */
025
026package net.sourceforge.cobertura.util;
027
028import java.io.BufferedReader;
029import java.io.File;
030import java.io.FileReader;
031import java.io.FileWriter;
032import java.io.IOException;
033import java.util.ArrayList;
034import java.util.List;
035
036import org.apache.log4j.Logger;
037
038/**
039 * Helper class for storing long command lines inside temporary file.
040 * <p>
041 * Typical usage:
042 * 
043 * <pre>
044 *  builder = new CommandLineBuilder();
045 *  builder.addArg(&quot;--someoption&quot;);
046 *  builder.addArg(&quot;optionValue&quot;);
047 *  ...
048 *  builder.saveArgs();
049 *  doSomething(builder.getCommandLineFile());
050 *  builder.dispose();
051 * </pre>
052 * 
053 * It will save options in <code>builder.getCommandLineFile()</code>. Options
054 * will be stored one in a line. To retrieve options from file helper method can
055 * be used (see documentation):
056 * 
057 * <pre>
058 * String[] args = CommandLineBuilder.preprocessCommandLineArguments(args);
059 * </pre>
060 * 
061 * </p>
062 * 
063 * <p>
064 * NOTICE: No protection against line separators in arguments, should be OK for
065 * Cobertura needs.
066 * </p>
067 * <p>
068 * NOTICE: This class depends on local machine settings (line separator, default
069 * encoding). If arguments are saved on different machine than they are loaded,
070 * results are unspecified. No problem in Cobertura.
071 * </p>
072 * 
073 * @author Grzegorz Lukasik
074 */
075public class CommandLineBuilder {
076        private static final Logger logger = Logger
077                        .getLogger(CommandLineBuilder.class);
078
079        private static final String LINESEP = System.getProperty("line.separator");
080
081        // File that will be used to store arguments
082        private File commandLineFile = null;
083
084        // Writer that will be used to write arguments to the file
085        private FileWriter commandLineWriter = null;
086
087        /**
088         * Creates a new instance of the builder. Instances of this class should not
089         * be reused to create many command lines.
090         * 
091         * @throws IOException
092         *             if problems with creating temporary file for storing command
093         *             line occur
094         */
095        public CommandLineBuilder() throws IOException {
096                commandLineFile = File.createTempFile("cobertura.", ".cmdline");
097                commandLineFile.deleteOnExit();
098                commandLineWriter = new FileWriter(commandLineFile);
099        }
100
101        /**
102         * Adds command line argument. Each argument can be thought as a single cell
103         * in array passed to main method. This method should not be used after
104         * arguments were saved.
105         * 
106         * @param arg command line argument to save
107         * @throws IOException
108         *             if problems with temporary file occur
109         * @throws NullPointerException 
110         *             if <code>arg</code> is <code>null</code>            
111         */
112        public void addArg(String arg) throws IOException {
113                if( arg==null)
114                        throw new NullPointerException();
115                commandLineWriter.write(arg + LINESEP);
116        }
117
118        
119        /**
120         * Adds two command line arguments. Convienience function, calls
121         * {@link #addArg(String)} two times.   
122         * 
123         * @param arg1 first command line argument to save
124         * @param arg2 second command line argument to save
125         * @throws IOException
126         *             if problems with temporary file occur
127         * @throws NullPointerException 
128         *             if any <code>arg</code> is <code>null</code>            
129         */
130    public void addArg(String arg1, String arg2) throws IOException {
131        addArg(arg1);
132        addArg(arg2);
133    }
134
135    
136        /**
137         * Saves options and made file available to use. Use method
138         * {@link #getCommandLineFile} to get the file the arguments are saved in.
139         * 
140         * @throws IOException
141         *             if problems with temporary file occur
142         */
143        public void saveArgs() throws IOException {
144                commandLineWriter.flush();
145                commandLineWriter.close();
146        }
147
148        /**
149         * Gets absolute path to the file with saved arguments. Notice, that however
150         * this method can be used as soon as an instance of this class is created,
151         * arguments should be read from the file after a call to
152         * {@link #saveArgs} method.
153         * 
154         * @return absolute path to the file with arguments
155         */
156        public String getCommandLineFile() {
157                return commandLineFile.getAbsolutePath();
158        }
159
160        /**
161         * Explicity frees all resources associated with this instance. Result of
162         * any other method call after disposing an instance of this class is
163         * unspecified.
164         */
165        public void dispose() {
166                commandLineFile.delete();
167        }
168
169        /**
170         * Loads arguments from file if <code>--commandsfile</code> option is used. Checks
171         * if passed array contains <code>--commandsfile</code> String, and if
172         * so arguments from file specified in the very next array cell are read. If
173         * there are more then one <code>--commandsfile</code> the result is unspecified. 
174         *
175         * @return The list of arguments read from commandsfile, or
176         *         <code>args</code> if commandsfile option was not specified
177         *         or the file cannot be read.
178         * @throws NullPointerException if args is null, or any argument is null
179         * @throws IllegalArgumentException if --commandsfile is specified as last option
180         * @throws IOException if I/O related error with temporary command line file occur
181         */
182        public static String[] preprocessCommandLineArguments(String[] args) throws IOException {
183                boolean hasCommandsFile = false;
184                String commandsFileName = null;
185                for (int i = 0; i < args.length; i++) {
186                        if ( args[i].equals( "--commandsfile")) {
187                                if( i==args.length-1) {
188                                        throw new IllegalArgumentException("'--commandsfile' specified as last option.");
189                                }
190                                hasCommandsFile = true;
191                                commandsFileName = args[++i];
192                        }
193                }
194
195                if (hasCommandsFile) {
196                        List arglist = new ArrayList();
197                        BufferedReader bufferedReader = null;
198
199                        try {
200                                bufferedReader = new BufferedReader(new FileReader(
201                                                commandsFileName));
202                                String line;
203
204                                while ((line = bufferedReader.readLine()) != null)
205                                        arglist.add(line);
206
207                        } catch (IOException e) {
208                                logger.info( "I/O error when reading temporary commands file", e);
209                                throw new IOException( "Unable to read temporary commands file "
210                                                + commandsFileName + ".");
211                        } finally {
212                                if (bufferedReader != null) {
213                                        try {
214                                                bufferedReader.close();
215                                        } catch (IOException e) {
216                                        }
217                                }
218                        }
219
220                        args = (String[]) arglist.toArray(new String[arglist.size()]);
221                }
222                return args;
223        }
224}