Coverage Report - au.csiro.netcdf.NcWriteVariable
 
Classes in this File Line Coverage Branch Coverage Complexity
NcWriteVariable
86%
218/251
74%
79/106
5.4
 
 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  
 /**
 18  
  * 
 19  
  */
 20  
 package au.csiro.netcdf;
 21  
 
 22  
 import java.io.BufferedInputStream;
 23  
 import java.io.BufferedReader;
 24  
 import java.io.DataInputStream;
 25  
 import java.io.EOFException;
 26  
 import java.io.File;
 27  
 import java.io.FileInputStream;
 28  
 import java.io.IOException;
 29  
 import java.io.InputStream;
 30  
 import java.io.InputStreamReader;
 31  
 import java.io.PrintWriter;
 32  
 import java.io.StringWriter;
 33  
 import java.util.ArrayList;
 34  
 import java.util.List;
 35  
 
 36  
 import org.apache.commons.cli.BasicParser;
 37  
 import org.apache.commons.cli.CommandLine;
 38  
 import org.apache.commons.cli.HelpFormatter;
 39  
 import org.apache.commons.cli.Option;
 40  
 import org.apache.commons.cli.OptionBuilder;
 41  
 import org.apache.commons.cli.Options;
 42  
 import org.apache.commons.cli.ParseException;
 43  
 import org.apache.log4j.Logger;
 44  
 
 45  
 import ucar.ma2.Array;
 46  
 import ucar.ma2.InvalidRangeException;
 47  
 import ucar.ma2.Range;
 48  
 import ucar.nc2.NetcdfFile;
 49  
 import ucar.nc2.NetcdfFileWriteable;
 50  
 import ucar.nc2.Variable;
 51  
 import au.csiro.netcdf.cli.Command;
 52  
 import au.csiro.netcdf.cli.CommandLineOptionsComparator;
 53  
 import au.csiro.netcdf.util.NetCDFUtils;
 54  
 import au.csiro.netcdf.util.Util;
 55  
 
 56  
 /**
 57  
  * The <strong>ncwriteVar</strong> command writes a specified portion of data to a {@link Variable} 
 58  
  * object in a netCDF file.
 59  
  * 
 60  
  * Copyright 2010, CSIRO Australia
 61  
  * All rights reserved.
 62  
  * 
 63  
  * @author      James Dempsey on 18/03/2010
 64  
  * @version     $Revision: 86 $  $Date: 2010-08-30 17:31:03 +1000 (Mon, 30 Aug 2010) $
 65  
  */
 66  2
 public class NcWriteVariable implements Command
 67  
 {
 68  
     /**
 69  
      * Constant that defines the logger to be used.
 70  
      */
 71  2
     private static final Logger LOG = Logger.getLogger(NcWriteVariable.class.getName());
 72  
 
 73  
     /**
 74  
      * The command name
 75  
      */
 76  
     public static final String NC_WRITE_VAR_COMMAND_NAME = "ncwriteVar";
 77  
 
 78  
     /**
 79  
      * The name of the command line option used for specifying the output netCDF file name.
 80  
      */
 81  
     private static final String OUTPUT_FILE = "outputFileName";
 82  
 
 83  
     /**
 84  
      * The name of the command line option used for specifying the input text file name.
 85  
      */
 86  
     private static final String INPUT_FILE = "inputFileName";
 87  
 
 88  
     /**
 89  
      * The name of the command line option used for specifying the name of the variable.
 90  
      */
 91  
     private static final String VARIABLE_NAME = "variableName";
 92  
 
 93  
     /**
 94  
      * The name of the command line option used for specifying the range to be filled.
 95  
      */
 96  
     private static final String FILL_RANGE = "fillRange";
 97  
 
 98  
     /**
 99  
      * The name of the command line option used for specifying the data is binary.
 100  
      */
 101  
     private static final String BINARY = "binary";
 102  
 
 103  
     /** Command options  */
 104  
     private Options options;
 105  
 
 106  
     /**
 107  
      * Default constructor.
 108  
      */
 109  48
     public NcWriteVariable()
 110  
     {
 111  48
         this.options = createOptions();
 112  48
     }
 113  
     
 114  
     /* (non-Javadoc)
 115  
      * @see au.csiro.netcdf.cli.Command#execute(java.lang.String[])
 116  
      */
 117  
     @Override
 118  
     public void execute(String[] args) throws ParseException, IOException
 119  
     {
 120  
         // parse the command line arguments
 121  2
         CommandLine parsedCommandLine = new BasicParser().parse(options, args);
 122  
 
 123  
         // get the command line argument values
 124  4
         String outputFilenameArg = (parsedCommandLine.hasOption(OUTPUT_FILE)) ? parsedCommandLine
 125  2
                 .getOptionValue(OUTPUT_FILE) : "";
 126  4
         String variableNameArg = (parsedCommandLine.hasOption(VARIABLE_NAME)) ? parsedCommandLine
 127  2
                 .getOptionValue(VARIABLE_NAME) : "";
 128  4
         String fillRangeArg = (parsedCommandLine.hasOption(FILL_RANGE)) ? parsedCommandLine
 129  2
                 .getOptionValue(FILL_RANGE) : "";
 130  4
         String inputFilenameArg = (parsedCommandLine.hasOption(INPUT_FILE)) ? parsedCommandLine
 131  2
                 .getOptionValue(INPUT_FILE) : "";
 132  2
         boolean binary = parsedCommandLine.hasOption(BINARY);
 133  
 
 134  
         // convert command line parameters to their associated types, note these can throw IllegalArgumentExceptions.
 135  2
         File outputFile = Util.getExistingFile(outputFilenameArg);
 136  
         InputStream input;
 137  2
         if (inputFilenameArg.length()>0)
 138  
         {
 139  2
             input = new FileInputStream(inputFilenameArg);
 140  
         }
 141  
         else
 142  
         {
 143  0
             input = System.in;
 144  
         }
 145  
 
 146  2
         this.execute(outputFile, variableNameArg, fillRangeArg, input, binary);
 147  2
         if (inputFilenameArg.length()>0)
 148  
         {
 149  2
             input.close();
 150  
         }
 151  2
     }
 152  
 
 153  
     /**
 154  
      * Write data to a variable. This is a library interface used by the command line 
 155  
      * ncwriteVar command to put data into a netCDF file. The fill range must be a comma 
 156  
      * separated list with values for each dimension of the variable. Values may be either 
 157  
      * a range n-n or a value n. e.g. 5-10, 3  
 158  
      * 
 159  
      * @param outputFile The netCDF file to be updated. 
 160  
      * @param variableName The variable to be updated.
 161  
      * @param fillRange The range in which the data is to be written. Should be of the form n-n, n-n, n...
 162  
      * @param dataStream The stream from which input data is read. Assumed to contain text with one value per line. 
 163  
      * @param binary True if the dataStream contains binary data, false if it is text
 164  
      * @throws IOException If either the netCDF file or the input stream cannot be accessed.
 165  
      */
 166  
     public void execute(File outputFile, String variableName, String fillRange, InputStream dataStream, boolean binary)
 167  
             throws IOException
 168  
     {
 169  
         // the netcdf file to be written.
 170  46
         NetcdfFileWriteable ncfile = null;
 171  
 
 172  46
         ncfile = NetcdfFileWriteable.openExisting(outputFile.getPath(), false); 
 173  
         try
 174  
         {
 175  46
             Variable variable = ncfile.findVariable(NetcdfFile.escapeName(variableName));
 176  
             
 177  
             // Check the variable exists
 178  46
             if (variable == null)
 179  
             {
 180  4
                 throw new IllegalArgumentException("The variable: " + variableName + " does not exist in the file: "
 181  2
                         + ncfile.getLocation());
 182  
             }
 183  
             
 184  44
             List<Range> ranges = variable.getRanges();
 185  
             
 186  
             // Check the ranges match
 187  44
             String fillRangeVal[] = fillRange.split(",");
 188  44
             if (ranges.size() != fillRangeVal.length)
 189  
             {
 190  4
                 throw new IllegalArgumentException("Expected " + ranges.size() + " fillRange arguments but only got "
 191  2
                         + fillRangeVal.length);
 192  
             }
 193  
 
 194  
             // Calculate the range blocks
 195  42
             Range fillRanges[] = convertStringsToRanges(fillRangeVal, variable, ncfile);
 196  36
             List<int[]> originList = createOriginList(fillRanges);
 197  
             
 198  
             // Make the array that will buffer the data for a block
 199  36
             Array buffer = createArray(fillRanges, variable);
 200  
             
 201  
             // Read the data
 202  36
             if (binary)
 203  
             {
 204  4
                 DataInputStream dataIn = new DataInputStream(new BufferedInputStream(dataStream));
 205  
                 
 206  
                 // Write each block to the variable
 207  12
                 for (int[] origin : originList)
 208  
                 {
 209  4
                     readBinaryData(dataIn, buffer);
 210  4
                     ncfile.write(variableName, origin, buffer);
 211  
                 }
 212  4
                 dataIn.close();
 213  
             }
 214  
             else
 215  
             {
 216  32
                 BufferedReader dataIn = new BufferedReader(new InputStreamReader(dataStream));
 217  
                 
 218  
                 // Write each block to the variable
 219  98
                 for (int[] origin : originList)
 220  
                 {
 221  36
                     readData(dataIn, buffer);
 222  34
                     ncfile.write(variableName, origin, buffer);
 223  
                 }
 224  30
                 dataIn.close();
 225  
             }
 226  
         }
 227  0
         catch (InvalidRangeException e)
 228  
         {
 229  0
             LOG.error("Failed to write data due to illegal shaped values array.", e);
 230  0
             throw new IllegalArgumentException("Failed to write data due to illegal shaped values array.", e);
 231  
         }
 232  
         finally
 233  12
         {
 234  46
             ncfile.close();
 235  12
         }
 236  34
     }
 237  
 
 238  
     /* (non-Javadoc)
 239  
      * @see au.csiro.netcdf.cli.Command#getCommandName()
 240  
      */
 241  
     @Override
 242  
     public String getCommandName()
 243  
     {
 244  12
         return NC_WRITE_VAR_COMMAND_NAME;
 245  
     }
 246  
 
 247  
     /* (non-Javadoc)
 248  
      * @see java.lang.Object#toString()
 249  
      */
 250  
     @Override
 251  
     public String toString()
 252  
     {
 253  
         // generate the help/usage statement
 254  0
         String header = "Write data to variable in a netCDF file.";
 255  0
         String footer = "\nExample: ncwritevar -outputFileName ABC.nc -variableName Lat " + "-fillRange 0-559\n"
 256  
                 + "Will write data from stdin to the variable Lat in the file ABC.nc. The data will "
 257  
                 + "be written to all 560 cells in the variable. The file must already exist and "
 258  
                 + "the variable must be already defined in the file. Data would normally be piped in "
 259  
                 + "from a command such as ncextractcsv.\n"
 260  
                 + "\nExample: ncwritevar -outputFileName ABC.nc -variableName rainfall "
 261  
                 + "-fillRange lookup(-42.8)-lookup(-35.3)\n"
 262  
                 + "Will write data from stdin to the variable rainfall in the file ABC.nc. The data will "
 263  
                 + "only be written to the range between the two listed values. \n"
 264  
                 + "The lookup function allows you to convert a value to an index for a particular dimension. "
 265  
                 + "For the lookup to work, the dimension must have a coordinate variable defined (a variable "
 266  
                 + "associated with the dimension and having the same name as the dimension). For best performance "
 267  
                 + "the values in the coordinate variable should be in ascending order.";
 268  0
         StringWriter sw = new StringWriter();
 269  0
         HelpFormatter formatter = new HelpFormatter();
 270  0
         formatter.setOptionComparator(new CommandLineOptionsComparator());
 271  0
         formatter.printHelp(new PrintWriter(sw), PRINT_WIDTH, NC_WRITE_VAR_COMMAND_NAME, header, options, 0, 1, footer);
 272  0
         return sw.toString();
 273  
     }
 274  
 
 275  
     /* (non-Javadoc)
 276  
      * @see au.csiro.netcdf.cli.Command#getUsageString()
 277  
      */
 278  
     @Override
 279  
     public String getUsageString()
 280  
     {
 281  0
         return toString();
 282  
     }
 283  
 
 284  
     /* (non-Javadoc)
 285  
      * @see au.csiro.netcdf.cli.Command#createOptions()
 286  
      */
 287  
     @SuppressWarnings("static-access")
 288  
     @Override
 289  
     public Options createOptions()
 290  
     {
 291  150
         Option outputFileName = OptionBuilder.withArgName("file").hasArg().withDescription(
 292  150
                 "1. the filename of the netCDF file to be updated.").isRequired(true).withLongOpt(OUTPUT_FILE).create(
 293  50
                 "o");
 294  
 
 295  150
         Option inputFileName = OptionBuilder.withArgName("file").hasArg().withDescription(
 296  50
                 "2. the filename of the text file to be loaded. "
 297  100
                         + "Stdin will be used if this option is not present. OPTIONAL ").isRequired(false).withLongOpt(
 298  100
                 INPUT_FILE).create("i");
 299  
 
 300  100
         Option variableName = OptionBuilder.withArgName("text").hasArg().withDescription("3. the name of the variable.")
 301  50
                 .isRequired(true).withLongOpt(VARIABLE_NAME).create("v");
 302  
 
 303  150
         Option fillRange = OptionBuilder.withArgName("text").hasArg().withDescription(
 304  50
                 "4. the range of the variable to be filled. Must be a comma "
 305  
                         + "separated list with values for each dimension of the variable. "
 306  100
                         + "Values may be a range n-n or a value n. e.g. 5-10, 3").isRequired(true).withLongOpt(
 307  100
                 FILL_RANGE).create("f");
 308  
 
 309  100
         Option binary = OptionBuilder.withDescription("5. indicates the data is in a binary format.").isRequired(false)
 310  50
                 .withLongOpt(BINARY).create("b");
 311  
 
 312  50
         Options options = new Options();
 313  
 
 314  50
         options.addOption(outputFileName);
 315  50
         options.addOption(variableName);
 316  50
         options.addOption(fillRange);
 317  50
         options.addOption(inputFileName);
 318  50
         options.addOption(binary);
 319  
         
 320  50
         return options;
 321  
     }
 322  
 
 323  
     /* (non-Javadoc)
 324  
      * @see au.csiro.netcdf.cli.Command#validCommand(java.lang.String[])
 325  
      */
 326  
     @Override
 327  
     public String validCommand(String[] args)
 328  
     {
 329  12
         String errorMsg = "";
 330  
 
 331  
         // validate the command line parameters
 332  
         try
 333  
         {
 334  
             // try parsing the command line, where ParseException can be thrown from
 335  12
             CommandLine parsedCommandLine = new BasicParser().parse(options, args);
 336  
 
 337  
             // try converting command line parameters to their associated types, where IllegalArgumentException can be
 338  
             // thrown from
 339  20
             String outputFilenameArg = (parsedCommandLine.hasOption(OUTPUT_FILE)) ? parsedCommandLine
 340  10
                     .getOptionValue(OUTPUT_FILE) : "";
 341  20
             String inputFilenameArg = (parsedCommandLine.hasOption(INPUT_FILE)) ? parsedCommandLine
 342  6
                     .getOptionValue(INPUT_FILE) : "";
 343  20
             String fillRangeArg = (parsedCommandLine.hasOption(FILL_RANGE)) ? parsedCommandLine
 344  10
                     .getOptionValue(FILL_RANGE) : "";
 345  
 
 346  10
             Util.getExistingFile(outputFilenameArg);
 347  10
             if (inputFilenameArg.length() > 0)
 348  
             {
 349  6
                 Util.getExistingFile(inputFilenameArg);
 350  
             }
 351  
             
 352  8
             validateRangeFormat(fillRangeArg);
 353  
             
 354  
             // Variables will be validated when processed and the file is available
 355  
         }
 356  2
         catch (ParseException pe)
 357  
         {
 358  2
             errorMsg = errorMsg + pe.getMessage();
 359  
         }
 360  4
         catch (IllegalArgumentException iae)
 361  
         {
 362  4
             errorMsg = errorMsg + iae.getMessage();
 363  
         }
 364  
 
 365  12
         return errorMsg;
 366  
     }
 367  
 
 368  
     /**
 369  
      * Fill range must be a comma separated list of ranges, each of which are either a 
 370  
      * lookup or numeric. Throws an IllegalArgumentException if any problems are found.
 371  
      * 
 372  
      * @param fillRangeArg The fill range value to be validated
 373  
      */
 374  
     private void validateRangeFormat(String fillRangeArg)
 375  
     {
 376  8
         final String lookupPattern = "(lookup\\(.*\\))|(\\d+)(-((lookup\\(.*\\))|(\\d+)))?";
 377  
         
 378  8
         String ranges[] = fillRangeArg.split(",");
 379  8
         List<String> errors = new ArrayList<String>();
 380  16
         for (String range : ranges)
 381  
         {
 382  8
             if (!range.trim().matches(lookupPattern))
 383  
             {
 384  4
                 errors.add(range + " is not a valid range. It must be of the form "
 385  2
                         + "0, 0-9, lookup(0) or lookup(0)-lookup(9) specifying "
 386  2
                         + "a start and optional end to the range.");
 387  
             }
 388  
         }
 389  8
         if (!errors.isEmpty())
 390  
         {
 391  2
             String err = "";
 392  6
             for (String string : errors)
 393  
             {
 394  2
                 err += "\n" + string;
 395  
             }
 396  4
             throw new IllegalArgumentException(FILL_RANGE + " value is not a comma separated String: " + fillRangeArg
 397  2
                     + err);
 398  
         }
 399  
         
 400  
         
 401  6
     }
 402  
 
 403  
     /**
 404  
      * Convert an array of strings defining fill ranges to range objects. Each string 
 405  
      * is assumed to be in the format <code>n-n</code> or the format <code>n</code> 
 406  
      * where n is a number or a lookup instruction.
 407  
      *   
 408  
      * @param rangeStrings The string fill ranges
 409  
      * @return An array of range objects
 410  
      * @throws IOException If the data cannot be read to perform a lookup
 411  
      * @throws NumberFormatException If numeric values are not valid numbers.
 412  
      */
 413  
     Range[] convertStringsToRanges(String[] rangeStrings, Variable variable, NetcdfFile file)
 414  
             throws NumberFormatException, IOException
 415  
     {
 416  46
         final String lookupPattern = "^lookup\\(.*\\)$";
 417  46
         final int lookupPrefixLen = 7;
 418  
 
 419  46
         Range[] ranges = new Range[rangeStrings.length];
 420  102
         for (int i = 0; i < rangeStrings.length; i++)
 421  
         {
 422  62
             String string = rangeStrings[i];
 423  62
             String[] digits = splitRange(string);
 424  
             int first;
 425  
             // Deal with the index lookup functions
 426  62
             if (digits[0].matches(lookupPattern))
 427  
             {
 428  24
                 first = NetCDFUtils.lookupIndex(file, variable.getDimension(i), digits[0].substring(lookupPrefixLen,
 429  12
                         digits[0].length() - 1));
 430  
             }
 431  
             else
 432  
             {
 433  50
                 first = Integer.parseInt(digits[0]);
 434  
             }
 435  58
             int last = first;
 436  58
             if (digits.length > 1)
 437  
             {
 438  46
                 if (digits[1].matches(lookupPattern))
 439  
                 {
 440  16
                     last = NetCDFUtils.lookupIndex(file, variable.getDimension(i), digits[1].substring(lookupPrefixLen,
 441  8
                             digits[1].length() - 1));
 442  
                 }
 443  
                 else
 444  
                 {
 445  38
                     last = Integer.parseInt(digits[1]);
 446  
                 }
 447  
             }
 448  
             try
 449  
             {
 450  58
                 ranges[i] = new Range(first, last, 1);
 451  
             }
 452  2
             catch (InvalidRangeException e)
 453  
             {
 454  2
                 LOG.error("Invalid range: " + string, e);
 455  2
                 throw new IllegalArgumentException("Invalid range: " + string);
 456  
             }
 457  
 
 458  
         }
 459  
 
 460  40
         return ranges;
 461  
     }
 462  
 
 463  
     /**
 464  
      * Split a range string with regard to the possibility of negative lookup values.
 465  
      * e.g. lookup(-52)-lookup(-40) get returned as lookup(-52) and lookup(-40).
 466  
      * @param rangeStr The range string to be split
 467  
      * @return The array of range start and end strings
 468  
      */
 469  
     String[] splitRange(String rangeStr)
 470  
     {
 471  64
         String safeString = rangeStr.trim().replaceAll("(\\()-([^\\)]*\\))"  , "$1~$2");
 472  64
         String[] result =  safeString.split("-");
 473  180
         for (int i = 0; i < result.length; i++)
 474  
         {
 475  116
             result[i] = result[i].trim().replaceAll("(\\()~([^\\)]*\\))"  , "$1-$2");
 476  
         }
 477  64
         return result;
 478  
     }
 479  
 
 480  
     /**
 481  
      * Produce a list of origin points, being all combinations of points in 
 482  
      * the range other than the final range. The final range will always be 
 483  
      * written as a single block. Each range represents the section of a 
 484  
      * dimension that is to be populated.
 485  
      *  
 486  
      * @param fillRanges The ranges of each dimension that is to be populated. 
 487  
      * @return A list of origin points to be written to.   
 488  
      */
 489  
     List<int[]> createOriginList(Range[] fillRanges)
 490  
     {
 491  
         // A single range is a special case, get it out of the way first
 492  42
         if (fillRanges.length == 1)
 493  
         {
 494  34
             int[] origin = new int[]{fillRanges[0].first()};
 495  34
             List<int[]> singleOrigin = new ArrayList<int[]>();
 496  34
             singleOrigin.add(origin);
 497  34
             return singleOrigin;
 498  
         }
 499  
         
 500  
         // The list of origin points for all ranges up to the current one
 501  8
         List<List<int[]>> originForRange = new ArrayList<List<int[]>>();
 502  
         
 503  
         // Create the list of all origin points for the first range
 504  8
         List<int[]> originList = new ArrayList<int[]>();
 505  28
         for (int j = fillRanges[0].first(); j <= fillRanges[0].last(); j+=fillRanges[0].stride())
 506  
         {
 507  20
             originList.add(new int[]{j});
 508  
         }
 509  8
         originForRange.add(originList);
 510  
         
 511  
         // Loop over the later ranges expanding the list of origin combinations
 512  14
         for (int rangeNum = 1; rangeNum < fillRanges.length-1; rangeNum++)
 513  
         {
 514  6
             Range currRange = fillRanges[rangeNum];
 515  
             // Grab the combo of origin points for all ranges up to the previous one
 516  6
             List<int[]> prevOrigins = originForRange.get(rangeNum-1); 
 517  6
             List<int[]> currOrigins = new ArrayList<int[]>();
 518  26
             for (int[] prevPoint : prevOrigins)
 519  
             {
 520  40
                 for (int j = currRange.first(); j <= currRange.last(); j+=currRange.stride())
 521  
                 {
 522  26
                     int[] origin = new int[rangeNum+1];
 523  52
                     for (int j2 = 0; j2 < prevPoint.length; j2++)
 524  
                     {
 525  26
                         origin[j2] = prevPoint[j2];
 526  
                     }
 527  26
                     origin[rangeNum] = j;
 528  26
                     currOrigins.add(origin);
 529  
                 }
 530  
             }
 531  
             
 532  6
             originForRange.add(currOrigins);
 533  
         }
 534  
         
 535  
         // Add the start of the last range to generated origins
 536  8
         List<int[]> origins = originForRange.get(fillRanges.length-2);
 537  8
         List<int[]> finalOrigins = new ArrayList<int[]>(origins.size());
 538  8
         int startFinalRange = fillRanges[fillRanges.length-1].first();
 539  48
         for (int[] origin : origins)
 540  
         {
 541  32
             int[] newOrigin = new int[fillRanges.length];
 542  90
             for (int i = 0; i < origin.length; i++)
 543  
             {
 544  58
                 newOrigin[i] = origin[i];
 545  
             }
 546  32
             newOrigin[fillRanges.length-1] = startFinalRange;
 547  32
             finalOrigins.add(newOrigin);
 548  
         }
 549  
         
 550  
         // Return the final entry, being the full set of combinations
 551  8
         return finalOrigins;
 552  
     }
 553  
 
 554  
     /**
 555  
      * Create an array to hold a block of data.
 556  
      * @param fillRanges The range definition
 557  
      * @param variable The variable to be written
 558  
      * @return An array suitable to load data into
 559  
      */
 560  
     Array createArray(Range[] fillRanges, Variable variable)
 561  
     {
 562  36
         int shape[] = new int[fillRanges.length];
 563  80
         for (int i = 0; i < fillRanges.length; i++)
 564  
         {
 565  44
             if (i<fillRanges.length-1)
 566  
             {
 567  8
                 shape[i] = 1;
 568  
             }
 569  
             else
 570  
             {
 571  36
                 Range lastRange = fillRanges[fillRanges.length-1]; 
 572  36
                 shape[i] = lastRange.last() - lastRange.first() + 1;
 573  
             }
 574  
         }
 575  36
         Array array = Array.factory(variable.getDataType(), shape);
 576  36
         return array;
 577  
     }
 578  
 
 579  
     /**
 580  
      * Read in data form a stream containing text.
 581  
      * @param dataIn The text stream to be read
 582  
      * @param buffer The Array to be populated
 583  
      * @throws IOException If data cannot be read from the stream
 584  
      */
 585  
     @SuppressWarnings("unchecked")
 586  
     void readData(BufferedReader dataIn, Array buffer) throws IOException
 587  
     {
 588  36
         Class cls = buffer.getElementType();// getClass();
 589  36
         boolean isFloat = cls.getName().equalsIgnoreCase("Float");
 590  36
         boolean isDouble = cls.getName().equalsIgnoreCase("Double");
 591  36
         boolean isLong = cls.getName().equalsIgnoreCase("Long");
 592  36
         boolean isInt = cls.getName().equalsIgnoreCase("Int");
 593  36
         boolean isChar = cls.getName().equalsIgnoreCase("Char");
 594  36
         boolean isByte = cls.getName().equalsIgnoreCase("Byte");
 595  36
         boolean isBoolean = cls.getName().equalsIgnoreCase("Boolean");
 596  36
         boolean isShort = cls.getName().equalsIgnoreCase("Short");
 597  
 
 598  36
         int i = 0;
 599  312
         while (i < buffer.getSize())
 600  
         {
 601  242
             String line = dataIn.readLine();
 602  242
             if (line == null)
 603  
             {
 604  2
                 String error = "Expected block of " + buffer.getSize() + " values but ran out after " + i + ".";
 605  2
                 System.err.println(error);
 606  2
                 throw new IllegalArgumentException(error);
 607  
             }
 608  240
             if (isFloat)
 609  
             {
 610  24
                 buffer.setFloat(i, Float.parseFloat(line));
 611  
             }
 612  216
             else if (isDouble)
 613  
             {
 614  40
                 buffer.setDouble(i, Double.parseDouble(line));
 615  
             }
 616  176
             else if (isLong)
 617  
             {
 618  0
                 buffer.setLong(i, Long.parseLong(line));
 619  
             }
 620  176
             else if (isInt)
 621  
             {
 622  90
                 buffer.setInt(i, Integer.parseInt(line));
 623  
             }
 624  86
             else if (isChar)
 625  
             {
 626  86
                 buffer.setChar(i, line.charAt(0));
 627  
             }
 628  0
             else if (isByte)
 629  
             {
 630  0
                 buffer.setByte(i, Byte.parseByte(line));
 631  
             }
 632  0
             else if (isBoolean)
 633  
             {
 634  0
                 buffer.setBoolean(i, Boolean.parseBoolean(line));
 635  
             }
 636  0
             else if (isShort)
 637  
             {
 638  0
                 buffer.setShort(i, Short.parseShort(line));
 639  
             }
 640  240
             i++;
 641  
         }
 642  
         
 643  34
     }
 644  
 
 645  
     /**
 646  
      * Read in data form a stream containing binary data.
 647  
      * @param dataIn The data stream to be read
 648  
      * @param buffer The Array to be populated
 649  
      * @throws IOException If data cannot be read from the stream
 650  
      */
 651  
     @SuppressWarnings("unchecked")
 652  
     void readBinaryData(DataInputStream dataIn, Array buffer) throws IOException
 653  
     {
 654  4
         Class cls = buffer.getElementType();// getClass();
 655  4
         boolean isFloat = cls.getName().equalsIgnoreCase("Float");
 656  4
         boolean isDouble = cls.getName().equalsIgnoreCase("Double");
 657  4
         boolean isLong = cls.getName().equalsIgnoreCase("Long");
 658  4
         boolean isInt = cls.getName().equalsIgnoreCase("Int");
 659  4
         boolean isChar = cls.getName().equalsIgnoreCase("Char");
 660  4
         boolean isByte = cls.getName().equalsIgnoreCase("Byte");
 661  4
         boolean isBoolean = cls.getName().equalsIgnoreCase("Boolean");
 662  4
         boolean isShort = cls.getName().equalsIgnoreCase("Short");
 663  
 
 664  4
         int i = 0;
 665  38
         while (i < buffer.getSize())
 666  
         {
 667  
             try
 668  
             {
 669  30
                 if (isFloat)
 670  
                 {
 671  20
                     buffer.setFloat(i, dataIn.readFloat());
 672  
                 }
 673  10
                 else if (isDouble)
 674  
                 {
 675  0
                     buffer.setDouble(i, dataIn.readDouble());
 676  
                 }
 677  10
                 else if (isLong)
 678  
                 {
 679  0
                     buffer.setLong(i, dataIn.readLong());
 680  
                 }
 681  10
                 else if (isInt)
 682  
                 {
 683  10
                     buffer.setInt(i, dataIn.readInt());
 684  
                 }
 685  0
                 else if (isChar)
 686  
                 {
 687  0
                     buffer.setChar(i, dataIn.readChar());
 688  
                 }
 689  0
                 else if (isByte)
 690  
                 {
 691  0
                     buffer.setByte(i, dataIn.readByte());
 692  
                 }
 693  0
                 else if (isBoolean)
 694  
                 {
 695  0
                     buffer.setBoolean(i, dataIn.readBoolean());
 696  
                 }
 697  0
                 else if (isShort)
 698  
                 {
 699  0
                     buffer.setShort(i, dataIn.readShort());
 700  
                 }
 701  
             }
 702  0
             catch (EOFException e)
 703  
             {
 704  0
                 String error = "Expected block of " + buffer.getSize() + " values but ran out after " + i + ".";
 705  0
                 System.err.println(error);
 706  0
                 throw new IllegalArgumentException(error);
 707  
             }
 708  30
             i++;
 709  
         }
 710  
 
 711  4
     }
 712  
 }