All Downloads are FREE. Search and download functionalities are using the official Maven repository.

deepdiff.app.DeepDiff Maven / Gradle / Ivy

There is a newer version: 1.0.2
Show newest version
/*
 * Copyright 2011 DeepDiff Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package deepdiff.app;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import deepdiff.core.DiffPointProcessor;
import deepdiff.core.DiffPointProcessorFactory;
import deepdiff.core.DiffScope;
import deepdiff.core.DiffUnitProcessor;
import deepdiff.core.DiffUnitProcessorFactory;

/**
 * Main class for the DeepDiff application. This class handles parsing command-line options, loads
 * the configuration file, and calls the configured processors on the specified scopes.
 */
public class DeepDiff {
    private static final Logger log = Logger.getLogger(DeepDiff.class);

    private static final String COMMAND_NAME = "deepdiff";
    private static final String USAGE = COMMAND_NAME
            + " [-h | -f  | -s ] ...";

    /** Command-line option to specify the configuration file to use */
    @SuppressWarnings("static-access")
    static final Option O_FILE = OptionBuilder.hasArg().withArgName("file")
            .withDescription("load configuration from file").create("f");
    /** Command-line option to output sample file */
    @SuppressWarnings("static-access")
    static final Option O_SAMPLE = OptionBuilder.hasArg().withArgName("file")
            .withDescription("output sample configuration file").create("s");
    /** Command-line option to display help */
    @SuppressWarnings("static-access")
    static final Option O_HELP = OptionBuilder.withDescription("display help information").create(
            "h");

    private static final Options CONFIGURED_OPTIONS = new Options();

    private static final String DEFAULT_CONFIG = "/default.conf.xml";

    static {
        // Initialize the options object
        OptionGroup opts = new OptionGroup();
        opts.addOption(O_FILE);
        opts.addOption(O_SAMPLE);
        opts.addOption(O_HELP);
        CONFIGURED_OPTIONS.addOptionGroup(opts);
    }

    /**
     * Entry point for the application
     * 
     * @param args the command-line arguments
     */
    public static void main(String[] args) {
        CommandLine cl = parseCommandLine(args);
        if (cl != null) { // Valid args
            processCommand(cl);
        } else { // Invalid args
            System.exit(-1);
        }
    }

    /**
     * Parses a command-line
     * 
     * @param args the command-line arguments
     * 
     * @return the parsed command-line
     */
    static CommandLine parseCommandLine(String[] args) {
        CommandLineParser parser = new GnuParser();
        CommandLine cl = null;
        try {
            cl = parser.parse(CONFIGURED_OPTIONS, args);
        } catch (ParseException pe) {
            System.err.println("Invalid command-line options: " + pe.getMessage());
            System.err.println();
            showUsage(new PrintWriter(System.err), CONFIGURED_OPTIONS);
        }
        return cl;
    }

    /**
     * Process the command. Load the specified configuration file, and process scopes as specified
     * within it.
     * 
     * @param cl the commmand line options
     */
    private static void processCommand(CommandLine cl) {
        InputSource configSource = null;

        log.debug("Processing command-line options...");
        Option[] opts = cl.getOptions();
        for (int i = 0; i < opts.length; i++) {
            Option opt = opts[i];
            if (O_HELP.getOpt().equals(opt.getOpt())) {
                showUsage(new PrintWriter(System.out), CONFIGURED_OPTIONS);
            } else if (O_SAMPLE.getOpt().equals(opt.getOpt())) {
                String filePath = opt.getValue();
                try {
                    copy(getDefaultConfigStream(), new File(filePath));
                    log.info("Wrote sample configuration file to " + filePath);
                    return;
                } catch (IOException ex) {
                    log.fatal("Failed to write sample configuration file to " + filePath);
                    System.exit(-1);
                }
            } else if (O_FILE.getOpt().equals(opt.getOpt())) {
                if (configSource == null) {
                    String filePath = opt.getValue();
                    File configFile = new File(filePath);
                    if (!configFile.exists()) {
                        log.fatal("Specified configuration file does not exist: " + filePath);
                        System.exit(-1);
                    }
                    try {
                        configSource = new InputSource(new FileReader(configFile));
                    } catch (IOException ex) {
                        log.fatal("Failed to read configuration file from " + filePath);
                        System.exit(-1);
                    }
                } else {
                    log.fatal("Multiple -" + opt.getOpt() + " options specified");
                    PrintWriter pw = new PrintWriter(System.err);
                    showUsage(pw, CONFIGURED_OPTIONS);
                    System.exit(-1);
                }
            } else {
                log.warn("Unknown option: " + opt.getOpt());
            }
        }
        log.debug("Done processing command-line options");

        if (configSource == null) { // Use default configuration
            configSource = new InputSource(getDefaultConfigStream());
        }

        ConfigHandler configHandler = new ConfigHandler();
        Collection scopes = new LinkedList();
        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();
            parser.parse(configSource, configHandler);
            scopes.addAll(configHandler.getScopes());
        } catch (SAXException saxe) {
            log.fatal("Failure reading config", saxe);
            System.exit(-1);
        } catch (ParserConfigurationException pce) {
            log.fatal("Failure reading config", pce);
            System.exit(-1);
        } catch (IOException ioe) {
            log.fatal("Failure reading config", ioe);
            System.exit(-1);
        }

        DiffUnitProcessor unitProcessor = DiffUnitProcessorFactory.getDefault();
        DiffPointProcessor pointProcessor = DiffPointProcessorFactory.getDefault();
        if (unitProcessor == null) {
            log.fatal("No unit processor configured; exiting");
            System.exit(-1);
        }
        if (pointProcessor == null) {
            log.fatal("No point processor configured; exiting");
            System.exit(-1);
        }

        List argList = cl.getArgList();
        if (argList.size() % 2 != 0) {
            log.fatal("Comparison scopes specified as command-line arguments must be in pairs.");
            System.exit(-1);
        }
        for (int i = 0; i < argList.size(); i += 2) {
            File root1 = new File((String) argList.get(i));
            File root2 = new File((String) argList.get(i + 1));
            DiffScope scope = DiffUnitProcessorFactory.createInitialScope(root1, root2);
            scopes.add(scope);
        }

        if (scopes.isEmpty()) {
            log.warn("No scopes found");
        } else {
            int i = 0;
            for (Iterator it = scopes.iterator(); it.hasNext();) {
                i++;
                DiffScope scope = it.next();
                log.info("Scanning scope " + i + "/" + scopes.size() + ": " + scope.getPath());
                scope.scan(unitProcessor, pointProcessor);
            }
            log.info("Completed scanning");
        }
    }

    private static InputStream getDefaultConfigStream() {
        return DeepDiff.class.getResourceAsStream(DEFAULT_CONFIG);
    }

    /**
     * Shows usage information for the application
     * 
     * @param pw the PrintWriter to show the usage information on
     * @param options the supported options
     */
    private static void showUsage(PrintWriter pw, Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(pw, 80, USAGE,
                "Files or directories to compare should be specified in pairs.", options, 1, 3, "",
                false);
        pw.flush();
    }

    private static void copy(InputStream inStream, File outFile) throws IOException {
        ReadableByteChannel inChannel = Channels.newChannel(inStream);
        try {
            FileOutputStream os = new FileOutputStream(outFile);
            try {
                WritableByteChannel outChannel = os.getChannel();
                try {
                    copy(inChannel, outChannel);
                } finally {
                    outChannel.close();
                }
            } finally {
                os.close();
            }
        } finally {
            inChannel.close();
        }
    }

    private static void copy(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(4 * 1024);
        while (src.read(buffer) != -1) {
            buffer.flip();
            dest.write(buffer);
            buffer.compact();
        }
        buffer.flip();
        while (buffer.hasRemaining()) {
            dest.write(buffer);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy