Package org.openquark.cal.services

Source Code of org.openquark.cal.services.CarTool

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* CarTool.java
* Creation date: Jan 24, 2006.
* By: Joseph Wong
*/
package org.openquark.cal.services;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openquark.cal.compiler.CompilerMessage;
import org.openquark.cal.compiler.MessageLogger;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.Version;
import org.openquark.cal.machine.StatusListener;
import org.openquark.util.FileSystemHelper;
import org.openquark.util.SimpleConsoleHandler;


/**
* This class provides a command line interface to the CAL Archive (Car) building tool.
*
* This class is not meant to be instantiated or subclassed.
*
* @author Joseph Wong
*/
public final class CarTool {
   
    /**
     * Encapsulates the command line argument list.
     *
     * @author Joseph Wong
     */
    private static class ArgumentList {
       
        /**
         * Exception for representing that there are not enough arguments supplied.
         *
         * @author Joseph Wong
         */
        private static class NotEnoughArgumentsException extends Exception {
           
            private static final long serialVersionUID = -5402072553834237273L;

            /** Constructs an instance of this exception. */
            private NotEnoughArgumentsException() {}
        }
       
        /**
         * Exception for representing that there are too many arguments supplied.
         *
         * @author Joseph Wong
         */
        private static class TooManyArgumentsException extends Exception {
           
            private static final long serialVersionUID = 1154558440221248070L;

            /** Constructs an instance of this exception. */
            private TooManyArgumentsException() {}
        }
       
        /** The arguments in a List. */
        private final List<String> argsList;
       
        /**
         * Constructs an ArgumentList.
         * @param args the arguments to be encapsulated.
         */
        private ArgumentList(String[] args) {
            argsList = new ArrayList<String>();
           
            // we drop all empty string command-line arguments
            // (these can be supplied on unix platforms via quoted empty strings e.g. '')
            for (final String element : args) {
                if (element.length() > 0) {
                    argsList.add(element);
                }
            }
        }
       
        /**
         * @return the current argument being examined.
         * @throws NotEnoughArgumentsException
         */
        private String getArgument() throws NotEnoughArgumentsException {
            if (argsList.isEmpty()) {
                throw new NotEnoughArgumentsException();
            } else {
                return argsList.get(0);
            }
        }
       
        /**
         * Removes the current argument from the list.
         * @throws NotEnoughArgumentsException
         */
        private void consumeArgument() throws NotEnoughArgumentsException {
            if (argsList.isEmpty()) {
                throw new NotEnoughArgumentsException();
            } else {
                argsList.remove(0);
            }
        }
       
        /**
         * Returns the current argument and removes it from the list.
         * @return the current argument, which is removed.
         * @throws NotEnoughArgumentsException
         */
        private String getAndConsumeArgument() throws NotEnoughArgumentsException {
            if (argsList.isEmpty()) {
                throw new NotEnoughArgumentsException();
            } else {
                return argsList.remove(0);
            }
        }
       
        /**
         * @return an Iterator for the remaining arguments.
         */
        private Iterator<String> getRemainingArgumentsIterator() {
            return argsList.iterator();
        }
       
        /**
         * @return a duplicate of this argument list.
         */
        private ArgumentList duplicate() {
            return new ArgumentList(argsList.toArray(new String[argsList.size()]));
        }
       
        /**
         * Verifies that there are no more arguments in the list, or throws an exception otherwise.
         * Don't use this if {@link #getRemainingArgumentsIterator} is used to fetch the trailing
         * variadic argument list.
         *
         * @throws TooManyArgumentsException
         */
        private void noMoreArgumentsAllowed() throws TooManyArgumentsException {
            if (!argsList.isEmpty()) {
                throw new TooManyArgumentsException();
            }
        }
    }
   
    /**
     * Exception for representing that the operation of building Cars has failed.
     *
     * @author Joseph Wong
     */
    private static class OperationFailedException extends Exception {
       
        private static final long serialVersionUID = 1248202291878880653L;

        /** Constructs an instance of this exception. */
        private OperationFailedException() {}
    }
   
    /** Private constructor. */
    private CarTool() {}

    /**
     * Runs the Car building tool with the given command line arguments.
     * @param args the command line arguments.
     * @return true if the operation succeeds; false otherwise.
     */
    private boolean runWithCommandLine(String[] args) {
       
        ////
        /// Set up a logger for printing status messages to standard output
        //
       
        Logger logger = Logger.getLogger(getClass().getName());
        logger.setLevel(Level.FINEST);
        logger.setUseParentHandlers(false);
       
        SimpleConsoleHandler handler = new SimpleConsoleHandler();
        handler.setLevel(Level.FINE);
        logger.addHandler(handler);

        logger.info("Car Tool      Version " + Version.CURRENT + "      (c) 2006 Business Objects");

        try {
            return runWithCommandLineAndLogger(new ArgumentList(args), logger);
        } catch (ArgumentList.NotEnoughArgumentsException e) {
            logger.info("Not enough arguments.");
            logger.info(getUsage(getClass()));
            return false;
        } catch (ArgumentList.TooManyArgumentsException e) {
            logger.info("Too many arguments.");
            logger.info(getUsage(getClass()));
            return false;
        }
    }

    /**
     * Runs the Car building tool with the given command line arguments and logger.
     * @param arguments the command line arguments.
     * @param logger the logger to use for logging output.
     * @return true if the operation succeeds; false otherwise.
     */
    private boolean runWithCommandLineAndLogger(ArgumentList arguments, final Logger logger) throws ArgumentList.NotEnoughArgumentsException, ArgumentList.TooManyArgumentsException {
       
        boolean success = true;
       
        if (arguments.getArgument().equals("-unsource")) {
            arguments.consumeArgument();
           
            List<String> carOrCarJarsList = new ArrayList<String>();
           
            if (arguments.getArgument().equals("-incars")) {
                arguments.consumeArgument();
               
                while (!arguments.getArgument().equals("--")) {
                    carOrCarJarsList.add(arguments.getAndConsumeArgument());
                }
                arguments.consumeArgument(); // consume the -- delimiter
           
            } else {
                carOrCarJarsList.add(arguments.getAndConsumeArgument());
            }
       
            if (arguments.getArgument().equals("-d")) {
                arguments.consumeArgument();
               
                String outputFolderString = arguments.getAndConsumeArgument();
               
                arguments.noMoreArgumentsAllowed();

                for (final String carOrCarJarPath : carOrCarJarsList) {
                    try {
                        unsourceCar(carOrCarJarPath, outputFolderString, logger);
                    } catch (OperationFailedException e) {
                        success = false;
                    }
                }
               
            } else {
                success = false;
            }
           
        } else if (arguments.getArgument().equals("-multi")) {
            arguments.consumeArgument();

            List<String> cwsNames = new ArrayList<String>();
           
            while (!arguments.getArgument().equals("--")) {
                cwsNames.add(arguments.getAndConsumeArgument());
            }
            arguments.consumeArgument(); // consume the -- delimiter

            ArgumentList origArgs = arguments;
           
            Set<String> carsAlreadyBuilt = new HashSet<String>();
           
            for (int i = 0, n = cwsNames.size(); i < n; i++) {
                final String workspaceDeclarationName = cwsNames.get(i);
               
                final BasicCALServices calServices = BasicCALServices.make(workspaceDeclarationName);
               
                arguments = origArgs.duplicate();
               
                try {
                    String[] carsBuilt = helpRun(arguments, logger, workspaceDeclarationName, calServices, carsAlreadyBuilt, true);

                    carsAlreadyBuilt.addAll(Arrays.asList(carsBuilt));
                   
                } catch (OperationFailedException e) {
                    success = false;
                }
            }
           
        } else {
            final String workspaceDeclarationName = arguments.getAndConsumeArgument();
           
            final BasicCALServices calServices = BasicCALServices.make(workspaceDeclarationName);

            try {
                helpRun(arguments, logger, workspaceDeclarationName, calServices, Collections.<String>emptySet(), false);
            } catch (OperationFailedException e) {
                success = false;
            }
        }
       
        return success;
    }

    /**
     * Helper method for generating a car (or car jar) without source, given the car with source.
     * @param carOrCarJarPath the path to the car or car jar.
     * @param outputFolderString the path to the output folder
     */
    private void unsourceCar(String carOrCarJarPath, String outputFolderString, Logger logger) throws OperationFailedException {
        File outputFolder = new File(outputFolderString);
       
        if (!outputFolder.isDirectory()) {
            if (!FileSystemHelper.ensureDirectoryExists(outputFolder)) {
                logger.info("Could not create folder: " + outputFolder);
                throw new OperationFailedException();
            }
        }
       
        try {
            CarUnsourcer.unsourceCar(carOrCarJarPath, outputFolderString, logger);
       
        } catch (IOException e) {
            logger.info("Error occurred generating output file: " + e.getMessage());
            throw new OperationFailedException();
        }
       
    }

    /**
     * Helper method for running the Car building tool.
     *
     * @param arguments the remaining command line arguments.
     * @param logger the logger to use for logging output.
     * @param workspaceDeclarationName the name of the workspace declaration to process.
     * @param calServices the associated copy of BasicCALServices initialized with the named workspace.
     * @param additionalCarsToExclude names of Car files to exclude from being generated.
     * @param isMulti whether the -multi option specified.
     * @return an array of the names of the Car files built.
     * @throws ArgumentList.TooManyArgumentsException
     */
    private String[] helpRun(ArgumentList arguments, final Logger logger, final String workspaceDeclarationName, final BasicCALServices calServices, Set<String> additionalCarsToExclude, boolean isMulti) throws ArgumentList.NotEnoughArgumentsException, ArgumentList.TooManyArgumentsException, OperationFailedException {
        ////
        /// Create and compile a workspace through a BasicCALServices
        //
       
        WorkspaceManager workspaceManager = calServices.getWorkspaceManager();
       
        MessageLogger msgLogger = new MessageLogger();
       
        StatusListener.StatusListenerAdapter statusListener = new StatusListener.StatusListenerAdapter() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void setModuleStatus(StatusListener.Status.Module moduleStatus, ModuleName moduleName) {
                if (moduleStatus == StatusListener.SM_GENCODE) {
                    logger.fine("Compiling " + moduleName);
                }
            }
        };
       
        logger.info("Compiling CAL workspace...");
        calServices.compileWorkspace(statusListener, msgLogger);
       
        if (msgLogger.getNErrors() == 0) {
            logger.info("Compilation successful");
           
            ////
            /// We only run the CarBuilder if everything compiled properly without errors
            //
           
            CarBuilder.Monitor monitor = new CarBuilder.Monitor() {
                public boolean isCanceled() {
                    return false;
                }
                public void operationStarted(int nCars, int nTotalModules) {}
                public void showMessages(String[] messages) {
                    for (final String element : messages) {
                        logger.info(element);
                    }
                }
                public void carBuildingStarted(String carName, int nModules) {
                    logger.info("Creating the Car file " + carName);
                }
                public void processingModule(String carName, ModuleName moduleName) {
                    logger.info("Adding module " + moduleName + " to the Car...");
                }
                public void carBuildingDone(String carName) {
                    logger.info("Done creating the Car file " + carName);
                }
                public void operationDone() {
                    logger.info("All done");
                }
            };
           
            ///
            // Command line syntax:
            //
            // [-notVerbose] [-keepsource] [-nocws | -nosuffix] [-s] [-excludeCarsInDirs ... --] [-excludeCarJarsInDirs ... --] [-jar] outputDirectory [carSpecName1 carSpecName2...]
            // OR
            // [-notVerbose] [-keepsource] [-nocws | -nosuffix] [-s] [-excludeCarsInDirs ... --] [-excludeCarJarsInDirs ... --] [-jar] -d outputDirectory
           
            boolean verboseMode = true; // default: verbose
            if (arguments.getArgument().equals("-notVerbose")) {
                verboseMode = false;
                arguments.consumeArgument();
            }
           
            boolean shouldBuildSourcelessModules = true; // default: generate sourceless modules
            if (arguments.getArgument().equals("-keepsource")) {
                shouldBuildSourcelessModules = false;
                arguments.consumeArgument();
            }
           
            boolean shouldGenerateCorrespWorspaceDecl = true; // default: generated the cws files
            boolean noCarSuffixInWorkspaceDeclName = false;   // default: generated cws files end with .car.cws
           
            if (arguments.getArgument().equals("-nocws")) {
                shouldGenerateCorrespWorspaceDecl = false;
                arguments.consumeArgument();
            } else if (arguments.getArgument().equals("-nosuffix")) {
                noCarSuffixInWorkspaceDeclName = true;
                arguments.consumeArgument();
            }
           
            boolean shouldSkipModulesAlreadyInCars = false; // default: do not skip modules already in Cars
            if (arguments.getArgument().equals("-s")) {
                shouldSkipModulesAlreadyInCars = true;
                arguments.consumeArgument();
            }
           
            Set<String> carsToExclude = new HashSet<String>(additionalCarsToExclude);
            if (arguments.getArgument().equals("-excludeCarsInDirs")) {
                arguments.consumeArgument();
               
                while (!arguments.getArgument().equals("--")) {
                    File directory = new File(arguments.getAndConsumeArgument(), "Car");
                   
                    File[] files = directory.listFiles();
                    if (files == null) {
                        logger.warning("Folder does not exist: " + directory);
                    } else {
                        for (final File element : files) {
                            carsToExclude.add(element.getName());
                        }
                    }
                }
                arguments.consumeArgument(); // consume the -- delimiter
            }
           
            if (arguments.getArgument().equals("-excludeCarJarsInDirs")) {
                arguments.consumeArgument();
               
                while (!arguments.getArgument().equals("--")) {
                    File directory = new File(arguments.getAndConsumeArgument());
                   
                    File[] files = directory.listFiles();
                    if (files == null) {
                        logger.warning("Folder does not exist: " + directory);
                    } else {
                        for (final File element : files) {
                            String fileName = element.getName();
                            if (fileName.endsWith(CarBuilder.DOT_CAR_DOT_JAR)) {
                                String carName = fileName.substring(0, fileName.length() - 4); // chop ".jar" off the end
                                carsToExclude.add(carName);
                            }
                        }
                    }
                }
                arguments.consumeArgument(); // consume the -- delimiter
            }
           
            boolean shouldGenerateCarJarSuffix = false; // default: generate Cars and not Car-jars.
            if (arguments.getArgument().equals("-jar")) {
                shouldGenerateCarJarSuffix = true;
                arguments.consumeArgument();
            }
           
            if (verboseMode) {
                System.out.println();
                System.out.println(workspaceManager.getDebugInfo());
                System.out.println();
            }
           
            CarBuilder.BuilderOptions options = new CarBuilder.BuilderOptions(
                shouldSkipModulesAlreadyInCars,
                shouldGenerateCorrespWorspaceDecl,
                noCarSuffixInWorkspaceDeclName,
                shouldBuildSourcelessModules,
                carsToExclude,
                shouldGenerateCarJarSuffix);
               
            if (arguments.getArgument().equals("-d")) {
                // the user wants one Car per workspace declaration file
               
                arguments.consumeArgument();
               
                final String outputDir = arguments.getAndConsumeArgument();
               
                arguments.noMoreArgumentsAllowed();
               
                final File outputDirectory = new File(outputDir);
               
                try {
                    String[] carsBuilt = CarBuilder.buildOneCarPerWorkspaceDeclaration(workspaceManager, monitor, outputDirectory, options);
                    return carsBuilt;
                   
                } catch (IOException e) {
                    logger.info("Error occurred generating the Car files: " + e.getMessage());
                    throw new OperationFailedException();
                }
               
               
            } else {
                // the user wants just one Car for all modules in the workspace
                if (!carsToExclude.equals(additionalCarsToExclude)) {
                    logger.info("The option -excludeCarsInDirs does not apply for building just one Car. Ignoring...");
                }
               
                final String carName = CarBuilder.makeCarNameFromSourceWorkspaceDeclName(workspaceDeclarationName);
                   
                final String outputDir = arguments.getAndConsumeArgument();
               
                final File outputDirectory = new File(outputDir);
               
                CarBuilder.Configuration config =
                    CarBuilder.Configuration.makeConfigOptionallySkippingModulesAlreadyInCars(workspaceManager, options);
               
                for (Iterator<String> it = arguments.getRemainingArgumentsIterator(); it.hasNext(); ) {
                    String carSpecName = it.next();
                    File specFile = new File(carSpecName);
                    config.addFileBasedCarSpec(specFile.getName(), specFile);
                }
               
                config.setMonitor(monitor);
               
                try {
                    boolean notCanceled = CarBuilder.buildCar(config, outputDirectory, carName, options);
                    if (notCanceled) {
                        return new String[] {carName};
                    }
                   
                } catch (IOException e) {
                    logger.info("Error occurred generating the Car file: " + e.getMessage());
                    throw new OperationFailedException();
                }
            }
           
        } else {
            logger.severe("Compilation failed:");
            List<CompilerMessage> messages = msgLogger.getCompilerMessages();
            for (int i = 0, n = messages.size(); i < n; i++) {
                logger.info("  " + messages.get(i).toString());
            }
           
            throw new OperationFailedException();
        }
       
        return new String[0];
    }
   
    /** The main method */
    public static void main(String[] args) {
        boolean success = new CarTool().runWithCommandLine(args);
        if (!success) {
            System.err.println("Car Tool - Car building failed.");
            System.exit(1);
        }
    }
   
    /**
     * @return the usage string.
     */
    private static String getUsage(Class<? extends CarTool> mainClass) {
        String usage =
            "Usage 1 - to generate a single Car file per source workspace declaration specified:\n" +
            "java " + mainClass.getName() + " [workspaceDeclaration | -multi workspaceDeclaration ... --] [-notVerbose] [-keepsource] [-nocws | -nosuffix] [-s] [-excludeCarsInDirs dirName ... --] [-excludeCarJarsInDirs dirName ... --] [-jar] outputDirectory [specFileName ...]\n" +
            "\n" +
            "Where:\n" +
            "  workspaceDeclaration - the name of the workspace declaration file, e.g. everything.default.cws\n" +
            "  -multi               - if specified, then the generated Cars will be based on the set of workspace declaration files between -multi and --.\n" +
            "  -notVerbose          - if specified, then additional diagnostic information will not be displayed.\n" +
            "  -keepsource          - if specified, then the generated Car will contain the full source of the modules (versus having sourceless modules with empty CAL file stubs).\n" +
            "  -nocws               - if specified, then a new workspace declaration file referencing the output Car will not be generated.\n" +
            "  -nosuffix            - if specified, then the workspace declaration file generated will end simply with .cws rather than .car.cws.\n" +
            "  -s                   - if specified, then the modules that come from Cars will be skipped over in the output Car.\n" +
            "  -excludeCarsInDirs   - if specified, then the Cars found in the 'Car' subdirectories of the listed directories will not be generated.\n" +
            "  -excludeCarJarsInDirs- if specified, then the Car-jars found in the listed directories will not be generated.\n" +
            "  -jar                 - if specified, then the output will be in the form of Car-jars.\n" +
            "  outputDirectory      - the name of the output directory to which the single Car file will be generated (under a 'Car' subfolder).\n" +
            "                         and also the corresponding workspace declaration if requested (under a 'Workspace Declarations' subfolder).\n" +
            "  specFileName         - the name of an additional Car workspace spec file\n" +
            "\n" +
            "Usage 2 - to generate multiple Car files - one Car file per workspace declaration that is imported:\n" +
            "java " + mainClass.getName() + " [workspaceDeclaration | -multi workspaceDeclaration ... --] [-notVerbose] [-keepsource] [-nocws | -nosuffix] [-s] [-excludeCarsInDirs dirName ... --] [-excludeCarJarsInDirs dirName ... --] [-jar] -d outputDirectory\n" +
            "\n" +
            "Where:\n" +
            "  workspaceDeclaration - the name of the workspace declaration file, e.g. everything.default.cws\n" +
            "  -multi               - if specified, then the generated Cars will be based on the set of workspace declaration files between -multi and --.\n" +
            "  -notVerbose          - if specified, then additional diagnostic information will not be displayed.\n" +
            "  -keepsource          - if specified, then the generated Cars will contain the full source of the modules (versus having sourceless modules with empty CAL file stubs).\n" +
            "  -nocws               - if specified, then a new workspace declaration file will not be generated for each output Car.\n" +
            "  -nosuffix            - if specified, then the workspace declaration files generated will end simply with .cws rather than .car.cws.\n" +
            "  -s                   - if specified, then the modules that come from Cars will be skipped over in the output Cars.\n" +
            "  -excludeCarsInDirs   - if specified, then the Cars found in the 'Car' subdirectories of the listed directories will not be generated.\n" +
            "  -excludeCarJarsInDirs- if specified, then the Car-jars found in the listed directories will not be generated.\n" +
            "  -jar                 - if specified, then the output will be in the form of Car-jars.\n" +
            "  outputDirectory      - the name of the output directory to which one Car file per workspace declaration file will be generated (under a 'Car' subfolder)\n" +
            "                         and also the corresponding workspace declarations if requested (under a 'Workspace Declarations' subfolder).\n" +
            "\n" +
            "Usage 3 - to remove source from an existing car or carjar file. \n" +
            "java " + mainClass.getName() + " -unsource [carOrCarJar | -incars carOrCarJar ... --] -d outputDirectory\n" +
            "\n" +
            "Where:\n" +
            "  -incars              - if specified, then the generated Cars (or car jars) will be based on the set of input files between -incars and --.\n" +
            "  carOrCarJar          - the path to the car or carjar file, e.g. \"c:\\\\everything.default.car\"\n" +
            "  outputDirectory      - the name of the output directory to the output Car files will be generated\n" +
            "\n";
        return usage;
    }
}
TOP

Related Classes of org.openquark.cal.services.CarTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.