diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/FormatTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/FormatTests.java index 0eba2c612f..4389e526ca 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/FormatTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/FormatTests.java @@ -35,6 +35,7 @@ */ package org.jooq.test._.testcases; +import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -124,6 +125,46 @@ extends BaseTest result1 = create().fetchFromCSV( + "A,B,\"C\",\"\"\"D\"\n" + + "1,,a,b\n" + + "1,2,a\n" + + "1,2,a,b,c"); + + // Check meta data + assertEquals(4, result1.getFields().size()); + assertEquals(3, result1.size()); + assertEquals("A", result1.getField(0).getName()); + assertEquals("B", result1.getField(1).getName()); + assertEquals("C", result1.getField(2).getName()); + assertEquals("\"D", result1.getField(3).getName()); + + // Check column correctness + assertEquals(asList("1", "1", "1"), result1.getValues(0)); + assertEquals(asList(1, 1, 1), result1.getValues(0, Integer.class)); + assertEquals(asList("", "2", "2"), result1.getValues(1)); + assertEquals(asList(null, 2, 2), result1.getValues(1, Integer.class)); + assertEquals(asList("a", "a", "a"), result1.getValues(2)); + assertEquals(asList("b", null, "b"), result1.getValues(3)); + + // Check row correctness + assertEquals(asList("1", "", "a", "b"), asList(result1.get(0).intoArray())); + assertEquals(asList("1", "2", "a", null), asList(result1.get(1).intoArray())); + assertEquals(asList("1", "2", "a", "b"), asList(result1.get(2).intoArray())); + + // Factory.fetchFromCSV() should be the inverse of Result.formatCSV() + // ... apart from the loss of type information + String csv = create().selectFrom(TBook()).orderBy(TBook_ID()).fetch().formatCSV(); + Result result2 = create().fetchFromCSV(csv); + + assertEquals(4, result2.size()); + assertEquals(BOOK_IDS, result2.getValues(TBook_ID(), Integer.class)); + assertEquals(BOOK_AUTHOR_IDS, result2.getValues(TBook_AUTHOR_ID(), Integer.class)); + assertEquals(BOOK_TITLES, result2.getValues(TBook_TITLE())); + } + @Test public void testFormatCSV() throws Exception { List> fields = TBook().getFields(); diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index cfaaff4d94..3fbb3b458f 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -1150,6 +1150,11 @@ public abstract class jOOQAbstractTest< new FormatTests(this).testFormatHTML(); } + @Test + public void testFetchFromCSV() throws Exception { + new FormatTests(this).testFetchFromCSV(); + } + @Test public void testFormatCSV() throws Exception { new FormatTests(this).testFormatCSV(); diff --git a/jOOQ/src/main/java/org/jooq/FactoryOperations.java b/jOOQ/src/main/java/org/jooq/FactoryOperations.java index 45d46ba7e6..7e2e78d7ed 100644 --- a/jOOQ/src/main/java/org/jooq/FactoryOperations.java +++ b/jOOQ/src/main/java/org/jooq/FactoryOperations.java @@ -225,6 +225,62 @@ public interface FactoryOperations extends Configuration { @Support Result fetch(ResultSet rs) throws DataAccessException; + /** + * Fetch all data from a CSV string. + *

+ * This is the same as calling fetchFromCSV(string, ',') and + * the inverse of calling {@link Result#formatCSV()}. The first row of the + * CSV data is required to hold field name information. Subsequent rows may + * contain data, which is interpreted as {@link String}. Use the various + * conversion methods to retrieve other data types from the + * Result: + *

    + *
  • {@link Result#getValues(Field, Class)}
  • + *
  • {@link Result#getValues(int, Class)}
  • + *
  • {@link Result#getValues(String, Class)}
  • + *
  • {@link Result#getValues(Field, Converter)}
  • + *
  • {@link Result#getValues(int, Converter)}
  • + *
  • {@link Result#getValues(String, Converter)}
  • + *
+ *

+ * Missing values result in null. Empty values result in empty + * Strings + * + * @param string The CSV string + * @return The transformed result + * @throws DataAccessException If anything went wrong parsing the CSV file + * @see #fetchFromCSV(String, char) + */ + Result fetchFromCSV(String string) throws DataAccessException; + + /** + * Fetch all data from a CSV string. + *

+ * This is inverse of calling {@link Result#formatCSV(char)}. The first row + * of the CSV data is required to hold field name information. Subsequent + * rows may contain data, which is interpreted as {@link String}. Use the + * various conversion methods to retrieve other data types from the + * Result: + *

    + *
  • {@link Result#getValues(Field, Class)}
  • + *
  • {@link Result#getValues(int, Class)}
  • + *
  • {@link Result#getValues(String, Class)}
  • + *
  • {@link Result#getValues(Field, Converter)}
  • + *
  • {@link Result#getValues(int, Converter)}
  • + *
  • {@link Result#getValues(String, Converter)}
  • + *
+ *

+ * Missing values result in null. Empty values result in empty + * Strings + * + * @param string The CSV string + * @param delimiter The delimiter to expect between records + * @return The transformed result + * @throws DataAccessException If anything went wrong parsing the CSV file + * @see #fetchFromCSV(String) + */ + Result fetchFromCSV(String string, char delimiter) throws DataAccessException; + // ------------------------------------------------------------------------- // XXX Global Query factory // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index aae28c5120..4d5b4d8839 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -55,6 +55,7 @@ import static org.jooq.impl.Util.combine; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.StringReader; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; @@ -138,6 +139,7 @@ import org.jooq.exception.DataAccessException; import org.jooq.exception.InvalidResultException; import org.jooq.exception.SQLDialectNotSupportedException; import org.jooq.tools.JooqLogger; +import org.jooq.tools.csv.CSVReader; import org.jooq.types.DayToSecond; /** @@ -1428,6 +1430,57 @@ public class Factory implements FactoryOperations { } } + /** + * {@inheritDoc} + */ + @Override + public final Result fetchFromCSV(String string) { + return fetchFromCSV(string, ','); + } + + /** + * {@inheritDoc} + */ + @Override + public final Result fetchFromCSV(String string, char delimiter) { + CSVReader reader = new CSVReader(new StringReader(string), delimiter); + List all = null; + + try { + all = reader.readAll(); + } + catch (IOException e) { + throw new DataAccessException("Could not read the CSV string", e); + } + + FieldList fields = new FieldList(); + + if (all.size() == 0) { + return new ResultImpl(this, fields); + } + else { + for (String name : all.get(0)) { + fields.add(fieldByName(String.class, name)); + } + + Result result = new ResultImpl(this, fields); + + if (all.size() > 1) { + for (String[] values : all.subList(1, all.size())) { + Record record = new RecordImpl(fields); + + for (int i = 0; i < Math.min(values.length, fields.size()); i++) { + Util.setValue(record, fields.get(i), values[i]); + } + + result.add(record); + } + } + + return result; + } + } + // ------------------------------------------------------------------------- // XXX Global Condition factory // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/FactoryProxy.java b/jOOQ/src/main/java/org/jooq/impl/FactoryProxy.java index 8bfefec7e4..7981e00409 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FactoryProxy.java +++ b/jOOQ/src/main/java/org/jooq/impl/FactoryProxy.java @@ -385,6 +385,16 @@ public final class FactoryProxy implements FactoryOperations { return getDelegate().fetch(rs); } + @Override + public final Result fetchFromCSV(String string) { + return null; + } + + @Override + public final Result fetchFromCSV(String string, char delimiter) { + return null; + } + @Override public final Result fetch(String sql) { return getDelegate().fetch(sql);