public abstract class AbstractCommand {
    protected final org.jsoftware.dbpatch.log.Log log = LogFactory.getInstance();
    protected final EnvSettings envSettings;
    private File configFile;
    protected ConfigurationEntry configurationEntry;
    protected File directory;

    protected AbstractCommand(EnvSettings envSettings) {
        this.envSettings = envSettings;
    }

    public void setConfigFile(File configFile) {
        this.configFile = configFile;
    }

    public void setConf(ConfigurationEntry conf) {
        this.configurationEntry = conf;
    }

    public void setDirectory(File directory) {
        this.directory = directory;
    }

    public File getConfigFile() {
        if (configFile != null) {
            return configFile;
        }
        String cFile = System.getProperty(envSettings.getDbPatchFile());
        if (cFile != null) {
            return new File(cFile);
        } else {
            return null;
        }
    }

    public ConfigurationEntry getConf() {
        return configurationEntry;	/* findBugs ok - unwritten field (maven writes it) */
    }


    public abstract void execute() throws CommandExecutionException, CommandFailureException;

} public class CommandExecutionException extends Exception {
    public CommandExecutionException(String message) {
        super(message);
    }

    public CommandExecutionException(String message, Throwable init) {
        super(message);
        initCause(init);
    }
} public class CommandFailureException extends Exception {
    private final Object source;
    private final String messageString, descriptionString;

    public CommandFailureException(Object source, String message, String description) {
        super(message + " - " + description);
        this.source = source;
        this.messageString = message;
        this.descriptionString = description;
    }

    public CommandFailureException(String message) {
        super(message);
        this.messageString = message;
        this.source = null;
        this.descriptionString = null;
    }

    public Object getSource() {
        return source;
    }

    public String getMessageString() {
        return messageString;
    }

    public String getDescriptionString() {
        return descriptionString;
    }
} public class HelpCommand extends AbstractCommand {
    private static final Pattern TASK_PREFIX_PATTERN = Pattern.compile("\\[prefix:task\\]");
    private static final Pattern PROPERTY_PREFIX_PATTERN = Pattern.compile("\\[prefix:property\\]");
    private final String taskPrefix, propertyPrefix;
    private final String commandTitle;

    private HelpCommand(EnvSettings envSettings, String taskPrefix, String propertyPrefix, String commandTitle) {
        super(envSettings);
        this.taskPrefix = taskPrefix;
        this.propertyPrefix = propertyPrefix;
        this.commandTitle = commandTitle;
    }

    public void execute() throws CommandExecutionException {
        execute(System.out);
    }

    void execute(PrintStream printStream) throws CommandExecutionException {
        InputStream in = getClass().getResourceAsStream("/dbpatch-help.txt");
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
            String s;
            while ((s = br.readLine()) != null) {
                s = replace(s);
                printStream.println(s);
            }
        } catch (IOException e) {
            throw new CommandExecutionException(e.getMessage(), e);
        } finally {
            CloseUtil.close(br);
            CloseUtil.close(in);
        }
    }

    private String replace(String textInput) {
        String text = textInput;
        text = TASK_PREFIX_PATTERN.matcher(text).replaceAll(taskPrefix);
        text = PROPERTY_PREFIX_PATTERN.matcher(text).replaceAll(propertyPrefix);
        text = text.replace("[commands-title]", commandTitle);
        return text;
    }

    public static HelpCommand helpCommandGradle() {
        return new HelpCommand(EnvSettings.gradle(), "dbpatch-", "gradle.", "Tasks");
    }

    public static HelpCommand helpCommandMaven() {
        return new HelpCommand(EnvSettings.maven(), "dbpatch:", "maven.", "Goals");
    }

    public static HelpCommand helpCommandStandalone() {
        return new HelpCommand(EnvSettings.standalone(), "", "", "Commands");
    }

} public class HelpParseCommand extends AbstractCommand {

    public HelpParseCommand(EnvSettings envSettings) {
        super(envSettings);
    }

    @Override
    public void execute() throws CommandExecutionException, CommandFailureException {
        String file = System.getProperty(envSettings.getDbPatchFile());
        if (file == null) {
            throw new CommandFailureException("Set system property \"" + envSettings.getDbPatchFile() + "\" to file you want to parse.");
        }
        execute(System.out, new File(file));
    }

    void execute(PrintStream out, File patchFile) throws CommandExecutionException, CommandFailureException {
        if (!patchFile.exists()) {
            CommandFailureException cfe = new CommandFailureException("File " + patchFile.getAbsolutePath() + " not found.");
            cfe.initCause(new FileNotFoundException(patchFile.getAbsolutePath()));
            throw cfe;
        }
        DefaultPatchParser parser = new DefaultPatchParser();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(patchFile);
            ParseResult pr = parser.parse(fis, null);
            out.append("Statements count: ").append(Integer.toString(pr.executableCount())).append('\n');
            for (PatchStatement ps : pr.getStatements()) {
                out.append('{').append(ps.toString()).append("}\n");
            }
        } catch (IOException e) {
            throw new CommandExecutionException("Exception", e);
        } finally {
            CloseUtil.close(fis);
        }
    }


} public class InteractiveCommand extends AbstractCommand {

    public InteractiveCommand(EnvSettings envSettings) {
        super(envSettings);
    }

    public void execute() throws CommandExecutionException, CommandFailureException {
        try {
            Collection<ConfigurationEntry> conf = AbstractConfigurationParser.discoverConfiguration(getConfigFile());
            InteractivePanel interactive = new InteractivePanel(conf);
            interactive.start();
            interactive.join();
        } catch (Exception e) {
            throw new CommandExecutionException(e.getMessage(), e);
        }
    }

} public class ListCommand extends AbstractListCommand<Patch> {

    public ListCommand(EnvSettings envSettings) {
        super(envSettings);
    }


    protected List<Patch> generateList(List<Patch> inList) throws IOException {
        ApplyStrategy strategy = configurationEntry.getApplyStarters();
        log.debug("Apply strategy is " + strategy.getClass().getSimpleName() + ", configurationId:" + configurationEntry.getId());
        List<Patch> patchesToApply = strategy.filter(manager.getConnection(), inList);
        StringBuilder sb = new StringBuilder("Patch list:\n");
        for (Patch p : inList) {
            getConfigurationEntry().getPatchParser().parse(p, getConfigurationEntry());
            sb.append('\t');
            if (p.getDbState() == AbstractPatch.DbState.COMMITTED) {
                sb.append('*');
            }
            if (p.getDbState() == AbstractPatch.DbState.IN_PROGRESS) {
                sb.append('P');
            }
            if (p.getDbState() == AbstractPatch.DbState.NOT_AVAILABLE) {
                if (patchesToApply.contains(p)) {
                    sb.append('+');
                } else {
                    sb.append('-');
                }
            }
            sb.append(' ').append(p.getName());
            for (int a = p.getName().length(); a < SPACES; a++) {
                sb.append(' ');
            }
            sb.append("  statements:").append(p.getStatementCount());
            sb.append('\n');
        }
        log.info(sb.toString().trim());
        log.info("Patches to apply: " + patchesToApply.size() + " using configuration:" + configurationEntry.getId());
        return patchesToApply;
    }



    protected void executeInternal() throws Exception {
        getList();
    }

} public class SkipErrorsCommand extends AbstractSingleConfDbPatchCommand {

    public SkipErrorsCommand(EnvSettings envSettings) {
        super(envSettings);
    }


    protected void executeInternal() throws Exception {
        Connection connection = manager.getConnection();
        Statement statement = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
        try {
            statement = connection.createStatement();
            rs = statement.executeQuery("SELECT patch_name FROM " + manager.getTableName() + " WHERE patch_db_date IS NULL");
            ps = connection.prepareStatement("UPDATE " + manager.getTableName() + " SET patch_db_date=? WHERE patch_name=?");
            ps.setDate(1, new Date(0));
            while (rs.next()) {
                ps.setString(2, rs.getString(1));
                ps.execute();
                log.info("Mark to skip " + rs.getString(1));
            }
            connection.commit();
        } finally {
            CloseUtil.close(statement);
            CloseUtil.close(rs);
            CloseUtil.close(ps);
        }
    }
} public abstract class AbstractConfigurationParser {

    public static Collection<ConfigurationEntry> discoverConfiguration(File confFile) throws ParseException, IOException {
        final Log log = LogFactory.getInstance();
        InputStream input = null;
        File baseDir = null;
        try {
            if (confFile == null) {
                log.debug("Looking for dbpach.properties in classpath.");
                input = Thread.currentThread().getContextClassLoader().getResourceAsStream("/dbpatch.properties");
                log.debug("Resource dbpatch.properties " + (input == null ? "not" : "") + " found in classpath.");
                if (input == null) {
                    log.debug("Looking for dbpach.properties in current directory.");
                    File f = new File("dbpatch.properties");
                    log.debug("Resource dbpatch.properties " + (!f.exists() ? "not" : "") + " found in current directory.");
                    input = new FileInputStream(f);
                    baseDir = new File(".");
                }
            } else {
                if (!confFile.exists()) {
                    throw new FileNotFoundException(confFile.getAbsolutePath());
                }
                log.debug("Configuration found - " + confFile.getPath());
                input = new FileInputStream(confFile);
                baseDir = confFile.getParentFile();
            }
            AbstractConfigurationParser parser = new PropertiesConfigurationParser();
            Collection<ConfigurationEntry> conf = parser.parse(baseDir, input);
            for (ConfigurationEntry ce : conf) {
                ce.validate();
            }
            return conf;
        } finally {
            CloseUtil.close(input);
        }
    }

    protected abstract Collection<ConfigurationEntry> parse(File baseDir, InputStream input) throws ParseException, IOException;
} public class EnvSettings {
    private final String prefix;

    private EnvSettings(String prefix) {
        this.prefix = prefix;
    }

    public String getDbPatchFile() {
        return prefix + "dbpatch.file";
    }

    public String getDbPatchConfiguration() {
        return prefix + "dbpatch.configuration";
    }

    public String getDbPatchStop() {
        return prefix + "dbpatch.stop";
    }

    public String getDbPatchSingle() {
        return prefix + "dbpatch.single";
    }

    public String getLogLevel() {
        return prefix + "dbpatch.logLevel";
    }

    public static EnvSettings standalone() {
        return new EnvSettings("");
    }

    public static EnvSettings maven() {
        return new EnvSettings("maven.");
    }

    public static EnvSettings gradle() {
        return new EnvSettings("gradle.");
    }

}
import org.jsoftware.dbpatch.command.AbstractCommand;
import org.jsoftware.dbpatch.command.CommandExecutionException;
import org.jsoftware.dbpatch.command.CommandFailureException;
import org.jsoftware.dbpatch.command.HelpCommand;
import org.jsoftware.dbpatch.command.HelpParseCommand;
import org.jsoftware.dbpatch.command.InteractiveCommand;
import org.jsoftware.dbpatch.command.ListCommand;
import org.jsoftware.dbpatch.command.PatchCommand;
import org.jsoftware.dbpatch.command.SkipErrorsCommand;
import org.jsoftware.dbpatch.config.AbstractConfigurationParser;
import org.jsoftware.dbpatch.config.ConfigurationEntry;
import org.jsoftware.dbpatch.config.EnvSettings;
import org.jsoftware.dbpatch.impl.gui.InteractivePanel;
import org.jsoftware.dbpatch.log.LogFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.util.Collection;
package org.jsoftware.dbpatch.app;



/**
 * Standalone application with command-line and GUI support
 *
 * @author szalik
 */
public class DbPatch {

    public static void main(String[] args) throws ParseException, IOException, CommandFailureException, CommandExecutionException {
        EnvSettings envSettings = EnvSettings.standalone();
        LogFactory.initLocal(envSettings);
        File confFile;

        if (args.length > 1) {
            confFile = new File(args[1]);
            if (!confFile.exists()) {
                throw new FileNotFoundException("Cannot find configuration file - " + confFile.getAbsolutePath());
            }
        } else {
            confFile = null;
        }
        if (args.length == 0) { // interactive mode
            LogFactory.getInstance().debug("Lunching interactive mode.");
            Collection<ConfigurationEntry> conf = AbstractConfigurationParser.discoverConfiguration(confFile);
            InteractivePanel interactive = new InteractivePanel(conf);
            interactive.start();
        } else { // mojo (text) mode
            AbstractCommand command = argToCommand(args[0]);
            if (command == null) {
                System.err.println("Arg 1 must be command name, arg 2 can be configuration file.");
                HelpCommand.helpCommandStandalone().execute();
            } else {
                LogFactory.getInstance().debug("Lunching text mode - " + command + ".");
                command.setConfigFile(confFile);
                command.execute();
            }
        }
    }

    private static AbstractCommand argToCommand(String mojoArg) {
        EnvSettings envSettings = EnvSettings.standalone();
        if ("help".equalsIgnoreCase(mojoArg)) {
            return HelpCommand.helpCommandStandalone();
        }
        if ("help-parse".equalsIgnoreCase(mojoArg)) {
            return new HelpParseCommand(envSettings);
        }
        if ("interactive".equalsIgnoreCase(mojoArg)) {
            return new InteractiveCommand(envSettings);
        }
        if ("list".equalsIgnoreCase(mojoArg)) {
            return new ListCommand(envSettings);
        }
        if ("patch".equalsIgnoreCase(mojoArg)) {
            return new PatchCommand(envSettings);
        }
        if ("skip-errors".equalsIgnoreCase(mojoArg)) {
            return new SkipErrorsCommand(envSettings);