groovycsv-releases-1.0/000077500000000000000000000000001171771240300152175ustar00rootroot00000000000000groovycsv-releases-1.0/.gitignore000066400000000000000000000000601171771240300172030ustar00rootroot00000000000000.gradle/* build/* lib/* _site gradle.properties groovycsv-releases-1.0/LICENSE000066400000000000000000000011131171771240300162200ustar00rootroot00000000000000 Copyright 2010 Leonard Axelsson Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. groovycsv-releases-1.0/README.md000066400000000000000000000053341171771240300165030ustar00rootroot00000000000000# GroovyCSV GroovyCSV is a library for Groovy which aims to make csv data easier (and more idiomatically Groovy) to work with. The library was inspired by @[goeh's](http://twitter.com/goeh) [ExcelBuilder](http://www.technipelago.se/blog/?p=44) that lets you iterate over rows in the excel file using `eachLine` and access values using the column names. *Important* Package structure was changed from `com.xlson.csvparser` to `com.xlson.groovycsv` between release 0.1 and 0.2. ## Features * Value-access by header name or position * Iteration using the ordinary collection methods (`findAll`, `collect` and so on) * Full support for OpenCSV's configurability * Support for guessing separator and/or quote character * Support for reading csv without headers ## Example The parse method returns an iterator over the rows in the csv. This means we can use any of the default groovy ways to iterate, in this example we see the for each loop in use. @Grab('com.xlson.groovycsv:groovycsv:1.0') import static com.xlson.groovycsv.CsvParser.parseCsv def csv = '''Name,Lastname Mark,Andersson Pete,Hansen''' def data = parseCsv(csv) for(line in data) { println "$line.Name $line.Lastname" } The parse method takes a String or a Reader as argument. **Output:** Mark Andersson Pete Hansen ## Getting GroovyCSV GroovyCSV is available in Maven Central. It is also available directly from GitHub (links below). ### Maven & Ivy configuration #### Latest stable * *GroupId:* com.xlson.groovycsv * *ArtifactId:* groovycsv * *Version:* 1.0 #### Latest snapshot * *Version:* 1.0-SNAPSHOT * *Repository:* https://oss.sonatype.org/content/groups/public/ ### Downloads *GroovyCSV 1.0* * [groovycsv-1.0.jar](https://github.com/downloads/xlson/groovycsv/groovycsv-0.2.jar) * [groovycsv-1.0-javadoc.jar](https://github.com/downloads/xlson/groovycsv/groovycsv-0.2-javadoc.jar) * [Javadoc Online](http://xlson.github.com/groovycsv/docs/1.0/javadoc/) ## Dependencies * [Groovy 1.7.x](http://groovy.codehaus.org) * [OpenCSV 2.x](http://opencsv.sourceforge.net/) Many thanks to Glen Smith and the other's in the OpenCSV team for doing all the heavy lifting. ## Building GroovyCSV uses Gradle for building as is packaged with the gradle wrapper which will download and install gradle for you behind the scenes the first time you run it. **Build instruction** 1. Fetch the latest code: `git clone git://github.com/xlson/groovycsv.git` 2. (Optional) Run the tests using the gradle wrapper `./gradlew test` 4. Go to the project directory and run: `./gradlew jar` You will find the built jar in `./build/libs`. If you need any dependencies you can download them using `./gradlew downloadDeps`, they end up in the `lib` folder. groovycsv-releases-1.0/build.gradle000066400000000000000000000073011171771240300174770ustar00rootroot00000000000000buildscript { repositories { mavenCentral() } dependencies { classpath 'de.huxhorn.gradle:de.huxhorn.gradle.pgp-plugin:0.0.4' } } apply plugin: 'groovy' apply plugin: 'maven' apply plugin: de.huxhorn.gradle.pgp.PgpPlugin apply plugin: 'idea' repositories { mavenCentral() mavenRepo urls: "http://m2repo.spockframework.org/snapshots" } dependencies { groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.3' compile 'net.sf.opencsv:opencsv:2.1' testCompile "org.spockframework:spock-core:0.4-groovy-1.7" testCompile "cglib:cglib-nodep:2.2" testCompile "org.objenesis:objenesis:1.2" } version = '1.0' group = 'com.xlson.groovycsv' sourceSets { main { groovy { srcDir 'src' } } test { groovy { srcDir 'test' } } } task groovydocJar(type: Jar, dependsOn: groovydoc) { classifier = 'javadoc' from 'build/docs/groovydoc' } task sourcesJar(type: Jar) { from sourceSets.main.allSource classifier = 'sources' } artifacts { archives groovydocJar archives sourcesJar } def deployer = null def installer = install.repositories.mavenInstaller uploadArchives { // Checks if login information for the logon repo is set correctly. // Should be set in gradle.properties (check gradle.properties.example) if(project.hasProperty('repoUserName') && project.hasProperty('repoPassword')) { repositories { deployer = mavenDeployer { configureAuth = { authentication(userName: repoUserName, password: repoPassword) } snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/", configureAuth) repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/", configureAuth) } } } } [installer, deployer]*.pom*.whenConfigured {pom -> pom.project { name 'GroovyCSV' packaging 'jar' // not working description 'Library for parsing csv in Groovy' url 'http://github.com/xlson/groovycsv' inceptionYear '2010' scm { url 'scm:git:git@github.com:xlson/groovycsv.git' connection 'http://github.com/xlson/groovycsv' } licenses { license { name 'The Apache Software License, Version 2.0' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' } } developers { developer { id 'xlson' name 'Leonard Axelsson' email 'leonard.axelsson@gmail.com' url 'http://xlson.com/' timezone '+1' } } } pom.withXml { XmlProvider xmlProvider -> def xml = xmlProvider.asString() def pomXml = new XmlParser().parseText(xml.toString()) pomXml.version[0] + { packaging('jar') } def newXml = new StringWriter() def printer = new XmlNodePrinter(new PrintWriter(newXml)) printer.preserveWhitespace = true printer.print(pomXml) xml.setLength(0) xml.append(newXml.toString()) } } task wrapper(type: Wrapper) { gradleVersion = '1.0-milestone-3' } task downloadDeps << { def libDir = file('lib') ant.delete(dir: libDir) copy { from configurations.testRuntime into libDir } } groovycsv-releases-1.0/gradle.properties.example000066400000000000000000000002001171771240300222150ustar00rootroot00000000000000// This must be configured if you wanna deploy to the Nexus OSS Public Repo repoUserName=usernameHere repoPassword=passwordHere groovycsv-releases-1.0/samples/000077500000000000000000000000001171771240300166635ustar00rootroot00000000000000groovycsv-releases-1.0/samples/GettingDependencyUsingGrab.groovy000066400000000000000000000003571171771240300253410ustar00rootroot00000000000000@Grab('com.xlson.groovycsv:groovycsv:0.2') import com.xlson.groovycsv.CsvParser def csv = '''Name,Lastname Mark,Andersson Pete,Hansen''' def data = new CsvParser().parse(csv) for(line in data) { println "$line.Name $line.Lastname" } groovycsv-releases-1.0/samples/UsingAutoDetection.groovy000066400000000000000000000003251171771240300237070ustar00rootroot00000000000000import com.xlson.groovycsv.CsvParser def csv = '''Name:Lastname Mark:Andersson Pete:Hansen''' def data = new CsvParser().parse(csv, autoDetect:true) for(line in data) { println "$line.Name $line.Lastname" } groovycsv-releases-1.0/src/000077500000000000000000000000001171771240300160065ustar00rootroot00000000000000groovycsv-releases-1.0/src/com/000077500000000000000000000000001171771240300165645ustar00rootroot00000000000000groovycsv-releases-1.0/src/com/xlson/000077500000000000000000000000001171771240300177275ustar00rootroot00000000000000groovycsv-releases-1.0/src/com/xlson/groovycsv/000077500000000000000000000000001171771240300217705ustar00rootroot00000000000000groovycsv-releases-1.0/src/com/xlson/groovycsv/AutoDetectHandler.groovy000066400000000000000000000015731171771240300266040ustar00rootroot00000000000000package com.xlson.groovycsv class AutoDetectHandler { List autoDetectSeparators = [",", ";", ":", "|"] List autoDetectQuoteChars = ['"', "'", "%"] String linesToInspect String autoDetectQuoteChar() { return mostFrequentChar(linesToInspect, autoDetectQuoteChars) } String autoDetectSeparator() { return mostFrequentChar(linesToInspect, autoDetectSeparators) } /** * Find the most frequent character in a string among a list of characters. * Falls back on the first character in the list if no character is found. * * @param sequence The string to search. * @param characters The list of characters to search. * @return The most frequent character. */ private mostFrequentChar(String sequence, List characters) { characters.max{ sequence.count(it) } } } groovycsv-releases-1.0/src/com/xlson/groovycsv/CsvIterator.groovy000066400000000000000000000061331171771240300255070ustar00rootroot00000000000000/* * Copyright 2010 Leonard Axelsson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.xlson.groovycsv import au.com.bytecode.opencsv.CSVReader /** * Iterates over the csv data in a non-synchronized way. * * @author Leonard Axelsson * @since 0.1 */ class CsvIterator implements Iterator { private def columns private CSVReader csvReader private def readValue private Boolean closed = false def CsvIterator(def columnNames, CSVReader csvReader) { this.columns = [:] columnNames.eachWithIndex { name, i -> columns."$name" = i } this.csvReader = csvReader } /** * Closes the underlying reader object. Could be useful if one would * not like to read all of the csv into memory. * * @throws IllegalStateException if the underlying dataset is already closed. */ void close() { throwsExceptionIfClosed() closed = true csvReader.close() } /** * Checks if there is more data available. Will close the underlying dataset * if there isn't any more data. * * @return true if there is more data in the iterator */ boolean hasNext() { if(isClosed()) { return false } else if(nextValueIsRead()) { return true } else { readValue = csvReader.readNext() if(readValue == null) { close() } return readValue != null } } /** * Checks if the underlying reader is closed. * * @return true if the underlying reader is closed */ boolean isClosed() { closed } private boolean nextValueIsRead() { readValue as boolean } private def getNextValue() { if(nextValueIsRead()) { def value = readValue readValue = null return value } else { return csvReader.readNext() } } /** * Gets the next row in the csv file. * * @return an instance of PropertyMapper */ def next() { throwsExceptionIfClosed() new PropertyMapper(columns: columns, values: nextValue) } private def throwsExceptionIfClosed() { if (isClosed()) { throw new IllegalStateException("The connection the underlying dataset has already been closed.") } } /** * remove is not supported in CsvIterator. * * @throws UnsupportedOperationException when called */ void remove() { throw new UnsupportedOperationException() } } groovycsv-releases-1.0/src/com/xlson/groovycsv/CsvParser.groovy000066400000000000000000000145571171771240300251630ustar00rootroot00000000000000/* * Copyright 2010 Leonard Axelsson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.xlson.groovycsv import au.com.bytecode.opencsv.CSVReader /** * Helper class used to parse information from csv files using the column names * in the first line. Currently it only supports csv files where the first line * contains the column names. *

* Usage: *

 * def csv = '''Name,Lastname
 * Mark,Andersson
 * Pete,Hansen'''
 *
 * def data = new CsvParser().parse(csv)
 * for(line in data) {
 *   println "$line.Name $line.Lastname"
 * }
* * * @author Leonard Axelsson * @since 0.1 */ class CsvParser { /** * Number of characters used to provide to autodetection (in case auto * detection is used. */ Integer autoDetectCharNumber = 1000 /** * Parses a string as csv in the same way as CsvParser.parse(...). * * @param args * @param csv * @return */ static Iterator parseCsv(Map args = [:], String csv) { new CsvParser().parse(args, csv) } /** * Parses a reader as csv in the same way as CsvParser.parse(...). * * @param args * @param csv * @return */ static Iterator parseCsv(Map args = [:], Reader reader) { new CsvParser().parse(args, reader) } /** * Parses the csv supplied using the reader. See parse(Reader reader) for * more information about usage. * * @param args configurable parameters * @param csv the csv to parse * @return an instance of com.xlson.groovycsv.CsvIterator */ Iterator parse(Map args = [:], String csv) { parse(args, new StringReader(csv)) } /** * Parses the supplied csv and returns a CsvIterator that can be * use to access the data. The first line of the csv will be used * as column-headers. Named paramenters can be used to configure the * parsing, see the class documentation for more more information on * usage. There's also support for autodetecting the quote and separator * characters. *

* Arguments for configuration: *

  • separator: configures the separator character to use (default: ,) *
  • quoteChar: configures the quote character to use (default: ") *
  • escapeChar: configures the escape character for the separator and quoteChar (default:\ *
  • autoDetect: sets up autodetect that will honor other configurations you've done (default: false) *
  • columnNames: set custom column names instead of using the first line *
  • readFirstLine: reads the first line as csv instead of using it as headers * *

    * Usage: *

         * def csv = '''Fruit-Quantity
         * Apple-2
         * Pear-5'''
         *
         * def data = new CsvParser().parse(csv, separator: '-')
         *
         * // Print all fruits that have a quantity higher than 3
         * data.findAll{ (it.Quantity as int) > 3 }.each{ println it }
         * 
    *

    * @param reader the csv to parse * @param args the configuration arguments * @return an instance of com.xlson.groovycsv.CsvIterator */ Iterator parse(Map args = [:], Reader reader) { def csvReader = createCSVReader(args, reader) def columnNames = parseColumnNames(args, csvReader) new CsvIterator(columnNames, csvReader) } private def parseColumnNames(Map args, CSVReader csvReader) { def columnNames if (!args.readFirstLine) { columnNames = csvReader.readNext() } if (args.columnNames) { columnNames = args.columnNames } return columnNames } private CSVReader createCSVReader(Map args = [:], Reader reader) { char separator char quoteChar char escapeChar = args.escapeChar if (args.autoDetect == true) { reader = new PushbackReader(reader, autoDetectCharNumber) doAutoDetection(args, reader) separator = args.separator quoteChar = args.quoteChar } else { separator = args.separator ?: ',' quoteChar = args.quoteChar ?: '"' } if(escapeChar) { return new CSVReader(reader, separator, quoteChar, escapeChar) } else { return new CSVReader(reader, separator, quoteChar) } } /** * Performs automatic detection of separator and quote character. * * It will search arguments for values 'auto' in separator and quoteChar. It * will return a new version of the arguments modified with the values that * were found. * * If nothing is detected, the values are removed from the args. * * Note that * * @param args the configuration arguments. * @param text the CSV as a String. * @return modified args with detected. */ private Map doAutoDetection(Map args, PushbackReader reader) { def buf = new char[autoDetectCharNumber] def charsRead = reader.read(buf) def linesToInspect = new String(buf) reader.unread(buf, 0, charsRead) def autoDetector = new AutoDetectHandler(linesToInspect: linesToInspect) if (args.autoDetectQuoteChars) { autoDetector.autoDetectQuoteChars = args.autoDetectQuoteChars } if (args.autoDetectSeparators) { autoDetector.autoDetectSeparators = args.autoDetectSeparators } if (!args.separator) { def detectedSeparator = autoDetector.autoDetectSeparator() if (detectedSeparator) args.separator = detectedSeparator else args.remove("separator") } if(!args.quoteChar) { def detectedQuoteChar = autoDetector.autoDetectQuoteChar() if (detectedQuoteChar) args.quoteChar = detectedQuoteChar else args.remove("quoteChar") } return args } } groovycsv-releases-1.0/src/com/xlson/groovycsv/PropertyMapper.groovy000066400000000000000000000033101171771240300262250ustar00rootroot00000000000000/* * Copyright 2010 Leonard Axelsson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.xlson.groovycsv /** * Maps between column names and values in a list. Uses propertyMissing * to allow for named access. * * @author Leonard Axelsson * @since 0.1 */ class PropertyMapper { /** * A list of values for one csv line. */ def values /** * The columns of the csv. */ def columns = [:] /** * Maps properties to values. * * @param name the name of the property * @return the value as a String * @throws MissingPropertyException where the values-list doesn't contain enough data. */ def propertyMissing(String name) { def index = columns[name] if (index != null) { values[index] } else { throw new MissingPropertyException(name) } } /** * Allows values to be obtained using their position * * @param index * @return the value at that position */ def getAt(Integer index) { values[index] } String toString() { columns.collect { key, index -> "$key: ${values[index]}" }.join(', ') } } groovycsv-releases-1.0/test/000077500000000000000000000000001171771240300161765ustar00rootroot00000000000000groovycsv-releases-1.0/test/com/000077500000000000000000000000001171771240300167545ustar00rootroot00000000000000groovycsv-releases-1.0/test/com/xlson/000077500000000000000000000000001171771240300201175ustar00rootroot00000000000000groovycsv-releases-1.0/test/com/xlson/groovycsv/000077500000000000000000000000001171771240300221605ustar00rootroot00000000000000groovycsv-releases-1.0/test/com/xlson/groovycsv/AutoDetectHandlerSpec.groovy000066400000000000000000000032201171771240300275760ustar00rootroot00000000000000package com.xlson.groovycsv import spock.lang.Specification; class AutoDetectHandlerSpec extends Specification { def "should auto detect quote character"() { setup: def adh = new AutoDetectHandler(linesToInspect: csvData) expect: adh.autoDetectQuoteChar() == quoteChar where: csvData | quoteChar testDataWithColumnNamesAnd3Rows | '"' testDataWithColumnNamesAnd2Rows | '"' csvUsingDoubleQuoteAsQuoteChar | '"' csvUsingPercentageAsQuoteChar | "%" } def "should auto detect separator"() { setup: def adh = new AutoDetectHandler(linesToInspect: csvData) expect: adh.autoDetectSeparator() == separator where: csvData | separator testDataWithColumnNamesAnd3Rows | "," testDataWithColumnNamesAnd2Rows | "," csvUsingDoubleQuoteAsQuoteChar | ',' csvWithColonAsSeparator | ":" } def getTestDataWithColumnNamesAnd3Rows() { '''Name,Lastname,Age,Email Mark,Hamilton,45,mark@hamilton.com Bosse,Bildoktorn,50,bildoktorn@tv4.se Peps,Persson,65,peps.persson@hotmail.com''' } def getTestDataWithColumnNamesAnd2Rows() { '''Letter,Word,Number a,paris,5 h,drink,60''' } def getCsvWithColonAsSeparator() { '''Fruit:Count Apple:5 Pear:10 Kiwi:200''' } def getCsvUsingDoubleQuoteAsQuoteChar() { '''Typo,Desc 123,"text ,and more"''' } def getCsvUsingPercentageAsQuoteChar() { '''Typo,Desc 1123,%bla, ha%''' } } groovycsv-releases-1.0/test/com/xlson/groovycsv/ConfigurableColumnNamesSpec.groovy000066400000000000000000000041461171771240300310110ustar00rootroot00000000000000package com.xlson.groovycsv import spock.lang.Specification import au.com.bytecode.opencsv.CSVReader class ConfigurableColumnNamesSpec extends Specification { def csvWithoutCoulmnNames = """field1,field2,field3, Joe,Doe,18 Jane,Doe,24""" def "Replaces existing column names with new ones"() { setup: def persons = new CsvParser().parse(csvWithoutCoulmnNames, columnNames: ['name', 'lastname', 'age']) when: def joe = persons.next() then: joe.name == 'Joe' joe.lastname == 'Doe' joe.age == '18' } def "Read all lines of csv content using custom column as column names"() { setup: def persons = new CsvParser().parse(csvWithoutCoulmnNames, readFirstLine: true, columnNames: ['name', 'lastname', 'age']) when: def names = persons*.name then: names == ['field1', 'Joe', 'Jane'] } def "Parses columns from the first line by default"() { setup: def reader = Mock(CSVReader) when: def columnNames = new CsvParser().parseColumnNames([:], reader) then: reader.readNext() >> ['a', 'b', 'c'] columnNames == ['a', 'b', 'c'] } def "Throws away the first line by default when using custom column names."() { setup: def reader = Mock(CSVReader) when: def customColumnNames = ['1', '2', '3'] def columnNames = new CsvParser().parseColumnNames([columnNames:customColumnNames], reader) then: 1 * reader.readNext() columnNames == customColumnNames } def "Does not read the first line as header when readFirstLine is specified."() { setup: def reader = Mock(CSVReader) when: def customColumnNames = ['1', '2', '3'] def columnNames = new CsvParser().parseColumnNames([columnNames:customColumnNames, readFirstLine: true], reader) then: 0 * reader.readNext() columnNames == customColumnNames } } groovycsv-releases-1.0/test/com/xlson/groovycsv/CsvIteratorSpec.groovy000066400000000000000000000045071171771240300265150ustar00rootroot00000000000000package com.xlson.groovycsv import spock.lang.Specification import au.com.bytecode.opencsv.CSVReader class CsvIteratorSpec extends Specification { def getCsvData() { def csv = """a,b,c 1,2,3 4,5,6""" def csvReader = new CSVReader(new StringReader(csv)) def columnNames = csvReader.readNext() return [columnNames, csvReader] } def "CsvIterator iterates correctly over the CSVReader"() { setup: def (colNames, csvReader) = csvData def iter = new CsvIterator(colNames, csvReader) expect: iter.hasNext() iter.next().a == '1' iter.hasNext() iter.next().c == '6' !iter.hasNext() } def "CsvIterator should close the underlying CSVReader instance when reaching the end of the file."() { setup: CSVReader csvReader = Mock(CSVReader) def iter = new CsvIterator(["a", "b"], csvReader) csvReader.readNext() >>> [["1","2"],["3","4"], null] when: iter.next() then: iter.hasNext() !iter.isClosed() 0 * csvReader.close() when: iter.next() iter.hasNext() then: iter.isClosed() 1 * csvReader.close() } def "CsvIterator isClosed after a full iteration."() { setup: def csvIterator = new CsvIterator(*csvData) when: csvIterator.each { } then: csvIterator.isClosed() when: csvIterator.next() then: thrown(IllegalStateException) } def "close can be called on the CsvIterator to close the connection to the reader."() { setup: def (colNames, csvReader) = csvData def iter = new CsvIterator(colNames, csvReader) when: iter.next() iter.close() then: iter.isClosed() when: iter.next() then: thrown(IllegalStateException) } def "CsvIterator.hasNext() returns false when the underlying reader instance is closed."() { setup: 'Create an instance of CsvIterator consisting of 2 rows.' def csvIterator = new CsvIterator(*csvData) when: 'Iterates over the iterator until hasNext() is false' csvIterator.each {} then: 'hasNext() should return false.' !csvIterator.hasNext() } } groovycsv-releases-1.0/test/com/xlson/groovycsv/CsvParserSpec.groovy000066400000000000000000000144061171771240300261570ustar00rootroot00000000000000package com.xlson.groovycsv import spock.lang.* import au.com.bytecode.opencsv.CSVReader class CsvParserSpec extends Specification { def getTestDataWithColumnNamesAnd3Rows() { '''Name,Lastname,Age,Email Mark,Hamilton,45,mark@hamilton.com Bosse,Bildoktorn,50,bildoktorn@tv4.se Peps,Persson,65,peps.persson@hotmail.com''' } def getTestDataWithColumnNamesAnd2Rows() { '''Letter,Word,Number a,paris,5 h,drink,60''' } def getCsvWithColonAsSeparator() { '''Fruit:Count Apple:5 Pear:10 Kiwi:200''' } def getTestDataWithQuotedComma() { '''a,b -,abc-,4 abc,-4-''' } def "Iterating over the parsed csv values are available by column name."() { setup: def data = new CsvParser().parse(getTestDataWithColumnNamesAnd3Rows()) expect: data*."$columnName" == values where: columnName | values "Name" | ['Mark', 'Bosse', "Peps"] "Lastname" | ['Hamilton', 'Bildoktorn', 'Persson'] 'Age' | ['45', '50', '65'] "Email" | ['mark@hamilton.com', 'bildoktorn@tv4.se', 'peps.persson@hotmail.com'] } def "Functional collection methods are available on parsed object."() { setup: def data = new CsvParser().parse(getTestDataWithColumnNamesAnd3Rows()) expect: data.findAll { (it.Age as int) > 46 }.size() == 2 } def "PropertyMapper has a toString() that returns all the data in it's columns."() { setup: def pm = new PropertyMapper(values: values, columns: columns) expect: pm.toString() == toStringRepresentation where: columns | values | toStringRepresentation ['a': 0, 'b': 1, 'c': 2] | ['1', '2', '3'] | "a: 1, b: 2, c: 3" ['Name': 0, 'Age': 1] | ['Mark', '56'] | "Name: Mark, Age: 56" } def "readAll should never be called on the CSVReader instance used to parse the csv."() { setup: CSVReader csvReader = Mock(CSVReader) def partiallyMockedCsvParser = new CsvParser() partiallyMockedCsvParser.metaClass.createCSVReader = { Reader reader -> csvReader } when: "csv is parsed and looped through" def data = partiallyMockedCsvParser.parse(getTestDataWithColumnNamesAnd2Rows()) for(d in data) {} then: "readAll() should not be called." 0 * csvReader.readAll() } def "Parse supports a custom separator."() { setup: def data = new CsvParser().parse(csvWithColonAsSeparator, separator: ':') expect: data*."$columnName" == values where: columnName | values "Fruit" | ['Apple', 'Pear', 'Kiwi'] "Count" | ["5", "10", "200"] } def getCsvUsingDoubleQuoteAsQuoteChar() { '''Typo,Desc 123,"text ,and more"''' } def getCsvUsingPercentageAsQuoteChar() { '''Typo,Desc 1123,%bla, ha%''' } def "Parse supports custom quote character."() { when: def csv = new CsvParser().parse(csvData, quoteChar: quoteChar) then: csv*."$columnName" == values where: csvData | quoteChar | values | columnName csvUsingDoubleQuoteAsQuoteChar | '"' | ['text ,and more'] | "Desc" csvUsingPercentageAsQuoteChar | "%" | ['bla, ha'] | "Desc" } def "Parse supports custom escape char."() { setup: def csvData = '''Test,It 1,"this is \"a quote\""''' def csv = new CsvParser().parse(csvData, escapeChar: "\\") expect: csv*.It == ['this is "a quote"'] } def "Parse supports java.io.Reader as input."() { when: def csv = new CsvParser().parse(new StringReader(testDataWithColumnNamesAnd2Rows)) then: csv*.Number == ['5', '60'] } def "CsvParser should auto detect separator and quote character"() { when: "a CSV file is parsed with auto detection" def csv = new CsvParser().parse(autoDetect: true, csvData) then: "it should return the correct columns" csv*."$property" == values where: csvData | property | values testDataWithColumnNamesAnd3Rows | "Age" | ["45", "50", "65"] csvWithColonAsSeparator | "Count" | ["5", "10", "200"] csvUsingDoubleQuoteAsQuoteChar | "Desc" | ["text ,and more"] csvUsingDoubleQuoteAsQuoteChar | "Typo" | ["123"] testDataWithColumnNamesAnd2Rows | "Word" | ["paris", "drink"] testDataWithColumnNamesAnd3Rows | "Email" | ["mark@hamilton.com", "bildoktorn@tv4.se", "peps.persson@hotmail.com"] } def "should allow to override auto detection"() { when: "autoDetect is active and a separator is provided" def csv = new CsvParser().parse(autoDetect: true, separator: ',', csvWithColonAsSeparator) then: "the separator provided is used" csv*."Fruit:Count" == ["Apple:5", "Pear:10", "Kiwi:200"] } def "The separator should be allowed in the csv data if its quoted"() { when: def csv = new CsvParser().parse(quoteChar:'-', testDataWithQuotedComma) then: csv*.a == [',abc','abc'] } def "Values in the csv can be obtained by using the index of the column."() { when: def csv = new CsvParser().parse(testDataWithColumnNamesAnd2Rows) def line = csv.next() then: line[0] == 'a' line[1] == 'paris' line[2] == '5' } def csvWithoutHeaders = 'Joe,Doe,19' def "Parsing csv without headers using the position of the values."() { when: def csv = new CsvParser().parse(readFirstLine: true, csvWithoutHeaders) def line = csv.next() then: line[0] == 'Joe' line[1] == 'Doe' line[2] == '19' } def "CsvParser.parseCsv can be used statically."() { when: def csv = CsvParser.parseCsv(csvData) then: csv*.Letter == letterValues where: csvData | letterValues testDataWithColumnNamesAnd2Rows | ['a', 'h'] new StringReader(testDataWithColumnNamesAnd2Rows) | ['a', 'h'] } }