[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n\n    working_directory: ~/DbSetup\n\n    docker:\n      - image: circleci/openjdk:8-jdk\n\n    steps:\n\n      - checkout\n      - run: ./gradlew build\n      - store_test_results:\n          path: DbSetup-core/build/test-results/test\n      - store_test_results:\n          path: DbSetup-kotlin/build/test-results/test\n"
  },
  {
    "path": ".gitignore",
    "content": "/.project\n.gradle\n/.settings\n/bin\nbuild/\n/.classpath\n/.classpath.idea/\n/.java-version\n*.iml\n.idea\nout\nclasses\n.sdkmanrc\n"
  },
  {
    "path": "DbSetup-core/build.gradle.kts",
    "content": "plugins {\n    `java-library-convention`\n}\n\nproject.description = \"Helps you setup your database with test data\"\n\njava {\n    withJavadocJar()\n}\n\ndependencies {\n    compileOnly(\"com.google.code.findbugs:jsr305:2.0.0\")\n    compileOnly(\"net.sourceforge.findbugs:annotations:1.3.2\")\n\n    testImplementation(\"junit:junit:4.+\")\n    testImplementation(\"org.mockito:mockito-all:1.9.0\")\n    testImplementation(\"org.hsqldb:hsqldb:2.3.3\")\n}\n\ntasks {\n    javadoc {\n        (options as StandardJavadocDocletOptions).apply {\n            overview(file(\"src/main/java/com/ninja_squad/dbsetup/overview.html\").path)\n            noTimestamp(true)\n            linkSource(true)\n            addBooleanOption(\"Xdoclint:all,-missing\", true)\n        }\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/DbSetup.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport javax.annotation.Nonnull;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\nimport com.ninja_squad.dbsetup.destination.Destination;\nimport com.ninja_squad.dbsetup.operation.Operation;\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\n/**\n * Allows executing a sequence of database operations. This object is reusable, and can thus be used several times\n * to launch the same sequence of database operations. Here's a typical usage scenario in a unit test:\n * <pre>\n * &#064;Before\n * public void setUp() throws Exception {\n *     Operation operation =\n *         Operations.sequenceOf(\n *             CommonOperations.DELETE_ALL,\n *             CommonOperations.INSERT_REFERENCE_DATA,\n *             Operations.insertInto(\"CLIENT\")\n *                       .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"COUNTRY_ID\")\n *                       .values(1L, \"John\", \"Doe\", \"1975-07-19\", 1L)\n *                       .values(2L, \"Jack\", \"Smith\", \"1969-08-22\", 2L)\n *                       .build());\n *     DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource), operation);\n *     dbSetup.launch();\n * }\n * </pre>\n * In the above code, <code>CommonOperations.DELETE_ALL</code> and <code>CommonOperations.INSERT_REFERENCE_DATA</code>\n * are operations shared by multiple test classes.\n * <p>\n * Note that, to speed up test executions, a {@link DbSetupTracker} can be used, at the price of a slightly\n * bigger complexity.\n * @author JB Nizet\n */\npublic final class DbSetup {\n    private final Destination destination;\n    private final Operation operation;\n    private final BinderConfiguration binderConfiguration;\n\n    /**\n     * Constructor which uses the {@link DefaultBinderConfiguration#INSTANCE default binder configuration}.\n     * @param destination the destination of the sequence of database operations\n     * @param operation the operation to execute (most of the time, an instance of\n     * {@link com.ninja_squad.dbsetup.operation.CompositeOperation}\n     */\n    public DbSetup(@Nonnull Destination destination, @Nonnull Operation operation) {\n        this(destination, operation, DefaultBinderConfiguration.INSTANCE);\n    }\n\n    /**\n     * Constructor allowing to use a custom {@link BinderConfiguration}.\n     * @param destination the destination of the sequence of database operations\n     * @param operation the operation to execute (most of the time, an instance of\n     * {@link com.ninja_squad.dbsetup.operation.CompositeOperation}\n     * @param binderConfiguration the binder configuration to use.\n     */\n    public DbSetup(@Nonnull Destination destination,\n                   @Nonnull Operation operation,\n                   @Nonnull BinderConfiguration binderConfiguration) {\n        Preconditions.checkNotNull(destination, \"destination may not be null\");\n        Preconditions.checkNotNull(operation, \"operation may not be null\");\n        Preconditions.checkNotNull(binderConfiguration, \"binderConfiguration may not be null\");\n\n        this.destination = destination;\n        this.operation = operation;\n        this.binderConfiguration = binderConfiguration;\n    }\n\n    /**\n     * Executes the sequence of operations. All the operations use the same connection, and are grouped\n     * in a single transaction. The transaction is rolled back if any exception occurs.\n     */\n    public void launch() {\n        try {\n            Connection connection = destination.getConnection();\n            try {\n                connection.setAutoCommit(false);\n                operation.execute(connection, binderConfiguration);\n                connection.commit();\n            }\n            catch (SQLException e) {\n                connection.rollback();\n                throw e;\n            }\n            catch (RuntimeException e) {\n                connection.rollback();\n                throw e;\n            }\n            finally {\n                connection.close();\n            }\n        }\n        catch (SQLException e) {\n            throw new DbSetupRuntimeException(e);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"DbSetup [destination=\"\n               + destination\n               + \", operation=\"\n               + operation\n               + \", binderConfiguration=\"\n               + binderConfiguration\n               + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + binderConfiguration.hashCode();\n        result = prime * result + destination.hashCode();\n        result = prime * result + operation.hashCode();\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        DbSetup other = (DbSetup) obj;\n        return binderConfiguration.equals(other.binderConfiguration)\n               && destination.equals(other.destination)\n               && operation.equals(other.operation);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/DbSetupRuntimeException.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\n/**\n * A runtime exception indicating that a DbSetup failed.\n * @author JB Nizet\n */\npublic class DbSetupRuntimeException extends RuntimeException {\n\n    public DbSetupRuntimeException() {\n        super();\n    }\n\n    public DbSetupRuntimeException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public DbSetupRuntimeException(String message) {\n        super(message);\n    }\n\n    public DbSetupRuntimeException(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/DbSetupTracker.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\nimport javax.annotation.Nonnull;\n\n\n/**\n * <p>\n * This class allows speeding up test execution, by avoiding re-executing the same sequence of database operations\n * before each test method even if each of these test methods leaves the database as it is (and only performs read-only\n * operations, which is the most frequent case).\n * </p>\n * <p>Example usage:</p>\n * <pre>\n * // the tracker is static because JUnit uses a separate Test instance for every test method.\n * private static DbSetupTracker dbSetupTracker = new DbSetupTracker();\n *\n * &#064;Before\n * public void setUp() throws Exception {\n *     Operation operation =\n *         Operations.sequenceOf(\n *             CommonOperations.DELETE_ALL,\n *             CommonOperations.INSERT_REFERENCE_DATA,\n *             Operations.insertInto(\"CLIENT\")\n *                       .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"COUNTRY_ID\")\n *                       .values(1L, \"John\", \"Doe\", \"1975-07-19\", 1L)\n *                       .values(2L, \"Jack\", \"Smith\", \"1969-08-22\", 2L)\n *                       .build());\n *     DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource), operation);\n *     dbSetupTracker.launchIfNecessary(dbSetup);\n * }\n *\n * &#064;Test\n * public void readOnlyTest1() {\n *     dbSetupTracker.skipNextLaunch();\n *     ...\n * }\n *\n * &#064;Test\n * public void readOnlyTest2() {\n *     dbSetupTracker.skipNextLaunch();\n *     ...\n * }\n *\n * &#064;Test\n * public void readOnlyTest3() {\n *     dbSetupTracker.skipNextLaunch();\n *     ...\n * }\n *\n * &#064;Test\n * public void readWriteTest1() {\n *     // No call to dbSetupTracker.skipNextLaunch();\n *     ...\n * }\n * </pre>\n * @author JB Nizet\n */\npublic final class DbSetupTracker {\n    private DbSetup lastSetupLaunched;\n    private boolean nextLaunchSkipped;\n\n    /**\n     * Executes the given DbSetup unless all the following conditions are <code>true</code>:\n     * <ul>\n     *   <li>{@link #skipNextLaunch()} has been called since the last call to this method;</li>\n     *   <li>the given <code>dbSetup</code> is equal to the last DbSetup launched by this method</li>\n     * </ul>\n     * This method resets the <code>skipNextLaunch</code> flag to <code>false</code>.\n     * @param dbSetup the DbSetup to execute (or skip)\n     */\n    public void launchIfNecessary(@Nonnull DbSetup dbSetup) {\n        boolean skipLaunch = nextLaunchSkipped && dbSetup.equals(lastSetupLaunched);\n        nextLaunchSkipped = false;\n        if (skipLaunch) {\n            return;\n        }\n        dbSetup.launch();\n        lastSetupLaunched = dbSetup;\n    }\n\n    /**\n     * Marks the current test method as read-only, and thus the need for the next test method to re-execute the same\n     * sequence of database setup operations.\n     */\n    public void skipNextLaunch() {\n        this.nextLaunchSkipped = true;\n    }\n\n    @Override\n    public String toString() {\n        return \"DbSetupTracker [lastSetupLaunched=\"\n                + lastSetupLaunched\n                + \", nextLaunchSkipped=\"\n                + nextLaunchSkipped\n                + \"]\";\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/Operations.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\nimport java.util.List;\n\nimport javax.annotation.Nonnull;\n\nimport com.ninja_squad.dbsetup.operation.CompositeOperation;\nimport com.ninja_squad.dbsetup.operation.DeleteAll;\nimport com.ninja_squad.dbsetup.operation.Insert;\nimport com.ninja_squad.dbsetup.operation.Operation;\nimport com.ninja_squad.dbsetup.operation.SqlOperation;\nimport com.ninja_squad.dbsetup.operation.Truncate;\n\n/**\n * A static factory class for operations. Static import of this class can help make the code more readable.\n * @author JB Nizet\n */\npublic final class Operations {\n    private Operations() {\n    }\n\n    /**\n     * Creates a <code>delete from table</code> operation.\n     * @param table the table to delete all from\n     * @see DeleteAll\n     */\n    public static DeleteAll deleteAllFrom(@Nonnull String table) {\n        return DeleteAll.from(table);\n    }\n\n    /**\n     * Creates a sequence of <code>delete from table</code> operations.\n     * @param tables the tables to delete all from\n     * @see DeleteAll\n     */\n    public static Operation deleteAllFrom(@Nonnull String... tables) {\n        return DeleteAll.from(tables);\n    }\n\n    /**\n     * Creates a sequence of <code>delete from ...</code> operations.\n     * @param tables the tables to delete all from\n     * @see DeleteAll\n     */\n    public static Operation deleteAllFrom(@Nonnull List<String> tables) {\n        return DeleteAll.from(tables);\n    }\n\n    /**\n     * Creates a <code>truncate table ...</code> operation.\n     * @param table the table to truncate\n     * @see Truncate\n     */\n    public static Truncate truncate(@Nonnull String table) {\n        return Truncate.table(table);\n    }\n\n    /**\n     * Creates a sequence of <code>truncate table ...</code> operations.\n     * @param tables the tables to truncate\n     * @see Truncate\n     */\n    public static Operation truncate(@Nonnull String... tables) {\n        return Truncate.tables(tables);\n    }\n\n    /**\n     * Creates a sequence of <code>truncate table ...</code> operations.\n     * @param tables the tables to truncate\n     * @see Truncate\n     */\n    public static Operation truncate(@Nonnull List<String> tables) {\n        return Truncate.tables(tables);\n    }\n\n    /**\n     * Creates a SQL operation.\n     * @param sqlStatement the SQL statement to execute (using {@link java.sql.Statement#executeUpdate(String)})\n     * @see SqlOperation\n     */\n    public static SqlOperation sql(@Nonnull String sqlStatement) {\n        return SqlOperation.of(sqlStatement);\n    }\n\n    /**\n     * Creates a sequence of SQL operations.\n     * @param sqlStatements the SQL statements to execute (using {@link java.sql.Statement#executeUpdate(String)})\n     * @see SqlOperation\n     */\n    public static Operation sql(@Nonnull String... sqlStatements) {\n        return SqlOperation.of(sqlStatements);\n    }\n\n    /**\n     * Creates a sequence of SQL operations.\n     * @param sqlStatements the SQL statements to execute (using {@link java.sql.Statement#executeUpdate(String)})\n     * @see SqlOperation\n     */\n    public static Operation sql(@Nonnull List<String> sqlStatements) {\n        return SqlOperation.of(sqlStatements);\n    }\n\n    /**\n     * Creates a builder for a sequence of insert operations.\n     * @param table the table to insert into\n     * @see Insert\n     */\n    public static Insert.Builder insertInto(@Nonnull String table) {\n        return Insert.into(table);\n    }\n\n    /**\n     * Creates a sequence of operations.\n     * @param operations the operations to put in a sequence\n     * @see CompositeOperation\n     */\n    public static Operation sequenceOf(@Nonnull Operation... operations) {\n        return CompositeOperation.sequenceOf(operations);\n    }\n\n    /**\n     * Creates a sequence of operations.\n     * @param operations the operations to put in a sequence\n     * @see CompositeOperation\n     */\n    public static Operation sequenceOf(@Nonnull List<? extends Operation> operations) {\n        return CompositeOperation.sequenceOf(operations);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/bind/Binder.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.bind;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\n\nimport com.ninja_squad.dbsetup.DbSetupTracker;\n\n/**\n * An object which binds a value to a prepared statement parameter. It's advised to make implementations of this\n * interface immutable, and to make them implement equals and hashCode in order for {@link DbSetupTracker} to function\n * properly, or to make them singletons.\n * @author JB Nizet\n */\npublic interface Binder {\n    /**\n     * Binds the given value to the given parameter in the given prepared statement.\n     * @param statement the statement to bind the parameter to\n     * @param param The index of the parameter to bind in the statement\n     * @param value The value to bind (may be <code>null</code>)\n     * @throws SQLException if the binding throws a {@link SQLException}\n     */\n    void bind(PreparedStatement statement, int param, Object value) throws SQLException;\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/bind/BinderConfiguration.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.bind;\n\nimport com.ninja_squad.dbsetup.DbSetup;\nimport com.ninja_squad.dbsetup.DbSetupTracker;\n\nimport javax.annotation.Nullable;\nimport java.sql.ParameterMetaData;\nimport java.sql.SQLException;\n\n/**\n * An object which returns the appropriate {@link Binder} based on the metadata of the prepared statement.\n * The default instance of this interface is {@link DefaultBinderConfiguration}. If the binders returned by this\n * default configuration don't fit for the particular database you're using, or if you would like the binders\n * returned by the configuration to support additional data types, you might want to provide a different implementation\n * of this interface to the {@link DbSetup}.\n * <p>\n * It's advised to make implementations of this interface immutable, and to make them implement equals and hashCode\n * in order for {@link DbSetupTracker} to function properly, or to make them singletons.\n * @author JB Nizet\n */\npublic interface BinderConfiguration {\n\n    /**\n     * Returns the appropriate {@link Binder} for the given parameter, based on the given metadata.\n     * @param metadata the metadata allowing to decide which Binder to return. <code>null</code> if the Insert has been\n     * configured to not use metadata, or if the JDBC driver returned null metadata, or the JDBC driver threw a\n     * SQLException when asked for the metadata\n     * @param param the param for which a binder is requested\n     * @return the binder for the given param and its metadata\n     * @throws SQLException if a SQLException occurs while using the metadata\n     */\n    Binder getBinder(@Nullable ParameterMetaData metadata, int param) throws SQLException;\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/bind/Binders.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.bind;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.sql.Date;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.ZonedDateTime;\nimport java.util.Calendar;\nimport java.util.TimeZone;\n\n/**\n * Utility class allowing to get various kinds of binders. The {@link DefaultBinderConfiguration} uses binders\n * returned by this class, based on the type of the parameter.\n * @author JB Nizet\n */\npublic final class Binders {\n\n    private static final Binder DEFAULT_BINDER = new DefaultBinder();\n    private static final Binder DATE_BINDER = new DateBinder();\n    private static final Binder TIMESTAMP_BINDER = new TimestampBinder();\n    private static final Binder DECIMAL_BINDER = new DecimalBinder();\n    private static final Binder INTEGER_BINDER = new IntegerBinder();\n    private static final Binder TIME_BINDER = new TimeBinder();\n    private static final Binder STRING_BINDER = new StringBinder();\n\n    private Binders() {\n    }\n\n    /**\n     * Returns the default binder. This binder is normally used for columns of a type that is not handled by the other\n     * binders. It is also used when the metadata are not used and the Insert thus doesn't know the type of the column.\n     * It simply uses <code>stmt.setObject()</code> to bind the parameter, except if the value being bound is of some\n     * some well-known type not handled by JDBC:\n     * <ul>\n     *     <li><code>enum</code>: the name of the enum is bound</li>\n     *     <li><code>java.util.Date</code>: the date is transformed to a <code>java.sql.Timestamp</code></li>\n     *     <li><code>java.util.Calendar</code>: the calendar is transformed to a <code>java.sql.Timestamp</code>,\n     *         and is passed as third argument of\n     *         <code>PreparedStatement.setTimestamp()</code> to pass the timezone</li>\n     *     <li><code>java.time.LocalDate</code>: transformed to a <code>java.sql.Date</code></li>\n     *     <li><code>java.time.LocalTime</code>: transformed to a <code>java.sql.Time</code></li>\n     *     <li><code>java.time.LocalDateTime</code>: transformed to a <code>java.sql.Timestamp</code></li>\n     *     <li><code>java.time.Instant</code>: transformed to a <code>java.sql.Timestamp</code></li>\n     *     <li><code>java.time.ZonedDateTime</code> and <code>OffsetDateTime</code>: transformed to a\n     *         <code>java.sql.Timestamp</code>. The time zone is also used to create a <code>Calendar</code> passed as\n     *         third argument of <code>PreparedStatement.setTimestamp()</code> to pass the timezone</li>\n     *     <li><code>java.time.OffsetTime</code>: transformed to a\n     *         <code>java.sql.Time</code>. The time zone is also used to create a <code>Calendar</code> passed as third\n     *         argument of <code>PreparedStatement.setTime()</code> to pass the timezone</li>\n     * </ul>\n     */\n    public static Binder defaultBinder() {\n        return DEFAULT_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for columns of type CHAR and VARCHAR. The returned binder supports values of type\n     * <ul>\n     *   <li><code>String</code></li>\n     *   <li><code>enum</code>: the name of the enum is used as bound value</li>\n     *   <li><code>Object</code>: the <code>toString()</code> of the object is used as bound value</li>\n     * </ul>\n     */\n    public static Binder stringBinder() {\n        return STRING_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for columns of type DATE. The returned binder supports values of type\n     * <ul>\n     *   <li><code>java.sql.Date</code></li>\n     *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a\n     *       <code>java.sql.Date</code>.</li>\n     *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a\n     *       <code>java.sql.Date</code>, and the calendar is passed as third argument of\n     *       <code>PreparedStatement.setDate()</code> to pass the timezone\n     *   </li>\n     *   <li><code>String</code>: the string is transformed to a java.sql.Date using the <code>Date.valueOf()</code>\n     *       method</li>\n     *   <li><code>java.time.LocalDate</code>: transformed to a <code>java.sql.Date</code> using\n     *       <code>Date.valueOf()</code></li>\n     *   <li><code>java.time.LocalDateTime</code>: transformed to a LocalDate (and thus ignoring the time),\n     *       and then transformed to a <code>java.sql.Date</code> using <code>Date.valueOf()</code></li>\n     *   <li><code>java.time.Instant</code>the milliseconds of the instant are used to construct a\n     *       <code>java.sql.Date</code>.</li>\n     *   <li><code>java.time.ZonedDateTime</code> and <code>java.time.OffsetDateTime</code>: transformed to an Instant\n     *       and then to a <code>java.sql.Date</code>. The time zone is also used to create a <code>Calendar</code>\n     *       passed as third argument of <code>PreparedStatement.setDate()</code> to pass the timezone</li>\n     * </ul>\n     * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.\n     */\n    public static Binder dateBinder() {\n        return DATE_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for columns of type TIMESTAMP and TIMESTAMP_WITH_TIMEZONE. The returned binder\n     * supports values of type\n     * <ul>\n     *   <li><code>java.sql.Timestamp</code></li>\n     *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a\n     *       <code>java.sql.Timestamp</code></li>\n     *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a\n     *       <code>java.sql.Timestamp</code>, and the calendar is passed as third argument of\n     *       <code>PreparedStatement.setTimestamp()</code> to pass the timezone</li>\n     *   <li><code>String</code>: the string is transformed to a <code>java.sql.Timestamp</code> using the\n     *       <code>Timestamp.valueOf()</code> method, or using the <code>java.sql.Date.valueOf()</code> method if the\n     *       string has less than 19 characters</li>\n     *   <li><code>java.time.LocalDateTime</code>: transformed to a <code>java.sql.Timestamp</code> using\n     *       <code>Timestamp.valueOf()</code></li>\n     *   <li><code>java.time.LocalDate</code>: transformed to a LocalDateTime with the time at start of day,\n     *       and then transformed to a <code>java.sql.Timestamp</code> using <code>Timestamp.valueOf()</code></li>\n     *   <li><code>java.time.Instant</code>: transformed to a <code>java.sql.Timestamp</code> using\n     *       <code>Timestamp.from()</code></li>\n     *   <li><code>java.time.ZonedDateTime</code> and <code>java.time.OffsetDateTime</code>: transformed to an Instant\n     *       and then to a <code>java.sql.Timestamp</code> using <code>Timestamp.from()</code>. The time zone is also\n     *       used to create a <code>Calendar</code> passed as third argument of\n     *       <code>PreparedStatement.setTimestamp()</code> to pass the timezone</li>\n     * </ul>\n     * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.\n     */\n    public static Binder timestampBinder() {\n        return TIMESTAMP_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for columns of type TIME or TIME_WITH_TIMEZONE. The returned binder supports values\n     * of type\n     * <ul>\n     *   <li><code>java.sql.Time</code></li>\n     *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a\n     *      <code>java.sql.Time</code></li>\n     *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a\n     *      <code>java.sql.Time</code>, and the calendar is passed as third argument of\n     *      <code>PreparedStatement.setTimestamp()</code> to pass the timezone\n     *   </li>\n     *   <li><code>String</code>: the string is transformed to a java.sql.Time using the\n     *       <code>Time.valueOf()</code> method</li>\n     *   <li><code>java.time.LocalTime</code>: transformed to a <code>java.sql.Time</code> using\n     *       <code>Time.valueOf()</code></li>\n     *   <li><code>java.time.OffsetTime</code>: transformed to a <code>LocalTime</code> and then to a\n     *       <code>java.sql.Time</code> using <code>Time.valueOf()</code>. The time zone is also\n     *       used to create a <code>Calendar</code> passed as third argument of\n     *       <code>PreparedStatement.setTime()</code> to pass the timezone</li>\n     * </ul>\n     * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.\n     */\n    public static Binder timeBinder() {\n        return TIME_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for numeric, decimal columns. The returned binder supports values of type\n     * <ul>\n     *   <li><code>String</code>: the string is transformed to a java.math.BigDecimal using its constructor</li>\n     * </ul>\n     * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.\n     */\n    public static Binder decimalBinder() {\n        return DECIMAL_BINDER;\n    }\n\n    /**\n     * Returns a binder suitable for numeric, integer columns. The returned binder supports values of type\n     * <ul>\n     *   <li><code>BigInteger</code>: the object is transformed to a String and bound using\n     *       <code>stmt.setObject()</code>, with <code>BIGINT</code> as target type.\n     *   </li>\n     *   <li><code>enum</code>: the enum is transformed into an integer by taking its ordinal</li>\n     *   <li><code>String</code>: the string is bound using <code>stmt.setObject()</code>, with <code>BIGINT</code> as\n     *       target type.\n     *   </li>\n     * </ul>\n     * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.\n     */\n    public static Binder integerBinder() {\n        return INTEGER_BINDER;\n    }\n\n    /**\n     * The implementation for {@link Binders#stringBinder()}\n     * @author JB Nizet\n     */\n    private static final class StringBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof String) {\n                stmt.setString(param, (String) value);\n            }\n            else if (value instanceof Enum<?>) {\n                stmt.setString(param, ((Enum<?>) value).name());\n            }\n            else if (value == null) {\n                stmt.setObject(param, null);\n            }\n            else {\n                stmt.setString(param, value.toString());\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.stringBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#timeBinder()}\n     * @author JB Nizet\n     */\n    private static final class TimeBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof Time) {\n                stmt.setTime(param, (Time) value);\n            }\n            else if (value instanceof java.util.Date) {\n                stmt.setTime(param, new Time(((java.util.Date) value).getTime()));\n            }\n            else if (value instanceof Calendar) {\n                Calendar calendar = (Calendar) value;\n                stmt.setTime(param, new Time(calendar.getTimeInMillis()), calendar);\n            }\n            else if (value instanceof String) {\n                stmt.setTime(param, Time.valueOf((String) value));\n            }\n            else if (value instanceof LocalTime) {\n                stmt.setTime(param, Time.valueOf((LocalTime) value));\n            }\n            else if (value instanceof OffsetTime) {\n                OffsetTime offsetTime = (OffsetTime) value;\n                stmt.setTime(param,\n                             Time.valueOf(offsetTime.toLocalTime()),\n                             Calendar.getInstance(TimeZone.getTimeZone(offsetTime.getOffset())));\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.timeBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#integerBinder()}\n     * @author JB Nizet\n     */\n    private static final class IntegerBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof BigInteger) {\n                stmt.setObject(param, value.toString(), Types.BIGINT);\n            }\n            else if (value instanceof Enum<?>) {\n                stmt.setInt(param, ((Enum<?>) value).ordinal());\n            }\n            else if (value instanceof String) {\n                stmt.setObject(param, value, Types.BIGINT);\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.integerBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#decimalBinder()}\n     * @author JB Nizet\n     */\n    private static final class DecimalBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof String) {\n                stmt.setBigDecimal(param, new BigDecimal((String) value));\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.decimalBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#timestampBinder()}\n     * @author JB Nizet\n     */\n    private static final class TimestampBinder implements Binder {\n        // the number of chars in yyyy-mm-dd hh:mm:ss\n        private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;\n\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof Timestamp) {\n                stmt.setTimestamp(param, (Timestamp) value);\n            }\n            else if (value instanceof java.util.Date) {\n                stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));\n            }\n            else if (value instanceof Calendar) {\n                stmt.setTimestamp(param, new Timestamp(((Calendar) value).getTimeInMillis()), (Calendar) value);\n            }\n            else if (value instanceof String) {\n                String valueAsString = (String) value;\n                if (valueAsString.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {\n                    stmt.setTimestamp(param, Timestamp.valueOf(valueAsString));\n                }\n                else {\n                    Date valueAsDate = Date.valueOf(valueAsString);\n                    stmt.setTimestamp(param, new Timestamp(valueAsDate.getTime()));\n                }\n            }\n            else if (value instanceof LocalDateTime) {\n                LocalDateTime localDateTime = (LocalDateTime) value;\n                stmt.setTimestamp(param, Timestamp.valueOf(localDateTime));\n            }\n            else if (value instanceof Instant) {\n                Instant instant = (Instant) value;\n                stmt.setTimestamp(param, Timestamp.from(instant));\n            }\n            else if (value instanceof ZonedDateTime) {\n                ZonedDateTime zonedDateTime = (ZonedDateTime) value;\n                stmt.setTimestamp(param,\n                                  Timestamp.from(zonedDateTime.toInstant()),\n                                  Calendar.getInstance(TimeZone.getTimeZone(zonedDateTime.getZone())));\n            }\n            else if (value instanceof OffsetDateTime) {\n                OffsetDateTime offsetDateTime = (OffsetDateTime) value;\n                stmt.setTimestamp(param,\n                                  Timestamp.from(offsetDateTime.toInstant()),\n                                  Calendar.getInstance(TimeZone.getTimeZone(offsetDateTime.getOffset())));\n            }\n            else if (value instanceof LocalDate) {\n                LocalDate localDate = (LocalDate) value;\n                stmt.setTimestamp(param, Timestamp.valueOf(localDate.atStartOfDay()));\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.timestampBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#dateBinder()}\n     * @author JB Nizet\n     */\n    private static final class DateBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof Date) {\n                stmt.setDate(param, (Date) value);\n            }\n            else if (value instanceof java.util.Date) {\n                stmt.setDate(param, new Date(((java.util.Date) value).getTime()));\n            }\n            else if (value instanceof Calendar) {\n                Calendar calendar = (Calendar) value;\n                stmt.setDate(param, new Date(calendar.getTimeInMillis()), calendar);\n            }\n            else if (value instanceof String) {\n                stmt.setDate(param, Date.valueOf((String) value));\n            }\n            else if (value instanceof LocalDate) {\n                LocalDate localDate = (LocalDate) value;\n                stmt.setDate(param, Date.valueOf(localDate));\n            }\n            else if (value instanceof LocalDateTime) {\n                LocalDateTime localDateTime = (LocalDateTime) value;\n                stmt.setDate(param, Date.valueOf(localDateTime.toLocalDate()));\n            }\n            else if (value instanceof Instant) {\n                Instant instant = (Instant) value;\n                stmt.setDate(param, new Date(instant.toEpochMilli()));\n            }\n            else if (value instanceof ZonedDateTime) {\n                ZonedDateTime zonedDateTime = (ZonedDateTime) value;\n                stmt.setDate(param,\n                             new Date(zonedDateTime.toInstant().toEpochMilli()),\n                             Calendar.getInstance(TimeZone.getTimeZone(zonedDateTime.getZone())));\n            }\n            else if (value instanceof OffsetDateTime) {\n                OffsetDateTime offsetDateTime = (OffsetDateTime) value;\n                stmt.setDate(param,\n                             new Date(offsetDateTime.toInstant().toEpochMilli()),\n                             Calendar.getInstance(TimeZone.getTimeZone(offsetDateTime.getOffset())));\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.dateBinder\";\n        }\n    }\n\n    /**\n     * The implementation for {@link Binders#defaultBinder()}\n     * @author JB Nizet\n     */\n    private static final class DefaultBinder implements Binder {\n        @Override\n        public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {\n            if (value instanceof Enum) {\n                stmt.setString(param, ((Enum) value).name());\n            }\n            else if (value instanceof java.util.Date) {\n                stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));\n            }\n            else if (value instanceof Calendar) {\n                Calendar calendar = (Calendar) value;\n                stmt.setTimestamp(param, new Timestamp(calendar.getTime().getTime()), calendar);\n            }\n            else if (value instanceof LocalDate) {\n                stmt.setDate(param, Date.valueOf((LocalDate) value));\n            }\n            else if (value instanceof LocalTime) {\n                stmt.setTime(param, Time.valueOf((LocalTime) value));\n            }\n            else if (value instanceof LocalDateTime) {\n                stmt.setTimestamp(param, Timestamp.valueOf((LocalDateTime) value));\n            }\n            else if (value instanceof Instant) {\n                stmt.setTimestamp(param, Timestamp.from((Instant) value));\n            }\n            else if (value instanceof ZonedDateTime) {\n                ZonedDateTime zonedDateTime = (ZonedDateTime) value;\n                stmt.setTimestamp(param,\n                                  Timestamp.from(zonedDateTime.toInstant()),\n                                  Calendar.getInstance(TimeZone.getTimeZone(zonedDateTime.getZone())));\n            }\n            else if (value instanceof OffsetDateTime) {\n                OffsetDateTime offsetDateTime = (OffsetDateTime) value;\n                stmt.setTimestamp(param,\n                                  Timestamp.from(offsetDateTime.toInstant()),\n                                  Calendar.getInstance(TimeZone.getTimeZone(offsetDateTime.getOffset())));\n            }\n            else if (value instanceof OffsetTime) {\n                OffsetTime offsetTime = (OffsetTime) value;\n                stmt.setTime(param,\n                             Time.valueOf(offsetTime.toLocalTime()),\n                             Calendar.getInstance(TimeZone.getTimeZone(offsetTime.getOffset())));\n            }\n            else {\n                stmt.setObject(param, value);\n            }\n        }\n\n        @Override\n        public String toString() {\n            return \"Binders.defaultBinder\";\n        }\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/bind/DefaultBinderConfiguration.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.bind;\n\nimport java.sql.ParameterMetaData;\nimport java.sql.SQLException;\nimport java.sql.Types;\n\nimport com.ninja_squad.dbsetup.DbSetup;\n\n/**\n * Default implementation of {@link BinderConfiguration}, used by default by {@link DbSetup}.\n * @author JB Nizet\n */\npublic class DefaultBinderConfiguration implements BinderConfiguration {\n\n    /**\n     * A shareable, reusable instance of this class.\n     */\n    public static final DefaultBinderConfiguration INSTANCE = new DefaultBinderConfiguration();\n\n    /**\n     * Constructor. Protected because it doesn't make much sense to instantiate this class,\n     * but extending it can be useful.\n     */\n    protected DefaultBinderConfiguration() {\n    }\n\n    /**\n     * Uses the parameter type of the given parameter and returns the following Binders depending on the type\n     * got from the metadata.\n     * <ul>\n     *   <li>null metadata (i.e. metadata not used or not returned): {@link Binders#defaultBinder()}</li>\n     *   <li>VARCHAR, CHAR, LONGNVARCHAR, LONGVARCHAR, NCHAR, NVARCHAR :\n     *       {@link Binders#stringBinder()}</li>\n     *   <li>DATE : {@link Binders#dateBinder()}</li>\n     *   <li>TIME : {@link Binders#timeBinder()}</li>\n     *   <li>TIMESTAMP : {@link Binders#timestampBinder()}</li>\n     *   <li>INTEGER, BIGINT, SMALLINT, TINYINT : {@link Binders#integerBinder()}</li>\n     *   <li>DECIMAL, DOUBLE, FLOAT, NUMERIC, REAL : {@link Binders#decimalBinder()}</li>\n     *   <li>other : {@link Binders#defaultBinder()}</li>\n     * </ul>\n     *\n     * If the parameter type can't be obtained from the metadata, the default binder is returned.\n     */\n    @Override\n    public Binder getBinder(ParameterMetaData metadata, int param) throws SQLException {\n        if (metadata == null) {\n            return Binders.defaultBinder();\n        }\n        try {\n            int sqlType = metadata.getParameterType(param);\n            if (sqlType == Types.DATE) {\n                return Binders.dateBinder();\n            }\n            if (sqlType == Types.TIME || sqlType == Types.TIME_WITH_TIMEZONE) {\n                return Binders.timeBinder();\n            }\n            if (sqlType == Types.TIMESTAMP || sqlType == Types.TIMESTAMP_WITH_TIMEZONE) {\n                return Binders.timestampBinder();\n            }\n            if (sqlType == Types.BIGINT\n                || sqlType == Types.INTEGER\n                || sqlType == Types.SMALLINT\n                || sqlType == Types.TINYINT) {\n                return Binders.integerBinder();\n            }\n            if (sqlType == Types.DECIMAL\n                || sqlType == Types.DOUBLE\n                || sqlType == Types.FLOAT\n                || sqlType == Types.NUMERIC\n                || sqlType == Types.REAL) {\n                return Binders.decimalBinder();\n            }\n            if (sqlType == Types.VARCHAR\n                || sqlType == Types.CHAR\n                || sqlType == Types.LONGNVARCHAR\n                || sqlType == Types.LONGVARCHAR\n                || sqlType == Types.NCHAR\n                || sqlType == Types.NVARCHAR) {\n                return Binders.stringBinder();\n            }\n            return Binders.defaultBinder();\n        }\n        catch (SQLException e) {\n            // the database can't return types from parameters. Fall back to default binder.\n            return Binders.defaultBinder();\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"DefaultBinderConfiguration\";\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/destination/DataSourceDestination.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.destination;\n\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * A destination which wraps a DataSource and gets its connection from the wrapped DataSource\n * @author JB Nizet\n */\n@Immutable\npublic final class DataSourceDestination implements Destination {\n    private final DataSource dataSource;\n\n    /**\n     * Constructor\n     * @param dataSource the wrapped DataSource\n     */\n    public DataSourceDestination(@Nonnull DataSource dataSource) {\n        Preconditions.checkNotNull(dataSource, \"dataSource may not be null\");\n        this.dataSource = dataSource;\n    }\n\n    /**\n     * Factory method creating a new DataSourceDestination. This allows a more readable style than using the\n     * constructor:\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(DataSourceDestination.with(dataSource), operation);\n     * </pre>\n     *\n     * or, if this method is statically imported:\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(with(dataSource), operation);\n     * </pre>\n     *\n     * instead of\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource), operation);\n     * </pre>\n     *\n     * @param dataSource the wrapped DataSource\n     */\n    public static DataSourceDestination with(@Nonnull DataSource dataSource) {\n        return new DataSourceDestination(dataSource);\n    }\n\n    @Override\n    public Connection getConnection() throws SQLException {\n        return dataSource.getConnection();\n    }\n\n    @Override\n    public String toString() {\n        return \"DataSourceDestination [dataSource=\" + dataSource + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        return dataSource.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        DataSourceDestination other = (DataSourceDestination) obj;\n        return dataSource.equals(other.dataSource);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/destination/Destination.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.destination;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport com.ninja_squad.dbsetup.DbSetupTracker;\n\n/**\n * The destination of a database setup. It's advised to make implementations of this\n * interface immutable, and to make them implement equals and hashCode in order for {@link DbSetupTracker} to function\n * properly, or to make them singletons.\n * @author JB Nizet\n */\npublic interface Destination {\n    /**\n     * Returns a connection to the destination database\n     * @return a connection to the destination database\n     * @throws SQLException if a connection can't be returned\n     */\n    Connection getConnection() throws SQLException;\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/destination/DriverManagerDestination.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.destination;\n\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\n\n/**\n * A destination which uses the {@link DriverManager} to get a connection\n * @author JB Nizet\n */\n@Immutable\npublic final class DriverManagerDestination implements Destination {\n\n    private final String url;\n    private final String user;\n    private final String password;\n\n    /**\n     * Constructor\n     * @param url the URL of the database\n     * @param user the user used to get a connection\n     * @param password the password used to get a connection\n     */\n    public DriverManagerDestination(@Nonnull String url, String user, String password) {\n        Preconditions.checkNotNull(url, \"url may not be null\");\n        this.url = url;\n        this.user = user;\n        this.password = password;\n    }\n\n    /**\n     * Factory method creating a new DriverManagerDestination. This allows a more readable style than using the\n     * constructor:\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(DriverManagerDestination.with(url, user, password), operation);\n     * </pre>\n     *\n     * or, if this method is statically imported:\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(with(url, user, password), operation);\n     * </pre>\n     *\n     * instead of\n     *\n     * <pre>\n     *    DbSetup dbSetup = new DbSetup(new DriverManagerDestination(url, user, password), operation);\n     * </pre>\n     *\n     * @param url the URL of the database\n     * @param user the user used to get a connection\n     * @param password the password used to get a connection\n     */\n    public static DriverManagerDestination with(@Nonnull String url, String user, String password) {\n        return new DriverManagerDestination(url, user, password);\n    }\n\n    @Override\n    public Connection getConnection() throws SQLException {\n        return DriverManager.getConnection(url, user, password);\n    }\n\n    @Override\n    public String toString() {\n        return \"DriverManagerDestination [url=\"\n               + url\n               + \", user=\"\n               + user\n               + \", password=\"\n               + password\n               + \"]\";\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + url.hashCode();\n        result = prime * result + ((password == null) ? 0 : password.hashCode());\n        result = prime * result + ((user == null) ? 0 : user.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        DriverManagerDestination other = (DriverManagerDestination) obj;\n        if (password == null) {\n            if (other.password != null) {\n                return false;\n            }\n        }\n        else if (!password.equals(other.password)) {\n            return false;\n        }\n        if (!url.equals(other.url)) {\n            return false;\n        }\n        if (user == null) {\n            if (other.user != null) {\n                return false;\n            }\n        }\n        else if (!user.equals(other.user)) {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/generator/DateSequenceValueGenerator.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport javax.annotation.Nonnull;\nimport java.sql.Timestamp;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalUnit;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\n/**\n * A {@link ValueGenerator} that returns a sequence of dates, starting at a given zoned date time and incremented by a\n * given time, specified as an increment and a temporal unit.\n * @author JB\n */\npublic final class DateSequenceValueGenerator implements ValueGenerator<ZonedDateTime> {\n\n    // the number of chars in yyyy-mm-dd hh:mm:ss\n    private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;\n\n    /**\n     * The available units for the increment of this sequence\n     * @deprecated use ChronoField instead. This enum is only kept to maintain backward compatibility\n     */\n    @Deprecated\n    public enum CalendarField {\n        YEAR(ChronoUnit.YEARS),\n        MONTH(ChronoUnit.MONTHS),\n        DAY(ChronoUnit.DAYS),\n        HOUR(ChronoUnit.HOURS),\n        MINUTE(ChronoUnit.MINUTES),\n        SECOND(ChronoUnit.SECONDS),\n        MILLISECOND(ChronoUnit.MILLIS);\n\n        private TemporalUnit unit;\n\n        CalendarField(TemporalUnit unit) {\n            this.unit = unit;\n        }\n\n        private TemporalUnit toTemporalUnit() {\n            return unit;\n        }\n    }\n\n    private ZonedDateTime next;\n    private int increment;\n    private TemporalUnit unit;\n\n    DateSequenceValueGenerator() {\n        this(LocalDate.now().atStartOfDay().atZone(ZoneId.systemDefault()), 1, ChronoUnit.DAYS);\n    }\n\n    private DateSequenceValueGenerator(ZonedDateTime next, int increment, TemporalUnit unit) {\n        this.next = next;\n        this.increment = increment;\n        this.unit = unit;\n    }\n\n    /**\n     * Restarts the sequence at the given date, in the given time zone\n     * @return this instance, for chaining\n     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument\n     */\n    @Deprecated\n    public DateSequenceValueGenerator startingAt(@Nonnull Date startDate, @Nonnull TimeZone timeZone) {\n        Preconditions.checkNotNull(startDate, \"startDate may not be null\");\n        Preconditions.checkNotNull(timeZone, \"timeZone may not be null\");\n        next = startDate.toInstant().atZone(timeZone.toZoneId());\n        return this;\n    }\n\n    /**\n     * Restarts the sequence at the given date, in the default time zone\n     * @return this instance, for chaining\n     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument\n     */\n    @Deprecated\n    public DateSequenceValueGenerator startingAt(@Nonnull Date startDate) {\n        return startingAt(startDate, TimeZone.getDefault());\n    }\n\n    /**\n     * Restarts the sequence at the given date\n     * @return this instance, for chaining\n     * @deprecated use one of the other <code>startingAt()</code> methods taking java.time types as argument\n     */\n    @Deprecated\n    public DateSequenceValueGenerator startingAt(@Nonnull Calendar startDate) {\n        Preconditions.checkNotNull(startDate, \"startDate may not be null\");\n        next = startDate.toInstant().atZone(startDate.getTimeZone().toZoneId());\n        return this;\n    }\n\n    /**\n     * Restarts the sequence at the given date, in the default time zone\n     * @param startDate the starting date, as a String. The supported formats are the same as the ones supported by\n     * {@link com.ninja_squad.dbsetup.bind.Binders#timestampBinder()}, i.e. the formats supported by\n     * <code>java.sql.Timestamp.valueOf()</code> and <code>java.sql.Date.valueOf()</code>\n     * @return this instance, for chaining\n     */\n    public DateSequenceValueGenerator startingAt(@Nonnull String startDate) {\n        Preconditions.checkNotNull(startDate, \"startDate may not be null\");\n        if (startDate.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {\n            return startingAt(new Date(Timestamp.valueOf(startDate).getTime()));\n        }\n        else {\n            return startingAt(new Date(java.sql.Date.valueOf(startDate).getTime()));\n        }\n    }\n\n    /**\n     * Restarts the sequence at the given local date, in the default time zone\n     * @return this instance, for chaining\n     */\n    public DateSequenceValueGenerator startingAt(@Nonnull LocalDate startDate) {\n        return startingAt(startDate.atStartOfDay());\n    }\n\n    /**\n     * Restarts the sequence at the given local date time, in the default time zone\n     * @return this instance, for chaining\n     */\n    public DateSequenceValueGenerator startingAt(@Nonnull LocalDateTime startDate) {\n        return startingAt(startDate.atZone(ZoneId.systemDefault()));\n    }\n\n    /**\n     * Restarts the sequence at the given zoned date time\n     * @return this instance, for chaining\n     */\n    public DateSequenceValueGenerator startingAt(@Nonnull ZonedDateTime startDate) {\n        next = startDate;\n        return this;\n    }\n\n    /**\n     * Increments the date by the given increment of the given unit.\n     * @return this instance, for chaining\n     * @deprecated use the other {@link #incrementingBy(int, TemporalUnit)} method\n     */\n    @Deprecated\n    public DateSequenceValueGenerator incrementingBy(int increment, @Nonnull CalendarField unit) {\n        Preconditions.checkNotNull(unit, \"unit may not be null\");\n        return incrementingBy(increment, unit.toTemporalUnit());\n    }\n\n    /**\n     * Increments the date by the given increment of the given unit. One of the constants of ChronoField is typically\n     * used for the unit.\n     * @return this instance, for chaining\n     */\n    public DateSequenceValueGenerator incrementingBy(int increment, @Nonnull TemporalUnit unit) {\n        Preconditions.checkNotNull(unit, \"unit may not be null\");\n        this.increment = increment;\n        this.unit = unit;\n        return this;\n    }\n\n    @Override\n    public ZonedDateTime nextValue() {\n        ZonedDateTime result = next;\n        next = next.plus(increment, unit);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"DateSequenceValueGenerator[\"\n               + \"next=\" + next\n               + \", increment=\" + increment\n               + \", unit=\" + unit\n               + \"]\";\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/generator/SequenceValueGenerator.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\n/**\n * A {@link ValueGenerator} which generates a sequence of long values. By default, the sequence starts at 1 and\n * increments by 1, but this can be customized. Instances of this class are created using\n * {@link ValueGenerators#sequence()}.\n * @author JB Nizet\n */\npublic final class SequenceValueGenerator implements ValueGenerator<Long> {\n\n    private long next = 1L;\n    private int increment = 1;\n\n    SequenceValueGenerator() {\n        this(1, 1);\n    }\n\n    private SequenceValueGenerator(long start, int increment) {\n        this.next = start;\n        this.increment = increment;\n    }\n\n    /**\n     * Restarts the sequence at the given value\n     * @param start the starting value of the created generator\n     * @return this instance, for chaining\n     */\n    public SequenceValueGenerator startingAt(long start) {\n        this.next = start;\n        return this;\n    }\n\n    /**\n     * Increments the value by the given increment.\n     * @return this instance, for chaining\n     */\n    public SequenceValueGenerator incrementingBy(int increment) {\n        this.increment = increment;\n        return this;\n    }\n\n    @Override\n    public Long nextValue() {\n        long result = next;\n        next += increment;\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"SequenceValueGenerator[\"\n               + \"next=\"\n               + next\n               + \", increment=\"\n               + increment\n               + \"]\";\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/generator/StringSequenceValueGenerator.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\n/**\n * A {@link ValueGenerator} that returns a string prefix followed by a sequence number, optionally left-padded\n * with 0 to ensure a correct ordering (for example: CODE_001, CODE_002, etc.). Instances of this generator\n * are created by {@link ValueGenerators#stringSequence(String)}.\n * @author JB\n */\npublic final class StringSequenceValueGenerator implements ValueGenerator<String> {\n    private String prefix;\n    private long next;\n    private int increment;\n\n    /**\n     * The length of the number once padded. 0 if no padding must be applied\n     */\n    private int paddedNumberLength;\n\n    StringSequenceValueGenerator(String prefix) {\n        this(prefix, 1L, 1, 0);\n    }\n\n    private StringSequenceValueGenerator(String prefix, long next, int increment, int paddedNumberLength) {\n        this.prefix = prefix;\n        this.next = next;\n        this.increment = increment;\n        this.paddedNumberLength = paddedNumberLength;\n    }\n\n    /**\n     * Tells the generator to left-pad the number it generates with 0 until the length of the number is the given\n     * length. For example, passing 3 to this method will generate numbers 001, 002, 003, 004, etc. If the generated\n     * number, before padding, has a length already equal or larger that the given length, the number is not padded.\n     * @param paddedNumberLength the length of the number once padded. Must be &gt; 0.\n     * @return this instance, for chaining\n     */\n    public StringSequenceValueGenerator withLeftPadding(int paddedNumberLength) {\n        Preconditions.checkArgument(paddedNumberLength > 0, \"paddedNumberLength must be > 0\");\n        this.paddedNumberLength = paddedNumberLength;\n        return this;\n    }\n\n    /**\n     * Tells the generator to avoid left-padding the number it generates with 0\n     * @return this instance, for chaining\n     */\n    public StringSequenceValueGenerator withoutLeftPadding() {\n        this.paddedNumberLength = 0;\n        return this;\n    }\n\n    /**\n     * Restarts the sequence at the given value\n     * @param start the new starting value of the sequence\n     * @return this instance, for chaining\n     */\n    public StringSequenceValueGenerator startingAt(long start) {\n        this.next = start;\n        return this;\n    }\n\n    /**\n     * Increments the number by the given increment.\n     * @return this instance, for chaining\n     */\n    public StringSequenceValueGenerator incrementingBy(int increment) {\n        this.increment = increment;\n        return this;\n    }\n\n    @Override\n    public String nextValue() {\n        long number = next;\n        next += increment;\n        return prefix + leftPadIfNecessary(number);\n    }\n\n    private String leftPadIfNecessary(long number) {\n        String numberAsString = Long.toString(number);\n        if (numberAsString.length() >= paddedNumberLength) {\n            return numberAsString;\n        }\n        StringBuilder builder = new StringBuilder(paddedNumberLength);\n        for (int i = 0; i < paddedNumberLength - numberAsString.length(); i++) {\n            builder.append('0');\n        }\n        return builder.append(numberAsString).toString();\n    }\n\n    @Override\n    public String toString() {\n        return \"StringSequenceValueGenerator[\"\n               + \"prefix='\" + prefix + '\\''\n               + \", next=\" + next\n               + \", increment=\" + increment\n               + \", paddedNumberLength=\" + paddedNumberLength\n               + \"]\";\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/generator/ValueGenerator.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\n/**\n * A value generator allows generating values for a specific column in a sequence of inserts. This is useful when you\n * don't want to specify a value for each of the inserted rows in a table, and when a default value is not an option\n * either because, for example, the column has a unique constraint.\n * @param <T> the type of value that this generator generates\n *\n * @see ValueGenerators for useful implementations of this interface\n * @author JB Nizet\n */\npublic interface ValueGenerator<T> {\n    /**\n     * Called each time a new row is inserted, to get the value to insert in the column using this value generator.\n     * @return the value to insert in the column associated with this generator.\n     */\n    T nextValue();\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/generator/ValueGenerators.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\n\n/**\n * Utility class containing factory methods for {@link ValueGenerator}\n * @author JB Nizet\n */\npublic final class ValueGenerators {\n    private ValueGenerators() {\n    }\n\n    /**\n     * Returns a value generator which generates a sequence of long values starting with 1, with an increment of 1.\n     * The starting value and increment can be customized using\n     * <pre>\n     *     ValueGenerators.increment().startingAt(1000).incrementingBy(5)\n     * </pre>\n     */\n    public static SequenceValueGenerator sequence() {\n        return new SequenceValueGenerator();\n    }\n\n    /**\n     * Returns a value generator which always returns the same, given value.\n     */\n    public static <T> ValueGenerator<T> constant(@Nullable final T constant) {\n        return new ValueGenerator<T>() {\n            @Override\n            public T nextValue() {\n                return constant;\n            }\n\n            @Override\n            public String toString() {\n                return \"ValueGenerators.constant(\" + constant + \")\";\n            }\n        };\n    }\n\n    /**\n     * Returns a value generator that returns a string prefix followed by a sequence number, optionally left-padded\n     * with 0 to ensure a correct ordering (for example: CODE_001, CODE_002, etc.). The returned generator starts the\n     * sequence at 1 and increments by 1, and doesn't pad the numbers.\n     * @param prefix the prefix before the generated number (for example: <code>\"CODE_\"</code>).\n     */\n    public static StringSequenceValueGenerator stringSequence(@Nonnull String prefix) {\n        Preconditions.checkNotNull(prefix, \"prefix may not be null\");\n        return new StringSequenceValueGenerator(prefix);\n    }\n\n    /**\n     * Returns a value generator that returns a sequence of dates, starting at a given date and incremented by a given\n     * time, specified as an increment and a calendar field. The returned generator starts today at 00:00:00\n     * and increments by 1 day by default.\n     */\n    public static DateSequenceValueGenerator dateSequence() {\n        return new DateSequenceValueGenerator();\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/CompositeOperation.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\n\n/**\n * A composite operation or, in other words, an operation which consists in executing a sequence of other operations.\n * @author JB Nizet\n */\n@Immutable\npublic final class CompositeOperation implements Operation {\n\n    private static final Operation NOP = new Operation() {\n\n        @Override\n        public void execute(Connection connection, BinderConfiguration configuration) {\n            // does nothing since it's a NOP\n        }\n\n        @Override\n        public String toString() {\n            return \"NOP\";\n        }\n    };\n\n    private final List<Operation> operations;\n\n    private CompositeOperation(List<? extends Operation> operations) {\n        this.operations = new ArrayList<Operation>(operations);\n    }\n\n    /**\n     * Creates a new Operation containing all the given operations\n     * @param operations the sequence of operations\n     */\n    public static Operation sequenceOf(@Nonnull Operation... operations) {\n        return sequenceOf(Arrays.asList(operations));\n    }\n\n    /**\n     * Creates a new Operation containing all the given operations\n     * @param operations the sequence of operations\n     */\n    public static Operation sequenceOf(@Nonnull List<? extends Operation> operations) {\n        if (operations.isEmpty()) {\n            return NOP;\n        }\n        else if (operations.size() == 1) {\n            return operations.get(0);\n        }\n        return new CompositeOperation(operations);\n    }\n\n    /**\n     * Executes the sequence of operations\n     * @throws SQLException as soon as one of the operations in the sequence throws a SQLException\n     */\n    @Override\n    public void execute(Connection connection, BinderConfiguration configuration) throws SQLException {\n        for (Operation operation : operations) {\n            operation.execute(connection, configuration);\n        }\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder builder = new StringBuilder();\n        boolean first = true;\n        for (Operation operation : operations) {\n            if (!first) {\n                builder.append('\\n');\n            }\n            else {\n                first = false;\n            }\n            builder.append(operation);\n        }\n        return builder.toString();\n    }\n\n    @Override\n    public int hashCode() {\n        return operations.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null) {\n            return false;\n        }\n        if (getClass() != o.getClass()) {\n            return false;\n        }\n        CompositeOperation other = (CompositeOperation) o;\n        return this.operations.equals(other.operations);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/DeleteAll.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * An operation which deletes everything from a given database table.\n * @author JB Nizet\n */\n@Immutable\npublic final class DeleteAll implements Operation {\n\n    private final String table;\n\n    private DeleteAll(String table) {\n        Preconditions.checkNotNull(table, \"table may not be null\");\n        this.table = table;\n    }\n\n    @Override\n    public void execute(Connection connection, BinderConfiguration configuration) throws SQLException {\n        Statement stmt = connection.createStatement();\n        try {\n            stmt.executeUpdate(\"delete from \" + table);\n        }\n        finally {\n            stmt.close();\n        }\n    }\n\n    /**\n     * Returns an operation which deletes all the rows from the given table.\n     * @param table the table to delete everything from.\n     */\n    public static DeleteAll from(@Nonnull String table) {\n        return new DeleteAll(table);\n    }\n\n    /**\n     * Returns a composite operation which deletes all the rows from the given tables, in the same order as the\n     * tables. If A has a foreign key to B, which has a foreign key to C, tables should be listed in the following\n     * order: A, B, C. Otherwise, referential constraint will break. If there is a cycle in the dependencies, you might\n     * want to use a sequence of {@link SqlOperation} to disable the foreign key constraints, then delete everything\n     * from the tables, then use another sequence of {@link SqlOperation} to re-enable the foreign key constraints.\n     * @param tables the tables to delete everything from.\n     */\n    public static Operation from(@Nonnull String... tables) {\n        return from(Arrays.asList(tables));\n    }\n\n    /**\n     * Returns a composite operation which deletes all the rows from the given tables, in the same order as the\n     * tables. If A has a foreign key to B, which has a foreign key to C, tables should be listed in the following\n     * order: A, B, C. Otherwise, referential constraint will break. If there is a cycle in the dependencies, you might\n     * want to use a sequence of {@link SqlOperation} to disable the foreign key constraints, then delete everything\n     * from the tables, then use another sequence of {@link SqlOperation} to re-enable the foreign key constraints.\n     * @param tables the tables to delete everything from.\n     */\n    public static Operation from(@Nonnull List<String> tables) {\n        List<DeleteAll> operations = new ArrayList<DeleteAll>(tables.size());\n        for (String table : tables) {\n            operations.add(new DeleteAll(table));\n        }\n        return CompositeOperation.sequenceOf(operations);\n    }\n\n    @Override\n    public String toString() {\n        return \"delete from \" + table;\n    }\n\n    @Override\n    public int hashCode() {\n        return table.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        DeleteAll other = (DeleteAll) obj;\n        return this.table.equals(other.table);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/Insert.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\nimport java.sql.Connection;\nimport java.sql.ParameterMetaData;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport com.ninja_squad.dbsetup.bind.Binder;\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.bind.Binders;\nimport com.ninja_squad.dbsetup.generator.ValueGenerator;\nimport com.ninja_squad.dbsetup.generator.ValueGenerators;\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\n/**\n * Operation which inserts one or several rows into a table. Example usage:\n * <pre>\n *   Insert insert =\n *       Insert.into(\"CLIENT\")\n *             .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"CLIENT_TYPE\")\n *             .values(1L, \"John\", \"Doe\", \"1975-07-19\", ClientType.NORMAL)\n *             .values(2L, \"Jack\", \"Smith\", \"1969-08-22\", ClientType.HIGH_PRIORITY)\n *             .withDefaultValue(\"DELETED\", false)\n *             .withDefaultValue(\"VERSION\", 1)\n *             .withBinder(new ClientTypeBinder(), \"CLIENT_TYPE\")\n *             .build();\n * </pre>\n *\n * The above operation will insert two rows inside the CLIENT table. For each row, the column DELETED will be set to\n * <code>false</code> and the column VERSION will be set to 1. For the column CLIENT_TYPE, instead of using the\n * {@link Binder} associated to the type of the column found in the metadata of the table, a custom binder will be used.\n * <p>\n * Instead of specifying values as an ordered sequence which must match the sequence of column names, some might prefer\n * passing a map of column/value associations. This makes things more verbose, but can be more readable in some cases,\n * when the number of columns is high. This also allows not specifying any value for columns that must stay null.\n * The map can be constructed like any other map and passed to the builder, or it can be added using a fluent builder.\n * The following snippet:\n *\n * <pre>\n *   Insert insert =\n *       Insert.into(\"CLIENT\")\n *             .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"CLIENT_TYPE\")\n *             .row().column(\"CLIENT_ID\", 1L)\n *                   .column(\"FIRST_NAME\", \"John\")\n *                   .column(\"LAST_NAME\", \"Doe\")\n *                   .column(\"DATE_OF_BIRTH\", \"1975-07-19\")\n *                   .end()\n *             .row().column(\"CLIENT_ID\", 2L)\n *                   .column(\"FIRST_NAME\", \"Jack\")\n *                   .column(\"LAST_NAME\", \"Smith\")\n *                   .end() // null date of birth, because it's not in the row\n *             .build();\n * </pre>\n *\n * is thus equivalent to:\n *\n * <pre>\n *   Map&lt;String, Object&gt; johnDoe = new HashMap&lt;String, Object&gt;();\n *   johnDoe.put(\"CLIENT_ID\", 1L);\n *   johnDoe.put(\"FIRST_NAME\", \"John\");\n *   johnDoe.put(\"LAST_NAME\", \"Doe\");\n *   johnDoe.put(\"DATE_OF_BIRTH\", \"1975-07-19\");\n *\n *   Map&lt;String, Object&gt; jackSmith = new HashMap&lt;String, Object&gt;();\n *   jackSmith.put(\"CLIENT_ID\", 2L);\n *   jackSmith.put(\"FIRST_NAME\", \"Jack\");\n *   jackSmith.put(\"LAST_NAME\", \"Smith\");\n *\n *   Insert insert =\n *       Insert.into(\"CLIENT\")\n *             .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"CLIENT_TYPE\")\n *             .values(johnDoe)\n *             .values(jackSmith)\n *             .build();\n * </pre>\n *\n * When building the Insert using column/value associations, it might seem redundant to specify the set of column names\n * before inserting the rows. Remember, though, that all the rows of an Insert are inserted using the same\n * parameterized SQL query. We thus need a robust and easy way to know all the columns to insert for every row of the\n * insert. To be able to spot errors easily and early, and to avoid complex rules, the rule is thus simple: the set of\n * columns (excluding the generated ones) is specified either by columns(), or by the columns of the first row. All the\n * subsequent rows may not have additional columns. And <code>null</code> is inserted for all the absent columns of the\n * subsequent rows. The above example can thus be written as\n *\n * <pre>\n *   Insert insert =\n *       Insert.into(\"CLIENT\")\n *             .row().column(\"CLIENT_ID\", 1L)\n *                   .column(\"FIRST_NAME\", \"John\")\n *                   .column(\"LAST_NAME\", \"Doe\")\n *                   .column(\"DATE_OF_BIRTH\", \"1975-07-19\")\n *                   .end()\n *             .row().column(\"CLIENT_ID\", 2L)\n *                   .column(\"FIRST_NAME\", \"Jack\")\n *                   .column(\"LAST_NAME\", \"Smith\")\n *                   .end() // null date of birth, because it's not in the row\n *             .build();\n * </pre>\n *\n * but the following will throw an exception, because the DATE_OF_BIRTH column is not part of the first row:\n *\n * <pre>\n *   Insert insert =\n *       Insert.into(\"CLIENT\")\n *             .row().column(\"CLIENT_ID\", 2L)\n *                   .column(\"FIRST_NAME\", \"Jack\")\n *                   .column(\"LAST_NAME\", \"Smith\")\n *                   .column(\"CLIENT_TYPE\", ClientType.HIGH_PRIORITY)\n *                   .end()\n *             .row().column(\"CLIENT_ID\", 1L)\n *                   .column(\"FIRST_NAME\", \"John\")\n *                   .column(\"LAST_NAME\", \"Doe\")\n *                   .column(\"DATE_OF_BIRTH\", \"1975-07-19\")\n *                   .column(\"CLIENT_TYPE\", ClientType.NORMAL)\n *                   .end()\n *             .build();\n * </pre>\n *\n * @author JB Nizet\n */\n@Immutable\npublic final class Insert implements Operation {\n    private final String table;\n    private final List<String> columnNames;\n    private final Map<String, List<Object>> generatedValues;\n    private final List<List<?>> rows;\n    private final boolean metadataUsed;\n\n    private final Map<String, Binder> binders;\n\n    private Insert(Builder builder) {\n        this.table = builder.table;\n        this.columnNames = builder.columnNames;\n        this.rows = builder.rows;\n        this.generatedValues = generateValues(builder.valueGenerators, rows.size());\n        this.binders = builder.binders;\n        this.metadataUsed = builder.metadataUsed;\n    }\n\n    private Map<String, List<Object>> generateValues(Map<String, ValueGenerator<?>> valueGenerators,\n                                                      int count) {\n        Map<String, List<Object>> result = new LinkedHashMap<String, List<Object>>();\n        for (Map.Entry<String, ValueGenerator<?>> entry : valueGenerators.entrySet()) {\n            result.put(entry.getKey(), generateValues(entry.getValue(), count));\n        }\n        return result;\n    }\n\n    private List<Object> generateValues(ValueGenerator<?> valueGenerator, int count) {\n        List<Object> result = new ArrayList<Object>(count);\n        for (int i = 0; i < count; i++) {\n            result.add(valueGenerator.nextValue());\n        }\n        return result;\n    }\n\n    /**\n     * Inserts the values and generated values in the table. Unless <code>useMetadata</code> has been set to\n     * <code>false</code>, the given configuration is used to get the appropriate binder. Nevertheless, if a binder\n     * has explicitly been associated to a given column, this binder will always be used for this column.\n     */\n    @edu.umd.cs.findbugs.annotations.SuppressWarnings(\n        value = \"SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING\",\n        justification = \"The point here is precisely to compose a SQL String from column names coming from the user\")\n    @Override\n    public void execute(Connection connection, BinderConfiguration configuration) throws SQLException {\n        List<String> allColumnNames = new ArrayList<String>(columnNames);\n        allColumnNames.addAll(generatedValues.keySet());\n\n        String query = generateSqlQuery(allColumnNames);\n\n        PreparedStatement stmt = connection.prepareStatement(query);\n\n        try {\n            Map<String, Binder> usedBinders = initializeBinders(stmt, allColumnNames, configuration);\n\n            int rowIndex = 0;\n            for (List<?> row : rows) {\n                int i = 0;\n                for (Object value : row) {\n                    String columnName = columnNames.get(i);\n                    Binder binder = usedBinders.get(columnName);\n                    binder.bind(stmt, i + 1, value);\n                    i++;\n                }\n                for (Map.Entry<String, List<Object>> entry : generatedValues.entrySet()) {\n                    String columnName = entry.getKey();\n                    List<Object> rowValues = entry.getValue();\n                    Binder binder = usedBinders.get(columnName);\n                    binder.bind(stmt, i + 1, rowValues.get(rowIndex));\n                    i++;\n                }\n\n                stmt.executeUpdate();\n                rowIndex++;\n            }\n        }\n        finally {\n            stmt.close();\n        }\n    }\n\n    /**\n     * Gets the number of rows that are inserted in the database table when this insert operation is executed.\n     */\n    public int getRowCount() {\n        return rows.size();\n    }\n\n    private String generateSqlQuery(List<String> allColumnNames) {\n        StringBuilder sql = new StringBuilder(\"insert into \").append(table).append(\" (\");\n        for (Iterator<String> it = allColumnNames.iterator(); it.hasNext(); ) {\n            String columnName = it.next();\n            sql.append(columnName);\n            if (it.hasNext()) {\n                sql.append(\", \");\n            }\n        }\n        sql.append(\") values (\");\n        for (Iterator<String> it = allColumnNames.iterator(); it.hasNext(); ) {\n            it.next();\n            sql.append('?');\n            if (it.hasNext()) {\n                sql.append(\", \");\n            }\n        }\n        sql.append(')');\n\n        return sql.toString();\n    }\n\n    private Map<String, Binder> initializeBinders(PreparedStatement stmt,\n                                                  List<String> allColumnNames,\n                                                  BinderConfiguration configuration) throws SQLException {\n        Map<String, Binder> result = new HashMap<String, Binder>();\n        ParameterMetaData metadata = null;\n        if (metadataUsed) {\n            try {\n                metadata = stmt.getParameterMetaData();\n            }\n            catch (SQLException e) {\n                metadata = null;\n                // the parameter metadata are probably not supported by the database. Pass null to the configuration.\n                // The default configuration will return the default binder, just as if useMetadata(false) had been used\n            }\n        }\n        int i = 1;\n        for (String columnName : allColumnNames) {\n            Binder binder = this.binders.get(columnName);\n            if (binder == null) {\n                binder = configuration.getBinder(metadata, i);\n                if (binder == null) {\n                    throw new IllegalStateException(\"null binder returned from configuration \"\n                                                    + configuration.getClass());\n                }\n            }\n            result.put(columnName, binder);\n            i++;\n        }\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return \"insert into \"\n               + table\n               + \" [columns=\"\n               + columnNames\n               + \", generatedValues=\"\n               + generatedValues\n               + \", rows=\"\n               + rows\n               + \", metadataUsed=\"\n               + metadataUsed\n               + \", binders=\"\n               + binders\n               + \"]\";\n\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + binders.hashCode();\n        result = prime * result + columnNames.hashCode();\n        result = prime * result + generatedValues.hashCode();\n        result = prime * result + Boolean.valueOf(metadataUsed).hashCode();\n        result = prime * result + rows.hashCode();\n        result = prime * result + table.hashCode();\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Insert other = (Insert) obj;\n\n        return binders.equals(other.binders)\n               && columnNames.equals(other.columnNames)\n               && generatedValues.equals(other.generatedValues)\n               && metadataUsed == other.metadataUsed\n               && rows.equals(other.rows)\n               && table.equals(other.table);\n    }\n\n    /**\n     * Creates a new Builder instance, in order to build an Insert operation into the given table\n     * @param table the name of the table to insert into\n     * @return the created Builder\n     */\n    public static Builder into(@Nonnull String table) {\n        Preconditions.checkNotNull(table, \"table may not be null\");\n        return new Builder(table);\n    }\n\n    /**\n     * A builder used to create an Insert operation. Such a builder may only be used once. Once it has built its Insert\n     * operation, all its methods throw an {@link IllegalStateException}.\n     * @see Insert\n     * @see Insert#into(String)\n     * @author JB Nizet\n     */\n    public static final class Builder {\n        private final String table;\n        private final List<String> columnNames = new ArrayList<String>();\n        private final Map<String, ValueGenerator<?>> valueGenerators = new LinkedHashMap<String, ValueGenerator<?>>();\n        private final List<List<?>> rows = new ArrayList<List<?>>();\n\n        private boolean metadataUsed = true;\n        private final Map<String, Binder> binders = new HashMap<String, Binder>();\n\n        private boolean built;\n\n        private Builder(String table) {\n            this.table = table;\n        }\n\n        /**\n         * Specifies the list of columns into which values will be inserted. The values must the be specified, after,\n         * using the {@link #values(Object...)} method, or with the {@link #values(java.util.Map)} method, or by adding\n         * a row with named columns fluently using {@link #row()}.\n         * @param columns the names of the columns to insert into.\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built, or if this method has already been\n         * called, or if one of the given columns is also specified as one of the generated value columns, or if the\n         * set of columns has already been defined by adding a first row to the builder.\n         */\n        public Builder columns(@Nonnull String... columns) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkState(columnNames.isEmpty(), \"columns have already been specified\");\n            for (String column : columns) {\n                Preconditions.checkNotNull(column, \"column may not be null\");\n                Preconditions.checkState(!valueGenerators.containsKey(column),\n                                         \"column \"\n                                             + column\n                                             + \" has already been specified as generated value column\");\n            }\n            columnNames.addAll(Arrays.asList(columns));\n            return this;\n        }\n\n        /**\n         * Adds a row of values to insert.\n         * @param values the values to insert.\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built, or if the number of values doesn't match\n         * the number of columns.\n         */\n        public Builder values(@Nonnull Object... values) {\n            return addRepeatingValues(Arrays.asList(values), 1);\n        }\n\n        /**\n         * Allows adding many rows with the same non-generated values to insert.\n         * @param values the values to insert.\n         * @return A RowRepeater, allowing to choose how many similar rows to add.\n         * @throws IllegalStateException if the Insert has already been built, or if the number of values doesn't match\n         * the number of columns.\n         */\n        public RowRepeater repeatingValues(@Nonnull Object... values) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkArgument(values.length == columnNames.size(),\n                                        \"The number of values doesn't match the number of columns\");\n            return new ListRowRepeater(this, Arrays.asList(values));\n        }\n\n        /**\n         * Starts building a new row with named columns to insert. If the row is the first one being added and the\n         * columns haven't been set yet by calling <code>columns()</code>, then the columns of this row constitute the\n         * column names (excluding the generated ones) of the Insert being built\n         * @return a {@link RowBuilder} instance, which, when built, will add a row (or several ones) to this insert\n         * builder.\n         * @throws IllegalStateException if the Insert has already been built.\n         * @see RowBuilder\n         */\n        public RowBuilder row() {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            return new RowBuilder(this);\n        }\n\n        /**\n         * Adds a row to this builder. If no row has been added yet and the columns haven't been set yet by calling\n         * <code>columns()</code>, then the keys of this map constitute the column names (excluding the generated ones)\n         * of the Insert being built, in the order of the keys in the map (which is arbitrary unless an ordered or\n         * sorted map is used).\n         * @param row the row to add. The keys of the map are the column names, which must match with\n         * the column names specified in the call to {@link #columns(String...)}, or with the column names of the first\n         * added row. If a column name is not present in the map, null is inserted for this column.\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built.\n         * @throws IllegalArgumentException if a column name of the map doesn't match with any of the column names\n         * specified with {@link #columns(String...)}\n         */\n        public Builder values(@Nonnull Map<String, ?> row) {\n            return addRepeatingValues(row, 1);\n        }\n\n        /**\n         * Allows adding many rows with the same non-generated values to insert.\n         * @return A RowRepeater, allowing to choose how many similar rows to add.\n         * @throws IllegalStateException if the Insert has already been built.\n         * @see #values(Map)\n         */\n        public RowRepeater repeatingValues(@Nonnull Map<String, ?> row) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkNotNull(row, \"The row may not be null\");\n            return new MapRowRepeater(this, row);\n        }\n\n        /**\n         * Associates a Binder to one or several columns.\n         * @param binder the binder to use, regardless of the metadata, for the given columns\n         * @param columns the name of the columns to associate with the given Binder\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built,\n         * @throws IllegalArgumentException if any of the given columns is not\n         * part of the columns or \"generated value\" columns.\n         */\n        public Builder withBinder(@Nonnull Binder binder, @Nonnull String... columns) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkNotNull(binder, \"binder may not be null\");\n            for (String columnName : columns) {\n                Preconditions.checkArgument(this.columnNames.contains(columnName)\n                                            || this.valueGenerators.containsKey(columnName),\n                                            \"column \"\n                                                + columnName\n                                                + \" is not one of the registered column names\");\n                binders.put(columnName, binder);\n            }\n            return this;\n        }\n\n        /**\n         * Specifies a default value to be inserted in a column for all the rows inserted by the Insert operation.\n         * Calling this method is equivalent to calling\n         * <code>withGeneratedValue(column, ValueGenerators.constant(value))</code>\n         * @param column the name of the column\n         * @param value the default value to insert into the column\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built, or if the given column is part\n         * of the columns to insert.\n         */\n        public Builder withDefaultValue(@Nonnull String column, Object value) {\n            return withGeneratedValue(column, ValueGenerators.constant(value));\n        }\n\n        /**\n         * Allows the given column to be populated by a value generator, which will be called for every row of the\n         * Insert operation being built.\n         * @param column the name of the column\n         * @param valueGenerator the generator generating values for the given column of every row\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built, or if the given column is part\n         * of the columns to insert.\n         */\n        public Builder withGeneratedValue(@Nonnull String column, @Nonnull ValueGenerator<?> valueGenerator) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkNotNull(column, \"column may not be null\");\n            Preconditions.checkNotNull(valueGenerator, \"valueGenerator may not be null\");\n            Preconditions.checkArgument(!columnNames.contains(column),\n                                        \"column \"\n                                        + column\n                                        + \" is already listed in the list of column names\");\n            valueGenerators.put(column, valueGenerator);\n            return this;\n        }\n\n        /**\n         * Determines if the metadata must be used to get the appropriate binder for each inserted column (except\n         * the ones which have been associated explicitly with a Binder). The default is <code>true</code>. The insert\n         * can be faster if set to <code>false</code>, but in this case, the binder used will be the one returned\n         * by the {@link BinderConfiguration} for a null metadata (which is, by default, the\n         * {@link Binders#defaultBinder() default binder}), except the ones which have been associated explicitly with\n         * a Binder.<br>\n         * Before version 1.3.0, a SQLException was thrown if the database doesn't support parameter metadata and\n         * <code>useMetadata(false)</code> wasn't called. Since version 1.3.0, if <code>useMetadata</code> is true\n         * (the default) but the database doesn't support metadata, then the default binder configuration returns the\n         * default binder. Using this method is thus normally unnecessary as of 1.3.0.\n         * @return this Builder instance, for chaining.\n         * @throws IllegalStateException if the Insert has already been built.\n         */\n        public Builder useMetadata(boolean useMetadata) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            this.metadataUsed = useMetadata;\n            return this;\n        }\n\n        /**\n         * Builds the Insert operation.\n         * @return the created Insert operation.\n         * @throws IllegalStateException if the Insert has already been built, or if no column and no generated value\n         * column has been specified.\n         */\n        public Insert build() {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkState(!this.columnNames.isEmpty() || !this.valueGenerators.isEmpty(),\n                                     \"no column and no generated value column has been specified\");\n            built = true;\n            return new Insert(this);\n        }\n\n        @Override\n        public String toString() {\n            return \"insert into \"\n                + table\n                + \" [columns=\"\n                + columnNames\n                + \", rows=\"\n                + rows\n                + \", valueGenerators=\"\n                + valueGenerators\n                + \", metadataUsed=\"\n                + metadataUsed\n                + \", binders=\"\n                + binders\n                + \", built=\"\n                + built\n                + \"]\";\n        }\n\n        private Builder addRepeatingValues(List<?> values, int times) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkArgument(values.size() == columnNames.size(),\n                                        \"The number of values doesn't match the number of columns\");\n\n            List<Object> row = new ArrayList<Object>(values);\n            for (int i = 0; i < times; i++) {\n                rows.add(row);\n            }\n            return this;\n        }\n\n        private Builder addRepeatingValues(@Nonnull Map<String, ?> row, int times) {\n            Preconditions.checkState(!built, \"The insert has already been built\");\n            Preconditions.checkNotNull(row, \"The row may not be null\");\n\n            List<Object> values = mapToRow(row);\n            for (int i = 0; i < times; i++) {\n                rows.add(values);\n            }\n            return this;\n        }\n\n        private List<Object> mapToRow(@Nonnull Map<String, ?> row) {\n            boolean setColumns = rows.isEmpty() && columnNames.isEmpty();\n            if (setColumns) {\n                columns(row.keySet().toArray(new String[row.size()]));\n            }\n            else {\n                Set<String> rowColumnNames = new HashSet<String>(row.keySet());\n                rowColumnNames.removeAll(columnNames);\n                if (!rowColumnNames.isEmpty()) {\n                    throw new IllegalArgumentException(\n                        \"The following columns of the row don't match with any column name: \" + rowColumnNames);\n                }\n            }\n\n            List<Object> values = new ArrayList<Object>(columnNames.size());\n            for (String columnName : columnNames) {\n                values.add(row.get(columnName));\n            }\n            return values;\n        }\n    }\n\n    /**\n     * A row builder, constructed with {@link com.ninja_squad.dbsetup.operation.Insert.Builder#row()}. This builder\n     * allows adding a row with named columns to an Insert:\n     *\n     * <pre>\n     *   Insert insert =\n     *       Insert.into(\"CLIENT\")\n     *             .columns(\"CLIENT_ID\", \"FIRST_NAME\", \"LAST_NAME\", \"DATE_OF_BIRTH\", \"CLIENT_TYPE\")\n     *             .row().column(\"CLIENT_ID\", 1L)\n     *                   .column(\"FIRST_NAME\", \"John\")\n     *                   .column(\"LAST_NAME\", \"Doe\")\n     *                   .column(\"DATE_OF_BIRTH\", \"1975-07-19\")\n     *                   .column(\"CLIENT_TYPE\", ClientType.NORMAL)\n     *                   .end()\n     *             .row().column(\"CLIENT_ID\", 2L)\n     *                   .column(\"FIRST_NAME\", \"Jack\")\n     *                   .column(\"LAST_NAME\", \"Smith\")\n     *                   .column(\"DATE_OF_BIRTH\", \"1969-08-22\")\n     *                   .column(\"CLIENT_TYPE\", ClientType.HIGH_PRIORITY)\n     *                   .end()\n     *             .build();\n     * </pre>\n     *\n     * You may omit the call to <code>columns()</code>. In that case, the columns of the Insert will be the columns\n     * specified in the first added row.\n     */\n    public static final class RowBuilder {\n        private final Builder builder;\n        private final Map<String, Object> row;\n        private boolean ended;\n\n        private RowBuilder(Builder builder) {\n            this.builder = builder;\n            // note: very important to use a LinkedHashMap here, to guarantee the ordering of the columns.\n            this.row = new LinkedHashMap<String, Object>();\n        }\n\n        /**\n         * Adds a new named column to the row. If a previous value has already been added for the same column, it's\n         * replaced by this new value.\n         * @param name the name of the column, which must match with a column name defined in the Insert Builder\n         * @param value the value of the column for the constructed row\n         * @return this builder, for chaining\n         * @throws IllegalArgumentException if the given name is not the name of one of the columns to insert\n         */\n        public RowBuilder column(@Nonnull String name, Object value) {\n            Preconditions.checkState(!ended, \"The row has already been ended and added to the Insert Builder\");\n            if (!builder.columnNames.isEmpty()) {\n                Preconditions.checkNotNull(name, \"the column name may not be null\");\n                Preconditions.checkArgument(builder.columnNames.contains(name),\n                                            \"column \" + name + \" is not one of the registered column names\");\n            }\n            row.put(name, value);\n            return this;\n        }\n\n        /**\n         * Ends the row, adds it to the Insert Builder and returns it, for chaining.\n         * @return the Insert Builder\n         */\n        public Builder end() {\n            Preconditions.checkState(!ended, \"The row has already been ended and added to the Insert Builder\");\n            ended = true;\n            return builder.values(row);\n        }\n\n        /**\n         * Ends the row, adds it to the Insert Builder the given amount of times, and returns it, for chaining.\n         * @param times the number of rows to add. Must be &gt;= 0. If zero, no row is added.\n         * @return the Insert Builder\n         */\n        public Builder times(int times) {\n            Preconditions.checkArgument(times >= 0, \"the number of repeating values must be >= 0\");\n            Preconditions.checkState(!ended, \"The row has already been ended and added to the Insert Builder\");\n            ended = true;\n            return builder.addRepeatingValues(row, times);\n        }\n    }\n\n    /**\n     * Allows inserting the same list of non-generated values several times.\n     */\n    public interface RowRepeater {\n        /**\n         * Adds several rows with the same non-generated values to the insert. This method can only be called once.\n         * @param times the number of rows to add. Must be &gt;= 0. If zero, no row is added.\n         * @return the Insert Builder, for chaining\n         * @throws IllegalStateException if the rows have already been added\n         */\n        Builder times(int times);\n    }\n\n    /**\n     * Base abstract class for row repeaters.\n     */\n    private abstract static class AbstractRowRepeater implements RowRepeater {\n        protected final Builder builder;\n        private boolean ended;\n\n        public AbstractRowRepeater(Builder builder) {\n            this.builder = builder;\n        }\n\n        protected abstract Builder doTimes(int times);\n\n        @Override\n        public Builder times(int times) {\n            Preconditions.checkArgument(times >= 0, \"the number of repeating values must be >= 0\");\n            Preconditions.checkState(!ended, \"The rows have already been ended and added to the Insert Builder\");\n            ended = true;\n            return doTimes(times);\n        }\n    }\n\n    /**\n     * Allows inserting the same list of non-generated values as list several times.\n     */\n    private static final class ListRowRepeater extends AbstractRowRepeater {\n        private final List<Object> values;\n\n        private ListRowRepeater(Builder builder, List<Object> values) {\n            super(builder);\n            this.values = values;\n        }\n\n        @Override\n        public Builder doTimes(int times) {\n            return builder.addRepeatingValues(values, times);\n        }\n    }\n\n    /**\n     * Allows inserting the same list of non-generated values as map several times.\n     */\n    private static final class MapRowRepeater extends AbstractRowRepeater {\n        private final Map<String, ?> values;\n\n        private MapRowRepeater(Builder builder, Map<String, ?> values) {\n            super(builder);\n            this.values = values;\n        }\n\n        @Override\n        public Builder doTimes(int times) {\n            return builder.addRepeatingValues(values, times);\n        }\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/Operation.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport com.ninja_squad.dbsetup.DbSetupTracker;\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\n\n/**\n * An operation that the database setup executes. It's advised to make implementations of this interface\n * immutable, and to make them implement equals and hashCode in order for {@link DbSetupTracker} to function\n * properly.\n * @author JB Nizet\n */\npublic interface Operation {\n\n    /**\n     * Executes the operation\n     * @param connection the connection used to execute the operation\n     * @param configuration the binder configuration, used to get appropriate binders based on the metadata of\n     * the prepared statements\n     * @throws SQLException if the execution throws a SQLException\n     */\n    void execute(Connection connection, BinderConfiguration configuration) throws SQLException;\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/SqlOperation.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.util.Preconditions;\n\n/**\n * An operation which simply executes a SQL statement (using {@link Statement#executeUpdate(String)}). It can be useful,\n * for example, to disable or re-enable constraints before/after deleting everything from tables, or inserting into\n * tables having cross references.\n * @author JB Nizet\n */\n@Immutable\npublic final class SqlOperation implements Operation {\n\n    private final String sql;\n\n    /**\n     * Constructor\n     * @param sql the SQL query to execute\n     */\n    private SqlOperation(String sql) {\n        Preconditions.checkNotNull(sql, \"sql may not be null\");\n        this.sql = sql;\n    }\n\n    @Override\n    public void execute(Connection connection, BinderConfiguration configuration) throws SQLException {\n        Statement stmt = connection.createStatement();\n        try {\n            stmt.executeUpdate(sql);\n        }\n        finally {\n            stmt.close();\n        }\n    }\n\n    /**\n     * Creates a SqlOperation for the given SQL statement\n     * @param sqlStatement the SQL statement to execute\n     * @return the created SqlOperation\n     */\n    public static SqlOperation of(@Nonnull String sqlStatement) {\n        return new SqlOperation(sqlStatement);\n    }\n\n    /**\n     * Creates a sequence of SqlOperation for the given SQL statements.\n     * @param sqlStatements the SQL statements to execute\n     * @return the created sequence of operations\n     */\n    public static Operation of(@Nonnull String... sqlStatements) {\n        return of(Arrays.asList(sqlStatements));\n    }\n\n    /**\n     * Creates a sequence of SqlOperation for the given SQL statements.\n     * @param sqlStatements the SQL statements to execute\n     * @return the created sequence of operations\n     */\n    public static Operation of(@Nonnull List<String> sqlStatements) {\n        List<SqlOperation> operations = new ArrayList<SqlOperation>(sqlStatements.size());\n        for (String sql : sqlStatements) {\n            operations.add(new SqlOperation(sql));\n        }\n        return CompositeOperation.sequenceOf(operations);\n    }\n\n    @Override\n    public String toString() {\n        return sql;\n    }\n\n    @Override\n    public int hashCode() {\n        return sql.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (o == this) {\n            return true;\n        }\n        if (o == null) {\n            return false;\n        }\n        if (o.getClass() != this.getClass()) {\n            return false;\n        }\n\n        SqlOperation other = (SqlOperation) o;\n        return this.sql.equals(other.sql);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/operation/Truncate.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Nonnull;\nimport javax.annotation.concurrent.Immutable;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\n\n/**\n * An operation which deletes everything from a given database table using a TRUNCATE statement., which is sometimes\n * faster that using a DELETE statement.\n * @author JB Nizet\n */\n@Immutable\npublic final class Truncate implements Operation {\n\n    private final String tableToTruncate;\n\n    private Truncate(String table) {\n        this.tableToTruncate = table;\n    }\n\n    @Override\n    public void execute(Connection connection, BinderConfiguration configuration) throws SQLException {\n        Statement stmt = connection.createStatement();\n        try {\n            stmt.executeUpdate(\"truncate table \" + tableToTruncate);\n        }\n        finally {\n            stmt.close();\n        }\n    }\n\n    /**\n     * Returns an operation which truncates the given table.\n     * @param table the table to delete everything from.\n     */\n    public static Truncate table(@Nonnull String table) {\n        return new Truncate(table);\n    }\n\n    /**\n     * Returns a composite operation which truncates the given tables, in the same order as the\n     * tables. If A has a foreign key to B, which has a foreign key to C, tables should be listed in the following\n     * order: A, B, C. Otherwise, referential constraint will break. If there is a cycle in the dependencies, you might\n     * want to use a sequence of {@link SqlOperation} to disable the foreign key constraints, then truncate the tables,\n     * then use another sequence of {@link SqlOperation} to re-enable the foreign key constraints.\n     * @param tables the tables to truncate.\n     */\n    public static Operation tables(String... tables) {\n        return tables(Arrays.asList(tables));\n    }\n\n    /**\n     * Returns a composite operation which truncates the given tables, in the same order as the\n     * tables. If A has a foreign key to B, which has a foreign key to C, tables should be listed in the following\n     * order: A, B, C. Otherwise, referential constraint will break. If there is a cycle in the dependencies, you might\n     * want to use a sequence of {@link SqlOperation} to disable the foreign key constraints, then truncate the tables,\n     * then use another sequence of {@link SqlOperation} to re-enable the foreign key constraints.\n     * @param tables the tables to truncate.\n     */\n    public static Operation tables(List<String> tables) {\n        List<Truncate> operations = new ArrayList<Truncate>(tables.size());\n        for (String table : tables) {\n            operations.add(new Truncate(table));\n        }\n        return CompositeOperation.sequenceOf(operations);\n    }\n\n    @Override\n    public String toString() {\n        return \"truncate table \" + tableToTruncate;\n    }\n\n    @Override\n    public int hashCode() {\n        return tableToTruncate.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Truncate other = (Truncate) obj;\n        return this.tableToTruncate.equals(other.tableToTruncate);\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/overview.html",
    "content": "<html>\n    <body>\n DbSetup allows populating a database before executing automated integration tests\n (typically, DAO/Repository automated tests). Although DBUnit, which is a great project, allows doing the same thing\n and much more,  it's also harder to use and setup. And in our experience, in 98% of the cases, DBUnit is only used\n to pre-populate a database before executing every test method. This is the task on which DbSetup concentrates.\n <p>\n The philosophy of DbSetup is that DAO tests should not have to setup the database, execute tests, and then\n remove everything from the database. Instead, a single setup method should be used to delete everything from the\n database (whatever the previous test put in it, or the initial state of the database tables), and then populate it\n with the data necessary to execute the test.\n <p>\n Another design choice of DbSetup is to provide an easy to use and simple Java API to populate the database, rather than\n loading data from external XML files. Using a Java API has several advantages:\n <ul>\n   <li>It allows using real Java types as data (longs, enums, etc.)</li>\n   <li>It allows defining default values, looping to generate several similar rows, storing data sets in variables\n       or factorizing their creation using reusable methods\n   <li>It allows viewing the data sets easily, without having to open external files, by storing the data set\n       directly into the test class, or by navigating through classes and methods using the IDE shortcuts.</li>\n   <li>For more complex situations, like cyclic referential integrity constraints between rows, the Java API\n       allows easily integrating SQL statements into the sequence of operations to execute to pre-populate the\n       database. These SQL statements can, for example, disable constraints and re-enable them, or update rows\n       after their insertion.</li>\n </ul>\n The {@link com.ninja_squad.dbsetup.DbSetup} class and the {@link com.ninja_squad.dbsetup.DbSetupTracker} classes\n are the main entry points to the API that an automated test will use. Look at their javadoc below for example usage.\n    </body>\n</html>"
  },
  {
    "path": "DbSetup-core/src/main/java/com/ninja_squad/dbsetup/util/Preconditions.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.util;\n\n/**\n * Utility class to help verifying preconditions\n * @author JB Nizet\n */\npublic final class Preconditions {\n    private Preconditions() {\n    }\n\n    /**\n     * Throws a NullPointerException with the given message if the given argument is <code>null</code>.\n     * @param argument the argument to check for <code>null</code>\n     * @param message the message of the thrown NullPointerException\n     * @throws NullPointerException if argument is <code>null</code>.\n     */\n    public static void checkNotNull(Object argument, String message) throws NullPointerException {\n        if (argument == null) {\n            throw new NullPointerException(message);\n        }\n    }\n\n    /**\n     * Throws an IllegalStateException with the given message if the given condition is <code>false</code>.\n     * @param condition the condition to check\n     * @param message the message of the thrown IllegalStateException\n     * @throws IllegalStateException if the condition is <code>false</code>.\n     */\n    public static void checkState(boolean condition, String message) throws IllegalStateException {\n        if (!condition) {\n            throw new IllegalStateException(message);\n        }\n    }\n\n    /**\n     * Throws an IllegalARgumentException with the given message if the given condition is <code>false</code>.\n     * @param condition the condition to check\n     * @param message the message of the thrown IllegalArgumentException\n     * @throws IllegalArgumentException if the condition is <code>false</code>.\n     */\n    public static void checkArgument(boolean condition, String message) throws IllegalStateException {\n        if (!condition) {\n            throw new IllegalArgumentException(message);\n        }\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/DbSetupTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\nimport com.ninja_squad.dbsetup.destination.Destination;\nimport com.ninja_squad.dbsetup.operation.Operation;\n\n/**\n * @author JB Nizet\n */\npublic class DbSetupTest {\n\n    @Test\n    public void launchWorks() throws SQLException {\n        Destination destination = mock(Destination.class);\n        Connection connection = mock(Connection.class);\n        when(destination.getConnection()).thenReturn(connection);\n        Operation operation = mock(Operation.class);\n\n        DbSetup setup = new DbSetup(destination, operation);\n        setup.launch();\n        InOrder inOrder = inOrder(destination, connection, operation);\n        inOrder.verify(destination).getConnection();\n        inOrder.verify(operation).execute(connection, DefaultBinderConfiguration.INSTANCE);\n        inOrder.verify(connection).commit();\n    }\n\n    @Test\n    public void launchWorksWithCustomConfiguration() throws SQLException {\n        Destination destination = mock(Destination.class);\n        Connection connection = mock(Connection.class);\n        when(destination.getConnection()).thenReturn(connection);\n        Operation operation = mock(Operation.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n\n        DbSetup setup = new DbSetup(destination, operation, config);\n        setup.launch();\n        InOrder inOrder = inOrder(destination, connection, operation);\n        inOrder.verify(destination).getConnection();\n        inOrder.verify(operation).execute(connection, config);\n        inOrder.verify(connection).commit();\n    }\n\n    @Test\n    public void launchRollbacksIfSQLException() throws SQLException {\n        Destination destination = mock(Destination.class);\n        Connection connection = mock(Connection.class);\n        when(destination.getConnection()).thenReturn(connection);\n        Operation operation = mock(Operation.class);\n        doThrow(new SQLException()).when(operation).execute(connection, DefaultBinderConfiguration.INSTANCE);\n\n        DbSetup setup = new DbSetup(destination, operation);\n        try {\n            setup.launch();\n            fail(\"Expected a DbSetupRuntimeException\");\n        }\n        catch (DbSetupRuntimeException e) {\n            // expected\n        }\n        InOrder inOrder = inOrder(destination, connection, operation);\n        inOrder.verify(destination).getConnection();\n        inOrder.verify(operation).execute(connection, DefaultBinderConfiguration.INSTANCE);\n        inOrder.verify(connection).rollback();\n    }\n\n    @Test\n    public void launchRollbacksIfOtherException() throws SQLException {\n        Destination destination = mock(Destination.class);\n        Connection connection = mock(Connection.class);\n        when(destination.getConnection()).thenReturn(connection);\n        Operation operation = mock(Operation.class);\n        doThrow(new NullPointerException()).when(operation).execute(connection, DefaultBinderConfiguration.INSTANCE);\n\n        DbSetup setup = new DbSetup(destination, operation);\n        try {\n            setup.launch();\n            fail(\"Expected a DbSetupRuntimeException\");\n        }\n        catch (NullPointerException e) {\n            // expected\n        }\n        InOrder inOrder = inOrder(destination, connection, operation);\n        inOrder.verify(destination).getConnection();\n        inOrder.verify(operation).execute(connection, DefaultBinderConfiguration.INSTANCE);\n        inOrder.verify(connection).rollback();\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() throws SQLException {\n        Destination destination1 = mock(Destination.class);\n        Operation operation1 = mock(Operation.class);\n        BinderConfiguration config1 = DefaultBinderConfiguration.INSTANCE;\n\n        Destination destination2 = mock(Destination.class);\n        Operation operation2 = mock(Operation.class);\n        BinderConfiguration config2 = mock(BinderConfiguration.class);\n\n        DbSetup setup1 = new DbSetup(destination1, operation1, config1);\n        assertEquals(setup1, setup1);\n        assertEquals(setup1, new DbSetup(destination1, operation1, config1));\n        assertEquals(setup1.hashCode(), new DbSetup(destination1, operation1, config1).hashCode());\n\n        assertFalse(setup1.equals(null));\n        assertFalse(setup1.equals(\"hello\"));\n        assertFalse(setup1.equals(new DbSetup(destination2, operation1, config1)));\n        assertFalse(setup1.equals(new DbSetup(destination1, operation2, config1)));\n        assertFalse(setup1.equals(new DbSetup(destination1, operation1, config2)));\n    }\n\n    @Test\n    public void toStringWorks() throws SQLException {\n        Destination destination1 = mock(Destination.class);\n        when(destination1.toString()).thenReturn(\"destination1\");\n        Operation operation1 = mock(Operation.class);\n        when(operation1.toString()).thenReturn(\"operation1\");\n        BinderConfiguration config1 = mock(BinderConfiguration.class);\n        when(config1.toString()).thenReturn(\"config1\");\n\n        DbSetup setup1 = new DbSetup(destination1, operation1, config1);\n        assertEquals(\"DbSetup [destination=destination1, operation=operation1, binderConfiguration=config1]\",\n                     setup1.toString());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/DbSetupTrackerTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Matchers.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.destination.Destination;\nimport com.ninja_squad.dbsetup.operation.Operation;\n\n/**\n * @author JB Nizet\n */\npublic class DbSetupTrackerTest {\n\n    private Operation operation1;\n    private DbSetup dbSetup1;\n\n    private Operation operation2;\n    private DbSetup dbSetup2;\n\n    @Before\n    public void prepare() throws SQLException {\n        Destination destination = mock(Destination.class);\n        Connection connection = mock(Connection.class);\n        when(destination.getConnection()).thenReturn(connection);\n        operation1 = mock(Operation.class);\n        operation2 = mock(Operation.class);\n\n        dbSetup1 = new DbSetup(destination, operation1);\n        dbSetup2 = new DbSetup(destination, operation2);\n    }\n\n    @Test\n    public void launchIfNecessaryLaunchesTheFirstTime() throws SQLException {\n        DbSetupTracker tracker = new DbSetupTracker();\n        tracker.launchIfNecessary(dbSetup1);\n        verify(operation1).execute(any(Connection.class), any(BinderConfiguration.class));\n    }\n\n    @Test\n    public void launchIfNecessaryLaunchesIfNotSkipped() throws SQLException {\n        DbSetupTracker tracker = new DbSetupTracker();\n        tracker.launchIfNecessary(dbSetup1);\n        tracker.launchIfNecessary(dbSetup1);\n        verify(operation1, times(2)).execute(any(Connection.class), any(BinderConfiguration.class));\n    }\n\n    @Test\n    public void launchIfNecessaryDoesntLaunchIfSkipped() throws SQLException {\n        DbSetupTracker tracker = new DbSetupTracker();\n        tracker.launchIfNecessary(dbSetup1);\n        tracker.skipNextLaunch();\n        tracker.launchIfNecessary(dbSetup1);\n        verify(operation1, times(1)).execute(any(Connection.class), any(BinderConfiguration.class));\n    }\n\n    @Test\n    public void launchIfNecessaryResetsTheSkipFlag() throws SQLException {\n        DbSetupTracker tracker = new DbSetupTracker();\n        tracker.launchIfNecessary(dbSetup1);\n        tracker.skipNextLaunch();\n        tracker.launchIfNecessary(dbSetup1);\n        tracker.launchIfNecessary(dbSetup1);\n        verify(operation1, times(2)).execute(any(Connection.class), any(BinderConfiguration.class));\n    }\n\n    @Test\n    public void launchIfNecessaryDoesntLaunchIfDifferentSetup() throws SQLException {\n        DbSetupTracker tracker = new DbSetupTracker();\n        tracker.launchIfNecessary(dbSetup1);\n        tracker.skipNextLaunch();\n        tracker.launchIfNecessary(dbSetup2);\n        verify(operation1, times(1)).execute(any(Connection.class), any(BinderConfiguration.class));\n        verify(operation2, times(1)).execute(any(Connection.class), any(BinderConfiguration.class));\n    }\n\n    @Test\n    public void toStringWorks() {\n        DbSetupTracker tracker = new DbSetupTracker();\n        assertEquals(\"DbSetupTracker [lastSetupLaunched=null, nextLaunchSkipped=false]\", tracker.toString());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/bind/BindersTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.bind;\n\nimport static org.mockito.Matchers.argThat;\nimport static org.mockito.Matchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.sql.Date;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.util.Calendar;\nimport java.util.TimeZone;\n\nimport org.hamcrest.BaseMatcher;\nimport org.hamcrest.Description;\nimport org.junit.Before;\nimport org.junit.Test;\n\npublic class BindersTest {\n\n    private PreparedStatement stmt;\n\n    @Before\n    public void prepare() {\n        stmt = mock(PreparedStatement.class);\n    }\n\n    @Test\n    public void defaultBinderBindsObject() throws SQLException {\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, Boolean.TRUE);\n        verify(stmt).setObject(1, Boolean.TRUE);\n    }\n\n    @Test\n    public void defaultBinderBindsNull() throws SQLException {\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void defaultBinderBindsEnum() throws SQLException {\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, TestEnum.BAR);\n        verify(stmt).setString(1, TestEnum.BAR.name());\n    }\n\n    @Test\n    public void defaultBinderBindsUtilDate() throws SQLException {\n        java.util.Date date = new java.util.Date(Date.valueOf(\"1975-07-19\").getTime());\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, date);\n        verify(stmt).setTimestamp(1, new Timestamp(date.getTime()));\n    }\n\n    @Test\n    public void defaultBinderBindsCalendar() throws SQLException {\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(Date.valueOf(\"1975-07-19\").getTime());\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, calendar);\n        verify(stmt).setTimestamp(1, new Timestamp(calendar.getTime().getTime()), calendar);\n    }\n\n    @Test\n    public void defaultBinderBindsLocalDate() throws SQLException {\n        LocalDate localDate = LocalDate.parse(\"1975-07-19\");\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, localDate);\n        verify(stmt).setDate(1, Date.valueOf(localDate));\n    }\n\n    @Test\n    public void defaultBinderBindsLocalTime() throws SQLException {\n        LocalTime localTime = LocalTime.parse(\"01:02:03.000\");\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, localTime);\n        verify(stmt).setTime(1, Time.valueOf(\"01:02:03\"));\n    }\n\n    @Test\n    public void defaultBinderBindsLocalDateTime() throws SQLException {\n        LocalDateTime localDateTime = LocalDateTime.parse(\"1975-07-19T01:02:03.000\");\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, localDateTime);\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 01:02:03\"));\n    }\n\n    @Test\n    public void defaultBinderBindsInstant() throws SQLException {\n        Instant instant = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneId.systemDefault()).toInstant();\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, instant);\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 01:02:03\"));\n    }\n\n    @Test\n    public void defaultBinderBindsZonedDateTime() throws SQLException {\n        ZonedDateTime zonedDateTime = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneOffset.UTC);\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, zonedDateTime);\n        verify(stmt).setTimestamp(eq(1),\n                                  eq(Timestamp.from(zonedDateTime.toInstant())),\n                                  calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void defaultBinderBindsOffsetDateTime() throws SQLException {\n        OffsetDateTime offsetDateTime = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atOffset(ZoneOffset.UTC);\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, offsetDateTime);\n        verify(stmt).setTimestamp(eq(1),\n                                  eq(Timestamp.from(offsetDateTime.toInstant())),\n                                  calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void defaultBinderBindsOffsetTime() throws SQLException {\n        OffsetTime offsetTime = LocalTime.parse(\"01:02:03.000\").atOffset(ZoneOffset.UTC);\n        Binder binder = Binders.defaultBinder();\n        binder.bind(stmt, 1, offsetTime);\n        verify(stmt).setTime(eq(1),\n                             eq(Time.valueOf(offsetTime.toLocalTime())),\n                             calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void stringBinderBindsString() throws SQLException {\n        Binder binder = Binders.stringBinder();\n        binder.bind(stmt, 1, \"hello\");\n        verify(stmt).setString(1, \"hello\");\n    }\n\n    @Test\n    public void stringBinderBindsEnum() throws SQLException {\n        Binder binder = Binders.stringBinder();\n        binder.bind(stmt, 1, TestEnum.BAR);\n        verify(stmt).setString(1, \"BAR\");\n    }\n\n    @Test\n    public void stringBinderBindsObject() throws SQLException {\n        Binder binder = Binders.stringBinder();\n        binder.bind(stmt, 1, new Foo());\n        verify(stmt).setString(1, \"foo\");\n    }\n\n    @Test\n    public void stringBinderBindsNull() throws SQLException {\n        Binder binder = Binders.stringBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void dateBinderBindsSqlDate() throws SQLException {\n        Date date = Date.valueOf(\"1975-07-19\");\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, date);\n        verify(stmt).setDate(1, date);\n    }\n\n    @Test\n    public void dateBinderBindsUtilDate() throws SQLException {\n        java.util.Date date = new java.util.Date(Date.valueOf(\"1975-07-19\").getTime());\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, date);\n        verify(stmt).setDate(1, Date.valueOf(\"1975-07-19\"));\n    }\n\n    @Test\n    public void dateBinderBindsCalendar() throws SQLException {\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(Date.valueOf(\"1975-07-19\").getTime());\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, calendar);\n        verify(stmt).setDate(1, Date.valueOf(\"1975-07-19\"), calendar);\n    }\n\n    @Test\n    public void dateBinderBindsString() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, \"1975-07-19\");\n        verify(stmt).setDate(1, Date.valueOf(\"1975-07-19\"));\n    }\n\n    @Test\n    public void dateBinderBindsLocalDate() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, LocalDate.parse(\"1975-07-19\"));\n        verify(stmt).setDate(1, Date.valueOf(\"1975-07-19\"));\n    }\n\n    @Test\n    public void dateBinderBindsLocalDateTime() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, LocalDateTime.parse(\"1975-07-19T01:02:03.000\"));\n        verify(stmt).setDate(1, Date.valueOf(\"1975-07-19\"));\n    }\n\n    @Test\n    public void dateBinderBindsInstant() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        Instant value = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneId.systemDefault()).toInstant();\n        binder.bind(stmt, 1, value);\n        verify(stmt).setDate(1, new Date(value.toEpochMilli()));\n    }\n\n    @Test\n    public void dateBinderBindsZonedDateTime() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        ZonedDateTime value = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneOffset.UTC);\n        binder.bind(stmt, 1, value);\n        verify(stmt).setDate(eq(1),\n                             eq(new Date(value.toInstant().toEpochMilli())),\n                             calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void dateBinderBindsOffsetDateTime() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        OffsetDateTime value = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atOffset(ZoneOffset.UTC);\n        binder.bind(stmt, 1, value);\n        verify(stmt).setDate(eq(1),\n                             eq(new Date(value.toInstant().toEpochMilli())),\n                             calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void dateBinderBindsNull() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void dateBinderBindsObject() throws SQLException {\n        Binder binder = Binders.dateBinder();\n        binder.bind(stmt, 1, Boolean.TRUE);\n        verify(stmt).setObject(1, Boolean.TRUE);\n    }\n\n    @Test\n    public void timestampBinderBindsTimestamp() throws SQLException {\n        Timestamp ts = Timestamp.valueOf(\"1975-07-19 13:14:15\");\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, ts);\n        verify(stmt).setTimestamp(1, ts);\n    }\n\n    @Test\n    public void timestampBinderBindsUtilDate() throws SQLException {\n        Timestamp ts = Timestamp.valueOf(\"1975-07-19 13:14:15\");\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, new java.util.Date(ts.getTime()));\n        verify(stmt).setTimestamp(1, ts);\n    }\n\n    @Test\n    public void timestampBinderBindsCalendar() throws SQLException {\n        Timestamp ts = Timestamp.valueOf(\"1975-07-19 13:14:15\");\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(ts.getTime());\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, calendar);\n        verify(stmt).setTimestamp(1, ts, calendar);\n    }\n\n    @Test\n    public void timestampBinderBindsStringWithTimestampFormat() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, \"1975-07-19 13:14:15\");\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 13:14:15\"));\n    }\n\n    @Test\n    public void timestampBinderBindsStringWithDateFormat() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, \"1975-07-19\");\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 00:00:00\"));\n    }\n\n    @Test\n    public void timestampBinderBindsLocalDateTime() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, LocalDateTime.parse(\"1975-07-19T01:02:03.000\"));\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 01:02:03\"));\n    }\n\n    @Test\n    public void timestampBinderBindsLocalDate() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, LocalDate.parse(\"1975-07-19\"));\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 00:00:00\"));\n    }\n\n    @Test\n    public void timestampBinderBindsInstant() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneId.systemDefault()).toInstant());\n        verify(stmt).setTimestamp(1, Timestamp.valueOf(\"1975-07-19 01:02:03\"));\n    }\n\n    @Test\n    public void timestampBinderBindsZonedDateTime() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        ZonedDateTime value = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atZone(ZoneOffset.UTC);\n        binder.bind(stmt, 1, value);\n        verify(stmt).setTimestamp(eq(1),\n                                  eq(Timestamp.from(value.toInstant())),\n                                  calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void timestampBinderBindsOffsetDateTime() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        OffsetDateTime value = LocalDateTime.parse(\"1975-07-19T01:02:03.000\").atOffset(ZoneOffset.UTC);\n        binder.bind(stmt, 1, value);\n        verify(stmt).setTimestamp(eq(1),\n                                  eq(Timestamp.from(value.toInstant())),\n                                  calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void timestampBinderBindsNull() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void timestampBinderBindsObject() throws SQLException {\n        Binder binder = Binders.timestampBinder();\n        binder.bind(stmt, 1, Boolean.TRUE);\n        verify(stmt).setObject(1, Boolean.TRUE);\n    }\n\n    @Test\n    public void timeBinderBindsTime() throws SQLException {\n        Time time = Time.valueOf(\"13:14:15\");\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, time);\n        verify(stmt).setTime(1, time);\n    }\n\n    @Test\n    public void timeBinderBindsUtilDate() throws SQLException {\n        Time time = Time.valueOf(\"13:14:15\");\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, new java.util.Date(time.getTime()));\n        verify(stmt).setTime(1, time);\n    }\n\n    @Test\n    public void timeBinderBindsCalendar() throws SQLException {\n        Time time = Time.valueOf(\"13:14:15\");\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(time.getTime());\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, calendar);\n        verify(stmt).setTime(1, time, calendar);\n    }\n\n    @Test\n    public void timeBinderBindsString() throws SQLException {\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, \"13:14:15\");\n        verify(stmt).setTime(1, Time.valueOf(\"13:14:15\"));\n    }\n\n    @Test\n    public void timeBinderBindsLocalTime() throws SQLException {\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, LocalTime.parse(\"01:02:03.000\"));\n        verify(stmt).setTime(1, Time.valueOf(\"01:02:03\"));\n    }\n\n    @Test\n    public void timeBinderBindsOffsetTime() throws SQLException {\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, OffsetTime.of(LocalTime.parse(\"01:02:03.000\"), ZoneOffset.UTC));\n        verify(stmt).setTime(eq(1),\n                             eq(Time.valueOf(\"01:02:03\")),\n                             calendarWithTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC)));\n    }\n\n    @Test\n    public void timeBinderBindsNull() throws SQLException {\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void timeBinderBindsObject() throws SQLException {\n        Binder binder = Binders.timeBinder();\n        binder.bind(stmt, 1, Boolean.TRUE);\n        verify(stmt).setObject(1, Boolean.TRUE);\n    }\n\n    @Test\n    public void decimalBinderBindsString() throws SQLException {\n        Binder binder = Binders.decimalBinder();\n        binder.bind(stmt, 1, \"12.45\");\n        verify(stmt).setBigDecimal(1, new BigDecimal(\"12.45\"));\n    }\n\n    @Test\n    public void decimalBinderBindsObject() throws SQLException {\n        Binder binder = Binders.decimalBinder();\n        binder.bind(stmt, 1, 12.45);\n        verify(stmt).setObject(1, 12.45);\n    }\n\n    @Test\n    public void decimalBinderBindsNull() throws SQLException {\n        Binder binder = Binders.decimalBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    @Test\n    public void integerBinderBindsBigInteger() throws SQLException {\n        Binder binder = Binders.integerBinder();\n        binder.bind(stmt, 1, new BigInteger(\"12\"));\n        verify(stmt).setObject(1, \"12\", Types.BIGINT);\n    }\n\n    @Test\n    public void integerBinderBindsString() throws SQLException {\n        Binder binder = Binders.integerBinder();\n        binder.bind(stmt, 1, \"12\");\n        verify(stmt).setObject(1, \"12\", Types.BIGINT);\n    }\n\n    @Test\n    public void integerBinderBindsEnum() throws SQLException {\n        Binder binder = Binders.integerBinder();\n        binder.bind(stmt, 1, TestEnum.FOO);\n        verify(stmt).setInt(1, 0);\n    }\n\n    @Test\n    public void integerBinderBindsObject() throws SQLException {\n        Binder binder = Binders.integerBinder();\n        binder.bind(stmt, 1, 27);\n        verify(stmt).setObject(1, 27);\n    }\n\n    @Test\n    public void integerBinderBindsNull() throws SQLException {\n        Binder binder = Binders.integerBinder();\n        binder.bind(stmt, 1, null);\n        verify(stmt).setObject(1, null);\n    }\n\n    private enum TestEnum {\n        FOO, BAR;\n    }\n\n    private static class Foo {\n        @Override\n        public String toString() {\n            return \"foo\";\n        }\n    }\n\n    private static Calendar calendarWithTimeZone(TimeZone timeZone) {\n        return argThat(new BaseMatcher<Calendar>() {\n            @Override\n            public boolean matches(Object item) {\n                return (item instanceof Calendar) && ((Calendar) item).getTimeZone().equals(timeZone);\n            }\n\n            @Override\n            public void describeTo(Description description) {\n                description.appendText(\"a calendar with timezone \" + timeZone);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/bind/DefaultBinderConfigurationTest.java",
    "content": "package com.ninja_squad.dbsetup.bind;\n\nimport org.junit.Test;\n\nimport java.sql.ParameterMetaData;\nimport java.sql.SQLException;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\npublic class DefaultBinderConfigurationTest {\n\n    @Test\n    public void shouldReturnDefaultBinderIfNoParameterMetadata() throws SQLException {\n        assertEquals(Binders.defaultBinder(), DefaultBinderConfiguration.INSTANCE.getBinder(null, 1));\n    }\n\n    @Test\n    public void shouldReturnDefaultBinderIfParameterTypeCantBeObtained() throws SQLException {\n        ParameterMetaData mockMetaData = mock(ParameterMetaData.class);\n        when(mockMetaData.getParameterType(1)).thenThrow(new SQLException());\n        assertEquals(Binders.defaultBinder(), DefaultBinderConfiguration.INSTANCE.getBinder(mockMetaData, 1));\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/destination/DataSourceDestinationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.destination;\n\nimport org.junit.Test;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author JB Nizet\n */\npublic class DataSourceDestinationTest {\n    @Test\n    public void getConnectionWorks() throws SQLException {\n        DataSource dataSource = mock(DataSource.class);\n        Connection connection = mock(Connection.class);\n        when(dataSource.getConnection()).thenReturn(connection);\n        assertSame(connection, new DataSourceDestination(dataSource).getConnection());\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() throws SQLException {\n        DataSource dataSource1 = mock(DataSource.class);\n        DataSource dataSource2 = mock(DataSource.class);\n        Destination dest1 = new DataSourceDestination(dataSource1);\n        Destination dest1bis = DataSourceDestination.with(dataSource1);\n        Destination dest2 = new DataSourceDestination(dataSource2);\n\n        assertEquals(dest1, dest1);\n        assertEquals(dest1, dest1bis);\n        assertEquals(dest1.hashCode(), dest1bis.hashCode());\n        assertFalse(dest1.equals(dest2));\n        assertFalse(dest1.equals(null));\n        assertFalse(dest1.equals(\"hello\"));\n    }\n\n    @Test\n    public void toStringWorks() {\n        DataSource dataSource1 = mock(DataSource.class);\n        when(dataSource1.toString()).thenReturn(\"dataSource1\");\n        assertEquals(\"DataSourceDestination [dataSource=dataSource1]\",\n                     new DataSourceDestination(dataSource1).toString());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/destination/DriverManagerDestinationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.destination;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\n/**\n * @author JB Nizet\n */\npublic class DriverManagerDestinationTest {\n    @Test\n    public void equalsAndHashCodeWork() {\n        Destination dest1 = new DriverManagerDestination(\"url\", \"user\", \"password\");\n        Destination dest1bis = DriverManagerDestination.with(\"url\", \"user\", \"password\");\n\n        assertEquals(dest1, dest1);\n        assertEquals(dest1, dest1bis);\n        assertEquals(dest1.hashCode(), dest1bis.hashCode());\n        assertFalse(dest1.equals(new DriverManagerDestination(\"url2\", \"user\", \"password\")));\n        assertFalse(dest1.equals(new DriverManagerDestination(\"url\", \"user2\", \"password\")));\n        assertFalse(dest1.equals(new DriverManagerDestination(\"url\", \"user\", \"password2\")));\n        assertFalse(dest1.equals(new DriverManagerDestination(\"url\", null, \"password\")));\n        assertFalse(dest1.equals(new DriverManagerDestination(\"url\", \"user\", null)));\n        assertFalse(new DriverManagerDestination(\"url\", null, \"password\").equals(dest1));\n        assertFalse(new DriverManagerDestination(\"url\", \"user\", null).equals(dest1));\n        assertFalse(dest1.equals(null));\n        assertFalse(dest1.equals(\"hello\"));\n\n        assertEquals(new DriverManagerDestination(\"url\", null, null), new DriverManagerDestination(\"url\", null, null));\n        assertEquals(new DriverManagerDestination(\"url\", null, null).hashCode(),\n                     new DriverManagerDestination(\"url\", null, null).hashCode());\n    }\n\n    @Test\n    public void toStringWorks() {\n        assertEquals(\"DriverManagerDestination [url=theUrl, user=theUser, password=thePassword]\",\n                     new DriverManagerDestination(\"theUrl\", \"theUser\", \"thePassword\").toString());\n        assertEquals(\"DriverManagerDestination [url=theUrl, user=null, password=null]\",\n                     new DriverManagerDestination(\"theUrl\", null, null).toString());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/generator/DateSequenceValueGeneratorTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport org.junit.Test;\n\n/**\n * @author JB\n */\npublic class DateSequenceValueGeneratorTest {\n    @Test\n    public void startsAtToday() {\n        ZonedDateTime date = ValueGenerators.dateSequence().nextValue();\n        assertEquals(LocalDate.now().atStartOfDay(ZoneId.systemDefault()), date);\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void incrementsByOneDay() throws ParseException {\n        DateSequenceValueGenerator sequence = ValueGenerators.dateSequence().startingAt(july19Of2013AtMidnight());\n        sequence.nextValue();\n        ZonedDateTime date = sequence.nextValue();\n        assertEquals(LocalDateTime.parse(\"2013-07-20T00:00:00.000\").atZone(ZoneId.systemDefault()), date);\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewStartAsDate() throws ParseException {\n        DateSequenceValueGenerator sequence = ValueGenerators.dateSequence().startingAt(july19Of2013AtMidnight());\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        sequence.startingAt(july19Of1975AtMidnight());\n        assertEquals(\"1975-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewStartAsDateWithTimeZone() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnightInParisTimeZone(), TimeZone.getTimeZone(\"UTC\"));\n        assertEquals(\"2013-07-18T22:00:00.000\", toLongStringInUTC(sequence.nextValue()));\n    }\n\n    @Test\n    public void allowsSettingNewStartAsString() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(\"2013-07-19\");\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewStartAsCalendar() throws ParseException {\n        Calendar start = Calendar.getInstance();\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence().startingAt(start);\n        assertEquals(start.getTime().getTime(), sequence.nextValue().toInstant().toEpochMilli());\n    }\n\n    @Test\n    public void allowsSettingNewStartAsLocalDate() throws ParseException {\n        LocalDate start = LocalDate.parse(\"2000-01-01\");\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence().startingAt(start);\n        assertEquals(start.atStartOfDay(ZoneId.systemDefault()), sequence.nextValue());\n    }\n\n    @Test\n    public void allowsSettingNewStartAsLocalDateTime() throws ParseException {\n        LocalDateTime start = LocalDateTime.parse(\"2000-01-01T01:02:03.000\");\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence().startingAt(start);\n        assertEquals(start.atZone(ZoneId.systemDefault()), sequence.nextValue());\n    }\n\n    @Test\n    public void allowsSettingNewStartAsZonedDateTime() throws ParseException {\n        ZonedDateTime start = ZonedDateTime.parse(\"2000-01-01T01:02:03.000Z\");\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence().startingAt(start);\n        assertEquals(start, sequence.nextValue());\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrement() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(2, DateSequenceValueGenerator.CalendarField.DAY);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-07-21T00:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInYears() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.YEAR);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2014-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInMonths() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.MONTH);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-08-19T00:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInHours() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.HOUR);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-07-19T01:00:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInMinutes() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.MINUTE);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-07-19T00:01:00.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInSeconds() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.SECOND);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-07-19T00:00:01.000\", toLongString(sequence.nextValue()));\n    }\n\n    @Test\n    @SuppressWarnings(\"deprecation\")\n    public void allowsSettingNewIncrementInMilliseconds() throws ParseException {\n        DateSequenceValueGenerator sequence =\n            ValueGenerators.dateSequence()\n                           .startingAt(july19Of2013AtMidnight())\n                           .incrementingBy(1, DateSequenceValueGenerator.CalendarField.MILLISECOND);\n        assertEquals(\"2013-07-19T00:00:00.000\", toLongString(sequence.nextValue()));\n        assertEquals(\"2013-07-19T00:00:00.001\", toLongString(sequence.nextValue()));\n    }\n\n    private String toLongString(ZonedDateTime date) {\n        return DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ss.SSS\").format(date);\n    }\n\n    private String toLongStringInUTC(ZonedDateTime date) {\n        return toLongString(date.withZoneSameInstant(ZoneOffset.UTC));\n    }\n\n    private Date july19Of2013AtMidnight() throws ParseException {\n        return new SimpleDateFormat(\"yyyy-MM-dd\").parse(\"2013-07-19\");\n    }\n\n    private Date july19Of1975AtMidnight() throws ParseException {\n        return new SimpleDateFormat(\"yyyy-MM-dd\").parse(\"1975-07-19\");\n    }\n\n    // offset is +02:00 in Paris at this date\n    private Date july19Of2013AtMidnightInParisTimeZone() throws ParseException {\n        TimeZone zone = TimeZone.getTimeZone(\"Europe/Paris\");\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n        simpleDateFormat.setTimeZone(zone);\n        return simpleDateFormat.parse(\"2013-07-19\");\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/generator/SequenceValueGeneratorTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author JB\n */\npublic class SequenceValueGeneratorTest {\n    @Test\n    public void startsAtOne() {\n        assertEquals(1L, ValueGenerators.sequence().nextValue().longValue());\n    }\n\n    @Test\n    public void incrementsByOne() {\n        SequenceValueGenerator sequence = ValueGenerators.sequence();\n        sequence.nextValue();\n        assertEquals(2L, sequence.nextValue().longValue());\n    }\n\n    @Test\n    public void allowsSettingNewStart() {\n        SequenceValueGenerator sequence = ValueGenerators.sequence().startingAt(12L);\n        assertEquals(12L, sequence.nextValue().longValue());\n        sequence.startingAt(5L);\n        assertEquals(5L, sequence.nextValue().longValue());\n    }\n\n    @Test\n    public void allowsSettingNewIncrement() {\n        SequenceValueGenerator sequence = ValueGenerators.sequence().incrementingBy(10);\n        assertEquals(1L, sequence.nextValue().longValue());\n        assertEquals(11L, sequence.nextValue().longValue());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/generator/StringSequenceValueGeneratorTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2013, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.generator;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author JB\n */\npublic class StringSequenceValueGeneratorTest {\n    @Test\n    public void startsAtOne() {\n        assertEquals(\"CODE_1\", ValueGenerators.stringSequence(\"CODE_\").nextValue());\n    }\n\n    @Test\n    public void incrementsByOne() {\n        StringSequenceValueGenerator sequence = ValueGenerators.stringSequence(\"CODE_\");\n        sequence.nextValue();\n        assertEquals(\"CODE_2\", sequence.nextValue());\n    }\n\n    @Test\n    public void allowsSettingNewStart() {\n        StringSequenceValueGenerator sequence = ValueGenerators.stringSequence(\"CODE_\").startingAt(12L);\n        assertEquals(\"CODE_12\", sequence.nextValue());\n        sequence.startingAt(5L);\n        assertEquals(\"CODE_5\", sequence.nextValue());\n    }\n\n    @Test\n    public void allowsSettingNewIncrement() {\n        StringSequenceValueGenerator sequence = ValueGenerators.stringSequence(\"CODE_\").incrementingBy(10);\n        assertEquals(\"CODE_1\", sequence.nextValue());\n        assertEquals(\"CODE_11\", sequence.nextValue());\n    }\n\n    @Test\n    public void allowsSettingLeftPadding() {\n        StringSequenceValueGenerator sequence =\n            ValueGenerators.stringSequence(\"CODE_\").withLeftPadding(2);\n        assertEquals(\"CODE_01\", sequence.nextValue());\n        assertEquals(\"CODE_02\", sequence.nextValue());\n        sequence.startingAt(10L);\n        assertEquals(\"CODE_10\", sequence.nextValue());\n        assertEquals(\"CODE_11\", sequence.nextValue());\n        sequence.startingAt(100L);\n        assertEquals(\"CODE_100\", sequence.nextValue());\n        assertEquals(\"CODE_101\", sequence.nextValue());\n    }\n\n    @Test\n    public void allowsUnsettingLeftPadding() {\n        StringSequenceValueGenerator sequence =\n            ValueGenerators.stringSequence(\"CODE_\").withLeftPadding(2);\n        sequence.withoutLeftPadding();\n        assertEquals(\"CODE_1\", sequence.nextValue());\n        assertEquals(\"CODE_2\", sequence.nextValue());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/generator/ValueGeneratorsTest.java",
    "content": "package com.ninja_squad.dbsetup.generator;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author JB\n */\npublic class ValueGeneratorsTest {\n\n    @Test\n    public void contantShouldReturnGeneratorWhichGeneratesAConstantValue() {\n        ValueGenerator<String> constantGenerator = ValueGenerators.constant(\"hello\");\n        for (int i = 0; i < 3; i++) {\n            assertEquals(\"hello\", constantGenerator.nextValue());\n        }\n    }\n\n    // other methods are tested by the test for the class returned by the method\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/CommonOperations.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport com.ninja_squad.dbsetup.operation.Operation;\n\nimport static com.ninja_squad.dbsetup.Operations.*;\n\n/**\n * @author JB Nizet\n */\npublic class CommonOperations {\n    public static final Operation DROP_TABLES =\n        sequenceOf(sql(\"drop table if exists A cascade\"),\n                   sql(\"drop table if exists B cascade\"));\n\n    public static final Operation CREATE_TABLES =\n        sequenceOf(sql(\"create table A (a_id bigint primary key, va varchar(100), nu numeric(10, 2), bo boolean, da date, tis timestamp, tim time, seq numeric)\"),\n                   sql(\"create table B (b_id bigint primary key, a_id SMALLINT, va varchar(100), foreign key (a_id) references A (a_id))\"));\n\n    public static final Operation INSERT_ROWS =\n       sequenceOf(\n           insertInto(\"A\").columns(\"a_id\").values(1L).build(),\n           insertInto(\"B\").columns(\"b_id\", \"a_id\").values(1L, 1L).build());\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/Database.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport com.ninja_squad.dbsetup.destination.Destination;\nimport com.ninja_squad.dbsetup.destination.DriverManagerDestination;\n\n/**\n * @author JB Nizet\n */\npublic class Database {\n    static {\n        try {\n            Class.forName(\"org.hsqldb.jdbc.JDBCDriver\");\n        }\n        catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static final String URL = \"jdbc:hsqldb:mem:mymemdb\";\n    public static final String USER = \"SA\";\n    public static final String PASSWORD = \"\";\n\n    public static final Destination DESTINATION = new DriverManagerDestination(URL, USER, PASSWORD);\n\n    public static Connection getConnection() throws SQLException {\n        return DESTINATION.getConnection();\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/DeleteAllIntegrationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport static org.junit.Assert.*;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.ninja_squad.dbsetup.DbSetup;\nimport com.ninja_squad.dbsetup.Operations;\n\n/**\n * @author JB Nizet\n */\npublic class DeleteAllIntegrationTest {\n\n    private Connection connection;\n\n    @Before\n    public void prepare() throws SQLException {\n        new DbSetup(Database.DESTINATION, Operations.sequenceOf(CommonOperations.DROP_TABLES,\n                                                                CommonOperations.CREATE_TABLES,\n                                                                CommonOperations.INSERT_ROWS)).launch();\n        connection = Database.getConnection();\n    }\n\n    @After\n    public void cleanup() throws SQLException {\n        connection.close();\n    }\n\n    @Test\n    public void testDeleteAll() throws SQLException {\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A\");\n        assertTrue(rs.next());\n        rs.close();\n        rs = stmt.executeQuery(\"select * from B\");\n        assertTrue(rs.next());\n        connection.close();\n\n        new DbSetup(Database.DESTINATION, Operations.deleteAllFrom(\"B\", \"A\")).launch();\n        connection = Database.getConnection();\n        stmt = connection.createStatement();\n        rs = stmt.executeQuery(\"select * from A\");\n        assertFalse(rs.next());\n        rs.close();\n        rs = stmt.executeQuery(\"select * from B\");\n        assertFalse(rs.next());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/InsertIntegrationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport static org.junit.Assert.*;\n\nimport java.math.BigDecimal;\nimport java.sql.Connection;\nimport java.sql.Date;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.sql.Time;\nimport java.sql.Timestamp;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.ZonedDateTime;\nimport java.util.Calendar;\n\nimport com.ninja_squad.dbsetup.DbSetup;\nimport com.ninja_squad.dbsetup.DbSetupRuntimeException;\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.bind.Binder;\nimport com.ninja_squad.dbsetup.bind.Binders;\nimport com.ninja_squad.dbsetup.generator.ValueGenerators;\nimport com.ninja_squad.dbsetup.operation.Insert;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * @author JB Nizet\n */\npublic class InsertIntegrationTest {\n    private Connection connection;\n\n    @Before\n    public void prepare() throws SQLException {\n        new DbSetup(Database.DESTINATION, Operations.sequenceOf(CommonOperations.DROP_TABLES,\n                                                                CommonOperations.CREATE_TABLES)).launch();\n        connection = Database.getConnection();\n    }\n\n    @After\n    public void cleanup() throws SQLException {\n        connection.close();\n    }\n\n    @Test\n    public void testInsert() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\", \"bo\", \"da\", \"tim\", \"tis\")\n                  .values(1L, 12.6, true, \"1975-07-19\", \"14:15:22\", \"2012-12-25 13:05:12\")\n                  .values(2L, 13.6, false, \"1976-10-16\", \"14:15:23\", \"2012-12-25 13:05:13\")\n                  .withDefaultValue(\"va\", \"hello\")\n                  .withGeneratedValue(\"seq\", ValueGenerators.sequence().startingAt(10L).incrementingBy(5))\n                  .build();\n        Insert insertB1 =\n            Insert.into(\"B\")\n                  .columns(\"b_id\", \"a_id\", \"va\")\n                  .values(\"1\", 1, new Foo(\"foo\"))\n                  .withBinder(new FooBinder(), \"va\")\n                  .build();\n        Insert insertB2 =\n            Insert.into(\"B\")\n                  .columns(\"b_id\", \"a_id\", \"va\")\n                  .values(2, TestEnum.BAT, TestEnum.BAR)\n                  .build();\n        new DbSetup(Database.DESTINATION, Operations.sequenceOf(insertA, insertB1, insertB2)).launch();\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A order by a_id\");\n        assertTrue(rs.next());\n        assertEquals(1L, rs.getLong(\"a_id\"));\n        assertTrue(new BigDecimal(\"12.6\").compareTo(rs.getBigDecimal(\"nu\")) == 0);\n        assertEquals(true, rs.getBoolean(\"bo\"));\n        assertEquals(Date.valueOf(\"1975-07-19\"), rs.getDate(\"da\"));\n        assertEquals(Time.valueOf(\"14:15:22\"), rs.getTime(\"tim\"));\n        assertEquals(Timestamp.valueOf(\"2012-12-25 13:05:12\"), rs.getTimestamp(\"tis\"));\n        assertEquals(\"hello\", rs.getString(\"va\"));\n        assertEquals(10L, rs.getLong(\"seq\"));\n\n        assertTrue(rs.next());\n        assertEquals(2L, rs.getLong(\"a_id\"));\n        assertTrue(new BigDecimal(\"13.6\").compareTo(rs.getBigDecimal(\"nu\")) == 0);\n        assertEquals(false, rs.getBoolean(\"bo\"));\n        assertEquals(Date.valueOf(\"1976-10-16\"), rs.getDate(\"da\"));\n        assertEquals(Time.valueOf(\"14:15:23\"), rs.getTime(\"tim\"));\n        assertEquals(Timestamp.valueOf(\"2012-12-25 13:05:13\"), rs.getTimestamp(\"tis\"));\n        assertEquals(\"hello\", rs.getString(\"va\"));\n        assertEquals(15L, rs.getLong(\"seq\"));\n\n        rs = stmt.executeQuery(\"select * from B order by b_id\");\n        assertTrue(rs.next());\n        assertEquals(1L, rs.getLong(\"b_id\"));\n        assertEquals(1L, rs.getLong(\"a_id\"));\n        assertEquals(\"foo\", rs.getString(\"va\"));\n\n        assertTrue(rs.next());\n        assertEquals(2L, rs.getLong(\"b_id\"));\n        assertEquals(2L, rs.getLong(\"a_id\"));\n        assertEquals(\"BAR\", rs.getString(\"va\"));\n    }\n\n    @Test\n    public void testWithoutMetadata() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\", \"bo\", \"da\", \"tim\", \"tis\")\n                  .values(TestEnum.BAT, \"13.6\", false, \"1976-10-16\", \"14:15:23\", \"2012-12-25 13:05:13\")\n                  .withDefaultValue(\"va\", \"hello\")\n                  .useMetadata(false)\n                  .build();\n        try {\n            new DbSetup(Database.DESTINATION, insertA).launch();\n            fail(\"expected an exception due to the metadata not being used\");\n        }\n        catch (DbSetupRuntimeException e) {\n            // expected\n        }\n\n        insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\", \"bo\", \"da\", \"tim\", \"tis\")\n                  .values(\"2\", \"13.6\", false, \"1976-10-16\", \"14:15:23\", \"2012-12-25 13:05:13\")\n                  .withDefaultValue(\"va\", \"hello\")\n                  .useMetadata(false)\n                  .withBinder(Binders.integerBinder(), \"a_id\")\n                  .withBinder(Binders.decimalBinder(), \"nu\")\n                  .withBinder(Binders.dateBinder(), \"da\")\n                  .withBinder(Binders.timeBinder(), \"tim\")\n                  .withBinder(Binders.timestampBinder(), \"tis\")\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A order by a_id\");\n        assertTrue(rs.next());\n        assertEquals(2L, rs.getLong(\"a_id\"));\n        assertTrue(new BigDecimal(\"13.6\").compareTo(rs.getBigDecimal(\"nu\")) == 0);\n        assertEquals(false, rs.getBoolean(\"bo\"));\n        assertEquals(Date.valueOf(\"1976-10-16\"), rs.getDate(\"da\"));\n        assertEquals(Time.valueOf(\"14:15:23\"), rs.getTime(\"tim\"));\n        assertEquals(Timestamp.valueOf(\"2012-12-25 13:05:13\"), rs.getTimestamp(\"tis\"));\n        assertEquals(\"hello\", rs.getString(\"va\"));\n    }\n\n    @Test\n    public void insertEnumToVarcharColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"va\")\n                  .values(1, TestEnum.BAR)\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertJavaUtilDateToDateColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"da\")\n                  .values(1, new java.util.Date())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertJavaUtilCalendarToDateColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"da\")\n                  .values(1, Calendar.getInstance())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertJavaUtilDateToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, new java.util.Date())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertJavaUtilCalendarToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, Calendar.getInstance())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertLocalDateTimeToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, LocalDateTime.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertLocalDateToDateColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"da\")\n                  .values(1, LocalDate.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertLocalTimeToTimeColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tim\")\n                  .values(1, LocalTime.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertInstantToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, Instant.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertZonedDateTimeToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, ZonedDateTime.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertOffsetDateTimeToTimestampColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tis\")\n                  .values(1, OffsetDateTime.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    @Test\n    public void insertOffsetTimeToTimeColumnWithoutMetadataShouldWork() throws SQLException {\n        Insert insertA =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"tim\")\n                  .values(1, OffsetTime.now())\n                  .useMetadata(false)\n                  .build();\n        new DbSetup(Database.DESTINATION, insertA).launch();\n    }\n\n    private static class Foo {\n        private String label;\n\n        public Foo(String label) {\n            this.label = label;\n        }\n\n        public String getLabel() {\n            return label;\n        }\n    }\n\n    private static class FooBinder implements Binder {\n\n        @Override\n        public void bind(PreparedStatement statement, int param, Object value) throws SQLException {\n            statement.setString(param, ((Foo) value).getLabel());\n        }\n\n    }\n\n    private enum TestEnum {\n        BAR,\n        BAZ,\n        BAT;\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/RollbackIntegrationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport static org.junit.Assert.*;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.ninja_squad.dbsetup.DbSetup;\nimport com.ninja_squad.dbsetup.DbSetupRuntimeException;\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.operation.Insert;\n\n/**\n * @author JB Nizet\n */\npublic class RollbackIntegrationTest {\n    private Connection connection;\n\n    @Before\n    public void prepare() throws SQLException {\n        new DbSetup(Database.DESTINATION, Operations.sequenceOf(CommonOperations.DROP_TABLES,\n                                                                CommonOperations.CREATE_TABLES)).launch();\n        connection = Database.getConnection();\n    }\n\n    @After\n    public void cleanup() throws SQLException {\n        connection.close();\n    }\n\n    @Test\n    public void testRollbackIfSQLException() throws SQLException {\n        Insert insertA1 =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\")\n                  .values(1L, 12.6)\n                  .build();\n        Insert insertA2 =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"fooo\")\n                  .values(1L, \"hello\")\n                  .build();\n        try {\n            new DbSetup(Database.DESTINATION, Operations.sequenceOf(insertA1, insertA2)).launch();\n            fail(\"expected a DbSetupRuntimeException\");\n        }\n        catch (DbSetupRuntimeException e) {\n            // expected\n        }\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A order by a_id\");\n        assertFalse(rs.next());\n    }\n\n    @Test\n    public void testRollbackIfOtherException() throws SQLException {\n        Insert insertA1 =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\")\n                  .values(1L, 12.6)\n                  .build();\n        Insert insertA2 =\n            Insert.into(\"A\")\n                  .columns(\"a_id\", \"nu\")\n                  .values(1L, \"hello\")\n                  .build();\n        try {\n            new DbSetup(Database.DESTINATION, Operations.sequenceOf(insertA1, insertA2)).launch();\n            fail(\"expected a NumberFormatException\");\n        }\n        catch (NumberFormatException e) {\n            // expected\n        }\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A order by a_id\");\n        assertFalse(rs.next());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/integration/TruncateIntegrationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.integration;\n\nimport static org.junit.Assert.*;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.ninja_squad.dbsetup.DbSetup;\nimport com.ninja_squad.dbsetup.Operations;\n\n/**\n * @author JB Nizet\n */\npublic class TruncateIntegrationTest {\n\n    private Connection connection;\n\n    @Before\n    public void prepare() throws SQLException {\n        new DbSetup(Database.DESTINATION, Operations.sequenceOf(CommonOperations.DROP_TABLES,\n                                                                CommonOperations.CREATE_TABLES,\n                                                                CommonOperations.INSERT_ROWS)).launch();\n        connection = Database.getConnection();\n    }\n\n    @After\n    public void cleanup() throws SQLException {\n        connection.close();\n    }\n\n    @Test\n    public void testDeleteAll() throws SQLException {\n        Statement stmt = connection.createStatement();\n        ResultSet rs = stmt.executeQuery(\"select * from A\");\n        assertTrue(rs.next());\n        rs.close();\n        rs = stmt.executeQuery(\"select * from B\");\n        assertTrue(rs.next());\n        connection.close();\n\n        new DbSetup(Database.DESTINATION, Operations.truncate(\"B\", \"A\")).launch();\n        connection = Database.getConnection();\n        stmt = connection.createStatement();\n        rs = stmt.executeQuery(\"select * from A\");\n        assertFalse(rs.next());\n        rs.close();\n        rs = stmt.executeQuery(\"select * from B\");\n        assertFalse(rs.next());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/operation/CompositeOperationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.Collections;\n\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\n\n/**\n * @author JB Nizet\n */\npublic class CompositeOperationTest {\n    @Test\n    public void sequenceOfWorksWhenNop() throws SQLException {\n        testNoArgOpWorks(CompositeOperation.sequenceOf());\n        testNoArgOpWorks(CompositeOperation.sequenceOf(Collections.<Operation>emptyList()));\n        testNoArgOpWorks(Operations.sequenceOf());\n        testNoArgOpWorks(Operations.sequenceOf(Collections.<Operation>emptyList()));\n    }\n\n    private void testNoArgOpWorks(Operation nop) throws SQLException {\n        Connection connection = mock(Connection.class);\n        nop.execute(connection, DefaultBinderConfiguration.INSTANCE);\n        assertEquals(\"NOP\", nop.toString());\n    }\n\n    @Test\n    public void sequenceOfWorksWhenSingleArg() throws SQLException {\n        Operation a = mock(Operation.class);\n\n        assertSame(a, CompositeOperation.sequenceOf(a));\n        assertSame(a, CompositeOperation.sequenceOf(Collections.singletonList(a)));\n        assertSame(a, Operations.sequenceOf(a));\n        assertSame(a, Operations.sequenceOf(Collections.singletonList(a)));\n    }\n\n    @Test\n    public void sequenceOfWorksWhenSeveralArgs() throws SQLException {\n        Operation a = mock(Operation.class);\n        Operation b = mock(Operation.class);\n\n        testSequenceOfWorksWhenMultipleArgs(CompositeOperation.sequenceOf(a, b), a, b);\n        testSequenceOfWorksWhenMultipleArgs(CompositeOperation.sequenceOf(Arrays.asList(a, b)), a, b);\n        testSequenceOfWorksWhenMultipleArgs(Operations.sequenceOf(a, b), a, b);\n        testSequenceOfWorksWhenMultipleArgs(Operations.sequenceOf(Arrays.asList(a, b)), a, b);\n    }\n\n    private void testSequenceOfWorksWhenMultipleArgs(Operation composite, Operation a, Operation b) throws SQLException {\n        Connection connection = mock(Connection.class);\n        composite.execute(connection, DefaultBinderConfiguration.INSTANCE);\n        InOrder inOrder = inOrder(a, b);\n        inOrder.verify(a).execute(connection, DefaultBinderConfiguration.INSTANCE);\n        inOrder.verify(b).execute(connection, DefaultBinderConfiguration.INSTANCE);\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() {\n        SqlOperation a = SqlOperation.of(\"A\");\n        SqlOperation b = SqlOperation.of(\"B\");\n\n        Operation c1 = CompositeOperation.sequenceOf(a, b);\n        Operation c2 = CompositeOperation.sequenceOf(a, b);\n\n        assertEquals(c1, c2);\n        assertEquals(c1.hashCode(), c2.hashCode());\n        assertEquals(c1, c1);\n        assertFalse(c1.equals(null));\n        assertFalse(c1.equals(\"hello\"));\n    }\n\n    @Test\n    public void toStringWorks() {\n        Operation a = mock(Operation.class);\n        when(a.toString()).thenReturn(\"a\");\n        Operation b = mock(Operation.class);\n        when(b.toString()).thenReturn(\"b\");\n        assertEquals(\"a\\nb\", CompositeOperation.sequenceOf(a, b).toString());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/operation/DeleteAllTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.Arrays;\n\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\n\n/**\n * @author JB Nizet\n */\npublic class DeleteAllTest {\n\n    @Test\n    public void fromWorks() throws SQLException {\n        testFrom(DeleteAll.from(\"A\", \"B\"));\n        testFrom(DeleteAll.from(Arrays.asList(\"A\", \"B\")));\n        testFrom(Operations.deleteAllFrom(\"A\", \"B\"));\n        testFrom(Operations.deleteAllFrom(Arrays.asList(\"A\", \"B\")));\n    }\n\n    @Test\n    public void toStringWorks() {\n        assertEquals(\"delete from A\", DeleteAll.from(\"A\").toString());\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() {\n        assertEquals(DeleteAll.from(\"A\"), Operations.deleteAllFrom(\"A\"));\n        assertEquals(DeleteAll.from(\"A\").hashCode(), DeleteAll.from(\"A\").hashCode());\n        assertFalse(DeleteAll.from(\"A\").equals(DeleteAll.from(\"B\")));\n        assertFalse(DeleteAll.from(\"A\").equals(null));\n        assertFalse(DeleteAll.from(\"A\").equals(\"hello\"));\n        DeleteAll a = DeleteAll.from(\"A\");\n        assertEquals(a, a);\n    }\n\n    private void testFrom(Operation deleteAllFromAandB) throws SQLException {\n        Connection connection = mock(Connection.class);\n        Statement stmt = mock(Statement.class);\n        when(connection.createStatement()).thenReturn(stmt);\n        deleteAllFromAandB.execute(connection, DefaultBinderConfiguration.INSTANCE);\n        InOrder inOrder = inOrder(stmt);\n        inOrder.verify(stmt).executeUpdate(\"delete from A\");\n        inOrder.verify(stmt).close();\n        inOrder.verify(stmt).executeUpdate(\"delete from B\");\n        inOrder.verify(stmt).close();\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/operation/InsertTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012-2015, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.ParameterMetaData;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport com.ninja_squad.dbsetup.bind.Binder;\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration;\nimport com.ninja_squad.dbsetup.bind.Binders;\nimport com.ninja_squad.dbsetup.generator.ValueGenerators;\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\n/**\n * @author JB Nizet\n */\npublic class InsertTest {\n\n    @Test\n    public void insertWorks() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n        Binder cBinder = mock(Binder.class);\n        Binder dBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c, d) values (?, ?, ?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 3)).thenReturn(cBinder);\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .values(\"a1\", \"b1\")\n                              .values(\"a2\", \"b2\")\n                              .row().column(\"b\", \"b3\")\n                                    .column(\"a\", \"a3\")\n                                    .end()\n                              .row().column(\"a\", \"a4\")\n                                    .end()\n                              .withDefaultValue(\"c\", \"c3\")\n                              .withDefaultValue(\"d\", \"d4\")\n                              .withBinder(bBinder, \"b\")\n                              .withBinder(dBinder, \"d\")\n                              .build();\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, cBinder, dBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a2\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b2\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a3\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b3\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a4\");\n        inOrder.verify(bBinder).bind(statement, 2, null);\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void insertWorksWithoutMetadata() throws SQLException {\n        Binder bBinder = mock(Binder.class);\n        Binder dBinder = mock(Binder.class);\n\n        Binder defaultBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        when(config.getBinder(isNull(ParameterMetaData.class), anyInt())).thenReturn(defaultBinder);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c, d) values (?, ?, ?, ?)\")).thenReturn(statement);\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .values(\"a1\", \"b1\")\n                              .values(\"a2\", \"b2\")\n                              .withDefaultValue(\"c\", \"c3\")\n                              .withDefaultValue(\"d\", \"d4\")\n                              .withBinder(bBinder, \"b\")\n                              .withBinder(dBinder, \"d\")\n                              .useMetadata(false)\n                              .build();\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(defaultBinder, bBinder, dBinder, statement);\n        inOrder.verify(defaultBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(defaultBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(defaultBinder).bind(statement, 1, \"a2\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b2\");\n        inOrder.verify(defaultBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void insertWorksWhenMetadataNotSupported() throws SQLException {\n        Binder defaultBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(config.getBinder(null, 1)).thenReturn(defaultBinder);\n        when(config.getBinder(null, 2)).thenReturn(defaultBinder);\n\n        when(connection.prepareStatement(\"insert into A (a, b) values (?, ?)\")).thenReturn(statement);\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .values(\"a1\", \"b1\")\n                              .build();\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(statement, defaultBinder);\n        inOrder.verify(defaultBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(defaultBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void insertWorksWhenColumnsSpecifiedByFirstRow() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n        Binder cBinder = mock(Binder.class);\n        Binder dBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c, d) values (?, ?, ?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 3)).thenReturn(cBinder);\n\n        Insert insert = Insert.into(\"A\")\n                              .row().column(\"a\", \"a1\")\n                                    .column(\"b\", \"b1\")\n                                    .end()\n                              .row().column(\"b\", \"b2\")\n                                    .end()\n                              .values(\"a3\", \"b3\")\n                              .withDefaultValue(\"c\", \"c3\")\n                              .withDefaultValue(\"d\", \"d4\")\n                              .withBinder(bBinder, \"b\")\n                              .withBinder(dBinder, \"d\")\n                              .build();\n\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, cBinder, dBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, null);\n        inOrder.verify(bBinder).bind(statement, 2, \"b2\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a3\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b3\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(dBinder).bind(statement, 4, \"d4\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void insertWorksWhenColumnsSpecifiedByFirstRepeatedRow() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n        Binder cBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c) values (?, ?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 2)).thenReturn(bBinder);\n        when(config.getBinder(metadata, 3)).thenReturn(cBinder);\n\n        Insert insert = Insert.into(\"A\")\n                              .row().column(\"a\", \"a1\")\n                                    .column(\"b\", \"b1\")\n                                    .times(2)\n                              .withDefaultValue(\"c\", \"c3\")\n                              .build();\n\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, cBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, \"c3\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void rowBuilderColumnFailsWhenMapContainsUnknownColumnName() {\n        Insert.into(\"A\")\n              .columns(\"a\", \"b\")\n              .row().column(\"c\", \"value of c\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void valuesFailsWhenMapContainsUnknownColumnName() {\n        Map<String, Object> map1 = new LinkedHashMap<String, Object>();\n        map1.put(\"c\", \"value of c\");\n        Map<String, Object> map2 = new LinkedHashMap<String, Object>();\n        map2.put(\"b\", \"value of b\");\n        Insert.into(\"A\").values(map1).values(map2);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void endRowFailsWhenItContainsUnknownColumnNameAndColumnNamesSpecifiedByFirstRow() {\n        Insert.into(\"A\")\n              .row().column(\"c\", \"value of c\").end()\n              .row().column(\"b\", \"value of b\").end();\n    }\n\n    @Test\n    public void repeatingValuesWork() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b) values (?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 2)).thenReturn(bBinder);\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .repeatingValues(\"a1\", \"b1\").times(2)\n                              .build();\n\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void rowAsMapCanBeAddedSeveralTimes() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n        Binder cBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c) values (?, ?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 2)).thenReturn(bBinder);\n        when(config.getBinder(metadata, 3)).thenReturn(cBinder);\n\n        Map<String, Object> row = new HashMap<String, Object>();\n        row.put(\"a\", \"a1\");\n        row.put(\"b\", \"b1\");\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\", \"c\")\n                              .repeatingValues(row).times(2)\n                              .build();\n\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, cBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, null);\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, null);\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void dynamicRowCanBeAddedSeveralTimes() throws SQLException {\n        Binder aBinder = mock(Binder.class);\n        Binder bBinder = mock(Binder.class);\n        Binder cBinder = mock(Binder.class);\n\n        Connection connection = mock(Connection.class);\n        BinderConfiguration config = mock(BinderConfiguration.class);\n        ParameterMetaData metadata = mock(ParameterMetaData.class);\n        PreparedStatement statement = mock(PreparedStatement.class);\n        when(connection.prepareStatement(\"insert into A (a, b, c) values (?, ?, ?)\")).thenReturn(statement);\n        when(statement.getParameterMetaData()).thenReturn(metadata);\n        when(config.getBinder(metadata, 1)).thenReturn(aBinder);\n        when(config.getBinder(metadata, 2)).thenReturn(bBinder);\n        when(config.getBinder(metadata, 3)).thenReturn(cBinder);\n\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\", \"c\")\n                              .row()\n                                  .column(\"a\", \"a1\")\n                                  .column(\"b\", \"b1\")\n                              .times(2)\n                              .build();\n\n        insert.execute(connection, config);\n\n        InOrder inOrder = inOrder(aBinder, bBinder, cBinder, statement);\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, null);\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(aBinder).bind(statement, 1, \"a1\");\n        inOrder.verify(bBinder).bind(statement, 2, \"b1\");\n        inOrder.verify(cBinder).bind(statement, 3, null);\n        inOrder.verify(statement).executeUpdate();\n        inOrder.verify(statement).close();\n    }\n\n    @Test\n    public void toStringWorks() {\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .values(\"a1\", \"b1\")\n                              .values(\"a2\", \"b2\")\n                              .withDefaultValue(\"c\", \"c3\")\n                              .withDefaultValue(\"d\", \"d4\")\n                              .withBinder(Binders.decimalBinder(), \"b\")\n                              .withBinder(Binders.dateBinder(), \"d\")\n                              .useMetadata(false)\n                              .build();\n        assertNotNull(insert.toString());\n    }\n\n    @Test\n    public void builderToStringWorks() {\n        Insert.Builder builder =  Insert.into(\"A\");\n\n        assertNotNull(builder.toString());\n\n        builder.columns(\"a\", \"b\");\n\n        assertNotNull(builder.toString());\n\n        builder.values(\"a1\", \"b1\")\n               .values(\"a2\", \"b2\");\n\n        assertNotNull(builder.toString());\n\n        builder.withDefaultValue(\"c\", \"c3\")\n               .withDefaultValue(\"d\", \"d4\");\n\n        assertNotNull(builder.toString());\n\n        builder.withBinder(Binders.decimalBinder(), \"b\")\n               .withBinder(Binders.dateBinder(), \"d\");\n\n        assertNotNull(builder.toString());\n\n        builder.useMetadata(false);\n\n        assertNotNull(builder.toString());\n\n        builder.build();\n\n        assertNotNull(builder.toString());\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() {\n        Insert insertA = Insert.into(\"A\")\n                               .columns(\"a\", \"b\")\n                               .values(\"a1\", \"b1\")\n                               .values(\"a2\", \"b2\")\n                               .withDefaultValue(\"c\", \"c3\")\n                               .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                               .withBinder(Binders.decimalBinder(), \"b\")\n                               .useMetadata(false)\n                               .build();\n        Insert insertB = Insert.into(\"A\")\n                               .columns(\"a\", \"b\")\n                               .values(\"a1\", \"b1\")\n                               .values(\"a2\", \"b2\")\n                               .withDefaultValue(\"c\", \"c3\")\n                               .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                               .withBinder(Binders.decimalBinder(), \"b\")\n                               .useMetadata(false)\n                               .build();\n        assertEquals(insertA, insertA);\n        assertEquals(insertA, insertB);\n        assertEquals(insertA.hashCode(), insertB.hashCode());\n        assertFalse(insertA.equals(null));\n        assertFalse(insertA.equals(\"hello\"));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"e\", \"b\")\n                        .values(\"a1\", \"b1\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c3\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                        .withBinder(Binders.decimalBinder(), \"b\")\n                        .useMetadata(false)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"a\", \"b\")\n                        .values(\"a1\", \"b2\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c3\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                        .withBinder(Binders.decimalBinder(), \"b\")\n                        .useMetadata(false)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"a\", \"b\")\n                        .values(\"a1\", \"b1\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c4\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                        .withBinder(Binders.decimalBinder(), \"b\")\n                        .useMetadata(false)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"a\", \"b\")\n                        .values(\"a1\", \"b1\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c3\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                        .withBinder(Binders.integerBinder(), \"b\")\n                        .useMetadata(false)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"a\", \"b\")\n                        .values(\"a1\", \"b1\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c3\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence())\n                        .withBinder(Binders.decimalBinder(), \"b\")\n                        .useMetadata(true)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n\n        insertB = Insert.into(\"A\")\n                        .columns(\"a\", \"b\")\n                        .values(\"a1\", \"b1\")\n                        .values(\"a2\", \"b2\")\n                        .withDefaultValue(\"c\", \"c3\")\n                        .withGeneratedValue(\"d\", ValueGenerators.sequence().startingAt(2L))\n                        .withBinder(Binders.decimalBinder(), \"b\")\n                        .useMetadata(false)\n                        .build();\n        assertFalse(insertA.equals(insertB));\n    }\n\n    @Test\n    public void getRowCountWorks() {\n        Insert insert = Insert.into(\"A\")\n                              .columns(\"a\", \"b\")\n                              .values(\"a1\", \"b1\")\n                              .values(\"a2\", \"b2\")\n                              .build();\n        assertEquals(2, insert.getRowCount());\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/operation/SqlOperationTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.Arrays;\n\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\n\n/**\n * @author JB Nizet\n */\npublic class SqlOperationTest {\n\n    @Test\n    public void ofWorks() throws SQLException {\n        testOf(SqlOperation.of(\"A\", \"B\"));\n        testOf(SqlOperation.of(Arrays.asList(\"A\", \"B\")));\n        testOf(Operations.sql(\"A\", \"B\"));\n        testOf(Operations.sql(Arrays.asList(\"A\", \"B\")));\n    }\n\n    @Test\n    public void toStringWorks() {\n        assertEquals(\"A\", SqlOperation.of(\"A\").toString());\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() {\n        assertEquals(SqlOperation.of(\"A\"), Operations.sql(\"A\"));\n        assertEquals(SqlOperation.of(\"A\").hashCode(), SqlOperation.of(\"A\").hashCode());\n        assertFalse(SqlOperation.of(\"A\").equals(SqlOperation.of(\"B\")));\n        assertFalse(SqlOperation.of(\"A\").equals(null));\n        assertFalse(SqlOperation.of(\"A\").equals(\"hello\"));\n        SqlOperation a = SqlOperation.of(\"A\");\n        assertEquals(a, a);\n    }\n\n    private void testOf(Operation aAndB) throws SQLException {\n        Connection connection = mock(Connection.class);\n        Statement stmt = mock(Statement.class);\n        when(connection.createStatement()).thenReturn(stmt);\n        aAndB.execute(connection, DefaultBinderConfiguration.INSTANCE);\n        InOrder inOrder = inOrder(stmt);\n        inOrder.verify(stmt).executeUpdate(\"A\");\n        inOrder.verify(stmt).close();\n        inOrder.verify(stmt).executeUpdate(\"B\");\n        inOrder.verify(stmt).close();\n    }\n}\n"
  },
  {
    "path": "DbSetup-core/src/test/java/com/ninja_squad/dbsetup/operation/TruncateTest.java",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2012, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\npackage com.ninja_squad.dbsetup.operation;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.Mockito.*;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.Arrays;\n\nimport org.junit.Test;\nimport org.mockito.InOrder;\n\nimport com.ninja_squad.dbsetup.Operations;\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration;\n\n/**\n * @author JB Nizet\n */\npublic class TruncateTest {\n\n    @Test\n    public void tablesWorks() throws SQLException {\n        testFrom(Truncate.tables(\"A\", \"B\"));\n        testFrom(Truncate.tables(Arrays.asList(\"A\", \"B\")));\n        testFrom(Operations.truncate(\"A\", \"B\"));\n        testFrom(Operations.truncate(Arrays.asList(\"A\", \"B\")));\n    }\n\n    @Test\n    public void toStringWorks() {\n        assertEquals(\"truncate table A\", Truncate.tables(\"A\").toString());\n    }\n\n    @Test\n    public void equalsAndHashCodeWork() {\n        assertEquals(Truncate.table(\"A\"), Operations.truncate(\"A\"));\n        assertEquals(Truncate.table(\"A\").hashCode(), Truncate.table(\"A\").hashCode());\n        assertFalse(Truncate.table(\"A\").equals(Truncate.table(\"B\")));\n        assertFalse(Truncate.table(\"A\").equals(null));\n        assertFalse(Truncate.table(\"A\").equals(\"hello\"));\n        Truncate a = Truncate.table(\"A\");\n        assertEquals(a, a);\n    }\n\n    private void testFrom(Operation truncateAandB) throws SQLException {\n        Connection connection = mock(Connection.class);\n        Statement stmt = mock(Statement.class);\n        when(connection.createStatement()).thenReturn(stmt);\n        truncateAandB.execute(connection, DefaultBinderConfiguration.INSTANCE);\n        InOrder inOrder = inOrder(stmt);\n        inOrder.verify(stmt).executeUpdate(\"truncate table A\");\n        inOrder.verify(stmt).close();\n        inOrder.verify(stmt).executeUpdate(\"truncate table B\");\n        inOrder.verify(stmt).close();\n    }\n}\n"
  },
  {
    "path": "DbSetup-kotlin/build.gradle.kts",
    "content": "import java.time.Duration\n\nplugins {\n    `java-library-convention`\n    kotlin(\"jvm\") version \"1.4.20\"\n    id(\"org.jetbrains.dokka\") version \"1.4.20\"\n}\n\nrepositories {\n    // TODO replace this by other repo once dokka allows it\n    jcenter()\n}\n\nproject.description = \"Kotlin extensions for DbSetup\"\n\ndependencies {\n    api(project(\":DbSetup\"))\n    testImplementation(\"junit:junit:4.+\")\n    testImplementation(\"org.mockito:mockito-all:1.9.0\")\n}\n\nval javadocJar by tasks.registering(Jar::class) {\n    archiveFileName.set(\"dokka-html.jar\")\n    from(tasks.dokkaJavadoc)\n}\n\nval dokkaJar by tasks.registering(Jar::class) {\n    archiveFileName.set(\"dokka-javadoc.jar\")\n    from(tasks.dokkaHtml)\n}\n\npublishing {\n    publications.named<MavenPublication>(\"maven\") {\n        artifact(dokkaJar) {\n            classifier = \"dokka\"\n        }\n        artifact(javadocJar) {\n            classifier = \"javadoc\"\n        }\n    }\n}\n"
  },
  {
    "path": "DbSetup-kotlin/src/main/kotlin/com/ninja_squad/dbsetup_kotlin/DbSetup.kt",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\npackage com.ninja_squad.dbsetup_kotlin\n\nimport com.ninja_squad.dbsetup.DbSetup\nimport com.ninja_squad.dbsetup.DbSetupTracker\n\n/**\n * Extension function of DbSetup allowing to launch it with a tracker. This allows launching the setup in an easier way.\n *\n * Instead of doing\n *\n * ```\n * val theSetup = dbSetup {\n *     ...\n * }\n * tracker.launchIfNecessary(theSetup)\n * ```\n *\n * you can simply do\n *\n * ```\n * dbSetup {\n *     ...\n * }.launchWith(tracker)\n * ```\n *\n * @param tracker the tracker used to launch the DbSetup, if necessary.\n *\n * @author JB Nizet\n */\nfun DbSetup.launchWith(tracker: DbSetupTracker) = tracker.launchIfNecessary(this)\n"
  },
  {
    "path": "DbSetup-kotlin/src/main/kotlin/com/ninja_squad/dbsetup_kotlin/DbSetupBuilder.kt",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\npackage com.ninja_squad.dbsetup_kotlin\n\nimport com.ninja_squad.dbsetup.DbSetup\nimport com.ninja_squad.dbsetup.Operations\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration\nimport com.ninja_squad.dbsetup.destination.Destination\nimport com.ninja_squad.dbsetup.operation.Insert\nimport com.ninja_squad.dbsetup.operation.Operation\n\n/**\n * A builder allowing to configure a DbSetup from a lambda expression whose receiver type is this builder.\n * The intended usage is to use the [dbSetup] top level function.\n *\n * @param to The destination of the DbSetup\n * @property binderConfiguration The binder configuration of the DbSetup. It not set, the default configuration is used\n */\nclass DbSetupBuilder(private val to: Destination, var binderConfiguration: BinderConfiguration = DefaultBinderConfiguration.INSTANCE) {\n\n    private val operations = mutableListOf<Operation>()\n\n    /**\n     * Adds an Insert operation to the DbSetup, using a lambda expression to configure it.\n     *\n     * Example usage:\n     *\n     * ```\n     * dbSetup {\n     *     insertInto(\"user\") {\n     *         columns(\"id\", \"name\")\n     *         values(1, \"John Doe\")\n     *         ...\n     *     }\n     * }\n     * ```\n     *\n     * @param table the name of the table to insert into\n     * @param configure the function used to configure the insert.\n     */\n    inline fun insertInto(table: String, configure: Insert.Builder.() -> Unit) {\n        val builder = Insert.into(table)\n        builder.configure()\n        execute(builder.build())\n    }\n\n    /**\n     * Adds a DeleteAll operation to the DbSetup\n     * @param table the table to delete from\n     */\n    fun deleteAllFrom(table: String) = execute(Operations.deleteAllFrom(table))\n\n    /**\n     * Adds DeleteAll operations to the DbSetup\n     * @param tables the tables to delete from\n     */\n    fun deleteAllFrom(vararg tables: String) = execute(Operations.deleteAllFrom(*tables))\n\n    /**\n     * Adds DeleteAll operations to the DbSetup\n     * @param tables the tables to delete from\n     */\n    fun deleteAllFrom(tables: List<String>) = execute(Operations.deleteAllFrom(tables))\n\n    /**\n     * Adds a Truncate operation to the DbSetup\n     * @param table the table to truncate\n     */\n    fun truncate(table: String) = execute(Operations.truncate(table))\n\n    /**\n     * Adds Truncate operations to the DbSetup\n     * @param tables the tables to delete from\n     */\n    fun truncate(vararg tables: String) = execute(Operations.truncate(*tables))\n\n    /**\n     * Adds Truncate operations to the DbSetup\n     * @param tables the tables to truncate\n     */\n    fun truncate(tables: List<String>) = execute(Operations.truncate(tables))\n\n    /**\n     * Adds a SqlOperation to the DbSetup\n     * @param statement the SQL statement to execute\n     */\n    fun sql(statement: String) = execute(Operations.sql(statement))\n\n    /**\n     * Adds SqlOperations to the DbSetup\n     * @param statements the SQL statements to execute\n     */\n    fun sql(vararg statements: String) = execute(Operations.sql(*statements))\n\n    /**\n     * Adds SqlOperations to the DbSetup\n     * @param statements the SQL statements to execute\n     */\n    fun sql(statements: List<String>) = execute(Operations.sql(statements))\n\n    /**\n     * Adds an operation to the DbSetup. Custom extension functions typically delegate to this method to\n     * add the operation they want.\n     * @param operation the operation to add\n     */\n    fun execute(operation: Operation) = operations.add(operation)\n\n    /**\n     * Builds the DbSetup. This method is called by the dbSetup function after the builder has been configured.\n     */\n    internal fun build() = DbSetup(to, Operations.sequenceOf(operations), binderConfiguration)\n}\n"
  },
  {
    "path": "DbSetup-kotlin/src/main/kotlin/com/ninja_squad/dbsetup_kotlin/Functions.kt",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\npackage com.ninja_squad.dbsetup_kotlin\n\nimport com.ninja_squad.dbsetup.DbSetup\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration\nimport com.ninja_squad.dbsetup.destination.DataSourceDestination\nimport com.ninja_squad.dbsetup.destination.Destination\nimport com.ninja_squad.dbsetup.operation.Insert\nimport javax.sql.DataSource\n\n/**\n * Top-level function allowing to create a DbSetup by passing a destination and a lambda expression used to configure\n * the operations that must be made.\n *\n * Example usage:\n *\n * ```\n * val setup = dbSetup(to = DriverManagerDestination(url, user, password)) {\n *     deleteAllFrom(\"user\", \"country\")\n *     insertInto(\"country\") {\n *         ...\n *     }\n *     insertInto(\"user\") {\n *         ...\n *     }\n *     sql(...)\n * }\n * ```\n *\n * @param to the destination of the DbSetup\n * @param binderConfiguration a custom binder configuration. The default one is used if not specified\n * @param configure the function used to configure the DbSetup\n * @return the created DbSetup\n *\n * @author JB Nizet\n */\nfun dbSetup(to: Destination,\n            binderConfiguration: BinderConfiguration = DefaultBinderConfiguration.INSTANCE,\n            configure: DbSetupBuilder.() -> Unit): DbSetup {\n    val builder = DbSetupBuilder(to, binderConfiguration)\n    builder.configure()\n    return builder.build()\n}\n\n/**\n * Top-level function allowing to create a DbSetup by passing a DataSource and a lambda expression used to configure\n * the operations that must be made.\n *\n * Example usage:\n *\n * ```\n * val setup = dbSetup(to = dataSource) {\n *     deleteAllFrom(\"user\", \"country\")\n *     insertInto(\"country\") {\n *         ...\n *     }\n *     insertInto(\"user\") {\n *         ...\n *     }\n *     sql(...)\n * }\n * ```\n *\n * @param to the destination of the DbSetup\n * @param binderConfiguration a custom binder configuration. The default one is used if not specified\n * @param configure the function used to configure the DbSetup\n * @return the created DbSetup\n *\n * @author JB Nizet\n */\nfun dbSetup(to: DataSource,\n            binderConfiguration: BinderConfiguration = DefaultBinderConfiguration.INSTANCE,\n            configure: DbSetupBuilder.() -> Unit) = dbSetup(DataSourceDestination(to), binderConfiguration, configure)\n/**\n * Top-level function allowing to create an Insert operation by passing a lambda expression configuring it.\n *\n * Example usage:\n *\n * ```\n * val insertUsers = insertInto(\"user\") {\n *     columns(\"id\", \"name\")\n *     values(1, \"John Doe\")\n *     values(2, \"Jane Doe\")\n * }\n * ```\n * @param table the name of the table to insert into\n * @param configure the function used to configure the Insert\n * @return the created Insert operation\n *\n * @author JB Nizet\n */\ninline fun insertInto(table: String, configure: Insert.Builder.() -> Unit): Insert {\n    val builder = Insert.into(table)\n    builder.configure()\n    return builder.build()\n}\n"
  },
  {
    "path": "DbSetup-kotlin/src/main/kotlin/com/ninja_squad/dbsetup_kotlin/Insert.Builder.kt",
    "content": "/*\n * The MIT License\n *\n * Copyright (c) 2016, Ninja Squad\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\npackage com.ninja_squad.dbsetup_kotlin\n\nimport com.ninja_squad.dbsetup.operation.Insert\n\n/**\n * Extension function of InsertBuilder allowing to specify values as columns/value pairs instead of a Map.\n *\n * Example usage:\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"name\", \"birthDate\")\n *     mappedValues(\"id\" to 1, \"name\" to \"John Doe\")\n * }\n * ```\n *\n * This is equivalent to\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"name\", \"birthDate\")\n *     values(mapOf(\"id\" to 1, \"name\" to \"John Doe\"))\n * }\n * ```\n *\n * but is a bit shorter. Beware to NOT use\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"name\", \"birthDate\")\n *     values(\"id\" to 1, \"name\" to \"John Doe\")\n * }\n * ```\n *\n * because that would try to insert the Pairs themselves into the table, rather than the values of the pairs.\n *\n * @param entries the column/value pairs of the row to insert\n * @return the Insert Builder for chaining (although that is usually not necessary with the Kotlin DSL)\n *\n * @author JB Nizet\n */\nfun Insert.Builder.mappedValues(vararg entries: Pair<String, Any?>): Insert.Builder {\n    return this.values(mapOf(*entries))\n}\n\n/**\n * Allows inserting the same values multiple times, by specifying them as colum/value pairs instead of a Map.\n *\n * Example usage:\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"firstName\", \"lastName\")\n *     withGeneratedValue(\"id\", ValueGenerators.sequence())\n *     repeatingMappedValues(\"firstName\" to \"John\", \"lastName\" to \"Doe\").times(100)\n * }\n * ```\n *\n * This is equivalent to\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"firstName\", \"lastName\")\n *     withGeneratedValue(\"id\", ValueGenerators.sequence())\n *     repeatingValues(mapOf(\"firstName\" to \"John\", \"lastName\" to \"Doe\")).times(100)\n * }\n * ```\n *\n * but is a bit shorter. Beware to NOT use\n *\n * ```\n * insertInto(\"user\") {\n *     columns(\"id\", \"firstName\", \"lastName\")\n *     withGeneratedValue(\"id\", ValueGenerators.sequence())\n *     repeatingValues(\"firstName\" to \"John\", \"lastName\" to \"Doe\").times(100)\n * }\n * ```\n *\n * because that would try to insert the Pairs themselves into the table, rather than the values of the pairs.\n *\n * @param entries the column/value pairs of the row to insert\n * @return the RowRepeater, on which you must call times(N) to specify how many similar rows to insert\n *\n * @author JB Nizet\n */\nfun Insert.Builder.repeatingMappedValues(vararg entries: Pair<String, Any?>) = this.repeatingValues(mapOf(*entries))\n"
  },
  {
    "path": "DbSetup-kotlin/src/test/kotlin/com/ninja_squad/dbsetup_kotlin/DbSetupBuilderTest.kt",
    "content": "package com.ninja_squad.dbsetup_kotlin;\n\nimport com.ninja_squad.dbsetup.bind.BinderConfiguration\nimport com.ninja_squad.dbsetup.bind.DefaultBinderConfiguration\nimport com.ninja_squad.dbsetup.destination.Destination\nimport com.ninja_squad.dbsetup.operation.Operation\nimport org.junit.Before\nimport org.junit.Test\nimport org.mockito.Mockito.verify\nimport java.sql.Connection\nimport java.sql.PreparedStatement\nimport java.sql.Statement\nimport javax.sql.DataSource\n\nclass DbSetupBuilderTest {\n\n    private lateinit var mockDestination: Destination\n    private lateinit var mockConnection: Connection\n    private lateinit var mockOperation: Operation\n    private lateinit var mockConfig: BinderConfiguration\n    private lateinit var mockStatement: Statement\n\n    @Before\n    fun prepare() {\n        mockDestination = mock()\n        mockConnection = mock()\n        whenever(mockDestination.connection).thenReturn(mockConnection)\n        mockOperation = mock()\n        mockConfig = mock()\n        mockStatement = mock<Statement>()\n        whenever(mockConnection.createStatement()).thenReturn(mockStatement)\n    }\n\n    @Test\n    fun `should execute an operation with the binder configuration specified as property`() {\n        dbSetup(to = mockDestination) {\n            binderConfiguration = mockConfig\n            execute(mockOperation)\n        }.launch()\n\n        verify(mockOperation).execute(mockConnection, mockConfig)\n    }\n\n    @Test\n    fun `should execute an operation with the binder configuration specified as argument`() {\n        dbSetup(to = mockDestination, binderConfiguration = mockConfig) {\n            execute(mockOperation)\n        }.launch()\n\n        verify(mockOperation).execute(mockConnection, mockConfig)\n    }\n\n    @Test\n    fun `should execute an operation with a DataSource specified as argument`() {\n        val mockDataSource = mock<DataSource>()\n        whenever(mockDataSource.connection).thenReturn(mockConnection)\n        dbSetup(to = mockDataSource, binderConfiguration = mockConfig) {\n            execute(mockOperation)\n        }.launch()\n\n        verify(mockOperation).execute(mockConnection, mockConfig)\n    }\n\n    @Test\n    fun `should use the default binder configuration if not set`() {\n        dbSetup(to = mockDestination) {\n            execute(mockOperation)\n        }.launch()\n\n        verify(mockOperation).execute(mockConnection, DefaultBinderConfiguration.INSTANCE)\n    }\n\n    @Test\n    fun `should delete all from one table`() {\n        dbSetup(to = mockDestination) {\n            deleteAllFrom(\"user\")\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"delete from user\")\n    }\n\n    @Test\n    fun `should delete all from multiple tables passed as vararg`() {\n        dbSetup(to = mockDestination) {\n            deleteAllFrom(\"country\", \"user\")\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"delete from country\")\n        verify(mockStatement).executeUpdate(\"delete from user\")\n    }\n\n    @Test\n    fun `should delete all from multiple tables passed as list`() {\n        dbSetup(to = mockDestination) {\n            deleteAllFrom(listOf(\"country\", \"user\"))\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"delete from country\")\n        verify(mockStatement).executeUpdate(\"delete from user\")\n    }\n\n    @Test\n    fun `should truncate one table`() {\n        dbSetup(to = mockDestination) {\n            truncate(\"user\")\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"truncate table user\")\n    }\n\n    @Test\n    fun `should truncate multiple tables passed as vararg`() {\n        dbSetup(to = mockDestination) {\n            truncate(\"country\", \"user\")\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"truncate table country\")\n        verify(mockStatement).executeUpdate(\"truncate table user\")\n    }\n\n    @Test\n    fun `should truncate multiple tables passed as list`() {\n        dbSetup(to = mockDestination) {\n            truncate(listOf(\"country\", \"user\"))\n        }.launch()\n\n        verify(mockStatement).executeUpdate(\"truncate table country\")\n        verify(mockStatement).executeUpdate(\"truncate table user\")\n    }\n\n    @Test\n    fun `should execute one sql statement`() {\n        val query = \"update foo where bar = 1\"\n        dbSetup(to = mockDestination) {\n            sql(query)\n        }.launch()\n\n        verify(mockStatement).executeUpdate(query)\n    }\n\n    @Test\n    fun `should execute multiple sql statements passed as vararg`() {\n        val query1 = \"update foo where bar = 1\"\n        val query2 = \"update baz where qux = 1\"\n        dbSetup(to = mockDestination) {\n            sql(query1, query2)\n        }.launch()\n\n        verify(mockStatement).executeUpdate(query1)\n        verify(mockStatement).executeUpdate(query2)\n    }\n\n    @Test\n    fun `should execute multiple sql statements passed as list`() {\n        val query1 = \"update foo where bar = 1\"\n        val query2 = \"update baz where qux = 1\"\n        dbSetup(to = mockDestination) {\n            sql(listOf(query1, query2))\n        }.launch()\n\n        verify(mockStatement).executeUpdate(query1)\n        verify(mockStatement).executeUpdate(query2)\n    }\n\n    @Test\n    fun `should insert`() {\n        val mockPreparedStatement = mock<PreparedStatement>()\n        whenever(mockConnection.prepareStatement(\"insert into user (id, name) values (?, ?)\"))\n                .thenReturn(mockPreparedStatement)\n\n        dbSetup(to = mockDestination) {\n            insertInto(\"user\") {\n                columns(\"id\", \"name\")\n                values(1, \"John\")\n            }\n        }.launch()\n\n        verify(mockPreparedStatement).setObject(1, 1)\n        verify(mockPreparedStatement).setObject(2, \"John\")\n        verify(mockPreparedStatement).executeUpdate()\n    }\n}\n"
  },
  {
    "path": "DbSetup-kotlin/src/test/kotlin/com/ninja_squad/dbsetup_kotlin/InsertBuilderTest.kt",
    "content": "package com.ninja_squad.dbsetup_kotlin\n\nimport com.ninja_squad.dbsetup.generator.ValueGenerators\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\n/**\n * @author JB Nizet\n */\nclass InsertBuilderTest {\n    @Test\n    fun `should construct an insert with mapped values`() {\n        val insert = insertInto(\"user\") {\n            mappedValues(\"id\" to 1L, \"name\" to \"John\")\n        }\n\n        val expected = insertInto(\"user\") {\n            values(mapOf(\"id\" to 1L, \"name\" to \"John\"))\n        }\n\n        assertEquals(expected, insert);\n    }\n\n    @Test\n    fun `should construct an insert with repeating mapped values`() {\n        val insert = insertInto(\"user\") {\n            withGeneratedValue(\"id\", ValueGenerators.sequence())\n            repeatingMappedValues(\"name\" to \"John\").times(10)\n        }\n\n        val expected = insertInto(\"user\") {\n            withGeneratedValue(\"id\", ValueGenerators.sequence())\n            repeatingValues(mapOf(\"name\" to \"John\")).times(10)\n        }\n\n        assertEquals(expected, insert);\n    }\n}\n"
  },
  {
    "path": "DbSetup-kotlin/src/test/kotlin/com/ninja_squad/dbsetup_kotlin/MockitoExtensions.kt",
    "content": "package com.ninja_squad.dbsetup_kotlin\n\nimport org.mockito.Mockito\nimport org.mockito.stubbing.OngoingStubbing\n\n/**\n * @author JB Nizet\n */\nfun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)\n\ninline fun <reified T: Any> mock(): T = Mockito.mock(T::class.java)\n"
  },
  {
    "path": "README.md",
    "content": "[![CircleCI](https://circleci.com/gh/Ninja-Squad/DbSetup.svg?style=svg)](https://circleci.com/gh/Ninja-Squad/DbSetup)\n\n# Overview\n\nDbSetup allows populating a database before executing automated integration tests (typically, DAO/Repository automated tests). \nAlthough DBUnit, which is a great project, allows doing the same thing and much more, it's also harder to use and setup. \nAnd in our experience, in 98% of the cases, DBUnit is only used to pre-populate a database before executing every test method. \nThis is the task on which DbSetup concentrates.\n\nThe philosophy of DbSetup is that DAO tests should not have to setup the database, execute tests, and then remove everything from the database. \nInstead, a single setup method should be used to delete everything from the database (whatever the previous test put in it, or the initial state \nof the database tables), and then populate it with the data necessary to execute the test.\n\nAnother design choice of DbSetup is to provide an easy to use and simple Java API to populate the database, rather than loading data from external \nXML files. Using a Java API has several advantages:\n\n   - It allows using real Java types as data (longs, enums, etc.)\n   - It allows defining default values, looping to generate several similar rows, storing data sets in variables or factorizing their \n     creation using resusable methods\n   - It allows viewing the data sets easily, without having to open external files, by storing the data set directly into the test class, \n     or by navigating through classes and methods using the IDE shortcuts.\n   - For more complex situations, like cyclic referential integrity constraints between rows, the Java API allows easily integrating SQL \n     statements into the sequence of operations to execute to pre-populate the database. These SQL statements can, for example, disable \n     constraints and re-enable them.\n\n# Documentation\n\nThe documentation is available at [dbsetup.ninja-squad.com](http://dbsetup.ninja-squad.com). It's hosted by github,\nfrom the gh-pages branch.\n\n# Bugs and RFEs\n\nTo submit bugs or RFEs, use [Github](https://github.com/Ninja-Squad/DbSetup/issues).\n\n# How to...\n## build\n\n    ./gradlew build\n\n## install the artifacts in your own local Maven repository\n    \n    ./gradlew publishToMavenLocal\n    \n## release a new version of DbSetup\n\n - Make sure you have a file named `gradle.properties` under `HOME/.gradle`, and this file contains the following \nproperties:\n\n        sonatypeUsername=<the sonatype user name>\n        sonatypePassword=<the sonatype password>       \n        signing.keyId=<the ID of the PGP key to use>\n        signing.password=<the password used to protect your PGP key configured in the ninjasquad bintray account>\n        signing.secretKeyRingFile=<the path to the secring.jpg file>\n\n - Make sure the version of DbSetup in the project's `gradle.properties` file is the right one. It must not end with `-SNAPSHOT`\n - Execute\n \n        ./gradlew clean build publishToSonatype closeAndReleaseSonatypeStagingRepository\n        \nThat should create a new staging repo in Sonatype OSSRS, then upload the artefacts to this staging repo, \nsign close the repo and release (i.e. sync it to Maven central).\n\nTo only close the repo to test without syncing to Maven central, run\n\n    ./gradlew clean build publishToSonatype closeSonatypeStagingRepository\n\nNote: to check the jar file and pom publication before uploading, you can use \n\n    ./gradlew publishMavenPublicationToBuildRepository\n    \nwhich will simply publish under the `build/repo` directory of the root project.\n\nSee the publishing guide at https://github.com/rwinch/gradle-publish-ossrh-sample.\n\n# License\n\nDbSetup is released under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).\n\nThe MIT License\n\nCopyright (c) 2012-2013, Ninja Squad\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import java.time.Duration\n\nplugins {\n    id(\"io.github.gradle-nexus.publish-plugin\") version \"1.0.0\"\n}\n\ngroup = \"com.ninja-squad\"\n\nnexusPublishing {\n    repositories {\n        sonatype {\n            username.set(project.findProperty(\"sonatypeUsername\")?.toString() ?: \"\")\n            password.set(project.findProperty(\"sonatypePassword\")?.toString() ?: \"\")\n        }\n    }\n    connectTimeout.set(Duration.ofMinutes(3))\n    clientTimeout.set(Duration.ofMinutes(3))\n}\n"
  },
  {
    "path": "buildSrc/build.gradle.kts",
    "content": "plugins {\n    `kotlin-dsl`\n}\n\nrepositories {\n    mavenCentral()\n    gradlePluginPortal()\n}\n\n"
  },
  {
    "path": "buildSrc/settings.grade.kts",
    "content": ""
  },
  {
    "path": "buildSrc/src/main/kotlin/java-library-convention.gradle.kts",
    "content": "import org.gradle.api.tasks.testing.logging.TestExceptionFormat\n\nplugins {\n    `java-library`\n    signing\n    `maven-publish`\n    jacoco\n}\n\ngroup = rootProject.group\nversion = rootProject.version\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n    targetCompatibility = JavaVersion.VERSION_1_8\n    withSourcesJar()\n}\n\nrepositories {\n    mavenCentral()\n}\n\ntasks {\n    val checkJavaVersion by registering  {\n        doLast {\n            if (!JavaVersion.current().isJava8()) {\n                val message = \"ERROR: Java 1.8 required but ${JavaVersion.current()} found. Change your JAVA_HOME environment variable.\";\n                throw IllegalStateException(message);\n            }\n        }\n    }\n\n    compileJava {\n        dependsOn(checkJavaVersion)\n    }\n\n    withType<JavaCompile> {\n        options.encoding = \"UTF-8\"\n    }\n\n    withType<Jar> {\n        manifest {\n            attributes(\n                \"Implementation-Title\" to project.name,\n                \"Implementation-Version\" to project.version,\n                \"Implementation-Vendor\" to \"ninja-squad.com\",\n                \"Bundle-Vendor\" to \"ninja-squad.com\"\n            )\n        }\n    }\n\n    test {\n        testLogging {\n            exceptionFormat = TestExceptionFormat.FULL\n        }\n    }\n\n    check {\n        dependsOn(jacocoTestReport)\n    }\n}\n\npublishing {\n    publications {\n        create<MavenPublication>(\"maven\") {\n            from(components[\"java\"])\n\n            pom {\n                name.set(project.name)\n                description.set(project.provider(Callable { project.description }))\n                url.set(\"https://dbsetup.ninja-squad.com/\")\n                organization {\n                    name.set(\"Ninja Squad\")\n                    url.set(\"https://ninja-squad.com\")\n                }\n                licenses {\n                    license {\n                        name.set(\"MIT License\")\n                        url.set(\"https://dbsetup.ninja-squad.com/license.html\")\n                        distribution.set(\"repo\")\n                    }\n                }\n                developers {\n                    developer {\n                        id.set(\"jnizet\")\n                        name.set(\"Jean-Baptiste Nizet\")\n                        email.set(\"jb@ninja-squad.com\")\n                    }\n                }\n                scm {\n                    connection.set(\"scm:git:git://github.com/Ninja-Squad/DbSetup\")\n                    developerConnection.set(\"scm:git:git://github.com/Ninja-Squad/DbSetup\")\n                    url.set(\"https://github.com/Ninja-Squad/DbSetup\")\n                }\n            }\n        }\n    }\n    repositories {\n        maven {\n            name = \"build\"\n            url = uri(\"${rootProject.buildDir}/repo\")\n        }\n    }\n}\n\nsigning {\n    sign(publishing.publications[\"maven\"])\n}\n\n\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Feb 07 17:02:40 CET 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.8.2-bin.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "version = 2.1.1\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "rootProject.name = \"DbSetupParent\"\n\ninclude(\"DbSetup-core\", \"DbSetup-kotlin\")\n\nproject(\":DbSetup-core\").name = \"DbSetup\"\n"
  }
]