Coverage Report - com.legstar.cob2xsd.Cob2XsdMain
 
Classes in this File Line Coverage Branch Coverage Complexity
Cob2XsdMain
75 %
100/133
51 %
28/54
3,526
 
 1  
 /*******************************************************************************
 2  
  * Copyright (c) 2010 LegSem.
 3  
  * All rights reserved. This program and the accompanying materials
 4  
  * are made available under the terms of the GNU Lesser Public License v2.1
 5  
  * which accompanies this distribution, and is available at
 6  
  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 7  
  * 
 8  
  * Contributors:
 9  
  *     LegSem - initial API and implementation
 10  
  ******************************************************************************/
 11  
 package com.legstar.cob2xsd;
 12  
 
 13  
 import java.io.File;
 14  
 import java.io.FileInputStream;
 15  
 import java.io.FileNotFoundException;
 16  
 import java.io.IOException;
 17  
 import java.io.InputStream;
 18  
 import java.util.Properties;
 19  
 
 20  
 import org.apache.commons.cli.CommandLine;
 21  
 import org.apache.commons.cli.CommandLineParser;
 22  
 import org.apache.commons.cli.HelpFormatter;
 23  
 import org.apache.commons.cli.Option;
 24  
 import org.apache.commons.cli.Options;
 25  
 import org.apache.commons.cli.PosixParser;
 26  
 import org.apache.commons.io.FileUtils;
 27  
 import org.apache.commons.io.FilenameUtils;
 28  
 import org.apache.commons.logging.Log;
 29  
 import org.apache.commons.logging.LogFactory;
 30  
 
 31  
 import com.legstar.antlr.RecognizerException;
 32  
 
 33  
 /**
 34  
  * COBOL structure to XML schema executable.
 35  
  * <p/>
 36  
  * This is the main class for the executable jar. It takes options from the
 37  
  * command line and calls the {@link Cob2Xsd} API.
 38  
  * <p/>
 39  
  * Usage: <code>
 40  
  * java -jar legstar-cob2xsd-x.y.z-exe.jar -i&lt;input file or folder&gt; -o&lt;output folder&gt;
 41  
  * </code>
 42  
  * 
 43  
  */
 44  6
 public class Cob2XsdMain {
 45  
 
 46  
     /** The version properties file name. */
 47  
     private static final String VERSION_FILE_NAME = "/com/legstar/cob2xsd/version.properties";
 48  
 
 49  
     /** The default input. */
 50  
     private static final String DEFAULT_INPUT_FOLDER = "cobol";
 51  
 
 52  
     /** The default output. */
 53  
     private static final String DEFAULT_OUTPUT_FOLDER = "schema";
 54  
 
 55  
     /** The default output. */
 56  
     private static final String DEFAULT_CONFIG_FILE = "conf/cob2xsd.properties";
 57  
 
 58  
     /** A file containing parameters. */
 59  
     private File _configFile;
 60  
 
 61  
     /**
 62  
      * A file or folder containing COBOL code to translate to XSD. Defaults to
 63  
      * cobol relative folder.
 64  
      */
 65  
     private File _input;
 66  
 
 67  
     /**
 68  
      * A folder containing translated XML Schema. Defaults to schema relative
 69  
      * folder.
 70  
      */
 71  
     private File _output;
 72  
 
 73  
     /**
 74  
      * With this option turned on, the target namespace from the model will be
 75  
      * appended the input file base file name.
 76  
      */
 77  
     private boolean _appendBaseFileNameToNamespace;
 78  
 
 79  
     /** Set of translation options to use. */
 80  
     private Cob2XsdModel _model;
 81  
 
 82  
     /** Line separator (OS specific). */
 83  1
     public static final String LS = System.getProperty("line.separator");
 84  
 
 85  
     /** Logger. */
 86  6
     private final Log _log = LogFactory.getLog(getClass());
 87  
 
 88  
     /**
 89  
      * @param args translator options. Provides help if no arguments passed.
 90  
      */
 91  
     public static void main(final String[] args) {
 92  0
         Cob2XsdMain main = new Cob2XsdMain();
 93  0
         main.execute(args);
 94  0
     }
 95  
 
 96  
     /**
 97  
      * Process command line options and run translator.
 98  
      * <p/>
 99  
      * If no options are passed, prints the help. Help is also printed if the
 100  
      * command line options are invalid.
 101  
      * 
 102  
      * @param args translator options
 103  
      */
 104  
     public void execute(final String[] args) {
 105  
         try {
 106  2
             Options options = createOptions();
 107  2
             if (collectOptions(options, args)) {
 108  2
                 setDefaults();
 109  2
                 loadModel();
 110  2
                 execute(getInput(), getOutput());
 111  
             }
 112  0
         } catch (Exception e) {
 113  0
             _log.error("COBOL to Xsd translation failure", e);
 114  0
             throw new RuntimeException(e);
 115  2
         }
 116  2
     }
 117  
 
 118  
     /**
 119  
      * Take arguments received on the command line and setup corresponding
 120  
      * options.
 121  
      * <p/>
 122  
      * No arguments is valid. It means use the defaults.
 123  
      * 
 124  
      * @param options the expected options
 125  
      * @param args the actual arguments received on the command line
 126  
      * @return true if arguments were valid
 127  
      * @throws Exception if something goes wrong while parsing arguments
 128  
      */
 129  
     protected boolean collectOptions(final Options options, final String[] args)
 130  
             throws Exception {
 131  6
         if (args != null && args.length > 0) {
 132  5
             CommandLineParser parser = new PosixParser();
 133  5
             CommandLine line = parser.parse(options, args);
 134  4
             return processLine(line, options);
 135  
         }
 136  1
         return true;
 137  
     }
 138  
 
 139  
     /**
 140  
      * Make sure mandatory parameters have default values.
 141  
      */
 142  
     protected void setDefaults() {
 143  2
         if (getConfigFile() == null) {
 144  0
             setConfigFile(DEFAULT_CONFIG_FILE);
 145  
         }
 146  2
         if (getInput() == null) {
 147  0
             setInput(DEFAULT_INPUT_FOLDER);
 148  
         }
 149  2
         if (getOutput() == null) {
 150  0
             setOutput(DEFAULT_OUTPUT_FOLDER);
 151  
         }
 152  2
     }
 153  
 
 154  
     /**
 155  
      * A configuration file is expected. We load it into the generator model.
 156  
      * 
 157  
      * @throws XsdGenerationException if configuration file missing or file
 158  
      *             corrupt
 159  
      */
 160  
     protected void loadModel() throws XsdGenerationException {
 161  
         try {
 162  2
             if (getConfigFile() == null) {
 163  0
                 _model = new Cob2XsdModel();
 164  
             } else {
 165  2
                 Properties config = new Properties();
 166  2
                 config.load(new FileInputStream(getConfigFile()));
 167  2
                 _model = new Cob2XsdModel(config);
 168  
             }
 169  0
         } catch (FileNotFoundException e) {
 170  0
             throw new XsdGenerationException(e);
 171  0
         } catch (IOException e) {
 172  0
             throw new XsdGenerationException(e);
 173  2
         }
 174  2
     }
 175  
 
 176  
     /**
 177  
      * @param options options available
 178  
      * @throws Exception if help cannot be produced
 179  
      */
 180  
     protected void produceHelp(final Options options) throws Exception {
 181  1
         HelpFormatter formatter = new HelpFormatter();
 182  1
         String version = getVersion();
 183  1
         formatter.printHelp(
 184  
                 "java -jar legstar-cob2xsd-"
 185  
                         + version.substring(0, version.indexOf(' '))
 186  
                         + "-exe.jar followed by:", options);
 187  1
     }
 188  
 
 189  
     /**
 190  
      * @return the command line options
 191  
      */
 192  
     protected Options createOptions() {
 193  6
         Options options = new Options();
 194  
 
 195  6
         Option version = new Option("v", "version", false,
 196  
                 "print the version information and exit");
 197  6
         options.addOption(version);
 198  
 
 199  6
         Option help = new Option("h", "help", false,
 200  
                 "print the options available");
 201  6
         options.addOption(help);
 202  
 
 203  6
         Option configFile = new Option("c", "config", true,
 204  
                 "path to configuration file");
 205  6
         options.addOption(configFile);
 206  
 
 207  6
         Option input = new Option("i", "input", true,
 208  
                 "file or folder holding the COBOL code to translate."
 209  
                         + " Name is relative or absolute");
 210  6
         options.addOption(input);
 211  
 
 212  6
         Option output = new Option("o", "output", true,
 213  
                 "folder or file receiving the translated XML schema");
 214  6
         options.addOption(output);
 215  
 
 216  6
         Option appendBaseFileNameToNamespace = new Option("a",
 217  
                 "appendBaseFileNameToNamespace", false,
 218  
                 "add input base file name to namespace");
 219  6
         options.addOption(appendBaseFileNameToNamespace);
 220  
 
 221  6
         return options;
 222  
     }
 223  
 
 224  
     /**
 225  
      * Process the command line options selected.
 226  
      * 
 227  
      * @param line the parsed command line
 228  
      * @param options available
 229  
      * @return false if processing needs to stop, true if its ok to continue
 230  
      * @throws Exception if line cannot be processed
 231  
      */
 232  
     protected boolean processLine(final CommandLine line, final Options options)
 233  
             throws Exception {
 234  4
         if (line.hasOption("version")) {
 235  0
             System.out.println("version " + getVersion());
 236  0
             return false;
 237  
         }
 238  4
         if (line.hasOption("help")) {
 239  1
             produceHelp(options);
 240  1
             return false;
 241  
         }
 242  3
         if (line.hasOption("config")) {
 243  2
             setConfigFile(line.getOptionValue("config").trim());
 244  
         }
 245  3
         if (line.hasOption("input")) {
 246  3
             setInput(line.getOptionValue("input").trim());
 247  
         }
 248  2
         if (line.hasOption("output")) {
 249  2
             setOutput(line.getOptionValue("output").trim());
 250  
         }
 251  
 
 252  2
         if (line.hasOption("appendBaseFileNameToNamespace")) {
 253  1
             _appendBaseFileNameToNamespace = true;
 254  
         }
 255  
 
 256  2
         return true;
 257  
     }
 258  
 
 259  
     /**
 260  
      * Translate a single file or all files from an input folder. Place results
 261  
      * in the output folder.
 262  
      * 
 263  
      * @param input the input COBOL file or folder
 264  
      * @param target the output folder or file where XML schema file must go
 265  
      * @throws XsdGenerationException if XML schema cannot be generated
 266  
      */
 267  
     protected void execute(final File input, final File target)
 268  
             throws XsdGenerationException {
 269  
 
 270  
         try {
 271  2
             _log.info("Started translation from COBOL to XML Schema");
 272  2
             _log.info("Taking COBOL from      : " + input);
 273  2
             _log.info("Output XML Schema to   : " + target);
 274  2
             _log.info("Options in effect      : " + getModel().toString());
 275  2
             _log.info("Append base file name  : "
 276  
                     + _appendBaseFileNameToNamespace);
 277  
 
 278  2
             if (input.isFile()) {
 279  2
                 if (FilenameUtils.getExtension(target.getPath()).length() == 0) {
 280  0
                     FileUtils.forceMkdir(target);
 281  
                 }
 282  2
                 translate(input, target);
 283  
             } else {
 284  0
                 FileUtils.forceMkdir(target);
 285  0
                 for (File cobolFile : input.listFiles()) {
 286  0
                     if (cobolFile.isFile()) {
 287  0
                         translate(cobolFile, target);
 288  
                     }
 289  
                 }
 290  
             }
 291  2
             _log.info("Finished translation");
 292  0
         } catch (IOException e) {
 293  0
             throw new XsdGenerationException(e);
 294  2
         }
 295  
 
 296  2
     }
 297  
 
 298  
     /**
 299  
      * Translates a single COBOL source file.
 300  
      * 
 301  
      * @param cobolFile COBOL source file
 302  
      * @param target target file or folder
 303  
      * @throws XsdGenerationException if parser fails
 304  
      */
 305  
     protected void translate(final File cobolFile, final File target)
 306  
             throws XsdGenerationException {
 307  
         try {
 308  2
             Cob2XsdIO cob2XsdIO = new Cob2XsdIO(getModel());
 309  2
             cob2XsdIO.translate(cobolFile, target,
 310  
                     _appendBaseFileNameToNamespace);
 311  0
         } catch (RecognizerException e) {
 312  0
             throw new XsdGenerationException(e);
 313  2
         }
 314  2
     }
 315  
 
 316  
     /**
 317  
      * Pick up the version from the properties file.
 318  
      * 
 319  
      * @return the product version
 320  
      * @throws IOException if version cannot be identified
 321  
      */
 322  
     protected String getVersion() throws IOException {
 323  1
         InputStream stream = null;
 324  
         try {
 325  1
             Properties version = new Properties();
 326  1
             stream = Cob2XsdMain.class.getResourceAsStream(VERSION_FILE_NAME);
 327  1
             version.load(stream);
 328  1
             return version.getProperty("version");
 329  
         } finally {
 330  1
             if (stream != null) {
 331  1
                 stream.close();
 332  
             }
 333  
         }
 334  
     }
 335  
 
 336  
     /**
 337  
      * @return the file containing parameters
 338  
      */
 339  
     public File getConfigFile() {
 340  6
         return _configFile;
 341  
     }
 342  
 
 343  
     /**
 344  
      * Check the config parameter and keep it only if it is valid.
 345  
      * <p/>
 346  
      * If the default properties file is not there, we run with the defaults.
 347  
      * 
 348  
      * @param config a file name (relative or absolute)
 349  
      */
 350  
     public void setConfigFile(final String config) {
 351  2
         if (config == null) {
 352  0
             throw (new IllegalArgumentException(
 353  
                     "You must provide a configuration file"));
 354  
         }
 355  2
         File file = new File(config);
 356  2
         if (file.exists()) {
 357  2
             if (file.isDirectory()) {
 358  0
                 throw new IllegalArgumentException("Folder " + config
 359  
                         + " is not a configuration file");
 360  
             }
 361  
         } else {
 362  0
             if (config.equals(DEFAULT_CONFIG_FILE)) {
 363  0
                 file = null;
 364  
             } else {
 365  0
                 throw new IllegalArgumentException("Configuration file "
 366  
                         + config + " not found");
 367  
             }
 368  
         }
 369  2
         setConfigFile(file);
 370  2
     }
 371  
 
 372  
     /**
 373  
      * @param configFile the file containing parameters to set
 374  
      */
 375  
     public void setConfigFile(final File configFile) {
 376  2
         _configFile = configFile;
 377  2
     }
 378  
 
 379  
     /**
 380  
      * Check the input parameter and keep it only if it is valid.
 381  
      * 
 382  
      * @param input a file or folder name (relative or absolute)
 383  
      */
 384  
     public void setInput(final String input) {
 385  3
         if (input == null) {
 386  0
             throw (new IllegalArgumentException(
 387  
                     "You must provide a COBOL source folder or file"));
 388  
         }
 389  3
         File file = new File(input);
 390  3
         if (file.exists()) {
 391  2
             if (file.isDirectory() && file.list().length == 0) {
 392  0
                 throw new IllegalArgumentException("Folder " + input
 393  
                         + " is empty");
 394  
             }
 395  
         } else {
 396  1
             throw new IllegalArgumentException("Input file or folder " + input
 397  
                     + " not found");
 398  
         }
 399  2
         _input = file;
 400  2
     }
 401  
 
 402  
     /**
 403  
      * Check the output parameter and keep it only if it is valid.
 404  
      * 
 405  
      * @param output a file or folder name (relative or absolute)
 406  
      */
 407  
     public void setOutput(final String output) {
 408  2
         if (output == null) {
 409  0
             throw (new IllegalArgumentException(
 410  
                     "You must provide a target directory or file"));
 411  
         }
 412  2
         _output = new File(output);
 413  2
     }
 414  
 
 415  
     /**
 416  
      * Gather all parameters into a model object.
 417  
      * 
 418  
      * @return a parameter model to be used throughout all code
 419  
      */
 420  
     public Cob2XsdModel getModel() {
 421  4
         return _model;
 422  
     }
 423  
 
 424  
     /**
 425  
      * @return the file or folder containing COBOL code to translate to XSD
 426  
      */
 427  
     public File getInput() {
 428  4
         return _input;
 429  
     }
 430  
 
 431  
     /**
 432  
      * @return the folder containing translated XML Schema
 433  
      */
 434  
     public File getOutput() {
 435  4
         return _output;
 436  
     }
 437  
 
 438  
 }