From e648132697cc753b2ccd2e3a2815eb40e154c805 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 27 Feb 2013 15:52:03 +0100 Subject: [PATCH] [#2280] Improve supported formats for MockFileDatabase --- .../src/main/java/org/jooq/impl/Executor.java | 76 +++++++++++++++++++ jOOQ/src/main/java/org/jooq/impl/Utils.java | 64 +++++++++++++++- .../org/jooq/tools/jdbc/MockFileDatabase.java | 10 +-- .../src/test/java/org/jooq/test/MockTest.java | 48 ++++++++++++ .../test/resources/org/jooq/test/data/db.txt | 19 ++++- 5 files changed, 203 insertions(+), 14 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/Executor.java b/jOOQ/src/main/java/org/jooq/impl/Executor.java index 49a903ad34..f023b058f0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Executor.java +++ b/jOOQ/src/main/java/org/jooq/impl/Executor.java @@ -1590,6 +1590,82 @@ public class Executor implements Configuration { return fetchLazy(rs, Utils.getDataTypes(types)); } + /** + * Fetch all data from a formatted string. + *

+ * The supplied string is supposed to be formatted in the following, + * human-readable way:

+     * COL1  COL2   COL3 containing whitespace
+     * ----- ----   --------------------------
+     * val1  1      some text
+     * val2   2      more text
+     * 
This method will decode the above formatted string + * according to the following rules: + * + *

+ * This is the same as calling {@link #fetchFromTXT(String, String)} with + * "{null}" as nullLiteral + *

+ * A future version of jOOQ will also support the inverse operation of + * {@link Result#format()} through this method + * + * @param string The formatted string + * @return The transformed result + * @see #fetchFromTXT(String, String) + * @throws DataAccessException If the supplied string does not adhere to the + * above format rules. + */ + @Support + public final Result fetchFromTXT(String string) throws DataAccessException { + return fetchFromTXT(string, "{null}"); + } + + /** + * Fetch all data from a formatted string. + *

+ * The supplied string is supposed to be formatted in the following, + * human-readable way:

+     * COL1  COL2   COL3 containing whitespace
+     * ----- ----   --------------------------
+     * val1  1      some text
+     * val2   2      more text
+     * 
This method will decode the above formatted string + * according to the following rules: + * + *

+ * A future version of jOOQ will also support the inverse operation of + * {@link Result#format()} through this method + * + * @param string The formatted string + * @param nullLiteral The string literal to be used as null + * value. + * @return The transformed result + * @throws DataAccessException If the supplied string does not adhere to the + * above format rules. + */ + @Support + public final Result fetchFromTXT(String string, String nullLiteral) throws DataAccessException { + return fetchFromStringData(Utils.parseTXT(string, nullLiteral)); + } + /** * Fetch all data from a CSV string. *

diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index a1f0898cf8..09da952480 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -75,6 +75,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.Column; @@ -130,7 +131,7 @@ import org.jooq.util.postgres.PostgresUtils; */ final class Utils { - static final JooqLogger log = JooqLogger.getLogger(Utils.class); + static final JooqLogger log = JooqLogger.getLogger(Utils.class); // ------------------------------------------------------------------------ // Some constants for use with Configuration.setData() @@ -141,7 +142,7 @@ final class Utils { * clause in {@link Executor#batchStore(UpdatableRecord...)} calls for * {@link SQLDialect#POSTGRES} */ - static final String DATA_OMIT_RETURNING_CLAUSE = "org.jooq.configuration.omit-returning-clause"; + static final String DATA_OMIT_RETURNING_CLAUSE = "org.jooq.configuration.omit-returning-clause"; /** * [#1905] This constant is used internally by jOOQ to indicate to @@ -161,7 +162,7 @@ final class Utils { * The default escape character for [a] LIKE [b] ESCAPE [...] * clauses. */ - static final char ESCAPE = '!'; + static final char ESCAPE = '!'; /** * Indicating whether JPA (javax.persistence) is on the @@ -169,10 +170,15 @@ final class Utils { */ private static Boolean isJPAAvailable; + /** + * A pattern for the dash line syntax + */ + private static final Pattern DASH_PATTERN = Pattern.compile("(-+)"); ; + /** * A pattern for the JDBC escape syntax */ - private static final Pattern JDBC_ESCAPE_PATTERN = Pattern.compile("\\{(fn|d|t|ts)\\b.*"); + private static final Pattern JDBC_ESCAPE_PATTERN = Pattern.compile("\\{(fn|d|t|ts)\\b.*"); // ------------------------------------------------------------------------ // XXX: Record constructors and related methods @@ -2396,4 +2402,54 @@ final class Utils { private static final void pgSetValue(UDTRecord record, Field field, String value) throws SQLException { record.setValue(field, pgFromString(field.getType(), value)); } + + static List parseTXT(String string, String nullLiteral) { + String[] strings = string.split("[\\r\\n]+"); + + if (strings.length < 2) { + throw new DataAccessException("String must contain at least two lines"); + } + + // Find the set of positions defined by the dashed line number two: + // ----- ------- ---- + // results in + // [{0,5} {7,14} {15,19}] + List positions = new ArrayList(); + Matcher m = DASH_PATTERN.matcher(strings[1]); + + while (m.find()) { + positions.add(new int[] { m.start(1), m.end(1) }); + } + + // Parse header line and data lines into string arrays + List result = new ArrayList(); + parseTXTLine(positions, result, strings[0], nullLiteral); + + for (int j = 2; j < strings.length; j++) { + parseTXTLine(positions, result, strings[j], nullLiteral); + } + + return result; + } + + private static void parseTXTLine(List positions, List result, String string, String nullLiteral) { + String[] fields = new String[positions.size()]; + result.add(fields); + int length = string.length(); + + for (int i = 0; i < fields.length; i++) { + int[] position = positions.get(i); + + if (position[0] < length) { + fields[i] = string.substring(position[0], Math.min(position[1], length)).trim(); + } + else { + fields[i] = null; + } + + if (StringUtils.equals(fields[i], nullLiteral)) { + fields[i] = null; + } + } + } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockFileDatabase.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockFileDatabase.java index 8b7b28e8fc..5c812b13f4 100644 --- a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockFileDatabase.java +++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockFileDatabase.java @@ -128,12 +128,8 @@ public class MockFileDatabase implements MockDataProvider { // A line of result data else if (line.startsWith(">")) { - - // But ignore visual delimiter - if (!line.startsWith("> -")) { - currentResult.append(line.substring(2)); - currentResult.append("\n"); - } + currentResult.append(line.substring(2)); + currentResult.append("\n"); } // A result data termination literal @@ -219,7 +215,7 @@ public class MockFileDatabase implements MockDataProvider { rows = Integer.parseInt(rowString.substring(7).trim()); } - return new MockResult(rows, create.fetchFromCSV(currentResult.toString(), ' ')); + return new MockResult(rows, create.fetchFromTXT(currentResult.toString())); } private String readLine() throws IOException { diff --git a/jOOQ/src/test/java/org/jooq/test/MockTest.java b/jOOQ/src/test/java/org/jooq/test/MockTest.java index a5dda9ced2..d4e831d3f9 100644 --- a/jOOQ/src/test/java/org/jooq/test/MockTest.java +++ b/jOOQ/src/test/java/org/jooq/test/MockTest.java @@ -333,5 +333,53 @@ public class MockTest extends AbstractTest { @Test public void testFileDatabase_SELECT_ID1_NAME1_FROM_TABLE1() throws Exception { Result> r = MOCK.select(FIELD_ID1, FIELD_NAME1).from(TABLE1).fetch(); + + assertEquals(2, r.size()); + assertEquals("ID1", r.field(0).getName()); + assertEquals("NAME1", r.field(1).getName()); + assertEquals(asList(1, 2), r.getValues(0)); + assertEquals(asList("X", "Y"), r.getValues(1)); + } + + @Test + public void testFileDatabase_SELECT_COMPLEX_DATA() throws Exception { + List> results = MOCK.fetchMany("select complex_data"); + assertEquals(2, results.size()); + + Result r1 = results.get(0); + Result r2 = results.get(1); + + // Result 1 + // -------- + assertEquals(2, r1.size()); + + // Header + assertEquals(3, r1.fields().length); + assertEquals("F1", r1.field(0).getName()); + assertEquals("F2", r1.field(1).getName()); + assertEquals("F3 is a bit more complex", r1.field(2).getName()); + + // Data + assertEquals("1", r1.getValue(0, 0)); + assertEquals("2", r1.getValue(0, 1)); + assertEquals("and a string containing data", r1.getValue(0, 2)); + assertEquals("1.1", r1.getValue(1, 0)); + assertEquals("x", r1.getValue(1, 1)); + assertEquals("another string", r1.getValue(1, 2)); + + // Result 1 + // -------- + assertEquals(1, r2.size()); + + // Header + assertEquals(3, r2.fields().length); + assertEquals("A", r2.field(0).getName()); + assertEquals("B", r2.field(1).getName()); + assertEquals("\"C D\"", r2.field(2).getName()); + + // Data + assertEquals("x", r2.getValue(0, 0)); + assertEquals("y", r2.getValue(0, 1)); + assertEquals("z", r2.getValue(0, 2)); } } diff --git a/jOOQ/src/test/resources/org/jooq/test/data/db.txt b/jOOQ/src/test/resources/org/jooq/test/data/db.txt index 14a207438e..0d52428286 100644 --- a/jOOQ/src/test/resources/org/jooq/test/data/db.txt +++ b/jOOQ/src/test/resources/org/jooq/test/data/db.txt @@ -15,6 +15,19 @@ select 'A', 'B' from dual; select "TABLE1"."ID1", "TABLE1"."NAME1" from "TABLE1"; > ID1 NAME1 > --- ----- -> 1 X -> 2 Y -@ rows: 2 \ No newline at end of file +> 1 X +> 2 Y +@ rows: 2 + +# [#2280] Check if "advanced" CSV content can be handled, too +select complex_data; +> F1 F2 F3 is a bit more complex +> --- -- ---------------------------- +> 1 2 and a string containing data +> 1.1 x another string +@ rows: 2 + +> A B "C D" +> - - ----- +> x y z +@ rows: 1 \ No newline at end of file