org.obiba.genobyte.cli.BitwiseCli.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.genobyte.cli.BitwiseCli.java

Source

/*******************************************************************************
 * Copyright 2007(c) Gnome Qubec. All rights reserved.
 * 
 * This file is part of GenoByte.
 * 
 * GenoByte is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * GenoByte is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 *******************************************************************************/
package org.obiba.genobyte.cli;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.obiba.bitwise.query.Query;
import org.obiba.bitwise.query.QueryParser;
import org.obiba.bitwise.query.QueryResult;
import org.obiba.bitwise.util.StringUtil;
import org.obiba.genobyte.GenotypingStore;

/**
 * Command Line Interface for manipulating {@link GenotypingStore}s.
 * <p/>
 * Used as a shell for executing {@link CliCommand}s. Custom commands may be registered through 
 * the {@link BitwiseCli#registerCommand(CliCommand)} method. Commands have a long and a short option string, 
 * the short option string must be unique. Registering two commands with the same option string will
 * throw an exception. 
 */
public class BitwiseCli {

    PrintStream output = System.out;
    InputStream input = System.in;
    HelpCommand help = new HelpCommand();
    Map<String, CliCommand> commandMap = new HashMap<String, CliCommand>();
    Options options = new Options();
    Options noStoreOptions = new Options();

    public BitwiseCli() {
        registerCommand(help);
        registerCommand(new QuitCommand());
        registerCommand(new PrintCommand());
        registerCommand(new SwitchCommand());
        registerCommand(new PrintRecordCommand());
        registerCommand(new CloseCommand());
        registerCommand(new DropCommand());
        registerCommand(new StatsCommand());
        registerCommand(new InconsistenciesCommand());
        registerCommand(new PrintHistoryCommand());
    }

    public BitwiseCli(PrintStream output) {
        this();
        this.output = output;
    }

    public BitwiseCli(PrintStream output, InputStream input) {
        this(output);
        this.input = input;
    }

    /**
     * Adds a {@link CliCommand} to the set of registered commands. If a command
     * with the same short or long option string is already registered, an IllegalArgumentException is thrown.
     * @param command the command to register.
     * @throws IllegalArgumentException when a command with the same short option string already exists.
     */
    public void registerCommand(CliCommand command) {
        Option o = command.getOption();
        if (commandMap.containsKey(o.getLongOpt())) {
            throw new IllegalArgumentException(
                    "A command with key [" + o.getLongOpt() + "] is already registered.");
        }
        if (o.getOpt() != null) {
            for (CliCommand c : commandMap.values()) {
                Option commandOption = c.getOption();
                if (commandOption.getOpt() != null && commandOption.getOpt().equals(o.getOpt())) {
                    throw new IllegalArgumentException("Illegal option [" + o.getLongOpt()
                            + "] conflicts with existing option [" + commandOption.getLongOpt() + "].");
                }
            }
        }
        commandMap.put(o.getLongOpt(), command);
        options.addOption(o);
        if (command.requiresOpenStore() == false) {
            noStoreOptions.addOption(o);
        }
    }

    /**
     * Used to prompt the user for input.
     * @param context the current context of the CLI execution.
     */
    private void prompt(CliContext context) {
        StringBuilder sb = new StringBuilder();
        if (context.getActiveRecordStore() != null) {
            sb.append(context.getActiveRecordStore().getStore().getName());
        }
        sb.append("> ");
        output.print(sb.toString());
    }

    /**
     * Starts the CLI shell.
     * 
     * @throws IOException when an error occurs while reading user input.
     */
    public void execute() throws IOException {
        CliContext context = new CliContext(this.output);
        BufferedReader br = new BufferedReader(new InputStreamReader(input));
        BasicParser bp = new BasicParser();
        output.println("Type '-h' for help, '-q' to quit.");
        while (true) {
            prompt(context);
            boolean quit = false;
            String str = br.readLine();
            CommandLine cl = null;
            try {
                if (context.getStore() == null) {
                    cl = bp.parse(noStoreOptions, str.split(" "));
                } else {
                    cl = bp.parse(options, str.split(" "));
                }
            } catch (ParseException e) {
                quit = help.execute(null, context);
            }

            if (cl != null) {
                Iterator<Option> commands = cl.iterator();
                // We don't iterate to make sure we execute only one command
                if (commands.hasNext()) {
                    Option o = commands.next();
                    CliCommand c = commandMap.get(o.getLongOpt());
                    if (c == null) {
                        throw new IllegalStateException(
                                "No CliCommand associated with option [" + o.getOpt() + "]");
                    } else {
                        try {
                            quit = c.execute(o, context);
                        } catch (ParseException e) {
                            quit = help.execute(null, context);
                        } catch (Exception e) {
                            output.println(
                                    "An unexpected error occurred while executing the command: " + e.getMessage());
                        }
                    }
                }

                // Not handled by any command: it should be a query.
                String queryString = str;
                if (context.getStore() != null && (cl.getOptions() == null || cl.getOptions().length == 0)) {
                    try {
                        QueryParser parser = new QueryParser();
                        long start = System.currentTimeMillis();
                        if (StringUtil.isEmptyString(queryString) == false) {
                            Query q = parser.parse(queryString);
                            QueryResult qr = q.execute(context.getActiveRecordStore().getStore());
                            String reference = context.addQuery(queryString, qr);
                            long end = System.currentTimeMillis();
                            output.println(reference + ": " + qr.count() + " results in " + (end - start)
                                    + " milliseconds.");
                        }
                    } catch (org.obiba.bitwise.query.UnknownFieldException e) {
                        output.println(e.getMessage());
                    } catch (org.obiba.bitwise.query.ParseException e) {
                        output.println("The query [" + queryString
                                + "] is invalid. Please refer to the query syntax for more information.");
                    } catch (Exception e) {
                        output.println(
                                "An unexpected error occurred while executing the query. The following data may be helpful to debug the problem.");
                        e.printStackTrace(output);
                    }
                }
            }
            if (quit)
                break;
        }
    }

    public class HelpCommand implements CliCommand {

        private HelpFormatter hf = new HelpFormatter();

        public boolean requiresOpenStore() {
            return false;
        }

        public boolean execute(Option opt, CliContext context) {
            PrintWriter pw = new PrintWriter(context.getOutput());
            if (context.getStore() != null) {
                hf.printOptions(pw, 100, options, 1, 4);
            } else {
                hf.printOptions(pw, 100, noStoreOptions, 1, 4);
            }
            pw.flush();
            return false;
        }

        public Option getOption() {
            return OptionBuilder.withDescription("print this help message").withLongOpt("help").create('h');
        }
    }

    public class QuitCommand implements CliCommand {

        public boolean requiresOpenStore() {
            return false;
        }

        public boolean execute(Option opt, CliContext context) {
            if (context.getStore() != null) {
                context.getStore().close();
            }
            return true;
        }

        public Option getOption() {
            return OptionBuilder.withDescription("quit").withLongOpt("quit").create('q');
        }
    }
}