View Javadoc

1   /**
2    * Copyright 2010, CSIRO Australia.
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 au.csiro.netcdf;
18  
19  import java.io.BufferedReader;
20  import java.io.FileReader;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.io.StringWriter;
24  
25  import org.apache.commons.cli.BasicParser;
26  import org.apache.commons.cli.CommandLine;
27  import org.apache.commons.cli.HelpFormatter;
28  import org.apache.commons.cli.Option;
29  import org.apache.commons.cli.OptionBuilder;
30  import org.apache.commons.cli.Options;
31  import org.apache.commons.cli.ParseException;
32  
33  import au.csiro.netcdf.cli.Command;
34  import au.csiro.netcdf.cli.CommandLineOptionsComparator;
35  import au.csiro.netcdf.util.CSVTokenizer;
36  import au.csiro.netcdf.util.Util;
37  
38  /**
39   * The <strong>ncextractcsv</strong> command reads a specified column of data from a comma separated 
40   * file and then writes this column to standard output.
41   * <br/>
42   * 
43   * Copyright 2010, CSIRO Australia 
44   * All rights reserved.
45   * 
46   * @author $Author: robertbridle $ on 18/03/2010
47   * @version $Revision: 84 $ $Date: 2010-08-25 15:56:46 +1000 (Wed, 25 Aug 2010) $ $Id: CSVExtract.java 6525
48   *          2010-03-17 23:39:33Z che256 $
49   */
50  public class NcCSVExtract implements Command
51  {
52      /**
53       * The command name
54       */
55      public static final String EXTRACT_COMMAND_NAME = "ncextractcsv";
56      
57      /**
58       * The name of the command line option used for specifying the input csv file name.
59       */
60      public static final String INPUT_FILE = "inputFileName";
61      
62      /**
63       * The name of the command line option used for specifying the index of the column.
64       */
65      public static final String COLUMN_INDEX = "columnIndex";
66      
67      /**
68       * The name of the command line option used for specifying the start row.
69       */
70      public static final String START_ROW = "startRow";
71      
72      /**
73       * The name of the command line option used for specifying the end row.
74       */
75      public static final String END_ROW = "endRow";
76      
77      /**
78       * Command options
79       */
80      private Options options = null;
81      
82      /**
83       * Constructor
84       */
85      public NcCSVExtract()
86      {
87          this.options = createOptions();        
88      }
89  
90      /**
91       * Create the command line options for this CSVExtract command.
92       * 
93       * @return the command line options for this command.
94       */
95      @Override
96      @SuppressWarnings("static-access")
97      public Options createOptions()
98      {
99          Option fileName = OptionBuilder.withArgName("file").hasArg().withDescription(
100                 "1: the filename of csv file to read.").isRequired(true).withLongOpt(INPUT_FILE).create("i");
101 
102         Option columnIndex = OptionBuilder.withArgName("integer").hasArg().withDescription(
103                 "2: the column index from the csv file to read.").isRequired(true).withLongOpt(COLUMN_INDEX)
104                 .create("c");
105 
106         Option startRow = OptionBuilder.withArgName("integer").hasArg().withDescription(
107                 "3: the row to read from. OPTIONAL, if not specified row 0 is used.").isRequired(false).withLongOpt(
108                 START_ROW).create("s");
109 
110         Option endRow = OptionBuilder.withArgName("integer").hasArg().withDescription(
111                 "4: the row to read to. OPTIONAL, if not specified the last row in the csv file is used.").isRequired(
112                 false).withLongOpt(END_ROW).create("e");
113 
114         Options options = new Options();
115 
116         options.addOption(fileName);
117         options.addOption(columnIndex);
118         options.addOption(startRow);
119         options.addOption(endRow);
120 
121         return options;
122     }
123 
124     /**
125      * Run the CSVExtract command.
126      * 
127      * @param args
128      *            command line arguments.
129      * @throws ParseException
130      *             thrown if the command line arguments can not be parsed.
131      * @throws IOException
132      *             thrown if netCDF can to be written to or read from.
133      */
134     @Override
135     public void execute(String[] args) throws ParseException, IOException
136     {
137         // parse the command line arguments
138         CommandLine parsedCommandLine = new BasicParser().parse(options, args);
139 
140         String filename = parsedCommandLine.getOptionValue(INPUT_FILE);
141         int numRecords = Util.fastFileLineCounter(filename);
142         int columnIndex = getIntValueByName(parsedCommandLine, COLUMN_INDEX);
143 
144         int rowCount = 0;
145         int startRow = parsedCommandLine.hasOption(START_ROW) ? getIntValueByName(parsedCommandLine, START_ROW) : 0;
146         int endRow = parsedCommandLine.hasOption(END_ROW) ? getIntValueByName(parsedCommandLine, END_ROW) : numRecords;
147         endRow = (endRow > numRecords) ? numRecords : endRow;
148 
149         BufferedReader input = new BufferedReader(new FileReader(filename));
150         try
151         {
152             String line = null;
153             while ((line = input.readLine()) != null)
154             {
155                 // only read the required lines
156                 if (rowCount >= startRow && rowCount <= endRow)
157                 {
158                     String[] values = new CSVTokenizer(line).getAllColumns();
159 
160                     String value = "";
161                     try
162                     {
163                         value = values[columnIndex];
164                     }
165                     catch (ArrayIndexOutOfBoundsException ex)
166                     {
167                         throw new ParseException("incorrect column index");
168                     }
169                     System.out.println(value);
170                     rowCount++;
171                 }
172                 else
173                 {
174                     rowCount++;
175                     continue;
176                 }
177             }
178         }
179         finally
180         {
181             input.close();
182         }
183     }
184 
185     /**
186      * @return the command name for this command.
187      */
188     @Override
189     public String getCommandName()
190     {
191         return EXTRACT_COMMAND_NAME;
192     }
193 
194     /**
195      * Determine if this command is valid, check its syntax. Required options: inputFileName, columnIndex Type check:
196      * columnIndex, startRow, endRow can only be integer
197      * 
198      * @param commandLine command line arguments.
199      * @return error if found.
200      */
201     @Override
202     public String validCommand(String[] commandLine)
203     {
204         StringBuffer errorMsg = new StringBuffer();
205         CommandLine parsedCommandLine = null;
206 
207         // check required options are provided
208         try
209         {
210             parsedCommandLine = new BasicParser().parse(options, commandLine);
211         }
212         catch (ParseException ex)
213         {
214             return ex.getMessage();
215         }
216 
217         // check command name
218         String commandName = parsedCommandLine.getArgs()[0];
219         if (!getCommandName().equals(commandName))
220         {
221             return commandName + " is not a valid command.";
222         }
223 
224         // check if specified file exist
225         String fileName = parsedCommandLine.getOptionValue(INPUT_FILE);
226         if (!Util.fileExists(fileName))
227         {
228             return fileName + " does not exist.";
229         }
230         
231         // validate file type
232         if (!".csv".equalsIgnoreCase(Util.getFileExtension(fileName)))
233         {
234             return fileName + " is not a csv file.";
235         }
236 
237         // check required types:
238         // i.e. columnIndex, startRow and endRow are of integer type
239         int index = getIntValueByName(parsedCommandLine, COLUMN_INDEX);
240         if (index == -1)
241         {
242             errorMsg.append("ColumnIndex has to be a number.");
243         }
244 
245         if (parsedCommandLine.hasOption(START_ROW))
246         {
247             index = getIntValueByName(parsedCommandLine, START_ROW);
248             if (index == -1)
249             {
250                 errorMsg.append(" startRow has to be a number.");
251             }
252         }
253         if (parsedCommandLine.hasOption(END_ROW))
254         {
255             index = getIntValueByName(parsedCommandLine, END_ROW);
256             if (index == -1)
257             {
258                 errorMsg.append(" endRow has to be a number.");
259             }
260         }
261 
262         return errorMsg.toString();
263     }
264 
265     /**
266      * @return the usage for this command.
267      */
268     @Override
269     public String getUsageString()
270     {
271         // generate the help/usage statement
272         String header = "Extract a block of data from a CSV file. The data can then "
273                 + "be piped into the ncwritevar command to add the data to a variable in a netCDF file.";
274         String footer = "\nExample: extract -inputFileName modeldata.csv -columnIndex 1 -startRow 2\n"
275                 + "Will write the data contained in the second column of the modeldata.csv file to stdout, "
276                 + "but ignoring the first 2 lines. ";
277         StringWriter sw = new StringWriter();
278         HelpFormatter formatter = new HelpFormatter();
279         formatter.setOptionComparator(new CommandLineOptionsComparator());
280         formatter.printHelp(new PrintWriter(sw), PRINT_WIDTH, EXTRACT_COMMAND_NAME, header, options, 0, 1, footer);
281         return sw.toString();
282     }
283 
284     /**
285      * Return integer value of an option using specified name
286      * "-1" is returned if option name or value is not valid
287      * 
288      * @param parsedCommandLine Commandline object
289      * @param opName option name
290      * @return int value
291      */
292     private int getIntValueByName(CommandLine parsedCommandLine, String opName)
293     {
294         String str = (parsedCommandLine.hasOption(opName)) ? parsedCommandLine.getOptionValue(opName) : "";
295 
296         if (Util.validateNumber(str))
297         {
298             return Integer.valueOf(str);
299         }
300         return -1;
301     }
302 }