package org.skymaps; import java.io.File; import java.io.FilenameFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Formatter; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Pattern; /** * SkyMapCounter is a program designed to count the number of * files (usually sky maps and star subtraction files) associated with * standard SMEI dates in YYYY_DOY format. The counts are tallied and writted * to a tabulated text file. *

* The program has the following call syntax (from a terminal): *

*
* % java -cp /zshare/smei/java/common org.skymaps.SkyMapCounter * [Options] *
*

* Note that there may be a BASH alias for this program, such as * skymapcounter. *

*

* Options
* The following command-line options are allowed: *

*
*
*
-dest=dest_dir
*
This instructs SkyMapCounter to write the tabulated * tally information to the directory dest_dir, which must be * a valid path, either absolute or relative, to an existing directory. If * this option is not specified, then the default output directory is * $SMEIDB/cat/list.
* *
-file=filename
*
This instructs SkyMapCounter to name the tabulated * output file filename. This must be a valid file name and is * NOT meant to be a path. If filename * already exists in the output directory, then it will be overwritten when * SkyMapCounter is run (the user will be notified of this on * standard output). If this option is not specified, then the default * filename for the output file is smei_sky_cnt.txt.
* *
-help
*
This option makes SkyMapCounter display a helpful usage * message to the user on standard output.
* *
-src=source_dir
*
This instructs SkyMapCounter to begin its search in the * subdirectories of the directory source_dir, which must be a * valid path, either absolute or relative, to an existing directory. * The subdirectories that are searched within this directory are (in order) * c1, c1m0, c2, c2m0, * c3, and c3m0. If one of the subdirectories * cannot be located within the given search directory, then the user is * notified on standard output and the subdirectory is skipped. If this * option is not specified, then the default base search directory is * $SMEISKY0/sky.
*
*
* * @author Jordan T. Vaughan * @version 1.0 */ public class SkyMapCounter { // No $ permitted in Java representations of environment variables. private static final String SMEIDB_ENV_VARIABLE = "SMEIDB"; private static final String SMEISKY0_ENV_VARIABLE = "SMEISKY0"; private static final String DEFAULT_SMEISKY0_PATH = "/sky/"; private static final String DEFAULT_SMEIDB_PATH = "/cat/list/"; private static final String DEFAULT_OUTPUT_FILE = "smei_sky_cnt.txt"; /** * String array of subdirectory names. Each subdirectory in this array * is expected to be in the equ directory (star-subtracted sky map * directory) and in the unprocessed sky map directory. */ public static final String[] SUBDIRECTORIES = { "c1", "c1m0", "c2", "c2m0", "c3", "c3m0" }; private static final String DEST_OPTION = "-dest="; private static final String FILE_OPTION = "-file="; private static final String HELP_OPTION = "-help"; private static final String SRC_OPTION = "-src="; private static final String OUTPUT_HEADER_DESCRIPTION = "Contains counts of sky maps or star subtraction files based on\n" + "SMEI standard dates (YYYY_DOY).\n\n"; private static final String OUTPUT_HEADER_BASE_DIRECTORY = "Base directory for counting: "; private static final String OUTPUT_DATE_FIELD_TITLE = "date"; private static final int OUTPUT_DATE_FIELD_SIZE = 8; private static final int OUTPUT_TALLY_FIELD_SIZE = 5; private static final int OUTPUT_FIELD_SEPARATION_SIZE = 3; /** * The SkyMapCounter program begins here. The command line * options are processed before the sky map search and comparison * algorithms are run. * * @param args the command line options parsed as an array of Strings. * @since 1.0 */ public static void main(String[] args) { // Relevant local variables. File baseDirectory = null; File destinationFile = null; String destinationFileName = null; TreeMap tallies = null; TreeMap> totalTallies = new TreeMap>(); TreeSet dates = new TreeSet(); // Get all of the command line options. for(int counter = 0; counter < args.length; counter++) { if(args[counter].length() >= DEST_OPTION.length() && args[counter].substring(0, DEST_OPTION.length()). equalsIgnoreCase(DEST_OPTION)) { /* The user wants to set which directory to which the list file * will be written. */ if(destinationFile != null) { System.err.println("ERROR: " + DEST_OPTION + " specified more than once."); return; } args[counter] = args[counter].substring(DEST_OPTION.length()); if(args[counter].length() == 0) { System.err.println("ERROR: No destination directory " + "specified."); return; } destinationFile = new File(args[counter]); if(!destinationFile.exists()) { System.err.println("ERROR: Specified destination " + "directory does not exist -- " + args[counter]); return; } if(!destinationFile.isDirectory()) { System.err.println("ERROR: Specified destination " + "directory is not a directory -- " + args[counter]); return; } } else if(args[counter].length() >= FILE_OPTION.length() && args[counter].substring(0, FILE_OPTION.length()). equalsIgnoreCase(FILE_OPTION)) { // The user wants to set the name of the output file. if(destinationFileName != null) { System.err.println("ERROR: " + FILE_OPTION + " specified more than once."); return; } destinationFileName = args[counter].substring( FILE_OPTION.length()); if(destinationFileName.length() == 0) { System.err.println("ERROR: No output file name " + "specified."); return; } } else if(args[counter].equalsIgnoreCase(HELP_OPTION)) { // Display the usage message and exit. printUsageMessage(); return; } else if(args[counter].length() >= SRC_OPTION.length() && args[counter].substring(0, SRC_OPTION.length()). equalsIgnoreCase(SRC_OPTION)) { /* The user wants to set the base directory for the file * search. */ if(baseDirectory != null) { System.err.println("ERROR: " + SRC_OPTION + " specified more than once."); return; } args[counter] = args[counter].substring(SRC_OPTION.length()); if(args[counter].length() == 0) { System.err.println("ERROR: No base search directory " + "specified."); return; } baseDirectory = new File(args[counter]); if(!baseDirectory.exists()) { System.err.println("ERROR: Specified base search " + "directory does not exist -- " + args[counter]); return; } if(!baseDirectory.isDirectory()) { System.err.println("ERROR: Specified base search " + "directory is not a directory -- " + args[counter]); return; } } else { // Illegal command line option. System.err.println("ERROR: Illegal command line option -- " + args[counter]); System.err.println("Type \"java SkyMapCounter -help\" for " + "options."); return; } } // For those options not specified, use the defaults. if(baseDirectory == null) { baseDirectory = new File(System.getenv(SMEISKY0_ENV_VARIABLE) + DEFAULT_SMEISKY0_PATH); if(!baseDirectory.exists()) { System.err.println("ERROR: The base search directory $" + SMEISKY0_ENV_VARIABLE + DEFAULT_SMEISKY0_PATH + " does not exist."); return; } if(!baseDirectory.isDirectory()) { System.err.println("ERROR: The base search directory $" + SMEISKY0_ENV_VARIABLE + DEFAULT_SMEISKY0_PATH + " is not a directory."); return; } } if(destinationFile == null) { destinationFile = new File(System.getenv(SMEIDB_ENV_VARIABLE) + DEFAULT_SMEIDB_PATH); if(!destinationFile.exists()) { System.err.println("ERROR: The destination directory $" + SMEIDB_ENV_VARIABLE + DEFAULT_SMEIDB_PATH + " does not exist."); return; } if(!destinationFile.isDirectory()) { System.err.println("ERROR: The destination directory $" + SMEIDB_ENV_VARIABLE + DEFAULT_SMEIDB_PATH + " is not a directory."); return; } } if(destinationFileName == null) { destinationFileName = DEFAULT_OUTPUT_FILE; } /* Now comb through the subdirectories of the base search directory * and parse file names according to a predetermined SMEI date filter. * Tally up the files for each YYYY_DOY. */ for(int counter = 0; counter < SUBDIRECTORIES.length; counter++) { // Obtain a reference to the current subdirectory. tallies = new TreeMap(); File subdirectory = new File(baseDirectory, SUBDIRECTORIES[counter]); if(!subdirectory.exists()) { System.err.println("ERROR: Directory " + subdirectory + " does not exist. Skipping."); totalTallies.put(SUBDIRECTORIES[counter], tallies); continue; } // Get a list of all files in the subdirectory. System.out.print("Scanning directory " + subdirectory + "... "); String[] fileList = subdirectory.list( new SkyMapFilenameFilter()); for(int index = 0; index < fileList.length; index++) { String date = SkyMapFilenameFilter.extractDate( fileList[index]); if(tallies.containsKey(date)) { tallies.put(date, tallies.get(date) + 1); } else { tallies.put(date, 1); } if(!dates.contains(date)) { dates.add(date); } } totalTallies.put(SUBDIRECTORIES[counter], tallies); System.out.println("Done"); } // Open the output file for writing and write all of the entries. File outputFile = new File(destinationFile, destinationFileName); System.out.println("\nWriting tallies to file " + outputFile + "."); if(outputFile.exists() && !outputFile.canWrite()) { System.err.println("ERROR: File " + outputFile + " does not have" + " write permissions enabled. Aborting."); return; } if(outputFile.exists()) { System.out.println("NOTE: The output file " + outputFile + " already exists. Overwriting..."); } Formatter writer = null; try { writer = new Formatter(outputFile); } catch(FileNotFoundException e) { System.err.println("\nERROR: Could not write to " + outputFile + "\nAborting."); return; } writer.format(OUTPUT_HEADER_DESCRIPTION); writer.format(OUTPUT_HEADER_BASE_DIRECTORY + baseDirectory + "\n\n"); writer.format("%-" + OUTPUT_DATE_FIELD_SIZE + "s", OUTPUT_DATE_FIELD_TITLE); for(int counter = 0; counter < SUBDIRECTORIES.length; counter++) { writer.format("%" + OUTPUT_FIELD_SEPARATION_SIZE + "s%" + OUTPUT_TALLY_FIELD_SIZE + "s", " ", SUBDIRECTORIES[counter]); } writer.format("\n"); // Output each date's tallies. for(String date: dates) { writer.format("%-" + OUTPUT_DATE_FIELD_SIZE + "s", date); for(int counter = 0; counter < SUBDIRECTORIES.length; counter++) { TreeMap tallyMap = totalTallies.get(SUBDIRECTORIES[counter]); Integer value = null; if(tallyMap != null) { value = tallyMap.get(date); if(value == null) { value = 0; } } else { value = 0; } writer.format("%" + OUTPUT_FIELD_SEPARATION_SIZE + "s%" + OUTPUT_TALLY_FIELD_SIZE + "d", " ", value); } writer.format("\n"); if(writer.ioException() != null) { System.err.println("ERROR: An IO error occurred while writing"+ " tallies for the\ndate " + date + " to " + outputFile + ". Aborting."); writer.close(); return; } } writer.close(); // Success! System.out.println("\nDone!"); } /** * Prints a helpful usage message to standard output. This should only * be called when an egregious error occurs (when it's the user's fault) * or when the -help option was specified by the user. * * @since 1.0 */ public static void printUsageMessage() { System.out.println( "\nUsage:\n % java SkyMapCounter [Options]\n\n" + "Options:\n" + " -src=source_dir Specifies which directory to start " + "the search in.\n" + " The subdirectories c1, c1m0, c2, c2m0, " + "c3, and c3m0\n" + " should be locted in source_dir.\n" + " [default=$" + SMEISKY0_ENV_VARIABLE + DEFAULT_SMEISKY0_PATH + "]\n" + " -dest=dest_dir Specifies which directory to write the " + "list file\n" + " to when the counts are tallied.\n" + " [default=$" + SMEIDB_ENV_VARIABLE + DEFAULT_SMEIDB_PATH + "]\n" + " -file=filename The name of the output file that will " + "contain the sky map\n" + " counts. This is not a path.\n" + " [default=" + DEFAULT_OUTPUT_FILE + "]\n" + " -help Displays this usage message to standard" + " output.\n\n" + "Examples:\n" + " % java SkyMapCounter\n" + " Counts sky map files in the subdirectories of $" + SMEISKY0_ENV_VARIABLE + DEFAULT_SMEISKY0_PATH + "\n" + " and outputs the count results to $" + SMEIDB_ENV_VARIABLE + DEFAULT_SMEIDB_PATH + DEFAULT_OUTPUT_FILE + " (the\n" + " default output file).\n" + " % java SkyMapCounter -help\n" + " Displays this helpful usage message and exits.\n" + " % java SkyMapCounter -file=list_name.txt\n" + " Counts sky map files in the subdirectories of the default "+ "source directory\n" + " and writes the results to the file list_name.txt in the\n" + " default output directory.\n" + " % java SkyMapCounter -src=$SMEISKY0/equ -dest=$SMEIDB/fakedir"+ " -file=test.txt\n" + " Counts sky map files in the subdirectories of $SMEISKY0/" + "equ and outputs\n" + " the count results to $SMEIDB/fakedir/test.txt.\n\n"); } /* Class used to filter sky map files. This can be used flexibly to filter * ANY file, regardless of extensions and prefixes, based on the standard * SMEI date. */ private static class SkyMapFilenameFilter implements FilenameFilter { public SkyMapFilenameFilter() { } public boolean accept(File dir, String name) { if(Pattern.matches("(.*)\\d\\d\\d\\d_\\d\\d\\d_\\d\\d\\d\\d\\d\\d"+ "(.*)", name)) { return true; } return false; } public static String extractDate(String filename) { // Does the given file name even have a SMEI date in it? if(!new SkyMapFilenameFilter().accept(null, filename)) { return null; } // Go through the file name linearly and find the date. int index = 0; for(int counter = 0; counter < filename.length(); counter++) { if(Character.isDigit(filename.charAt(counter))) { // Start the extraction index afresh. index = counter; counter++; // Find the other digits. boolean continueExtraction = true; for(int subcounter = 0; subcounter < 3; subcounter++) { if(!Character.isDigit(filename.charAt(counter))) { continueExtraction = false; break; } counter++; } if(!continueExtraction) { counter = index; continue; } if(filename.charAt(counter) != '_') { counter = index; continue; } counter++; for(int subcounter = 0; subcounter < 3; subcounter++) { if(!Character.isDigit(filename.charAt(counter))) { continueExtraction = false; break; } counter++; } if(!continueExtraction) { counter = index; continue; } // Extract the substring and return it. return filename.substring(index, counter); } } // No date found. This shouldn't happen. System.err.println("ERROR: SkyMapFilenameFilter.extractDate " + "encountered an internal error and returned null."); return null; } } }