diff --git a/LICENSE b/LICENSE
index 7a4a3ea..78d7f1d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,11 @@
+Copyright 2005 Bytecode Pty Ltd.
+
+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
+
 
                                  Apache License
                            Version 2.0, January 2004
diff --git a/checkstyle.xml b/checkstyle.xml
index 473f871..242564b 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -7,7 +7,7 @@
 <module name="Checker">
     <module name="TreeWalker">
         <module name="JavadocMethod">
-            <property name="scope" value="package"/>
+            <property name="accessModifiers" value="package"/>
         </module>
         <module name="MissingJavadocMethod">
             <property name="scope" value="package"/>
diff --git a/debian/changelog b/debian/changelog
index 75f79f5..5d4a3fc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+opencsv (5.3+git20220506.1.a0704a7-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 30 May 2022 16:46:59 -0000
+
 opencsv (5.2-1) unstable; urgency=medium
 
   * New upstream release
diff --git a/lib/junit-4.7.jar b/lib/junit-4.7.jar
new file mode 100644
index 0000000..700ad69
Binary files /dev/null and b/lib/junit-4.7.jar differ
diff --git a/pom.xml b/pom.xml
index 721ca33..3f97183 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
     <groupId>com.opencsv</groupId>
     <artifactId>opencsv</artifactId>
     <packaging>jar</packaging>
-    <version>5.2</version>
+    <version>5.6.1-SNAPSHOT</version>
     <name>opencsv</name>
     <description>A simple library for reading and writing CSV in Java</description>
     <url>http://opencsv.sf.net</url>
@@ -12,19 +12,19 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.surefire.version>2.22.2</maven.surefire.version>
-        <maven.site.version>3.9.0</maven.site.version>
-        <jacoco.version>0.8.5</jacoco.version>
-        <maven.javadoc.version>3.2.0</maven.javadoc.version>
-        <maven-assembly-plugin.version>3.2.0</maven-assembly-plugin.version>
-        <commons-lang3.version>3.10</commons-lang3.version>
+        <maven.site.version>3.12.0</maven.site.version>
+        <jacoco.version>0.8.8</jacoco.version>
+        <maven.javadoc.version>3.4.0</maven.javadoc.version>
+        <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
+        <commons-lang3.version>3.12.0</commons-lang3.version>
         <commons-collections4.version>4.4</commons-collections4.version>
         <argLine>-Dfile.encoding=UTF-8</argLine>
-        <junit.version>5.6.2</junit.version>
-        <junit-platform.version>1.3.2</junit-platform.version>
-        <asciidoctor.maven.plugin.version>2.0.0-RC.1</asciidoctor.maven.plugin.version>
-        <asciidoctorj.version>2.2.0</asciidoctorj.version>
-        <jruby.version>9.2.11.0</jruby.version>
-        <asciidoctorj.diagram.version>2.0.1</asciidoctorj.diagram.version>
+        <junit.version>5.8.2</junit.version>
+        <junit.platform.version>1.8.2</junit.platform.version>
+        <asciidoctor.maven.plugin.version>2.2.2</asciidoctor.maven.plugin.version>
+        <asciidoctorj.version>2.5.3</asciidoctorj.version>
+        <jruby.version>9.2.19.0</jruby.version>
+        <asciidoctorj.diagram.version>2.2.1</asciidoctorj.diagram.version>
     </properties>
 
     <licenses>
@@ -237,7 +237,7 @@
                                     <pluginExecutionFilter>
                                         <groupId>org.codehaus.gmavenplus</groupId>
                                         <artifactId>gmavenplus-plugin</artifactId>
-                                        <versionRange>[1.8.1,)</versionRange>
+                                        <versionRange>[1.12.1,)</versionRange>
                                         <goals>
                                             <goal>addTestSources</goal>
                                             <goal>generateTestStubs</goal>
@@ -252,7 +252,7 @@
                                     <pluginExecutionFilter>
                                         <groupId>org.apache.felix</groupId>
                                         <artifactId>maven-bundle-plugin</artifactId>
-                                        <versionRange>[3.5.1,)</versionRange>
+                                        <versionRange>[5.1.1,)</versionRange>
                                         <goals>
                                             <goal>manifest</goal>
                                         </goals>
@@ -265,6 +265,18 @@
                         </lifecycleMappingMetadata>
                     </configuration>
                 </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <version>3.1.2</version>
+                    <dependencies>
+                        <dependency>
+                            <groupId>com.puppycrawl.tools</groupId>
+                            <artifactId>checkstyle</artifactId>
+                            <version>9.3</version>
+                        </dependency>
+                    </dependencies>
+                </plugin>
             </plugins>
         </pluginManagement>
         <plugins>
@@ -324,7 +336,7 @@
             <plugin>
                 <groupId>org.codehaus.gmavenplus</groupId>
                 <artifactId>gmavenplus-plugin</artifactId>
-                <version>1.9.0</version>
+                <version>1.13.1</version>
                 <configuration>
                     <testSources>
                         <testSource>
@@ -346,7 +358,7 @@
                     <dependency>
                         <groupId>org.codehaus.groovy</groupId>
                         <artifactId>groovy</artifactId>
-                        <version>3.0.0</version>
+                        <version>3.0.8</version>
                         <exclusions>
                             <exclusion>
                                 <artifactId>junit-dep</artifactId>
@@ -367,7 +379,7 @@
             </plugin>
             <plugin>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.1</version>
+                <version>3.10.1</version>
                 <configuration>
                     <source>1.8</source>
                     <target>1.8</target>
@@ -450,7 +462,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-gpg-plugin</artifactId>
-                <version>1.6</version>
+                <version>3.0.1</version>
                 <executions>
                     <execution>
                         <id>sign-artifacts</id>
@@ -464,7 +476,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-enforcer-plugin</artifactId>
-                <version>1.4.1</version>
+                <version>3.0.0</version>
                 <executions>
                     <execution>
                         <id>enforce</id>
@@ -490,9 +502,12 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>3.2.0</version>
+                <version>3.2.2</version>
                 <configuration>
                     <archive>
+                        <manifestEntries>
+                            <Automatic-Module-Name>com.opencsv</Automatic-Module-Name>
+                        </manifestEntries>
                         <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
                     </archive>
                 </configuration>
@@ -500,7 +515,7 @@
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
-                <version>4.2.1</version>
+                <version>5.1.4</version>
                 <extensions>true</extensions>
                 <executions>
                     <execution>
@@ -537,7 +552,7 @@
             <extension>
                 <groupId>org.apache.maven.wagon</groupId>
                 <artifactId>wagon-ssh</artifactId>
-                <version>3.3.4</version>
+                <version>3.4.3</version>
             </extension>
         </extensions>
     </build>
@@ -556,18 +571,24 @@
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>
-                <version>4.13</version>
+                <version>4.13.2</version>
                 <scope>test</scope>
             </dependency>
             <dependency>
-                <groupId>junit-platform-engine</groupId>
-                <artifactId>org.junit.platform</artifactId>
-                <version>${junit.version}</version>
+                <groupId>org.junit.platform</groupId>
+                <artifactId>junit-platform-engine</artifactId>
+                <version>${junit.platform.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.junit.platform</groupId>
+                <artifactId>junit-platform-commons</artifactId>
+                <version>${junit.platform.version}</version>
             </dependency>
             <dependency>
-                <groupId>junit-platform-commons</groupId>
-                <artifactId>org.junit.platform</artifactId>
-                <version>${junit.version}</version>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-core</artifactId>
+                <version>2.2</version>
+                <scope>test</scope>
             </dependency>
         </dependencies>
     </dependencyManagement>
@@ -579,7 +600,7 @@
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-text</artifactId>
-            <version>1.8</version>
+            <version>1.9</version>
         </dependency>
         <dependency>
             <groupId>commons-beanutils</groupId>
@@ -594,13 +615,13 @@
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
-            <version>3.3.3</version>
+            <version>3.12.4</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.spockframework</groupId>
             <artifactId>spock-core</artifactId>
-            <version>2.0-M2-groovy-3.0</version>
+            <version>2.0-groovy-3.0</version>
             <scope>test</scope>
             <exclusions>
                 <exclusion>
@@ -650,7 +671,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-project-info-reports-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>3.3.0</version>
                 <configuration>
                     <dependencyDetailsEnabled>false</dependencyDetailsEnabled>
                 </configuration>
@@ -658,7 +679,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-pmd-plugin</artifactId>
-                <version>3.13.0</version>
+                <version>3.16.0</version>
                 <configuration>
                     <rulesets>
                         <ruleset>pmd-ruleset.xml</ruleset>
@@ -669,7 +690,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>3.1.1</version>
+                <version>3.1.2</version>
                 <configuration>
                     <suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation>
                     <suppressionsFileExpression>checkstyle.suppressions.file</suppressionsFileExpression>
@@ -697,9 +718,9 @@
                 <version>${jacoco.version}</version>
             </plugin>
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>findbugs-maven-plugin</artifactId>
-                <version>3.0.5</version>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>4.6.0.0</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -712,7 +733,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jxr-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>3.2.0</version>
             </plugin>
         </plugins>
     </reporting>
diff --git a/src/main/java/com/opencsv/AbstractCSVWriter.java b/src/main/java/com/opencsv/AbstractCSVWriter.java
index ad935c8..179eb02 100644
--- a/src/main/java/com/opencsv/AbstractCSVWriter.java
+++ b/src/main/java/com/opencsv/AbstractCSVWriter.java
@@ -21,10 +21,11 @@ public abstract class AbstractCSVWriter implements ICSVWriter {
 
     /**
      * Constructor to initialize the common values.
-     * @param writer Writer used for output of csv data.
+     *
+     * @param writer  Writer used for output of csv data.
      * @param lineEnd String to append at end of data (either "\n" or "\r\n").
      */
-    public AbstractCSVWriter(Writer writer, String lineEnd) {
+    protected AbstractCSVWriter(Writer writer, String lineEnd) {
         this.writer = writer;
         this.lineEnd = lineEnd;
     }
diff --git a/src/main/java/com/opencsv/CSVParser.java b/src/main/java/com/opencsv/CSVParser.java
index 7cc578f..90cc5ec 100644
--- a/src/main/java/com/opencsv/CSVParser.java
+++ b/src/main/java/com/opencsv/CSVParser.java
@@ -36,7 +36,7 @@ import java.util.ResourceBundle;
  *
  * <p>The CSVParser has grown organically based on user requests and does not truly match
  * any current requirements (though it can be configured to match or come close).  There
- * is no plans to change this as it will break existing requirements.  Consider using
+ * are no plans to change this as it will break existing requirements.  Consider using
  * the RFC4180Parser for less configurability but closer match to the RFC4180 requirements.</p>
  *
  * @author Glen Smith
@@ -77,7 +77,7 @@ public class CSVParser extends AbstractCSVParser {
                 DEFAULT_IGNORE_QUOTATIONS,
                 DEFAULT_NULL_FIELD_INDICATOR, Locale.getDefault());
     }
-
+    
     /**
      * Constructs CSVParser.
      * <p>This constructor sets all necessary parameters for CSVParser, and
@@ -170,8 +170,8 @@ public class CSVParser extends AbstractCSVParser {
         boolean containsSeparatorChar = StringUtils.contains(testValue, getSeparator());
         boolean surroundWithQuotes = applyQuotestoAll || isSurroundWithQuotes(value, containsSeparatorChar);
 
-        String convertedString = !containsQuoteChar ? testValue : testValue.replaceAll(Character.toString(getQuotechar()), Character.toString(getQuotechar()) + Character.toString(getQuotechar()));
-        convertedString = !containsEscapeChar ? convertedString : convertedString.replace(Character.toString(getEscape()), Character.toString(getEscape()) + Character.toString(getEscape()));
+        String convertedString = !containsQuoteChar ? testValue : testValue.replaceAll(Character.toString(getQuotechar()), Character.toString(getQuotechar()) + getQuotechar());
+        convertedString = !containsEscapeChar ? convertedString : convertedString.replace(Character.toString(getEscape()), Character.toString(getEscape()) + getEscape());
 
         if (surroundWithQuotes) {
             builder.append(getQuotechar());
@@ -201,7 +201,6 @@ public class CSVParser extends AbstractCSVParser {
             }
             return null;
         }
-
         final List<String> tokensOnThisLine = tokensOnLastCompleteLine <= 0 ? new ArrayList<>() : new ArrayList<>((tokensOnLastCompleteLine + 1) * 2);
         final StringFragmentCopier sfc = new StringFragmentCopier(nextLine);
         boolean inQuotes = false;
@@ -211,9 +210,13 @@ public class CSVParser extends AbstractCSVParser {
             pending = null;
             inQuotes = !this.ignoreQuotations;
         }
+
         while (!sfc.isEmptyInput()) {
             final char c = sfc.takeInput();
             if (c == this.escape) {
+                if (!strictQuotes) {
+                    inField = true; // For the unusual case of escaping the first character
+                }
                 handleEscapeCharacter(nextLine, sfc, inQuotes);
             } else if (c == quotechar) {
                 if (isNextCharacterEscapedQuote(nextLine, inQuotes(inQuotes), sfc.i - 1)) {
@@ -316,7 +319,7 @@ public class CSVParser extends AbstractCSVParser {
     /**
      * Determines if we can process as if we were in quotes.
      *
-     * @param inQuotes Are we currently in quotes.
+     * @param inQuotes Are we currently in quotes?
      * @return True if we should process as if we are inside quotes.
      */
     private boolean inQuotes(boolean inQuotes) {
diff --git a/src/main/java/com/opencsv/CSVParserBuilder.java b/src/main/java/com/opencsv/CSVParserBuilder.java
index 6c7210c..8594c85 100644
--- a/src/main/java/com/opencsv/CSVParserBuilder.java
+++ b/src/main/java/com/opencsv/CSVParserBuilder.java
@@ -17,9 +17,10 @@ package com.opencsv;
 
 
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
-import java.util.Locale;
 import org.apache.commons.lang3.ObjectUtils;
 
+import java.util.Locale;
+
 /**
  * Builder for creating a CSVParser.
  * <p>Example code for using this class:<br><br>
@@ -217,4 +218,5 @@ public class CSVParserBuilder {
     public CSVReaderNullFieldIndicator nullFieldIndicator() {
         return nullFieldIndicator;
     }
+    
 }
diff --git a/src/main/java/com/opencsv/CSVReaderBaseBuilder.java b/src/main/java/com/opencsv/CSVReaderBaseBuilder.java
new file mode 100644
index 0000000..0bbe252
--- /dev/null
+++ b/src/main/java/com/opencsv/CSVReaderBaseBuilder.java
@@ -0,0 +1,134 @@
+package com.opencsv;
+
+import com.opencsv.enums.CSVReaderNullFieldIndicator;
+import com.opencsv.processor.RowProcessor;
+import com.opencsv.validators.LineValidatorAggregator;
+import com.opencsv.validators.RowValidatorAggregator;
+import org.apache.commons.lang3.ObjectUtils;
+
+import java.io.Reader;
+import java.util.Locale;
+
+/**
+ * Base class for the builders of various incarnations of CSVReaders.
+ * @param <T> The type pf the CSVReader class to return
+ *
+ * @author Andrew Rucker Jones
+ * @since 5.5.2
+ */
+abstract public class CSVReaderBaseBuilder<T> {
+    protected final Reader reader;
+    protected final LineValidatorAggregator lineValidatorAggregator = new LineValidatorAggregator();
+    protected final RowValidatorAggregator rowValidatorAggregator = new RowValidatorAggregator();
+    private final CSVParserBuilder parserBuilder = new CSVParserBuilder();
+    protected int skipLines = CSVReader.DEFAULT_SKIP_LINES;
+    protected ICSVParser icsvParser = null;
+    protected boolean keepCR;
+    protected boolean verifyReader = CSVReader.DEFAULT_VERIFY_READER;
+    protected CSVReaderNullFieldIndicator nullFieldIndicator = CSVReaderNullFieldIndicator.NEITHER;
+    protected int multilineLimit = CSVReader.DEFAULT_MULTILINE_LIMIT;
+    protected Locale errorLocale = Locale.getDefault();
+    protected RowProcessor rowProcessor = null;
+
+    /**
+     * Base Constructor
+     *
+     * @param reader The reader to an underlying CSV source.
+     */
+    protected CSVReaderBaseBuilder(final Reader reader) {
+        this.reader = reader;
+    }
+
+    /**
+     * Used by unit tests.
+     *
+     * @return The reader.
+     */
+    protected Reader getReader() {
+        return reader;
+    }
+
+    /**
+     * Used by unit tests.
+     *
+     * @return The set number of lines to skip
+     */
+    protected int getSkipLines() {
+        return skipLines;
+    }
+
+    /**
+     * Used by unit tests.
+     *
+     * @return The CSVParser used by the builder.
+     */
+    protected ICSVParser getCsvParser() {
+        return icsvParser;
+    }
+
+    /**
+     * Used by unit tests.
+     *
+     * @return The upper limit on lines in multiline records.
+     */
+    protected int getMultilineLimit() {
+        return multilineLimit;
+    }
+
+    /**
+     * Returns if the reader built will keep or discard carriage returns.
+     *
+     * @return {@code true} if the reader built will keep carriage returns,
+     * {@code false} otherwise
+     */
+    protected boolean keepCarriageReturn() {
+        return this.keepCR;
+    }
+
+    /**
+     * Creates a new {@link ICSVParser} if the class doesn't already hold one.
+     *
+     * @return The injected {@link ICSVParser} or a default parser.
+     */
+    protected ICSVParser getOrCreateCsvParser() {
+        return ObjectUtils.defaultIfNull(icsvParser,
+                parserBuilder
+                        .withFieldAsNull(nullFieldIndicator)
+                        .withErrorLocale(errorLocale)
+                        .build());
+    }
+
+    /**
+     * @return The flag indicating whether the reader should be verified before each read.
+     */
+    public boolean isVerifyReader() {
+        return verifyReader;
+    }
+
+    /**
+     * @return The locale for error messages
+     */
+    public Locale getErrorLocale() {
+        return errorLocale;
+    }
+
+    /**
+     * @return The {@link LineValidatorAggregator} for custom defined {@link com.opencsv.validators.LineValidator}s.
+     */
+    public LineValidatorAggregator getLineValidatorAggregator() {
+        return lineValidatorAggregator;
+    }
+
+    /**
+     * @return The {@link RowValidatorAggregator} for the custom defined {@link com.opencsv.validators.RowValidator}s.
+     */
+    public RowValidatorAggregator getRowValidatorAggregator() {
+        return rowValidatorAggregator;
+    }
+
+    /**
+     * Must create the CSVReader type requested.
+     * @return A new instance of {@link CSVReader} or derived class
+     */
+    public abstract T build();
+}
diff --git a/src/main/java/com/opencsv/CSVReaderBuilder.java b/src/main/java/com/opencsv/CSVReaderBuilder.java
index 00defbe..fe78803 100644
--- a/src/main/java/com/opencsv/CSVReaderBuilder.java
+++ b/src/main/java/com/opencsv/CSVReaderBuilder.java
@@ -19,9 +19,7 @@ package com.opencsv;
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import com.opencsv.processor.RowProcessor;
 import com.opencsv.validators.LineValidator;
-import com.opencsv.validators.LineValidatorAggregator;
 import com.opencsv.validators.RowValidator;
-import com.opencsv.validators.RowValidatorAggregator;
 import org.apache.commons.lang3.ObjectUtils;
 
 import java.io.Reader;
@@ -29,7 +27,7 @@ import java.util.Locale;
 import java.util.ResourceBundle;
 
 /**
- * Builder for creating a CSVReader.
+ * Builder for creating a {@link CSVReader}.
  * <p>This should be the preferred method of creating a Reader as there are so many
  * possible values to be set it is impossible to have constructors for all of
  * them and keep backwards compatibility with previous constructors.<br>
@@ -49,20 +47,7 @@ import java.util.ResourceBundle;
  *
  * @see com.opencsv.CSVReader
  */
-public class CSVReaderBuilder {
-
-    private final CSVParserBuilder parserBuilder = new CSVParserBuilder();
-    private final Reader reader;
-    private int skipLines = CSVReader.DEFAULT_SKIP_LINES;
-    private ICSVParser icsvParser = null;
-    private boolean keepCR;
-    private boolean verifyReader = CSVReader.DEFAULT_VERIFY_READER;
-    private CSVReaderNullFieldIndicator nullFieldIndicator = CSVReaderNullFieldIndicator.NEITHER;
-    private int multilineLimit = CSVReader.DEFAULT_MULTILINE_LIMIT;
-    private Locale errorLocale = Locale.getDefault();
-    private LineValidatorAggregator lineValidatorAggregator = new LineValidatorAggregator();
-    private RowValidatorAggregator rowValidatorAggregator = new RowValidatorAggregator();
-    private RowProcessor rowProcessor = null;
+public class CSVReaderBuilder extends CSVReaderBaseBuilder<CSVReader> {
 
     /**
      * Sets the reader to an underlying CSV source.
@@ -71,46 +56,10 @@ public class CSVReaderBuilder {
      */
     public CSVReaderBuilder(
             final Reader reader) {
+        super(reader);
         if (reader == null) {
             throw new IllegalArgumentException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME).getString("reader.null"));
         }
-        this.reader = reader;
-    }
-
-    /**
-     * Used by unit tests.
-     *
-     * @return The reader.
-     */
-    protected Reader getReader() {
-        return reader;
-    }
-
-    /**
-     * Used by unit tests.
-     *
-     * @return The set number of lines to skip
-     */
-    protected int getSkipLines() {
-        return skipLines;
-    }
-
-    /**
-     * Used by unit tests.
-     *
-     * @return The CSVParser used by the builder.
-     */
-    protected ICSVParser getCsvParser() {
-        return icsvParser;
-    }
-
-    /**
-     * Used by unit tests.
-     *
-     * @return The upper limit on lines in multiline records.
-     */
-    protected int getMultilineLimit() {
-        return multilineLimit;
     }
 
     /**
@@ -125,7 +74,6 @@ public class CSVReaderBuilder {
         return this;
     }
 
-
     /**
      * Sets the parser to use to parse the input.
      *
@@ -138,11 +86,11 @@ public class CSVReaderBuilder {
         return this;
     }
 
-
     /**
-     * Creates the CSVReader.
-     * @return The CSVReader based on the set criteria.
+     * Creates the {@link CSVReader}.
+     * @return The {@link CSVReader} based on the set criteria.
      */
+    @Override
     public CSVReader build() {
         final ICSVParser parser = getOrCreateCsvParser();
         return new CSVReader(reader, skipLines, parser, keepCR, verifyReader, multilineLimit, errorLocale,
@@ -161,29 +109,7 @@ public class CSVReaderBuilder {
     }
 
     /**
-     * Returns if the reader built will keep or discard carriage returns.
-     *
-     * @return {@code true} if the reader built will keep carriage returns,
-     * {@code false} otherwise
-     */
-    protected boolean keepCarriageReturn() {
-        return this.keepCR;
-    }
-
-    /**
-     * Creates a new {@link ICSVParser} if the class does't already hold one.
-     * @return The injected {@link ICSVParser} or a default parser.
-     */
-    protected ICSVParser getOrCreateCsvParser() {
-        return ObjectUtils.defaultIfNull(icsvParser,
-                parserBuilder
-                        .withFieldAsNull(nullFieldIndicator)
-                        .withErrorLocale(errorLocale)
-                        .build());
-    }
-
-    /**
-     * Checks to see if the CSVReader should verify the reader state before
+     * Checks to see if the {@link CSVReader} should verify the reader state before
      * reads or not.
      *
      * <p>This should be set to false if you are using some form of asynchronous
@@ -191,7 +117,7 @@ public class CSVReaderBuilder {
      *
      * <p>The default value is true.</p>
      *
-     * @param verifyReader True if CSVReader should verify reader before each read, false otherwise.
+     * @param verifyReader True if {@link CSVReader} should verify reader before each read, false otherwise.
      * @return {@code this}
      */
     public CSVReaderBuilder withVerifyReader(boolean verifyReader) {
@@ -199,17 +125,10 @@ public class CSVReaderBuilder {
         return this;
     }
 
-    /**
-     * @return The flag indicating whether the reader should be verified before each read.
-     */
-    public boolean isVerifyReader() {
-        return verifyReader;
-    }
-
     /**
      * Checks to see if it should treat a field with two separators, two quotes, or both as a null field.
      *
-     * @param indicator CSVReaderNullFieldIndicator set to what should be considered a null field.
+     * @param indicator {@link CSVReaderNullFieldIndicator} set to what should be considered a null field.
      * @return {@code this}
      */
     public CSVReaderBuilder withFieldAsNull(CSVReaderNullFieldIndicator indicator) {
@@ -243,31 +162,10 @@ public class CSVReaderBuilder {
     }
 
     /**
-     * @return The locale for error messages
-     */
-    public Locale getErrorLocale() {
-        return errorLocale;
-    }
-
-    /**
-     * @return The LineValidatorAggragator for custom defined LineValidators.
-     */
-    public LineValidatorAggregator getLineValidatorAggregator() {
-        return lineValidatorAggregator;
-    }
-
-    /**
-     * @return The RowValidatorAggregator for the custom defined RowValidators.
-     */
-    public RowValidatorAggregator getRowValidatorAggregator() {
-        return rowValidatorAggregator;
-    }
-
-    /**
-     * Adds a LineValidator to the CSVReader.
-     * Multiple LineValidators can be added with multiple calls.
+     * Adds a {@link LineValidator} to the {@link CSVReader}.
+     * Multiple {@link LineValidator}s can be added with multiple calls.
      *
-     * @param lineValidator LineValidator to inject.
+     * @param lineValidator {@link LineValidator} to inject.
      * @return {@code this}
      * @since 5.0
      */
@@ -277,10 +175,10 @@ public class CSVReaderBuilder {
     }
 
     /**
-     * Adds a RowValidator to the CSVReader.
-     * Multiple RowValidators can be added with multiple calls.
+     * Adds a {@link RowValidator} to the {@link CSVReader}.
+     * Multiple {@link RowValidator}s can be added with multiple calls.
      *
-     * @param rowValidator RowValidator to inject
+     * @param rowValidator {@link RowValidator} to inject
      * @return {@code this}
      * @since 5.0
      */
@@ -290,11 +188,11 @@ public class CSVReaderBuilder {
     }
 
     /**
-     * Adds a RowProcessor to the CSVReader.
-     * Only a single RowProcessor can be added so multiple calls will overwrite
-     * the previously set RowProcessor.
+     * Adds a {@link RowProcessor} to the {@link CSVReader}.
+     * Only a single {@link RowProcessor} can be added so multiple calls will overwrite
+     * the previously set {@link RowProcessor}.
      *
-     * @param rowProcessor RowProcessor to inject
+     * @param rowProcessor {@link RowProcessor} to inject
      * @return {@code this}
      * @since 5.0
      */
diff --git a/src/main/java/com/opencsv/CSVReaderHeaderAware.java b/src/main/java/com/opencsv/CSVReaderHeaderAware.java
index 4f77486..d73ebaf 100644
--- a/src/main/java/com/opencsv/CSVReaderHeaderAware.java
+++ b/src/main/java/com/opencsv/CSVReaderHeaderAware.java
@@ -1,6 +1,7 @@
 package com.opencsv;
 
 import com.opencsv.exceptions.CsvValidationException;
+import com.opencsv.processor.RowProcessor;
 import com.opencsv.validators.LineValidatorAggregator;
 import com.opencsv.validators.RowValidatorAggregator;
 
@@ -46,12 +47,13 @@ public class CSVReaderHeaderAware extends CSVReader {
      * @param errorLocale    Set the locale for error messages. If null, the default locale is used.
      * @param lineValidatorAggregator contains all the custom defined line validators.
      * @param rowValidatorAggregator  contains all the custom defined row validators.
+     * @param rowProcessor            Custom row processor to run on all columns on a csv record.
      * @throws IOException   If bad things happen while initializing the header
      */
     CSVReaderHeaderAware(Reader reader, int skipLines, ICSVParser parser, boolean keepCR, boolean verifyReader,
                          int multilineLimit, Locale errorLocale, LineValidatorAggregator lineValidatorAggregator,
-                         RowValidatorAggregator rowValidatorAggregator) throws IOException {
-        super(reader, skipLines, parser, keepCR, verifyReader, multilineLimit, errorLocale, lineValidatorAggregator, rowValidatorAggregator, null);
+                         RowValidatorAggregator rowValidatorAggregator, RowProcessor rowProcessor) throws IOException {
+        super(reader, skipLines, parser, keepCR, verifyReader, multilineLimit, errorLocale, lineValidatorAggregator, rowValidatorAggregator, rowProcessor);
         initializeHeader();
     }
 
diff --git a/src/main/java/com/opencsv/CSVReaderHeaderAwareBuilder.java b/src/main/java/com/opencsv/CSVReaderHeaderAwareBuilder.java
index 9bec566..9f31bbd 100644
--- a/src/main/java/com/opencsv/CSVReaderHeaderAwareBuilder.java
+++ b/src/main/java/com/opencsv/CSVReaderHeaderAwareBuilder.java
@@ -1,18 +1,26 @@
 package com.opencsv;
 
+import com.opencsv.enums.CSVReaderNullFieldIndicator;
+import com.opencsv.processor.RowProcessor;
+import com.opencsv.validators.LineValidator;
+import com.opencsv.validators.RowValidator;
+import org.apache.commons.lang3.ObjectUtils;
+
 import java.io.IOException;
 import java.io.Reader;
+import java.util.Locale;
+import java.util.ResourceBundle;
 
 /**
- * Builder for CSVReaderHeaderAware. No transformations on the original parameters accepted by CSVReaderBuilder.
+ * Builder for {@link CSVReaderHeaderAware}.
  *
  * @author Andre Rosot
  * @since 4.2
  */
-public class CSVReaderHeaderAwareBuilder extends CSVReaderBuilder {
+public class CSVReaderHeaderAwareBuilder extends CSVReaderBaseBuilder<CSVReaderHeaderAware> {
 
     /**
-     * Constructor for CSVReaderHeaderAwareBuilder.
+     * Sets the reader to an underlying CSV source.
      *
      * @param reader The reader to an underlying CSV source.
      */
@@ -20,14 +28,148 @@ public class CSVReaderHeaderAwareBuilder extends CSVReaderBuilder {
         super(reader);
     }
 
+    /**
+     * Sets the number of lines to skip before reading.
+     *
+     * @param skipLines The number of lines to skip before reading.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withSkipLines(
+            final int skipLines) {
+        this.skipLines = Math.max(skipLines, 0);
+        return this;
+    }
+
+    /**
+     * Sets the parser to use to parse the input.
+     *
+     * @param icsvParser The parser to use to parse the input.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withCSVParser(
+            final ICSVParser icsvParser) {
+        this.icsvParser = icsvParser;
+        return this;
+    }
+
+    /**
+     * Sets if the reader will keep or discard carriage returns.
+     *
+     * @param keepCR True to keep carriage returns, false to discard.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withKeepCarriageReturn(boolean keepCR) {
+        this.keepCR = keepCR;
+        return this;
+    }
+
+    /**
+     * Checks to see if the {@link CSVReaderHeaderAware} should verify the reader state before
+     * reads or not.
+     *
+     * <p>This should be set to false if you are using some form of asynchronous
+     * reader (like readers created by the java.nio.* classes).</p>
+     *
+     * <p>The default value is true.</p>
+     *
+     * @param verifyReader True if {@link CSVReaderHeaderAware} should verify reader before each read, false otherwise.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withVerifyReader(boolean verifyReader) {
+        this.verifyReader = verifyReader;
+        return this;
+    }
+
+    /**
+     * Checks to see if it should treat a field with two separators, two quotes, or both as a null field.
+     *
+     * @param indicator {@link CSVReaderNullFieldIndicator} set to what should be considered a null field.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withFieldAsNull(CSVReaderNullFieldIndicator indicator) {
+        this.nullFieldIndicator = indicator;
+        return this;
+    }
+
+    /**
+     * Sets the maximum number of lines allowed in a multiline record.
+     * More than this number in one record results in an IOException.
+     *
+     * @param multilineLimit No more than this number of lines is allowed in a
+     *                       single input record. The default is {@link CSVReader#DEFAULT_MULTILINE_LIMIT}.
+     * @return {@code this}
+     */
+    public CSVReaderHeaderAwareBuilder withMultilineLimit(int multilineLimit) {
+        this.multilineLimit = multilineLimit;
+        return this;
+    }
+
+    /**
+     * Sets the locale for all error messages.
+     *
+     * @param errorLocale Locale for error messages
+     * @return {@code this}
+     * @since 4.0
+     */
+    public CSVReaderHeaderAwareBuilder withErrorLocale(Locale errorLocale) {
+        this.errorLocale = ObjectUtils.defaultIfNull(errorLocale, Locale.getDefault());
+        return this;
+    }
+
+    /**
+     * Adds a {@link LineValidator} to the {@link CSVReaderHeaderAware}.
+     * Multiple {@link LineValidator}s can be added with multiple calls.
+     *
+     * @param lineValidator {@link LineValidator} to inject.
+     * @return {@code this}
+     * @since 5.0
+     */
+    public CSVReaderHeaderAwareBuilder withLineValidator(LineValidator lineValidator) {
+        lineValidatorAggregator.addValidator(lineValidator);
+        return this;
+    }
+
+    /**
+     * Adds a {@link RowValidator} to the {@link CSVReaderHeaderAware}.
+     * Multiple {@link RowValidator}s can be added with multiple calls.
+     *
+     * @param rowValidator {@link RowValidator} to inject
+     * @return {@code this}
+     * @since 5.0
+     */
+    public CSVReaderHeaderAwareBuilder withRowValidator(RowValidator rowValidator) {
+        rowValidatorAggregator.addValidator(rowValidator);
+        return this;
+    }
+
+    /**
+     * Adds a {@link RowProcessor} to the {@link CSVReaderHeaderAware}.
+     * Only a single {@link RowProcessor} can be added so multiple calls will overwrite
+     * the previously set {@link RowProcessor}.
+     *
+     * @param rowProcessor {@link RowProcessor} to inject
+     * @return {@code this}
+     * @since 5.0
+     */
+    public CSVReaderHeaderAwareBuilder withRowProcessor(RowProcessor rowProcessor) {
+        this.rowProcessor = rowProcessor;
+        return this;
+    }
+
+    /**
+     * Creates the {@link CSVReaderHeaderAware}.
+     * @return The {@link CSVReaderHeaderAware} based on the set criteria.
+     * @throws RuntimeException If the header mapping cannot be initialized
+     *   from the first line of data
+     */
     @Override
     public CSVReaderHeaderAware build() throws RuntimeException {
         final ICSVParser parser = getOrCreateCsvParser();
         try {
-            return new CSVReaderHeaderAware(getReader(), getSkipLines(), parser, keepCarriageReturn(), isVerifyReader(),
-                    getMultilineLimit(), getErrorLocale(), getLineValidatorAggregator(), getRowValidatorAggregator());
+            return new CSVReaderHeaderAware(reader, skipLines, parser, keepCR, verifyReader,
+                    multilineLimit, errorLocale, lineValidatorAggregator, rowValidatorAggregator, rowProcessor);
         } catch (IOException e) {
-            throw new RuntimeException("Failed to initialize CSVReaderHeaderAware", e);
+            throw new RuntimeException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("csvreaderheaderaware.impossible"), e);
         }
     }
 }
diff --git a/src/main/java/com/opencsv/ICSVParser.java b/src/main/java/com/opencsv/ICSVParser.java
index 16ff237..ef7c62c 100644
--- a/src/main/java/com/opencsv/ICSVParser.java
+++ b/src/main/java/com/opencsv/ICSVParser.java
@@ -3,9 +3,9 @@ package com.opencsv;
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
 
 import java.io.IOException;
+import java.util.Locale;
 
 import static com.opencsv.enums.CSVReaderNullFieldIndicator.NEITHER;
-import java.util.Locale;
 
 /**
  * This interface defines all of the behavior {@link com.opencsv.CSVReader}
diff --git a/src/main/java/com/opencsv/ResultSetColumnNameHelperService.java b/src/main/java/com/opencsv/ResultSetColumnNameHelperService.java
index e9f3cf3..0a5bfc9 100644
--- a/src/main/java/com/opencsv/ResultSetColumnNameHelperService.java
+++ b/src/main/java/com/opencsv/ResultSetColumnNameHelperService.java
@@ -122,7 +122,7 @@ public class ResultSetColumnNameHelperService extends ResultSetHelperService imp
         if (columnNamePositionMap.isEmpty()) {
             populateColumnData(rs);
         }
-        String[] realColumnValues = super.getColumnValues(rs, false, DEFAULT_DATE_FORMAT, DEFAULT_TIMESTAMP_FORMAT);
+        String[] realColumnValues = super.getColumnValues(rs, false, dateFormat, dateTimeFormat);
         return getColumnValueSubset(realColumnValues);
     }
 
@@ -139,7 +139,7 @@ public class ResultSetColumnNameHelperService extends ResultSetHelperService imp
         if (columnNamePositionMap.isEmpty()) {
             populateColumnData(rs);
         }
-        String[] realColumnValues = super.getColumnValues(rs, trim, DEFAULT_DATE_FORMAT, DEFAULT_TIMESTAMP_FORMAT);
+        String[] realColumnValues = super.getColumnValues(rs, trim, dateFormat, dateTimeFormat);
         return getColumnValueSubset(realColumnValues);
     }
 
diff --git a/src/main/java/com/opencsv/ResultSetHelperService.java b/src/main/java/com/opencsv/ResultSetHelperService.java
index 13887f5..20004f7 100644
--- a/src/main/java/com/opencsv/ResultSetHelperService.java
+++ b/src/main/java/com/opencsv/ResultSetHelperService.java
@@ -19,8 +19,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.text.TextStringBuilder;
 
 import java.io.IOException;
-import java.math.BigDecimal;
 import java.sql.*;
+import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.Objects;
 
@@ -34,8 +34,10 @@ public class ResultSetHelperService implements ResultSetHelper {
    static final String DEFAULT_TIMESTAMP_FORMAT = "dd-MMM-yyyy HH:mm:ss";
    private static final String DEFAULT_VALUE = StringUtils.EMPTY;
 
-   private String dateFormat = DEFAULT_DATE_FORMAT;
-   private String dateTimeFormat = DEFAULT_TIMESTAMP_FORMAT;
+   protected String dateFormat = DEFAULT_DATE_FORMAT;
+   protected String dateTimeFormat = DEFAULT_TIMESTAMP_FORMAT;
+   protected NumberFormat integerFormat;
+   protected NumberFormat floatingPointFormat;
 
    /**
     * Default constructor.
@@ -61,6 +63,24 @@ public class ResultSetHelperService implements ResultSetHelper {
       this.dateTimeFormat = dateTimeFormat;
    }
 
+   /**
+    * Set a default number formatter for floating point numbers that will be used by the service.
+    *
+    * @param format Desired number format. Should not be null
+    */
+   public void setIntegerFormat(NumberFormat format) {
+      this.integerFormat = format;
+   }
+
+   /**
+    * Set a default number formatter for integer numbers that will be used by the service.
+    *
+    * @param format Desired number format. Should not be null
+    */
+   public void setFloatingPointFormat(NumberFormat format) {
+      this.floatingPointFormat = format;
+   }
+
    @Override
    public String[] getColumnNames(ResultSet rs) throws SQLException {
       ResultSetMetaData metadata = rs.getMetaData();
@@ -119,24 +139,23 @@ public class ResultSetHelperService implements ResultSetHelper {
             value = handleClob(rs, colIndex);
             break;
          case Types.BIGINT:
-            BigDecimal d = rs.getBigDecimal(colIndex);
-            value = Objects.toString(d!=null?d.toBigInteger():null);
+            value = applyFormatter(integerFormat, rs.getBigDecimal(colIndex));
             break;
          case Types.DECIMAL:
          case Types.REAL:
          case Types.NUMERIC:
-            value = Objects.toString(rs.getBigDecimal(colIndex), DEFAULT_VALUE);
+            value = applyFormatter(floatingPointFormat, rs.getBigDecimal(colIndex));
             break;
          case Types.DOUBLE:
-            value = Objects.toString(rs.getDouble(colIndex));
+            value = applyFormatter(floatingPointFormat, rs.getDouble(colIndex));
             break;
          case Types.FLOAT:
-            value = Objects.toString(rs.getFloat(colIndex));
+            value = applyFormatter(floatingPointFormat, rs.getFloat(colIndex));
             break;
          case Types.INTEGER:
          case Types.TINYINT:
          case Types.SMALLINT:
-            value = Objects.toString(rs.getInt(colIndex));
+            value = applyFormatter(integerFormat, rs.getInt(colIndex));
             break;
          case Types.DATE:
             value = handleDate(rs, colIndex, dateFormatString);
@@ -171,7 +190,23 @@ public class ResultSetHelperService implements ResultSetHelper {
       return value;
    }
 
-   private String handleVarChar(ResultSet rs, int colIndex, boolean trim) throws SQLException {
+   private String applyFormatter(NumberFormat formatter, Number value) {
+      if (value != null && formatter != null) {
+         return formatter.format(value);
+      }
+      return Objects.toString(value, DEFAULT_VALUE);
+   }
+
+   /**
+    * retrieves the data from an VarChar in a result set
+    *
+    * @param rs       - result set
+    * @param colIndex - column location of the data in the result set
+    * @param trim     - should the value be trimmed before being returned
+    * @return a string representing the VarChar from the result set
+    * @throws SQLException
+    */
+   protected String handleVarChar(ResultSet rs, int colIndex, boolean trim) throws SQLException {
       String value;
       String columnValue = rs.getString(colIndex);
       if (trim && columnValue != null) {
@@ -182,7 +217,16 @@ public class ResultSetHelperService implements ResultSetHelper {
       return value;
    }
 
-   private String handleNVarChar(ResultSet rs, int colIndex, boolean trim) throws SQLException {
+   /**
+    * retrieves the data from an NVarChar in a result set
+    *
+    * @param rs       - result set
+    * @param colIndex - column location of the data in the result set
+    * @param trim     - should the value be trimmed before being returned
+    * @return a string representing the NVarChar from the result set
+    * @throws SQLException
+    */
+   protected String handleNVarChar(ResultSet rs, int colIndex, boolean trim) throws SQLException {
       String value;
       String nColumnValue = rs.getNString(colIndex);
       if (trim && nColumnValue != null) {
@@ -193,7 +237,16 @@ public class ResultSetHelperService implements ResultSetHelper {
       return value;
    }
 
-   private String handleDate(ResultSet rs, int colIndex, String dateFormatString) throws SQLException {
+   /**
+    * retrieves an date from a result set
+    *
+    * @param rs               - result set
+    * @param colIndex         - column location of the data in the result set
+    * @param dateFormatString - desired format of the date
+    * @return - a string representing the data from the result set in the format set in dateFomratString.
+    * @throws SQLException
+    */
+   protected String handleDate(ResultSet rs, int colIndex, String dateFormatString) throws SQLException {
       String value = DEFAULT_VALUE;
       Date date = rs.getDate(colIndex);
       if (date != null) {
@@ -203,7 +256,16 @@ public class ResultSetHelperService implements ResultSetHelper {
       return value;
    }
 
-   private String handleClob(ResultSet rs, int colIndex) throws SQLException, IOException {
+   /**
+    * retrieves the data out of a CLOB
+    *
+    * @param rs       - result set
+    * @param colIndex - column location of the data in the result set
+    * @return the data in the Clob as a string.
+    * @throws SQLException
+    * @throws IOException
+    */
+   protected String handleClob(ResultSet rs, int colIndex) throws SQLException, IOException {
       String value = DEFAULT_VALUE;
       Clob c = rs.getClob(colIndex);
       if (c != null) {
@@ -214,7 +276,16 @@ public class ResultSetHelperService implements ResultSetHelper {
       return value;
    }
 
-   private String handleNClob(ResultSet rs, int colIndex) throws SQLException, IOException {
+   /**
+    * retrieves the data out of a NCLOB
+    *
+    * @param rs       - result set
+    * @param colIndex - column location of the data in the result set
+    * @return the data in the NCLOB as a string.
+    * @throws SQLException
+    * @throws IOException
+    */
+   protected String handleNClob(ResultSet rs, int colIndex) throws SQLException, IOException {
       String value = DEFAULT_VALUE;
       NClob nc = rs.getNClob(colIndex);
       if (nc != null) {
diff --git a/src/main/java/com/opencsv/bean/AbstractBeanField.java b/src/main/java/com/opencsv/bean/AbstractBeanField.java
index 50349cd..2a0cacb 100644
--- a/src/main/java/com/opencsv/bean/AbstractBeanField.java
+++ b/src/main/java/com/opencsv/bean/AbstractBeanField.java
@@ -185,9 +185,7 @@ abstract public class AbstractBeanField<T, I> implements BeanField<T, I> {
     private String preProcessValue(PreAssignmentProcessor processor, String value) throws CsvValidationException {
         try {
             StringProcessor stringProcessor = processor.processor().newInstance();
-            if (Objects.nonNull(processor.paramString())) {
-                stringProcessor.setParameterString(processor.paramString());
-            }
+            stringProcessor.setParameterString(processor.paramString());
             return stringProcessor.processString(value);
         } catch (InstantiationException | IllegalAccessException e) {
             throw new CsvValidationException(String.format(
@@ -200,9 +198,7 @@ abstract public class AbstractBeanField<T, I> implements BeanField<T, I> {
     private void validateValue(PreAssignmentValidator validator, String value) throws CsvValidationException {
         try {
             StringValidator stringValidator = validator.validator().newInstance();
-            if (Objects.nonNull(validator.paramString())) {
-                stringValidator.setParameterString(validator.paramString());
-            }
+            stringValidator.setParameterString(validator.paramString());
             stringValidator.validate(value, this);
         } catch (InstantiationException | IllegalAccessException e) {
             throw new CsvValidationException(String.format(
@@ -364,8 +360,9 @@ abstract public class AbstractBeanField<T, I> implements BeanField<T, I> {
             // rather from write() using isFieldEmptyForWrite() to determine
             // when to throw the exception. But user code is still allowed
             // to override convertToWrite() and throw this exception
+            Class<?> beanClass = bean == null ? null : bean.getClass();
             CsvRequiredFieldEmptyException csve = new CsvRequiredFieldEmptyException(
-                    bean.getClass(), field, e.getMessage());
+                    beanClass, field, e.getMessage());
             csve.initCause(e.getCause());
             throw csve;
         }
diff --git a/src/main/java/com/opencsv/bean/AbstractCsvConverter.java b/src/main/java/com/opencsv/bean/AbstractCsvConverter.java
index b8a2c7f..2747a5d 100644
--- a/src/main/java/com/opencsv/bean/AbstractCsvConverter.java
+++ b/src/main/java/com/opencsv/bean/AbstractCsvConverter.java
@@ -58,7 +58,7 @@ public abstract class AbstractCsvConverter implements CsvConverter {
      *
      * @since 4.3
      */
-    public AbstractCsvConverter() {
+    protected AbstractCsvConverter() {
         this.type = null;
         this.locale = null;
         this.writeLocale = null;
@@ -67,7 +67,7 @@ public abstract class AbstractCsvConverter implements CsvConverter {
 
     /**
      * Currently the only constructor for this class.
-     * 
+     *
      * @param type The type to which (on reading) or from which (on writing) is
      *   being converted
      * @param locale The locale to be used when converting for reading, if a
@@ -76,7 +76,7 @@ public abstract class AbstractCsvConverter implements CsvConverter {
      *                    a locale is relevant
      * @param errorLocale The locale to be used for error messages
      */
-    public AbstractCsvConverter(Class<?> type, String locale, String writeLocale, Locale errorLocale) {
+    protected AbstractCsvConverter(Class<?> type, String locale, String writeLocale, Locale errorLocale) {
         this.type = type;
         this.locale = StringUtils.isNotEmpty(locale) ? Locale.forLanguageTag(locale) : null;
         this.writeLocale = StringUtils.isNotEmpty(writeLocale) ? Locale.forLanguageTag(writeLocale) : null;
diff --git a/src/main/java/com/opencsv/bean/AbstractMappingStrategy.java b/src/main/java/com/opencsv/bean/AbstractMappingStrategy.java
index 90e5657..484e6c0 100644
--- a/src/main/java/com/opencsv/bean/AbstractMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/AbstractMappingStrategy.java
@@ -20,6 +20,7 @@ import com.opencsv.exceptions.*;
 import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.collections4.MapIterator;
 import org.apache.commons.collections4.MultiValuedMap;
+import org.apache.commons.collections4.SetUtils;
 import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -30,8 +31,7 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.function.Function;
 
 /**
  * This class collects as many generally useful parts of the implementation
@@ -40,16 +40,16 @@ import java.util.stream.Stream;
  * assumes through {@link #getBindingAnnotations()} they are not in use.</p>
  * <p>Anyone is welcome to use it as a base class for their own mapping
  * strategies.</p>
- * 
+ *
  * @param <T> Type of object that is being processed.
  * @param <C> The type of the internal many-to-one mapping
  * @param <I> The initializer type used to build the internal many-to-one mapping
  * @param <K> The type of the key used for internal indexing
- * 
+ *
  * @author Andrew Rucker Jones
  * @since 4.2
  */
-abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C extends ComplexFieldMapEntry<I, K, T>, T> implements MappingStrategy<T> {
+public abstract class AbstractMappingStrategy<I, K extends Comparable<K>, C extends ComplexFieldMapEntry<I, K, T>, T> implements MappingStrategy<T> {
 
     /**
      * Set of classes where recursion is not allowed.   Using HashSet because, given the large number of types, the
@@ -83,23 +83,26 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
     /** Locale for error messages. */
     protected Locale errorLocale = Locale.getDefault();
 
+    /** The profile for configuring bean fields. */
+    protected String profile = StringUtils.EMPTY;
+
     /**
      * For {@link BeanField#indexAndSplitMultivaluedField(java.lang.Object, java.lang.Object)}
      * it is necessary to determine which index to pass in.
-     * 
+     *
      * @param index The current column position while transmuting a bean to CSV
-     *   output
+     *              output
      * @return The index to be used for this mapping strategy for
-     *   {@link BeanField#indexAndSplitMultivaluedField(java.lang.Object, java.lang.Object) }
+     * {@link BeanField#indexAndSplitMultivaluedField(java.lang.Object, java.lang.Object) }
      */
-    abstract protected K chooseMultivaluedFieldIndexFromHeaderIndex(int index);
-    
+    protected abstract K chooseMultivaluedFieldIndexFromHeaderIndex(int index);
+
     /**
      * Returns the {@link FieldMap} associated with this mapping strategy.
-     * 
+     *
      * @return The {@link FieldMap} used by this strategy
      */
-    abstract protected FieldMap<I,K,? extends C,T> getFieldMap();
+    protected abstract FieldMap<I, K, ? extends C, T> getFieldMap();
 
     /**
      * Returns a set of the annotations that are used for binding in this
@@ -137,16 +140,17 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      *               processed
      * @since 5.0
      */
-    abstract protected void loadUnadornedFieldMap(ListValuedMap<Class<?>, Field> fields);
+    protected abstract void loadUnadornedFieldMap(ListValuedMap<Class<?>, Field> fields);
 
     /**
      * Creates an empty binding-type-specific field map that can be filled in
      * later steps.
      * <p>This method may be called multiple times and must erase any state
      * information from previous calls.</p>
+     *
      * @since 5.0
      */
-    abstract protected void initializeFieldMap();
+    protected abstract void initializeFieldMap();
 
     /**
      * Gets the field for a given column position.
@@ -157,7 +161,7 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * @throws CsvBadConverterException If a custom converter for a field cannot
      *                                  be initialized
      */
-    abstract protected BeanField<T, K> findField(int col);
+    protected abstract BeanField<T, K> findField(int col);
 
     /**
      * Must be called once the length of input for a line/record is known to
@@ -174,7 +178,7 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * @throws CsvRequiredFieldEmptyException If a required column is missing
      * @since 4.0
      */
-    abstract protected void verifyLineLength(int numberOfFields) throws CsvRequiredFieldEmptyException;
+    protected abstract void verifyLineLength(int numberOfFields) throws CsvRequiredFieldEmptyException;
     
     /**
      * Implementation will return a bean of the type of object being mapped.
@@ -261,11 +265,11 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * Gets the name (or position number) of the header for the given column
      * number.
      * The column numbers are zero-based.
-     * 
+     *
      * @param col The column number for which the header is sought
      * @return The name of the header
      */
-    abstract public String findHeader(int col);
+    public abstract String findHeader(int col);
 
     /**
      * This method generates a header that can be used for writing beans of the
@@ -302,13 +306,13 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * @return The column name or null if the position is larger than the
      * header array or there are no headers defined.
      */
-    String getColumnName(int col) {
+    protected String getColumnName(int col) {
         // headerIndex is never null because it's final
         return headerIndex.getByPosition(col);
     }
 
     /**
-     * Get the class type that the Strategy is mapping.
+     * Get the class type that the strategy is mapping.
      *
      * @return Class of the object that this {@link MappingStrategy} will create.
      */
@@ -319,13 +323,29 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
     @SuppressWarnings("unchecked")
     @Override
     public T populateNewBean(String[] line)
-            throws CsvBeanIntrospectionException, CsvRequiredFieldEmptyException,
-            CsvDataTypeMismatchException, CsvConstraintViolationException,
-            CsvValidationException {
+            throws CsvBeanIntrospectionException, CsvFieldAssignmentException,
+            CsvChainedException {
         verifyLineLength(line.length);
         Map<Class<?>, Object> beanTree = createBean();
+
+        CsvChainedException chainedException = null;
         for (int col = 0; col < line.length; col++) {
-            setFieldValue(beanTree, line[col], col);
+            try {
+                setFieldValue(beanTree, line[col], col);
+            } catch (CsvFieldAssignmentException e) {
+                if(chainedException != null) {
+                    chainedException.add(e);
+                }
+                else {
+                    chainedException = new CsvChainedException(e);
+                }
+            }
+        }
+        if(chainedException != null) {
+            if (chainedException.hasOnlyOneException()) {
+                throw chainedException.getFirstException();
+            }
+            throw chainedException;
         }
         return (T)beanTree.get(type);
     }
@@ -343,6 +363,16 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
         loadFieldMap();
     }
 
+    /**
+     * Sets the profile this mapping strategy will use when configuring bean
+     * fields.
+     */
+    // The rest of the Javadoc is inherited.
+    @Override
+    public void setProfile(String profile) {
+        this.profile = StringUtils.defaultString(profile);
+    }
+
     @Override
     public void ignoreFields(MultiValuedMap<Class<?>, Field> fields)  throws IllegalArgumentException {
 
@@ -353,10 +383,10 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
         else {
             ignoredFields = fields;
             MapIterator<Class<?>, Field> it = ignoredFields.mapIterator();
-            it.forEachRemaining(type -> {
+            it.forEachRemaining(t -> {
                 final Field f = it.getValue();
-                if(type == null || f == null
-                        || !f.getDeclaringClass().isAssignableFrom(type)) {
+                if (t == null || f == null
+                        || !f.getDeclaringClass().isAssignableFrom(t)) {
                     throw new IllegalArgumentException(ResourceBundle.getBundle(
                             ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
                             .getString("ignore.field.inconsistent"));
@@ -379,10 +409,20 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * @param fields The fields to be filtered
      * @return A list of fields that exist for opencsv
      */
-    private List<Field> filterIgnoredFields(final Class<?> type, Field[] fields) {
-        return Stream.of(fields)
-                .filter(f -> !ignoredFields.containsMapping(type, f) && !f.isAnnotationPresent(CsvIgnore.class))
-                .collect(Collectors.toList());
+    protected List<Field> filterIgnoredFields(final Class<?> type, Field[] fields) {
+        final List<Field> filteredFields = new LinkedList<>();
+        for(Field f : fields) {
+            CsvIgnore ignoreAnnotation = f.getAnnotation(CsvIgnore.class);
+            Set<String> ignoredProfiles = ignoreAnnotation == null ?
+                    SetUtils.<String>emptySet() :
+                    new HashSet<String>(Arrays.asList(ignoreAnnotation.profiles())); // This is easier in Java 9 with Set.of()
+            if(!ignoredFields.containsMapping(type, f) &&
+                    !ignoredProfiles.contains(profile) &&
+                    !ignoredProfiles.contains(StringUtils.EMPTY)) {
+                filteredFields.add(f);
+            }
+        }
+        return filteredFields;
     }
 
     /**
@@ -409,6 +449,16 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
         }
     }
 
+    /**
+     * @param type Class to be checked
+     * @return Whether the type may be recursed into ({@code false}), or
+     *   must be considered a leaf node for recursion ({@code true}). This
+     *   implementation considers the boxed primitives forbidden.
+     */
+    protected boolean isForbiddenClassForRecursion(Class<?> type) {
+        return FORBIDDEN_CLASSES_FOR_RECURSION.contains(type);
+    }
+
     /**
      * Creates a tree of beans embedded in each other.
      * These are the member variables annotated with {@link CsvRecurse} and
@@ -428,7 +478,7 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
     protected RecursiveType loadRecursiveClasses(Class<?> newType, Set<Class<?>> encounteredTypes) {
 
         // We cannot recurse into primitive types
-        if (FORBIDDEN_CLASSES_FOR_RECURSION.contains(newType)) {
+        if (isForbiddenClassForRecursion(newType)) {
             throw new CsvRecursionException(
                     ResourceBundle.getBundle(
                             ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
@@ -583,7 +633,7 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
     }
     
     @Override
-    public String[] transmuteBean(T bean) throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+    public String[] transmuteBean(T bean) throws CsvFieldAssignmentException, CsvChainedException {
         int numColumns = headerIndex.findMaxIndex()+1;
         BeanField<T, K> firstBeanField, subsequentBeanField;
         K firstIndex, subsequentIndex;
@@ -606,15 +656,26 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
             throw csve;
         }
 
-
+        CsvChainedException chainedException = null;
         for(int i = 0; i < numColumns;) {
 
             // Determine the first value
             firstBeanField = findField(i);
             firstIndex = chooseMultivaluedFieldIndexFromHeaderIndex(i);
-            String[] fields = firstBeanField != null
-                    ? firstBeanField.write(instanceMap.get(firstBeanField.getType()), firstIndex)
-                    : ArrayUtils.EMPTY_STRING_ARRAY;
+            String[] fields = ArrayUtils.EMPTY_STRING_ARRAY;
+            if(firstBeanField != null) {
+                try {
+                    fields = firstBeanField.write(instanceMap.get(firstBeanField.getType()), firstIndex);
+                }
+                catch(CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
+                    if(chainedException != null) {
+                        chainedException.add(e);
+                    }
+                    else {
+                        chainedException = new CsvChainedException(e);
+                    }
+                }
+            }
 
             if(fields.length == 0) {
 
@@ -664,6 +725,15 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
                 }
             }
         }
+
+        // If there were exceptions, throw them
+        if(chainedException != null) {
+            if (chainedException.hasOnlyOneException()) {
+                throw chainedException.getFirstException();
+            }
+            throw chainedException;
+        }
+
         return contents.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
     }
 
@@ -684,13 +754,13 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
      * @since 4.2
      */
     protected CsvConverter determineConverter(Field field,
-            Class<?> elementType, String locale, String writeLocale,
-            Class<? extends AbstractCsvConverter> customConverter)
+                                              Class<?> elementType, String locale, String writeLocale,
+                                              Class<? extends AbstractCsvConverter> customConverter)
             throws CsvBadConverterException {
         CsvConverter converter;
 
         // A custom converter always takes precedence if specified.
-        if(customConverter != null && !customConverter.equals(AbstractCsvConverter.class)) {
+        if (customConverter != null && !customConverter.equals(AbstractCsvConverter.class)) {
             try {
                 converter = customConverter.newInstance();
             } catch (IllegalAccessException | InstantiationException oldEx) {
@@ -707,8 +777,15 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
         }
 
         // Perhaps a date instead
-        else if (field.isAnnotationPresent(CsvDate.class)) {
-            CsvDate annotation = field.getAnnotation(CsvDate.class);
+        else if (field.isAnnotationPresent(CsvDate.class) || field.isAnnotationPresent(CsvDates.class)) {
+            CsvDate annotation = selectAnnotationForProfile(
+                    field.getAnnotationsByType(CsvDate.class),
+                    CsvDate::profiles);
+            if(annotation == null) {
+                throw new CsvBadConverterException(CsvDate.class, String.format(
+                        ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME).getString("profile.not.found.date"),
+                        profile));
+            }
             String readFormat = annotation.value();
             String writeFormat = annotation.writeFormatEqualsReadFormat()
                     ? readFormat : annotation.writeFormat();
@@ -720,8 +797,15 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
         }
 
         // Or a number
-        else if(field.isAnnotationPresent(CsvNumber.class)) {
-            CsvNumber annotation = field.getAnnotation(CsvNumber.class);
+        else if(field.isAnnotationPresent(CsvNumber.class) || field.isAnnotationPresent(CsvNumbers.class)) {
+            CsvNumber annotation = selectAnnotationForProfile(
+                    field.getAnnotationsByType(CsvNumber.class),
+                    CsvNumber::profiles);
+            if(annotation == null) {
+                throw new CsvBadConverterException(CsvNumber.class, String.format(
+                        ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME).getString("profile.not.found.number"),
+                        profile));
+            }
             String readFormat = annotation.value();
             String writeFormat = annotation.writeFormatEqualsReadFormat()
                     ? readFormat : annotation.writeFormat();
@@ -729,38 +813,99 @@ abstract public class AbstractMappingStrategy<I, K extends Comparable<K>, C exte
                     errorLocale, readFormat, writeFormat);
         }
 
-        // Otherwise it must be a primitive or enumeration
+        // or a Currency
+        else if (elementType.equals(java.util.Currency.class)){
+            converter = new ConverterCurrency(errorLocale);
+        }
+
+        // Or an enumeration
+        else if (elementType.isEnum()) {
+            converter = new ConverterEnum(elementType, locale, writeLocale, errorLocale);
+        }
+
+        // or an UUID
+        else if (elementType.equals(UUID.class)) {
+            converter = new ConverterUUID(errorLocale);
+        }
+        // Otherwise a primitive
         else {
-            if(elementType.isEnum()) {
-                converter = new ConverterEnum(
-                        elementType, locale, writeLocale, errorLocale);
-            }
-            else {
-                converter = new ConverterPrimitiveTypes(
-                        elementType, locale, writeLocale, errorLocale);
-            }
+            converter = new ConverterPrimitiveTypes(elementType, locale, writeLocale, errorLocale);
         }
+
         return converter;
     }
 
+    /**
+     * Determines which one of a list of annotations applies to the currently
+     * selected profile.
+     * If no annotation specific to the profile is found, the annotation for
+     * the default profile is returned. If neither is found, {@code null} is
+     * returned.
+     *
+     * @param annotations All annotations of a given type
+     * @param getProfiles A function mapping an annotation of type {@code A} to
+     *                    the list of profiles it applies to
+     * @param <A> The annotation type being tested
+     * @return The annotation with the appropriate profile or {@code null} if
+     *   nothing appropriate is found
+     * @since 5.4
+     */
+    protected <A extends Annotation> A selectAnnotationForProfile(A[] annotations, Function<A, String[]> getProfiles) {
+        A defaultAnnotation = null;
+        String[] profilesForAnnotation;
+        for(A annotation : annotations) {
+            profilesForAnnotation = getProfiles.apply(annotation);
+            for(String p : profilesForAnnotation) {
+                if(profile.equals(p)) {
+                    return annotation; // I know. Bad style. I think we can live with it once.
+                }
+                if(StringUtils.EMPTY.equals(p)) {
+                    defaultAnnotation = annotation;
+                }
+            }
+        }
+        return defaultAnnotation;
+    }
+
     /**
      * Encapsulates a bean type and all of the member variables that need to be
      * recursed into.
      */
-    private static class RecursiveType {
+    protected static class RecursiveType {
         private final Class<?> type;
         private final Map<FieldAccess<Object>, RecursiveType> recursiveMembers = new HashMap<>();
 
+        /**
+         * Constructs a {@link RecursiveType} with the specified type.
+         *
+         * @param type Type associated with this branch
+         */
         RecursiveType(Class<?> type) {
             this.type = type;
         }
 
-        public Class<?> getType() {return type;}
+        /**
+         * @return Type associated with this branch
+         */
+        public Class<?> getType() {
+            return type;
+        }
 
-        public RecursiveType addRecursiveMember(FieldAccess<Object> member, RecursiveType memberType) {
-            return recursiveMembers.put(member, memberType);
+        /**
+         * Used to add a recursive type.
+         *
+         * @param member     Field access member to add a recursive type to
+         * @param memberType {@link RecursiveType} to add
+         */
+        public void addRecursiveMember(FieldAccess<Object> member, RecursiveType memberType) {
+            recursiveMembers.put(member, memberType);
         }
 
-        public Map<FieldAccess<Object>, RecursiveType> getRecursiveMembers() {return recursiveMembers;}
+        /**
+         * @return {@link Map} of field access to {@link RecursiveType}.
+         */
+        public Map<FieldAccess<Object>, RecursiveType> getRecursiveMembers() {
+            return recursiveMembers;
+        }
     }
 }
diff --git a/src/main/java/com/opencsv/bean/BeanVerifier.java b/src/main/java/com/opencsv/bean/BeanVerifier.java
index 7ef78f5..c7e1ab2 100644
--- a/src/main/java/com/opencsv/bean/BeanVerifier.java
+++ b/src/main/java/com/opencsv/bean/BeanVerifier.java
@@ -10,6 +10,7 @@ import com.opencsv.exceptions.CsvConstraintViolationException;
  *
  * @param <T> The type of bean being verified
  * @since 4.4
+ * @author Andrew Rucker Jones
  */
 public interface BeanVerifier<T> {
 
diff --git a/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategy.java b/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategy.java
index fa38071..e531203 100644
--- a/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategy.java
@@ -4,9 +4,7 @@ import com.opencsv.CSVReader;
 import com.opencsv.ICSVParser;
 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
 import org.apache.commons.collections4.ListValuedMap;
-import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ObjectUtils;
 
 import java.io.IOException;
 import java.lang.annotation.Annotation;
@@ -50,7 +48,8 @@ public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<St
     private Integer[] columnIndexForWriting = null;
 
     /**
-     * Default constructor.
+     * Default constructor. Considered stable.
+     * @see ColumnPositionMappingStrategyBuilder
      */
     public ColumnPositionMappingStrategy() {
     }
@@ -70,7 +69,7 @@ public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<St
                     .getString("type.unset"));
         }
 
-        String[] firstLine = ObjectUtils.defaultIfNull(reader.peek(), ArrayUtils.EMPTY_STRING_ARRAY);
+        String[] firstLine = ArrayUtils.nullToEmpty(reader.peek());
         fieldMap.setMaxIndex(firstLine.length - 1);
         if (!columnsExplicitlySet) {
             headerIndex.clear();
@@ -164,6 +163,84 @@ public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<St
         }
     }
 
+    /**
+     * Register a binding between a bean field and a custom converter.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerCustomBinding(CsvCustomBindByPosition annotation, Class<?> localType, Field localField) {
+        @SuppressWarnings("unchecked")
+        Class<? extends AbstractBeanField<T, Integer>> converter = (Class<? extends AbstractBeanField<T, Integer>>)annotation.converter();
+        BeanField<T, Integer> bean = instantiateCustomConverter(converter);
+        bean.setType(localType);
+        bean.setField(localField);
+        bean.setRequired(annotation.required());
+        fieldMap.put(annotation.position(), bean);
+    }
+
+    /**
+     * Register a binding between a bean field and a collection converter that
+     * splits input into multiple values.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerSplitBinding(CsvBindAndSplitByPosition annotation, Class<?> localType, Field localField) {
+        String fieldLocale = annotation.locale();
+        String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
+                ? fieldLocale
+                : annotation.writeLocale();
+        Class<?> elementType = annotation.elementType();
+        CsvConverter converter = determineConverter(localField, elementType,
+                fieldLocale, fieldWriteLocale, annotation.converter());
+        fieldMap.put(annotation.position(), new BeanFieldSplit<>(
+                localType, localField, annotation.required(), errorLocale, converter,
+                annotation.splitOn(), annotation.writeDelimiter(),
+                annotation.collectionType(), elementType, annotation.capture(),
+                annotation.format()));
+    }
+
+    /**
+     * Register a binding between a bean field and a multi-valued converter
+     * that joins values from multiple columns.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerJoinBinding(CsvBindAndJoinByPosition annotation, Class<?> localType, Field localField) {
+        String fieldLocale = annotation.locale();
+        String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
+                ? fieldLocale
+                : annotation.writeLocale();
+        CsvConverter converter = determineConverter(localField, annotation.elementType(),
+                fieldLocale, fieldWriteLocale, annotation.converter());
+        fieldMap.putComplex(annotation.position(), new BeanFieldJoinIntegerIndex<>(
+                localType, localField, annotation.required(), errorLocale, converter,
+                annotation.mapType(), annotation.capture(), annotation.format()));
+    }
+
+    /**
+     * Register a binding between a bean field and a simple converter.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerBinding(CsvBindByPosition annotation, Class<?> localType, Field localField) {
+        String fieldLocale = annotation.locale();
+        String fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
+                ? fieldLocale
+                : annotation.writeLocale();
+        CsvConverter converter = determineConverter(localField, localField.getType(), fieldLocale, fieldWriteLocale, null);
+        fieldMap.put(annotation.position(), new BeanFieldSingleValue<>(
+                localType, localField, annotation.required(), errorLocale,
+                converter, annotation.capture(), annotation.format()));
+    }
+
     /**
      * Creates a map of annotated fields in the bean to be processed.
      * <p>This method is called by {@link #loadFieldMap()} when at least one
@@ -171,81 +248,51 @@ public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<St
      */
     @Override
     protected void loadAnnotatedFieldMap(ListValuedMap<Class<?>, Field> fields) {
-        boolean required;
         for (Map.Entry<Class<?>, Field> classAndField : fields.entries()) {
             Class<?> localType = classAndField.getKey();
             Field localField = classAndField.getValue();
-            String fieldLocale, fieldWriteLocale, capture, format;
 
             // Custom converters always have precedence.
-            if (localField.isAnnotationPresent(CsvCustomBindByPosition.class)) {
-                CsvCustomBindByPosition annotation = localField
-                        .getAnnotation(CsvCustomBindByPosition.class);
-                @SuppressWarnings("unchecked")
-                Class<? extends AbstractBeanField<T, Integer>> converter = (Class<? extends AbstractBeanField<T, Integer>>)annotation.converter();
-                BeanField<T, Integer> bean = instantiateCustomConverter(converter);
-                bean.setType(localType);
-                bean.setField(localField);
-                required = annotation.required();
-                bean.setRequired(required);
-                fieldMap.put(annotation.position(), bean);
+            if (localField.isAnnotationPresent(CsvCustomBindByPosition.class)
+                    || localField.isAnnotationPresent(CsvCustomBindByPositions.class)) {
+                CsvCustomBindByPosition annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvCustomBindByPosition.class),
+                        CsvCustomBindByPosition::profiles);
+                if (annotation != null) {
+                    registerCustomBinding(annotation, localType, localField);
+                }
             }
 
             // Then check for a collection
-            else if (localField.isAnnotationPresent(CsvBindAndSplitByPosition.class)) {
-                CsvBindAndSplitByPosition annotation = localField.getAnnotation(CsvBindAndSplitByPosition.class);
-                required = annotation.required();
-                fieldLocale = annotation.locale();
-                fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? fieldLocale
-                        : annotation.writeLocale();
-                String splitOn = annotation.splitOn();
-                String writeDelimiter = annotation.writeDelimiter();
-                Class<? extends Collection> collectionType = annotation.collectionType();
-                Class<?> elementType = annotation.elementType();
-                Class<? extends AbstractCsvConverter> splitConverter = annotation.converter();
-                capture = annotation.capture();
-                format = annotation.format();
-
-                CsvConverter converter = determineConverter(localField, elementType, fieldLocale, fieldWriteLocale, splitConverter);
-                fieldMap.put(annotation.position(), new BeanFieldSplit<>(
-                        localType, localField, required, errorLocale, converter, splitOn,
-                        writeDelimiter, collectionType, elementType, capture, format));
+            else if (localField.isAnnotationPresent(CsvBindAndSplitByPosition.class)
+                    || localField.isAnnotationPresent(CsvBindAndSplitByPositions.class)) {
+                CsvBindAndSplitByPosition annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindAndSplitByPosition.class),
+                        CsvBindAndSplitByPosition::profiles);
+                if (annotation != null) {
+                    registerSplitBinding(annotation, localType, localField);
+                }
             }
 
             // Then check for a multi-column annotation
-            else if (localField.isAnnotationPresent(CsvBindAndJoinByPosition.class)) {
-                CsvBindAndJoinByPosition annotation = localField.getAnnotation(CsvBindAndJoinByPosition.class);
-                required = annotation.required();
-                fieldLocale = annotation.locale();
-                fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? fieldLocale
-                        : annotation.writeLocale();
-                Class<?> elementType = annotation.elementType();
-                Class<? extends MultiValuedMap> mapType = annotation.mapType();
-                Class<? extends AbstractCsvConverter> joinConverter = annotation.converter();
-                capture = annotation.capture();
-                format = annotation.format();
-
-                CsvConverter converter = determineConverter(localField, elementType, fieldLocale, fieldWriteLocale, joinConverter);
-                fieldMap.putComplex(annotation.position(), new BeanFieldJoinIntegerIndex<>(
-                        localType, localField, required, errorLocale, converter, mapType, capture, format));
+            else if (localField.isAnnotationPresent(CsvBindAndJoinByPosition.class)
+                    || localField.isAnnotationPresent(CsvBindAndJoinByPositions.class)) {
+                CsvBindAndJoinByPosition annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindAndJoinByPosition.class),
+                        CsvBindAndJoinByPosition::profiles);
+                if (annotation != null) {
+                    registerJoinBinding(annotation, localType, localField);
+                }
             }
 
             // Then it must be a bind by position.
             else {
-                CsvBindByPosition annotation = localField.getAnnotation(CsvBindByPosition.class);
-                required = annotation.required();
-                fieldLocale = annotation.locale();
-                fieldWriteLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? fieldLocale
-                        : annotation.writeLocale();
-                capture = annotation.capture();
-                format = annotation.format();
-                CsvConverter converter = determineConverter(localField, localField.getType(), fieldLocale, fieldWriteLocale, null);
-
-                fieldMap.put(annotation.position(), new BeanFieldSingleValue<>(
-                        localType, localField, required, errorLocale, converter, capture, format));
+                CsvBindByPosition annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindByPosition.class),
+                        CsvBindByPosition::profiles);
+                if (annotation != null) {
+                    registerBinding(annotation, localType, localField);
+                }
             }
         }
     }
@@ -278,6 +325,10 @@ public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<St
     protected Set<Class<? extends Annotation>> getBindingAnnotations() {
         // With Java 9 this can be done more easily with Set.of()
         return new HashSet<>(Arrays.asList(
+                CsvBindByPositions.class,
+                CsvCustomBindByPositions.class,
+                CsvBindAndJoinByPositions.class,
+                CsvBindAndSplitByPositions.class,
                 CsvBindByPosition.class,
                 CsvCustomBindByPosition.class,
                 CsvBindAndJoinByPosition.class,
diff --git a/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategyBuilder.java b/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategyBuilder.java
new file mode 100644
index 0000000..6aafdf8
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/ColumnPositionMappingStrategyBuilder.java
@@ -0,0 +1,25 @@
+package com.opencsv.bean;
+
+/**
+ * Builder for a {@link ColumnPositionMappingStrategy}.
+ * This allows opencsv to introduce new options for mapping strategies
+ * while maintaining backward compatibility and without creating
+ * reams of constructors for the mapping strategy.
+ *
+ * @param <T> The type of the bean being processed
+ * @since 5.5
+ * @author Andrew Rucker Jones
+ */
+public class ColumnPositionMappingStrategyBuilder<T> {
+
+    /** Default constructor. */
+    public ColumnPositionMappingStrategyBuilder() {}
+
+    /**
+     * Builds a new mapping strategy for parsing/writing.
+     * @return A new mapping strategy using the options selected
+     */
+    public ColumnPositionMappingStrategy<T> build() {
+        return new ColumnPositionMappingStrategy<>();
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/ConverterCurrency.java b/src/main/java/com/opencsv/bean/ConverterCurrency.java
new file mode 100644
index 0000000..b3e3a33
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/ConverterCurrency.java
@@ -0,0 +1,66 @@
+package com.opencsv.bean;
+
+import com.opencsv.ICSVParser;
+import com.opencsv.exceptions.CsvDataTypeMismatchException;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Currency;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * This class converts an input ISO 4217 currency code to a {@link java.util.Currency}
+ * instance.
+ *
+ * @author Andrew Munn
+ * @since 5.3
+ */
+public class ConverterCurrency extends AbstractCsvConverter {
+
+    /**
+     * Initializes the class.
+     * @param errorLocale     The locale to use for error messages
+     */
+    public ConverterCurrency(Locale errorLocale) {
+        super(Currency.class, null, null, errorLocale);
+    }
+
+    /**
+     * @param value The ISO 4217 currency code string to be converted
+     * @return {@link java.util.Currency} instance
+     */
+    @Override
+    public Object convertToRead(String value) throws CsvDataTypeMismatchException {
+        Currency c = null;
+        if (StringUtils.isNotEmpty(value)) {
+            try {
+                c = Currency.getInstance(value);
+            } catch (IllegalArgumentException e) {
+                CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(value, type, String.format(
+                        ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME).getString("invalid.currency.value"),
+                        value, type.getName()));
+                csve.initCause(e);
+                throw csve;
+            }
+
+        }
+        return c;
+    }
+
+    /**
+     * Converts {@link java.util.Currency} instance to a string.
+     *
+     * @param value The {@link java.util.Currency} instance
+     * @return ISO 4217 currency code or {@code null} if value was {@code null}
+     * @throws CsvDataTypeMismatchException If the value is not a {@link java.util.Currency}
+     */
+    @Override
+    public String convertToWrite(Object value) throws CsvDataTypeMismatchException {
+        String result = null;
+        if (value != null) {
+            Currency c = (Currency) value;
+            result = c.getCurrencyCode();
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/opencsv/bean/ConverterUUID.java b/src/main/java/com/opencsv/bean/ConverterUUID.java
new file mode 100644
index 0000000..bddb1a0
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/ConverterUUID.java
@@ -0,0 +1,43 @@
+package com.opencsv.bean;
+
+import com.opencsv.ICSVParser;
+import com.opencsv.exceptions.CsvDataTypeMismatchException;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+/**
+ * This class converts an String to a {@link java.util.UUID}
+ * instance.
+ *
+ * @author Scott Conway
+ * @since 5.4
+ */
+public class ConverterUUID extends AbstractCsvConverter {
+    private static final String UUID_REGEX_PATTERN = "\\b[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\\b[0-9a-fA-F]{12}\\b";
+    /**
+     * Initializes the class.
+     *
+     * @param errorLocale The locale to use for error messages
+     */
+    public ConverterUUID(Locale errorLocale) {
+        super(UUID.class, null, null, errorLocale);
+    }
+
+    @Override
+    public Object convertToRead(String value) throws CsvDataTypeMismatchException {
+        if (StringUtils.isBlank(value)) {
+            return null;
+        }
+        String trimmedString = value.trim();
+        if (!Pattern.matches(UUID_REGEX_PATTERN, trimmedString)) {
+            throw new CsvDataTypeMismatchException(value, type, String.format(
+                    ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME).getString("invalid.uuid.value"),
+                    value, type.getName()));
+        }
+        return UUID.fromString(trimmedString);
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndJoinByName.java b/src/main/java/com/opencsv/bean/CsvBindAndJoinByName.java
index 4ec7f85..8df3b33 100644
--- a/src/main/java/com/opencsv/bean/CsvBindAndJoinByName.java
+++ b/src/main/java/com/opencsv/bean/CsvBindAndJoinByName.java
@@ -30,6 +30,7 @@ import org.apache.commons.collections4.MultiValuedMap;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindAndJoinByNames.class)
 public @interface CsvBindAndJoinByName {
 
     /**
@@ -213,4 +214,29 @@ public @interface CsvBindAndJoinByName {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndJoinByNames.java b/src/main/java/com/opencsv/bean/CsvBindAndJoinByNames.java
new file mode 100644
index 0000000..18c6d00
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindAndJoinByNames.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindAndJoinByName}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindAndJoinByNames {
+    /** @return An array of {@link CsvBindAndJoinByName}. */
+    CsvBindAndJoinByName[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndJoinByPosition.java b/src/main/java/com/opencsv/bean/CsvBindAndJoinByPosition.java
index 86207be..8fe0a3f 100644
--- a/src/main/java/com/opencsv/bean/CsvBindAndJoinByPosition.java
+++ b/src/main/java/com/opencsv/bean/CsvBindAndJoinByPosition.java
@@ -30,6 +30,7 @@ import org.apache.commons.collections4.MultiValuedMap;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindAndJoinByPositions.class)
 public @interface CsvBindAndJoinByPosition {
 
     /**
@@ -214,4 +215,29 @@ public @interface CsvBindAndJoinByPosition {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndJoinByPositions.java b/src/main/java/com/opencsv/bean/CsvBindAndJoinByPositions.java
new file mode 100644
index 0000000..8ae6353
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindAndJoinByPositions.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindAndJoinByPosition}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindAndJoinByPositions {
+    /** @return An array of {@link CsvBindAndJoinByPosition}. */
+    CsvBindAndJoinByPosition[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndSplitByName.java b/src/main/java/com/opencsv/bean/CsvBindAndSplitByName.java
index 4d1cdeb..a3fe0b5 100644
--- a/src/main/java/com/opencsv/bean/CsvBindAndSplitByName.java
+++ b/src/main/java/com/opencsv/bean/CsvBindAndSplitByName.java
@@ -29,6 +29,7 @@ import java.util.regex.Matcher;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindAndSplitByNames.class)
 public @interface CsvBindAndSplitByName {
 
     /**
@@ -234,4 +235,29 @@ public @interface CsvBindAndSplitByName {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndSplitByNames.java b/src/main/java/com/opencsv/bean/CsvBindAndSplitByNames.java
new file mode 100644
index 0000000..c09e3c4
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindAndSplitByNames.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindAndSplitByName}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindAndSplitByNames {
+    /** @return An array of {@link CsvBindAndSplitByName}. */
+    CsvBindAndSplitByName[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndSplitByPosition.java b/src/main/java/com/opencsv/bean/CsvBindAndSplitByPosition.java
index 9fd4217..3a58aae 100644
--- a/src/main/java/com/opencsv/bean/CsvBindAndSplitByPosition.java
+++ b/src/main/java/com/opencsv/bean/CsvBindAndSplitByPosition.java
@@ -29,6 +29,7 @@ import java.util.regex.Matcher;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindAndSplitByPositions.class)
 public @interface CsvBindAndSplitByPosition {
 
     /**
@@ -234,4 +235,29 @@ public @interface CsvBindAndSplitByPosition {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindAndSplitByPositions.java b/src/main/java/com/opencsv/bean/CsvBindAndSplitByPositions.java
new file mode 100644
index 0000000..1ed2cfb
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindAndSplitByPositions.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindAndSplitByPosition}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindAndSplitByPositions {
+    /** @return An array of {@link CsvBindAndSplitByPosition}. */
+    CsvBindAndSplitByPosition[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindByName.java b/src/main/java/com/opencsv/bean/CsvBindByName.java
index 668d38c..d245d80 100644
--- a/src/main/java/com/opencsv/bean/CsvBindByName.java
+++ b/src/main/java/com/opencsv/bean/CsvBindByName.java
@@ -28,6 +28,7 @@ import java.util.regex.Matcher;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindByNames.class)
 public @interface CsvBindByName {
 
     /**
@@ -155,4 +156,29 @@ public @interface CsvBindByName {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindByNames.java b/src/main/java/com/opencsv/bean/CsvBindByNames.java
new file mode 100644
index 0000000..e970569
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindByNames.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindByName}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindByNames {
+    /** @return An array of {@link CsvBindByName}. */
+    CsvBindByName[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvBindByPosition.java b/src/main/java/com/opencsv/bean/CsvBindByPosition.java
index 0b376f0..eee92c6 100644
--- a/src/main/java/com/opencsv/bean/CsvBindByPosition.java
+++ b/src/main/java/com/opencsv/bean/CsvBindByPosition.java
@@ -28,6 +28,7 @@ import java.util.regex.Matcher;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvBindByPositions.class)
 public @interface CsvBindByPosition {
 
     /**
@@ -155,4 +156,29 @@ public @interface CsvBindByPosition {
      * @since 4.3
      */
     String format() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvBindByPositions.java b/src/main/java/com/opencsv/bean/CsvBindByPositions.java
new file mode 100644
index 0000000..47edda4
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvBindByPositions.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvBindByPosition}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvBindByPositions {
+    /** @return An array of {@link CsvBindByPosition}. */
+    CsvBindByPosition[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvCustomBindByName.java b/src/main/java/com/opencsv/bean/CsvCustomBindByName.java
index 0e9b897..dc8465a 100644
--- a/src/main/java/com/opencsv/bean/CsvCustomBindByName.java
+++ b/src/main/java/com/opencsv/bean/CsvCustomBindByName.java
@@ -30,6 +30,7 @@ import java.lang.annotation.*;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvCustomBindByNames.class)
 public @interface CsvCustomBindByName {
 
     /**
@@ -62,4 +63,29 @@ public @interface CsvCustomBindByName {
      * @since 3.10
      */
     boolean required() default false;
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvCustomBindByNames.java b/src/main/java/com/opencsv/bean/CsvCustomBindByNames.java
new file mode 100644
index 0000000..eb609cf
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvCustomBindByNames.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvCustomBindByName}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvCustomBindByNames {
+    /** @return An array of {@link CsvCustomBindByName}. */
+    CsvCustomBindByName[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvCustomBindByPosition.java b/src/main/java/com/opencsv/bean/CsvCustomBindByPosition.java
index 1fe616d..0eae9cb 100644
--- a/src/main/java/com/opencsv/bean/CsvCustomBindByPosition.java
+++ b/src/main/java/com/opencsv/bean/CsvCustomBindByPosition.java
@@ -29,6 +29,7 @@ import java.lang.annotation.*;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvCustomBindByPositions.class)
 public @interface CsvCustomBindByPosition {
 
     /**
@@ -61,4 +62,29 @@ public @interface CsvCustomBindByPosition {
      * @since 3.10
      */
     boolean required() default false;
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvCustomBindByPositions.java b/src/main/java/com/opencsv/bean/CsvCustomBindByPositions.java
new file mode 100644
index 0000000..1b20d5e
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvCustomBindByPositions.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvCustomBindByPosition}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvCustomBindByPositions {
+    /** @return An array of {@link CsvCustomBindByPosition}. */
+    CsvCustomBindByPosition[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvDate.java b/src/main/java/com/opencsv/bean/CsvDate.java
index b15eb2c..807be2c 100644
--- a/src/main/java/com/opencsv/bean/CsvDate.java
+++ b/src/main/java/com/opencsv/bean/CsvDate.java
@@ -73,6 +73,7 @@ import java.lang.annotation.*;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvDates.class)
 public @interface CsvDate {
 
     /**
@@ -151,4 +152,29 @@ public @interface CsvDate {
      * @since 5.0
      */
     String writeChronology() default "ISO";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvDates.java b/src/main/java/com/opencsv/bean/CsvDates.java
new file mode 100644
index 0000000..7cba4fa
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvDates.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvDate}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvDates {
+    /** @return An array of {@link CsvDate}. */
+    CsvDate[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvIgnore.java b/src/main/java/com/opencsv/bean/CsvIgnore.java
index f59937d..4822a5b 100644
--- a/src/main/java/com/opencsv/bean/CsvIgnore.java
+++ b/src/main/java/com/opencsv/bean/CsvIgnore.java
@@ -19,4 +19,15 @@ import java.lang.annotation.*;
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 public @interface CsvIgnore {
+
+    /**
+     * The names of the profiles for which this field should be ignored.
+     * <p>If the field should be ignored for only some profiles, but not all,
+     * list the profiles that should ignore the field here.</p>
+     * <p>The default value is an empty string, which means all profiles.</p>
+     *
+     * @return The profiles for which this field should be ignored
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvNumber.java b/src/main/java/com/opencsv/bean/CsvNumber.java
index 3b427d5..eb17fbe 100644
--- a/src/main/java/com/opencsv/bean/CsvNumber.java
+++ b/src/main/java/com/opencsv/bean/CsvNumber.java
@@ -40,6 +40,7 @@ import java.lang.annotation.*;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Repeatable(CsvNumbers.class)
 public @interface CsvNumber {
 
     /**
@@ -76,4 +77,29 @@ public @interface CsvNumber {
      * @since 5.0
      */
     String writeFormat() default "";
+
+    /**
+     * A profile can be used to annotate the same field differently for
+     * different inputs or outputs.
+     * <p>Perhaps you have multiple input sources, and they all use different
+     * header names or positions for the same data. With profiles, you don't
+     * have to create different beans with the same fields and different
+     * annotations for each input. Simply annotate the same field multiple
+     * times and specify the profile when you parse the input.</p>
+     * <p>The same applies to output: if you want to be able to represent the
+     * same data in multiple CSV formats (that is, with different headers or
+     * orders), annotate the bean fields multiple times with different profiles
+     * and specify which profile you want to use on writing.</p>
+     * <p>Results are undefined if profile names are not unique.</p>
+     * <p>If the same configuration applies to multiple profiles, simply list
+     * all applicable profile names here. This parameter is an array of
+     * strings.</p>
+     * <p>The empty string, which is the default value, specifies the default
+     * profile and will be used if no annotation for the specific profile
+     * being used can be found, or if no profile is specified.</p>
+     *
+     * @return The names of the profiles this configuration is for
+     * @since 5.4
+     */
+    String[] profiles() default "";
 }
diff --git a/src/main/java/com/opencsv/bean/CsvNumbers.java b/src/main/java/com/opencsv/bean/CsvNumbers.java
new file mode 100644
index 0000000..bf835e4
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/CsvNumbers.java
@@ -0,0 +1,16 @@
+package com.opencsv.bean;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation is the container annotation for {@link CsvNumber}.
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface CsvNumbers {
+    /** @return An array of {@link CsvNumber}. */
+    CsvNumber[] value();
+}
diff --git a/src/main/java/com/opencsv/bean/CsvToBean.java b/src/main/java/com/opencsv/bean/CsvToBean.java
index 2bc8cb2..0dfc3ac 100644
--- a/src/main/java/com/opencsv/bean/CsvToBean.java
+++ b/src/main/java/com/opencsv/bean/CsvToBean.java
@@ -34,6 +34,7 @@ import java.io.IOException;
 import java.util.*;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -48,30 +49,38 @@ import java.util.stream.StreamSupport;
  * @param <T> Class to convert the objects to.
  */
 public class CsvToBean<T> implements Iterable<T> {
-    
-   /** A list of all exceptions during parsing and mapping of the input. */
+
+    /**
+     * A list of all exceptions during parsing and mapping of the input.
+     */
     private final List<CsvException> capturedExceptions = new LinkedList<>();
 
-   /** The mapping strategy to be used by this CsvToBean. */
+    /**
+     * The mapping strategy to be used by this CsvToBean.
+     */
     private MappingStrategy<? extends T> mappingStrategy;
 
-   /** The reader this class will use to access the data to be read. */
+    /**
+     * The reader this class will use to access the data to be read.
+     */
     private CSVReader csvReader;
 
-   /** The filter this class will use on the beans it reads. */
+    /**
+     * The filter this class will use on the beans it reads.
+     */
     private CsvToBeanFilter filter = null;
 
     /**
      * Determines how exceptions thrown during processing will be handled.
      */
     private CsvExceptionHandler exceptionHandler = new ExceptionHandlerThrow();
-    
+
     /**
      * Determines whether resulting data sets have to be in the same order as
      * the input.
      */
     private boolean orderedResults = true;
-    
+
     /**
      * The {@link java.util.concurrent.ExecutorService} for parallel processing
      * of beans.
@@ -126,7 +135,7 @@ public class CsvToBean<T> implements Iterable<T> {
      *
      * @return A stream of populated beans based on the input
      * @throws IllegalStateException If either MappingStrategy or CSVReader is
-     *   not specified
+     *                               not specified
      * @see #parse()
      * @see #iterator()
      */
@@ -143,10 +152,12 @@ public class CsvToBean<T> implements Iterable<T> {
     /**
      * Returns the list of all exceptions that would have been thrown during the
      * import, but were queued by the exception handler.
+     * <p>The results returned by this method are not consistent until parsing
+     * is concluded.</p>
      *
      * @return The list of exceptions captured while processing the input file
      * @see #setExceptionHandler(CsvExceptionHandler)
-     * @see #setThrowExceptions(boolean) 
+     * @see #setThrowExceptions(boolean)
      */
     public List<CsvException> getCapturedExceptions() {
         // The exceptions are stored in different places, dependent on
@@ -156,6 +167,7 @@ public class CsvToBean<T> implements Iterable<T> {
 
     /**
      * Sets the mapping strategy to be used by this bean.
+     *
      * @param mappingStrategy Mapping strategy to convert CSV input to a bean
      */
     public void setMappingStrategy(MappingStrategy<? extends T> mappingStrategy) {
@@ -164,6 +176,7 @@ public class CsvToBean<T> implements Iterable<T> {
 
     /**
      * Sets the reader to be used to read in the information from the CSV input.
+     *
      * @param csvReader Reader for input
      */
     public void setCsvReader(CSVReader csvReader) {
@@ -173,6 +186,7 @@ public class CsvToBean<T> implements Iterable<T> {
     /**
      * Sets a filter to selectively remove some lines of input before they
      * become beans.
+     *
      * @param filter A class that filters the input lines
      */
     public void setFilter(CsvToBeanFilter filter) {
@@ -192,14 +206,13 @@ public class CsvToBean<T> implements Iterable<T> {
      * the last call wins.</p>
      *
      * @param throwExceptions Whether or not to throw exceptions during
-     *   processing
+     *                        processing
      * @see #setExceptionHandler(CsvExceptionHandler)
      */
     public void setThrowExceptions(boolean throwExceptions) {
-        if(throwExceptions) {
+        if (throwExceptions) {
             exceptionHandler = new ExceptionHandlerThrow();
-        }
-        else {
+        } else {
             exceptionHandler = new ExceptionHandlerQueue();
         }
     }
@@ -218,11 +231,20 @@ public class CsvToBean<T> implements Iterable<T> {
      * @since 5.2
      */
     public void setExceptionHandler(CsvExceptionHandler handler) {
-        if(handler != null) {
+        if (handler != null) {
             exceptionHandler = handler;
         }
     }
-    
+
+    /**
+     * Package scope method currently used by the CsvToBeanBuilderTest
+     *
+     * @return CsvExceptionHandler used by the CsvToBean object.
+     */
+    CsvExceptionHandler getExceptionHandler() {
+        return exceptionHandler;
+    }
+
     /**
      * Sets whether or not results must be returned in the same order in which
      * they appear in the input.
@@ -231,26 +253,28 @@ public class CsvToBean<T> implements Iterable<T> {
      * {@code orderedResults} to {@code false}. The lack of ordering then also
      * applies to any captured exceptions, if you have chosen not to have
      * exceptions thrown.
+     *
      * @param orderedResults Whether or not the beans returned are in the same
-     *   order they appeared in the input
+     *                       order they appeared in the input
      * @since 4.0
      */
     public void setOrderedResults(boolean orderedResults) {
         this.orderedResults = orderedResults;
     }
-    
+
     /**
      * Sets the locale for error messages.
+     *
      * @param errorLocale Locale for error messages. If null, the default locale
-     *   is used.
+     *                    is used.
      * @since 4.0
      */
     public void setErrorLocale(Locale errorLocale) {
         this.errorLocale = ObjectUtils.defaultIfNull(errorLocale, Locale.getDefault());
-        if(csvReader != null) {
+        if (csvReader != null) {
             csvReader.setErrorLocale(this.errorLocale);
         }
-        if(mappingStrategy != null) {
+        if (mappingStrategy != null) {
             mappingStrategy.setErrorLocale(this.errorLocale);
         }
     }
@@ -265,11 +289,11 @@ public class CsvToBean<T> implements Iterable<T> {
     public void setVerifiers(List<BeanVerifier<T>> verifiers) {
         this.verifiers = ObjectUtils.defaultIfNull(verifiers, Collections.<BeanVerifier<T>>emptyList());
     }
-    
+
     private void prepareToReadInput() throws IllegalStateException {
         // First verify that the user hasn't failed to give us the information
         // we need to do his or her work for him or her.
-        if(mappingStrategy == null || csvReader == null) {
+        if (mappingStrategy == null || csvReader == null) {
             throw new IllegalStateException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("specify.strategy.reader"));
         }
 
@@ -280,7 +304,7 @@ public class CsvToBean<T> implements Iterable<T> {
             throw new RuntimeException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("header.error"), e);
         }
     }
-    
+
     /**
      * The iterator returned by this method takes one line of input at a time
      * and returns one bean at a time.
@@ -288,6 +312,7 @@ public class CsvToBean<T> implements Iterable<T> {
      * parallel processing, reducing throughput.</p>
      * <p>The iterator respects all aspects of {@link CsvToBean}, including
      * filters and capturing exceptions.</p>
+     *
      * @return An iterator over the beans created from the input
      * @see #parse()
      * @see #stream()
@@ -311,31 +336,32 @@ public class CsvToBean<T> implements Iterable<T> {
      * A private inner class for implementing an iterator for the input data.
      */
     private class CsvToBeanIterator implements Iterator<T> {
-        private BlockingQueue<OrderedObject<T>> resultantBeansQueue;
-        private BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue;
+        private final BlockingQueue<OrderedObject<T>> resultantBeansQueue;
+        private final BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue;
         private final SingleLineReader lineReader = new SingleLineReader(csvReader, ignoreEmptyLines);
         private String[] line = null;
         private long lineProcessed = 0;
         private T bean;
-        
+
         CsvToBeanIterator() {
             resultantBeansQueue = new ArrayBlockingQueue<>(1);
-            thrownExceptionsQueue = new ArrayBlockingQueue<>(1);
+            thrownExceptionsQueue = new LinkedBlockingQueue<>();
             readSingleLine();
         }
-        
+
         private void processException() {
-            // An exception was thrown
+            // At least one exception was thrown
             OrderedObject<CsvException> o = thrownExceptionsQueue.poll();
-            if(o != null && o.getElement() != null) {
+            while (o != null && o.getElement() != null) {
                 capturedExceptions.add(o.getElement());
+                o = thrownExceptionsQueue.poll();
             }
         }
 
         private void readLineWithPossibleError() throws IOException, CsvValidationException {
             // Read a line
             bean = null;
-            while(bean == null && null != (line = lineReader.readNextLine())) {
+            while (bean == null && null != (line = lineReader.readNextLine())) {
                 lineProcessed = lineReader.getLinesRead();
 
                 // Create a bean
@@ -351,10 +377,10 @@ public class CsvToBean<T> implements Iterable<T> {
                     // No exception, so there really must always be a bean
                     // . . . unless it was filtered
                     OrderedObject<T> o = resultantBeansQueue.poll();
-                    bean = o==null?null:o.getElement();
+                    bean = o == null ? null : o.getElement();
                 }
             }
-            if(line == null) {
+            if (line == null) {
                 // There isn't any more
                 bean = null;
             }
@@ -377,14 +403,14 @@ public class CsvToBean<T> implements Iterable<T> {
 
         @Override
         public T next() {
-            if(bean == null) {
+            if (bean == null) {
                 throw new NoSuchElementException();
             }
             T intermediateBean = bean;
             readSingleLine();
             return intermediateBean;
         }
-        
+
         @Override
         public void remove() {
             throw new UnsupportedOperationException(ResourceBundle
diff --git a/src/main/java/com/opencsv/bean/CsvToBeanBuilder.java b/src/main/java/com/opencsv/bean/CsvToBeanBuilder.java
index ce7e071..dca4fa6 100644
--- a/src/main/java/com/opencsv/bean/CsvToBeanBuilder.java
+++ b/src/main/java/com/opencsv/bean/CsvToBeanBuilder.java
@@ -17,7 +17,6 @@ package com.opencsv.bean;
 
 import com.opencsv.*;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
-import com.opencsv.bean.exceptionhandler.ExceptionHandlerQueue;
 import com.opencsv.bean.exceptionhandler.ExceptionHandlerThrow;
 import com.opencsv.bean.util.OpencsvUtils;
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
@@ -25,6 +24,7 @@ import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.Reader;
 import java.lang.reflect.Field;
@@ -75,8 +75,10 @@ public class CsvToBeanBuilder<T> {
    /** @see CsvToBean#filter */
    private CsvToBeanFilter filter = null;
    
-   /** @see CsvToBean#throwExceptions */
-   private CsvExceptionHandler exceptionHandler = new ExceptionHandlerThrow();
+   /**
+    * @see CsvToBean#throwExceptions
+    */
+   private CsvExceptionHandler exceptionHandler = null;
    
    /** @see com.opencsv.CSVParser#nullFieldIndicator */
    private CSVReaderNullFieldIndicator nullFieldIndicator = null;
@@ -98,15 +100,26 @@ public class CsvToBeanBuilder<T> {
    
    /** @see com.opencsv.CSVParser#escape */
    private Character escapeChar = null;
-   
-   /** @see com.opencsv.CSVParser#strictQuotes */
-   private Boolean strictQuotes = null;
-   
-   /** @see com.opencsv.CSVParser#ignoreLeadingWhiteSpace */
-   private Boolean ignoreLeadingWhiteSpace = null;
-   
-   /** @see com.opencsv.CSVParser#ignoreQuotations */
-   private Boolean ignoreQuotations = null;
+
+    /**
+     * @see com.opencsv.CSVParser#strictQuotes
+     */
+    private Boolean strictQuotes = null;
+
+    /**
+     * @see com.opencsv.CSVParser#ignoreLeadingWhiteSpace
+     */
+    private Boolean ignoreLeadingWhiteSpace = null;
+
+    /**
+     * @see com.opencsv.CSVParser#ignoreQuotations
+     */
+    private Boolean ignoreQuotations = null;
+
+    /**
+     * @see com.opencsv.bean.CsvToBean#setThrowExceptions(boolean)
+     */
+    private Boolean throwsExceptions = true;
 
     /**
      * @see HeaderColumnNameMappingStrategy#type
@@ -142,7 +155,10 @@ public class CsvToBeanBuilder<T> {
      * @see com.opencsv.bean.AbstractMappingStrategy#ignoredFields
      */
     private final ListValuedMap<Class<?>, Field> ignoredFields = new ArrayListValuedHashMap<>();
-   
+
+    /** @see com.opencsv.bean.AbstractMappingStrategy#profile */
+    private String profile = StringUtils.EMPTY;
+
    /**
     * Constructor with the one parameter that is most definitely mandatory, and
     * always will be.
@@ -186,7 +202,7 @@ public class CsvToBeanBuilder<T> {
         if(mappingStrategy == null && type == null) {
             throw new IllegalStateException(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("strategy.type.missing"));
         }
-        
+
         // Build Parser and Reader
         CsvToBean<T> bean = new CsvToBean<>();
 
@@ -198,16 +214,24 @@ public class CsvToBeanBuilder<T> {
         }
 
         // Set variables in CsvToBean itself
-        bean.setExceptionHandler(exceptionHandler);
+
+        if (exceptionHandler != null) {
+            bean.setExceptionHandler(exceptionHandler);
+        } else {
+            bean.setThrowExceptions(throwsExceptions);
+        }
+
         bean.setOrderedResults(orderedResults);
-        if(filter != null) { bean.setFilter(filter); }
+        if (filter != null) {
+            bean.setFilter(filter);
+        }
         bean.setVerifiers(verifiers);
-        
+
         // Now find the mapping strategy and ignore irrelevant fields.
         // It's possible the mapping strategy has already been primed, so only
         // pass on our data if the user actually gave us something.
         if(mappingStrategy == null) {
-            mappingStrategy = OpencsvUtils.determineMappingStrategy(type, errorLocale);
+            mappingStrategy = OpencsvUtils.determineMappingStrategy(type, errorLocale, profile);
         }
         if(!ignoredFields.isEmpty()) {
             mappingStrategy.ignoreFields(ignoredFields);
@@ -299,24 +323,24 @@ public class CsvToBeanBuilder<T> {
     }
 
     /**
+     * Sets how the CsvToBean will act when an exception occurs.   If both withThrowsExcpetion and
+     * {@link #withExceptionHandler(CsvExceptionHandler)} are used then the withExceptionHandler takes
+     * precedence and is used.
+     *
      * @see CsvToBean#setThrowExceptions(boolean)
      * @see #withExceptionHandler(CsvExceptionHandler)
      * @param throwExceptions Please see the "See Also" section
      * @return {@code this}
      */
     public CsvToBeanBuilder<T> withThrowExceptions(boolean throwExceptions) {
-        if(throwExceptions) {
-            this.exceptionHandler = new ExceptionHandlerThrow();
-        }
-        else {
-            this.exceptionHandler = new ExceptionHandlerQueue();
-        }
+        this.throwsExceptions = throwExceptions;
         return this;
     }
 
     /**
      * Sets the handler for recoverable exceptions raised during processing of
-     * records.
+     * records. If both {@link #withThrowExceptions(boolean)} and withExceptionHandler are used then the
+     * withExceptionHandler takes precedence and is used.
      * <p>If neither this method nor {@link #withThrowExceptions(boolean)} is
      * called, the default exception handler is
      * {@link ExceptionHandlerThrow}.</p>
@@ -541,4 +565,17 @@ public class CsvToBeanBuilder<T> {
         this.ignoreEmptyLines = ignore;
         return this;
     }
+
+    /**
+     * Selects a profile for deciding which configurations to use for the bean
+     * fields.
+     *
+     * @param profile The name of the profile to be used
+     * @return {@code this}
+     * @since 5.4
+     */
+    public CsvToBeanBuilder<T> withProfile(String profile) {
+        this.profile = profile;
+        return this;
+    }
 }
diff --git a/src/main/java/com/opencsv/bean/FuzzyMappingStrategy.java b/src/main/java/com/opencsv/bean/FuzzyMappingStrategy.java
index bcf8198..cc18bd6 100644
--- a/src/main/java/com/opencsv/bean/FuzzyMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/FuzzyMappingStrategy.java
@@ -26,11 +26,27 @@ import java.util.stream.Stream;
 public class FuzzyMappingStrategy<T> extends HeaderColumnNameMappingStrategy<T> {
 
     /**
-     * Nullary constructor to make the style checker happy.
+     * Nullary constructor. Considered stable.
+     * @see FuzzyMappingStrategyBuilder
      */
     public FuzzyMappingStrategy() {
     }
 
+    /**
+     * Constructor to allow setting options for header name mapping.
+     * Not considered stable. As new options are introduced for the mapping
+     * strategy, they will be introduced here. You are encouraged to use
+     * {@link FuzzyMappingStrategyBuilder}.
+     *
+     * @param forceCorrectRecordLength If set, every record will be shortened
+     *                                 or lengthened to match the number of
+     *                                 headers
+     * @see FuzzyMappingStrategyBuilder
+     */
+    public FuzzyMappingStrategy(boolean forceCorrectRecordLength) {
+        super(forceCorrectRecordLength);
+    }
+
     /**
      * This implementation intentionally does nothing in order to allow fuzzy
      * matching in case there are no annotations at all in the class in
diff --git a/src/main/java/com/opencsv/bean/FuzzyMappingStrategyBuilder.java b/src/main/java/com/opencsv/bean/FuzzyMappingStrategyBuilder.java
new file mode 100644
index 0000000..d0a338e
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/FuzzyMappingStrategyBuilder.java
@@ -0,0 +1,45 @@
+package com.opencsv.bean;
+
+/**
+ * Builder for a {@link FuzzyMappingStrategy}.
+ * This allows opencsv to introduce new options for mapping strategies
+ * while maintaining backward compatibility and without creating
+ * reams of constructors for the mapping strategy.
+ *
+ * @param <T> The type of the bean being processed
+ * @since 5.5
+ * @author Andrew Rucker Jones
+ */
+public class FuzzyMappingStrategyBuilder<T> {
+
+    private boolean forceCorrectRecordLength = false;
+
+    /** Default constructor. */
+    public FuzzyMappingStrategyBuilder() {}
+
+    /**
+     * Builds a new mapping strategy for parsing/writing.
+     * @return A new mapping strategy using the options selected
+     */
+    public FuzzyMappingStrategy<T> build() {
+        return new FuzzyMappingStrategy<>(forceCorrectRecordLength);
+    }
+
+    /**
+     * Insists that every record will be considered to be of the correct
+     * length (that is, the same number of columns as the header).
+     * <p>Excess fields at the end of a record will be ignored. Missing
+     * fields at the end of a record will be interpreted as {@code null}.
+     * This is only relevant on reading.</p>
+     * <p>If not set, incorrect record length will throw an exception. That
+     * is, the default value is {@code false}.</p>
+     *
+     * @param forceCorrectRecordLength Whether records should be forced to
+     *                                 the correct length
+     * @return {@code this}
+     */
+    public FuzzyMappingStrategyBuilder<T> withForceCorrectRecordLength(boolean forceCorrectRecordLength) {
+        this.forceCorrectRecordLength = forceCorrectRecordLength;
+        return this;
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategy.java b/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategy.java
index 12b4e75..fbe0e2f 100644
--- a/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategy.java
@@ -1,7 +1,6 @@
 package com.opencsv.bean;
 
 import org.apache.commons.collections4.ListValuedMap;
-import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.lang3.StringUtils;
 
 import java.lang.annotation.Annotation;
@@ -33,11 +32,146 @@ import java.util.*;
 public class HeaderColumnNameMappingStrategy<T> extends HeaderNameBaseMappingStrategy<T> {
 
     /**
-     * Default constructor.
+     * Default constructor. Considered stable.
+     * @see HeaderColumnNameMappingStrategyBuilder
      */
     public HeaderColumnNameMappingStrategy() {
     }
 
+    /**
+     * Constructor to allow setting options for header name mapping.
+     * Not considered stable. As new options are introduced for the mapping
+     * strategy, they will be introduced here. You are encouraged to use
+     * {@link HeaderColumnNameMappingStrategyBuilder}.
+     *
+     * @param forceCorrectRecordLength If set, every record will be shortened
+     *                                 or lengthened to match the number of
+     *                                 headers
+     * @see HeaderColumnNameMappingStrategyBuilder
+     */
+    public HeaderColumnNameMappingStrategy(boolean forceCorrectRecordLength) {
+        super(forceCorrectRecordLength);
+    }
+
+    /**
+     * Register a binding between a bean field and a custom converter.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerCustomBinding(CsvCustomBindByName annotation, Class<?> localType, Field localField) {
+        String columnName = annotation.column().toUpperCase().trim();
+        if(StringUtils.isEmpty(columnName)) {
+            columnName = localField.getName().toUpperCase();
+        }
+        @SuppressWarnings("unchecked")
+        Class<? extends AbstractBeanField<T, String>> converter = (Class<? extends AbstractBeanField<T, String>>)annotation
+                .converter();
+        BeanField<T, String> bean = instantiateCustomConverter(converter);
+        bean.setType(localType);
+        bean.setField(localField);
+        bean.setRequired(annotation.required());
+        fieldMap.put(columnName, bean);
+    }
+
+    /**
+     * Register a binding between a bean field and a collection converter that
+     * splits input into multiple values.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerSplitBinding(CsvBindAndSplitByName annotation, Class<?> localType, Field localField) {
+        String columnName = annotation.column().toUpperCase().trim();
+        String locale = annotation.locale();
+        String writeLocale = annotation.writeLocaleEqualsReadLocale()
+                ? locale : annotation.writeLocale();
+        Class<?> elementType = annotation.elementType();
+
+        CsvConverter converter = determineConverter(
+                localField, elementType, locale,
+                writeLocale, annotation.converter());
+        if (StringUtils.isEmpty(columnName)) {
+            fieldMap.put(localField.getName().toUpperCase(),
+                    new BeanFieldSplit<>(
+                            localType, localField, annotation.required(),
+                            errorLocale, converter, annotation.splitOn(),
+                            annotation.writeDelimiter(),
+                            annotation.collectionType(), elementType,
+                            annotation.capture(), annotation.format()));
+        } else {
+            fieldMap.put(columnName, new BeanFieldSplit<>(
+                    localType, localField, annotation.required(),
+                    errorLocale, converter, annotation.splitOn(),
+                    annotation.writeDelimiter(), annotation.collectionType(),
+                    elementType, annotation.capture(), annotation.format()));
+        }
+    }
+
+    /**
+     * Register a binding between a bean field and a multi-valued converter
+     * that joins values from multiple columns.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerJoinBinding(CsvBindAndJoinByName annotation, Class<?> localType, Field localField) {
+        String columnRegex = annotation.column();
+        String locale = annotation.locale();
+        String writeLocale = annotation.writeLocaleEqualsReadLocale()
+                ? locale : annotation.writeLocale();
+
+        CsvConverter converter = determineConverter(
+                localField, annotation.elementType(), locale,
+                writeLocale, annotation.converter());
+        if (StringUtils.isEmpty(columnRegex)) {
+            fieldMap.putComplex(localField.getName(),
+                    new BeanFieldJoinStringIndex<>(
+                            localType, localField, annotation.required(),
+                            errorLocale, converter, annotation.mapType(),
+                            annotation.capture(), annotation.format()));
+        } else {
+            fieldMap.putComplex(columnRegex, new BeanFieldJoinStringIndex<>(
+                    localType, localField, annotation.required(), errorLocale,
+                    converter, annotation.mapType(), annotation.capture(),
+                    annotation.format()));
+        }
+    }
+
+    /**
+     * Register a binding between a bean field and a simple converter.
+     *
+     * @param annotation The annotation attached to the bean field
+     * @param localType The class/type in which the field resides
+     * @param localField The bean field
+     */
+    private void registerBinding(CsvBindByName annotation, Class<?> localType, Field localField) {
+        String columnName = annotation.column().toUpperCase().trim();
+        String locale = annotation.locale();
+        String writeLocale = annotation.writeLocaleEqualsReadLocale()
+                ? locale : annotation.writeLocale();
+        CsvConverter converter = determineConverter(
+                localField,
+                localField.getType(), locale,
+                writeLocale, null);
+
+        if (StringUtils.isEmpty(columnName)) {
+            fieldMap.put(localField.getName().toUpperCase(),
+                    new BeanFieldSingleValue<>(
+                            localType, localField, annotation.required(),
+                            errorLocale, converter, annotation.capture(),
+                            annotation.format()));
+        } else {
+            fieldMap.put(columnName, new BeanFieldSingleValue<>(
+                    localType, localField, annotation.required(),
+                    errorLocale, converter, annotation.capture(),
+                    annotation.format()));
+        }
+    }
+
     /**
      * Creates a map of annotated fields in the bean to be processed.
      * <p>This method is called by {@link #loadFieldMap()} when at least one
@@ -45,129 +179,50 @@ public class HeaderColumnNameMappingStrategy<T> extends HeaderNameBaseMappingStr
      */
     @Override
     protected void loadAnnotatedFieldMap(ListValuedMap<Class<?>, Field> fields) {
-        boolean required;
-
         for (Map.Entry<Class<?>, Field> classField : fields.entries()) {
             Class<?> localType = classField.getKey();
             Field localField = classField.getValue();
-            String columnName, locale, writeLocale, capture, format;
 
             // Always check for a custom converter first.
-            if (localField.isAnnotationPresent(CsvCustomBindByName.class)) {
-                CsvCustomBindByName annotation = localField
-                        .getAnnotation(CsvCustomBindByName.class);
-                columnName = annotation.column().toUpperCase().trim();
-                if(StringUtils.isEmpty(columnName)) {
-                    columnName = localField.getName().toUpperCase();
+            if (localField.isAnnotationPresent(CsvCustomBindByName.class)
+                    || localField.isAnnotationPresent(CsvCustomBindByNames.class)) {
+                CsvCustomBindByName annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvCustomBindByName.class),
+                        CsvCustomBindByName::profiles);
+                if(annotation != null) {
+                    registerCustomBinding(annotation, localType, localField);
                 }
-                @SuppressWarnings("unchecked")
-                Class<? extends AbstractBeanField<T, String>> converter = (Class<? extends AbstractBeanField<T, String>>)localField
-                        .getAnnotation(CsvCustomBindByName.class)
-                        .converter();
-                BeanField<T, String> bean = instantiateCustomConverter(converter);
-                bean.setType(localType);
-                bean.setField(localField);
-                required = annotation.required();
-                bean.setRequired(required);
-                fieldMap.put(columnName, bean);
             }
 
             // Then check for a collection
-            else if(localField.isAnnotationPresent(CsvBindAndSplitByName.class)) {
-                CsvBindAndSplitByName annotation = localField
-                        .getAnnotation(CsvBindAndSplitByName.class);
-                required = annotation.required();
-                columnName = annotation.column().toUpperCase().trim();
-                locale = annotation.locale();
-                writeLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? locale : annotation.writeLocale();
-                String splitOn = annotation.splitOn();
-                String writeDelimiter = annotation.writeDelimiter();
-                Class<? extends Collection> collectionType = annotation.collectionType();
-                Class<?> elementType = annotation.elementType();
-                Class<? extends AbstractCsvConverter> splitConverter = annotation.converter();
-                capture = annotation.capture();
-                format = annotation.format();
-
-                CsvConverter converter = determineConverter(
-                        localField, elementType, locale,
-                        writeLocale, splitConverter);
-                if (StringUtils.isEmpty(columnName)) {
-                    fieldMap.put(localField.getName().toUpperCase(),
-                            new BeanFieldSplit<>(
-                                    localType,
-                                    localField, required,
-                                    errorLocale, converter, splitOn,
-                                    writeDelimiter, collectionType,
-                                    elementType, capture, format));
-                } else {
-                    fieldMap.put(columnName, new BeanFieldSplit<>(
-                            localType,
-                            localField, required, errorLocale,
-                            converter, splitOn, writeDelimiter, collectionType,
-                            elementType, capture, format));
+            else if(localField.isAnnotationPresent(CsvBindAndSplitByName.class)
+                    || localField.isAnnotationPresent(CsvBindAndSplitByNames.class)) {
+                CsvBindAndSplitByName annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindAndSplitByName.class),
+                        CsvBindAndSplitByName::profiles);
+                if (annotation != null) {
+                    registerSplitBinding(annotation, localType, localField);
                 }
             }
 
             // Then for a multi-column annotation
-            else if(localField.isAnnotationPresent(CsvBindAndJoinByName.class)) {
-                CsvBindAndJoinByName annotation = localField
-                        .getAnnotation(CsvBindAndJoinByName.class);
-                required = annotation.required();
-                String columnRegex = annotation.column();
-                locale = annotation.locale();
-                writeLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? locale : annotation.writeLocale();
-                Class<?> elementType = annotation.elementType();
-                Class<? extends MultiValuedMap> mapType = annotation.mapType();
-                Class<? extends AbstractCsvConverter> joinConverter = annotation.converter();
-                capture = annotation.capture();
-                format = annotation.format();
-
-                CsvConverter converter = determineConverter(
-                        localField, elementType, locale,
-                        writeLocale, joinConverter);
-                if (StringUtils.isEmpty(columnRegex)) {
-                    fieldMap.putComplex(localField.getName(),
-                            new BeanFieldJoinStringIndex<>(
-                                    localType,
-                                    localField, required,
-                                    errorLocale, converter, mapType, capture,
-                                    format));
-                } else {
-                    fieldMap.putComplex(columnRegex, new BeanFieldJoinStringIndex<>(
-                            localType,
-                            localField, required, errorLocale,
-                            converter, mapType, capture, format));
+            else if(localField.isAnnotationPresent(CsvBindAndJoinByName.class)
+                    || localField.isAnnotationPresent(CsvBindAndJoinByNames.class)) {
+                CsvBindAndJoinByName annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindAndJoinByName.class),
+                        CsvBindAndJoinByName::profiles);
+                if (annotation != null) {
+                    registerJoinBinding(annotation, localType, localField);
                 }
             }
 
             // Otherwise it must be CsvBindByName.
             else {
-                CsvBindByName annotation = localField.getAnnotation(CsvBindByName.class);
-                required = annotation.required();
-                columnName = annotation.column().toUpperCase().trim();
-                locale = annotation.locale();
-                writeLocale = annotation.writeLocaleEqualsReadLocale()
-                        ? locale : annotation.writeLocale();
-                capture = annotation.capture();
-                format = annotation.format();
-                CsvConverter converter = determineConverter(
-                        localField,
-                        localField.getType(), locale,
-                        writeLocale, null);
-
-                if (StringUtils.isEmpty(columnName)) {
-                    fieldMap.put(localField.getName().toUpperCase(),
-                            new BeanFieldSingleValue<>(
-                                    localType,
-                                    localField, required,
-                                    errorLocale, converter, capture, format));
-                } else {
-                    fieldMap.put(columnName, new BeanFieldSingleValue<>(
-                            localType,
-                            localField, required, errorLocale,
-                            converter, capture, format));
+                CsvBindByName annotation = selectAnnotationForProfile(
+                        localField.getAnnotationsByType(CsvBindByName.class),
+                        CsvBindByName::profiles);
+                if (annotation != null) {
+                    registerBinding(annotation, localType, localField);
                 }
             }
         }
@@ -187,6 +242,10 @@ public class HeaderColumnNameMappingStrategy<T> extends HeaderNameBaseMappingStr
     protected Set<Class<? extends Annotation>> getBindingAnnotations() {
         // With Java 9 this can be done more easily with Set.of()
         return new HashSet<>(Arrays.asList(
+                CsvBindByNames.class,
+                CsvCustomBindByNames.class,
+                CsvBindAndSplitByNames.class,
+                CsvBindAndJoinByNames.class,
                 CsvBindByName.class,
                 CsvCustomBindByName.class,
                 CsvBindAndSplitByName.class,
diff --git a/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategyBuilder.java b/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategyBuilder.java
new file mode 100644
index 0000000..c783d09
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/HeaderColumnNameMappingStrategyBuilder.java
@@ -0,0 +1,45 @@
+package com.opencsv.bean;
+
+/**
+ * Builder for a {@link HeaderColumnNameMappingStrategy}.
+ * This allows opencsv to introduce new options for mapping strategies
+ * while maintaining backward compatibility and without creating
+ * reams of constructors for the mapping strategy.
+ *
+ * @param <T> The type of the bean being processed
+ * @since 5.5
+ * @author Andrew Rucker Jones
+ */
+public class HeaderColumnNameMappingStrategyBuilder<T> {
+
+    private boolean forceCorrectRecordLength = false;
+
+    /** Default constructor. */
+    public HeaderColumnNameMappingStrategyBuilder() {}
+
+    /**
+     * Builds a new mapping strategy for parsing/writing.
+     * @return A new mapping strategy using the options selected
+     */
+    public HeaderColumnNameMappingStrategy<T> build() {
+        return new HeaderColumnNameMappingStrategy<>(forceCorrectRecordLength);
+    }
+
+    /**
+     * Insists that every record will be considered to be of the correct
+     * length (that is, the same number of columns as the header).
+     * <p>Excess fields at the end of a record will be ignored. Missing
+     * fields at the end of a record will be interpreted as {@code null}.
+     * This is only relevant on reading.</p>
+     * <p>If not set, incorrect record length will throw an exception. That
+     * is, the default value is {@code false}.</p>
+     *
+     * @param forceCorrectRecordLength Whether records should be forced to
+     *                                 the correct length
+     * @return {@code this}
+     */
+    public HeaderColumnNameMappingStrategyBuilder<T> withForceCorrectRecordLength(boolean forceCorrectRecordLength) {
+        this.forceCorrectRecordLength = forceCorrectRecordLength;
+        return this;
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategy.java b/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategy.java
index 3135e4f..73d5055 100644
--- a/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategy.java
@@ -32,12 +32,29 @@ public class HeaderColumnNameTranslateMappingStrategy<T> extends HeaderNameBaseM
     private final Map<String, String> columnMapping;
 
     /**
-     * Default constructor.
+     * Default constructor. Considered stable.
+     * @see HeaderColumnNameTranslateMappingStrategyBuilder
      */
     public HeaderColumnNameTranslateMappingStrategy() {
         columnMapping = new HashMap<>();
     }
 
+    /**
+     * Constructor to allow setting options for header name mapping.
+     * Not considered stable. As new options are introduced for the mapping
+     * strategy, they will be introduced here. You are encouraged to use
+     * {@link HeaderColumnNameTranslateMappingStrategyBuilder}.
+     *
+     * @param forceCorrectRecordLength If set, every record will be shortened
+     *                                 or lengthened to match the number of
+     *                                 headers
+     * @see HeaderColumnNameTranslateMappingStrategyBuilder
+     */
+    public HeaderColumnNameTranslateMappingStrategy(boolean forceCorrectRecordLength) {
+        super(forceCorrectRecordLength);
+        columnMapping = new HashMap<>();
+    }
+
     @Override
     public String getColumnName(int col) {
         String name = headerIndex.getByPosition(col);
diff --git a/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyBuilder.java b/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyBuilder.java
new file mode 100644
index 0000000..cbbf965
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyBuilder.java
@@ -0,0 +1,45 @@
+package com.opencsv.bean;
+
+/**
+ * Builder for a {@link HeaderColumnNameMappingStrategy}.
+ * This allows opencsv to introduce new options for mapping strategies
+ * while maintaining backward compatibility and without creating
+ * reams of constructors for the mapping strategy.
+ *
+ * @param <T> The type of the bean being processed
+ * @since 5.5
+ * @author Andrew Rucker Jones
+ */
+public class HeaderColumnNameTranslateMappingStrategyBuilder<T> {
+
+    private boolean forceCorrectRecordLength = false;
+
+    /** Default constructor. */
+    public HeaderColumnNameTranslateMappingStrategyBuilder() {}
+
+    /**
+     * Builds a new mapping strategy for parsing/writing.
+     * @return A new mapping strategy using the options selected
+     */
+    public HeaderColumnNameTranslateMappingStrategy<T> build() {
+        return new HeaderColumnNameTranslateMappingStrategy<>(forceCorrectRecordLength);
+    }
+
+    /**
+     * Insists that every record will be considered to be of the correct
+     * length (that is, the same number of columns as the header).
+     * <p>Excess fields at the end of a record will be ignored. Missing
+     * fields at the end of a record will be interpreted as {@code null}.
+     * This is only relevant on reading.</p>
+     * <p>If not set, incorrect record length will throw an exception. That
+     * is, the default value is {@code false}.</p>
+     *
+     * @param forceCorrectRecordLength Whether records should be forced to
+     *                                 the correct length
+     * @return {@code this}
+     */
+    public HeaderColumnNameTranslateMappingStrategyBuilder<T> withForceCorrectRecordLength(boolean forceCorrectRecordLength) {
+        this.forceCorrectRecordLength = forceCorrectRecordLength;
+        return this;
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/HeaderNameBaseMappingStrategy.java b/src/main/java/com/opencsv/bean/HeaderNameBaseMappingStrategy.java
index 95d1c41..025dd3a 100644
--- a/src/main/java/com/opencsv/bean/HeaderNameBaseMappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/HeaderNameBaseMappingStrategy.java
@@ -6,7 +6,7 @@ import com.opencsv.exceptions.CsvBadConverterException;
 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
 import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.IOException;
 import java.io.Serializable;
@@ -32,6 +32,25 @@ abstract public class HeaderNameBaseMappingStrategy<T> extends AbstractMappingSt
     /** Holds a {@link java.util.Comparator} to sort columns on writing. */
     protected Comparator<String> writeOrder = null;
 
+    /** If set, every record will be shortened or lengthened to match the number of headers. */
+    protected final boolean forceCorrectRecordLength;
+
+    /** Nullary constructor for compatibility. */
+    public HeaderNameBaseMappingStrategy() {
+        this.forceCorrectRecordLength = false;
+    }
+
+    /**
+     * Constructor to allow setting options for header name mapping.
+     *
+     * @param forceCorrectRecordLength If set, every record will be shortened
+     *                                 or lengthened to match the number of
+     *                                 headers
+     */
+    public HeaderNameBaseMappingStrategy(boolean forceCorrectRecordLength) {
+        this.forceCorrectRecordLength = forceCorrectRecordLength;
+    }
+
     @Override
     public void captureHeader(CSVReader reader) throws IOException, CsvRequiredFieldEmptyException {
         // Validation
@@ -42,7 +61,14 @@ abstract public class HeaderNameBaseMappingStrategy<T> extends AbstractMappingSt
         }
 
         // Read the header
-        String[] header = ObjectUtils.defaultIfNull(reader.readNextSilently(), ArrayUtils.EMPTY_STRING_ARRAY);
+        String[] header = ArrayUtils.nullToEmpty(reader.readNextSilently());
+        for(int i = 0; i < header.length; i++) {
+            // For the case that a header is empty and someone configured
+            // empty fields to be null
+            if(header[i] == null) {
+                header[i] = StringUtils.EMPTY;
+            }
+        }
         headerIndex.initializeHeaderIndex(header);
 
         // Throw an exception if any required headers are missing
@@ -84,7 +110,7 @@ abstract public class HeaderNameBaseMappingStrategy<T> extends AbstractMappingSt
     @Override
     public void verifyLineLength(int numberOfFields) throws CsvRequiredFieldEmptyException {
         if(!headerIndex.isEmpty()) {
-            if (numberOfFields != headerIndex.getHeaderIndexLength()) {
+            if (numberOfFields != headerIndex.getHeaderIndexLength() && !forceCorrectRecordLength) {
                 throw new CsvRequiredFieldEmptyException(type, ResourceBundle
                         .getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
                         .getString("header.data.mismatch"));
@@ -107,30 +133,32 @@ abstract public class HeaderNameBaseMappingStrategy<T> extends AbstractMappingSt
     }
 
     /**
-     * Creates a map of fields in the bean to be processed that have no
+     * Creates a map of fields in the bean to be processed that have no binding
      * annotations.
      * <p>This method is called by {@link #loadFieldMap()} when absolutely no
-     * annotations that are relevant for this mapping strategy are found in the
-     * type of bean being processed. It is then assumed that every field is to
-     * be included, and that the name of the member variable must exactly match
-     * the header name of the input.</p>
+     * binding annotations that are relevant for this mapping strategy are
+     * found in the type of bean being processed. It is then assumed that every
+     * field is to be included, and that the name of the member variable must
+     * exactly match the header name of the input.</p>
      * <p>Two exceptions are made to the rule that everything is written:<ol>
      *     <li>Any field annotated with {@link CsvIgnore} will be
      *     ignored on writing</li>
      *     <li>Any field named "serialVersionUID" will be ignored if the
      *     enclosing class implements {@link java.io.Serializable}.</li>
      * </ol></p>
+     * <p>{@link CsvRecurse} is respected.</p>
      */
     @Override
     protected void loadUnadornedFieldMap(ListValuedMap<Class<?>, Field> fields) {
-        for(Map.Entry<Class<?>, Field> classFieldEntry : fields.entries()) {
-            if(!(Serializable.class.isAssignableFrom(classFieldEntry.getKey()) && "serialVersionUID".equals(classFieldEntry.getValue().getName()))) {
-                CsvConverter converter = determineConverter(classFieldEntry.getValue(), classFieldEntry.getValue().getType(), null, null, null);
-                fieldMap.put(classFieldEntry.getValue().getName().toUpperCase(), new BeanFieldSingleValue<>(
-                        classFieldEntry.getKey(), classFieldEntry.getValue(),
-                        false, errorLocale, converter, null, null));
-            }
-        }
+        fields.entries().stream()
+                .filter(entry -> !(Serializable.class.isAssignableFrom(entry.getKey()) && "serialVersionUID".equals(entry.getValue().getName())))
+                .filter(entry -> !entry.getValue().isAnnotationPresent(CsvRecurse.class))
+                .forEach(entry -> {
+                    final CsvConverter converter = determineConverter(entry.getValue(), entry.getValue().getType(), null, null, null);
+                    fieldMap.put(entry.getValue().getName().toUpperCase(), new BeanFieldSingleValue<>(
+                            entry.getKey(), entry.getValue(),
+                            false, errorLocale, converter, null, null));
+                });
     }
 
     @Override
diff --git a/src/main/java/com/opencsv/bean/MappingStrategy.java b/src/main/java/com/opencsv/bean/MappingStrategy.java
index bb1bcb2..c2e3f71 100644
--- a/src/main/java/com/opencsv/bean/MappingStrategy.java
+++ b/src/main/java/com/opencsv/bean/MappingStrategy.java
@@ -85,21 +85,16 @@ public interface MappingStrategy<T> {
      * @return A bean containing the converted information from the input
      * @throws CsvBeanIntrospectionException Generally, if some part of the bean cannot
      *   be accessed and used as needed
-     * @throws CsvRequiredFieldEmptyException If the input for a field defined
-     *   as required is empty
-     * @throws CsvDataTypeMismatchException If conversion of the input to a
-     *   field type fails
-     * @throws CsvConstraintViolationException If the value provided for a field
-     *   would in some way compromise the logical integrity of the data as a
-     *   whole
-     * @throws CsvValidationException If a user-supplied validator determines
-     * that the input is invalid
+     * @throws CsvFieldAssignmentException A more specific subclass of this
+     *   exception is thrown for any problem decoding and assigning a field
+     *   of the input to a bean field
+     * @throws CsvChainedException If multiple exceptions are thrown for the
+     * same input line
      * @since 4.2
      */
     T populateNewBean(String[] line)
-            throws CsvBeanIntrospectionException, CsvRequiredFieldEmptyException,
-            CsvDataTypeMismatchException, CsvConstraintViolationException,
-            CsvValidationException;
+            throws CsvBeanIntrospectionException, CsvFieldAssignmentException,
+            CsvChainedException;
     
     /**
      * Sets the locale for all error messages.
@@ -114,7 +109,10 @@ public interface MappingStrategy<T> {
    
     /**
      * Sets the class type that is being mapped.
-     * May perform additional initialization tasks.
+     * May perform additional initialization tasks. If instantiating a
+     * mapping strategy, this method should be called after any other
+     * initialization, for example a call to
+     * {@link #setErrorLocale(Locale)} or {@link #setProfile(String)}.
      *
      * @param type Class type.
      * @throws CsvBadConverterException If a field in the bean is annotated
@@ -124,6 +122,19 @@ public interface MappingStrategy<T> {
      */
     void setType(Class<? extends T> type) throws CsvBadConverterException;
 
+    /**
+     * Sets the profile this mapping strategy will use when configuring bean
+     * fields.
+     * <p>The default implementation throws
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @param profile The profile to use
+     * @since 5.4
+     */
+    default void setProfile(String profile) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * <p>
      *     When processing a bean for reading or writing, ignore the given fields
@@ -170,10 +181,11 @@ public interface MappingStrategy<T> {
      * @param bean The bean to be transmuted
      * @return The converted values of the bean fields in the correct order,
      *   ready to be passed to a {@link com.opencsv.CSVWriter}
-     * @throws CsvDataTypeMismatchException If expected to convert an
-     *   unsupported data type
-     * @throws CsvRequiredFieldEmptyException If the field is marked as required,
-     *   but is currently empty
+     * @throws CsvFieldAssignmentException A more specific subclass of this
+     *   exception is thrown for any problem decoding and assigning a field
+     *   of the input to a bean field
+     * @throws CsvChainedException If multiple exceptions are thrown for the
+     * same input line
      */
-    String[] transmuteBean(T bean) throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException;
+    String[] transmuteBean(T bean) throws CsvFieldAssignmentException, CsvChainedException;
 }
\ No newline at end of file
diff --git a/src/main/java/com/opencsv/bean/StatefulBeanToCsv.java b/src/main/java/com/opencsv/bean/StatefulBeanToCsv.java
index 7a8ab06..c663596 100644
--- a/src/main/java/com/opencsv/bean/StatefulBeanToCsv.java
+++ b/src/main/java/com/opencsv/bean/StatefulBeanToCsv.java
@@ -19,11 +19,11 @@ import com.opencsv.CSVWriter;
 import com.opencsv.ICSVParser;
 import com.opencsv.ICSVWriter;
 import com.opencsv.bean.concurrent.BeanExecutor;
-import com.opencsv.bean.util.OrderedObject;
 import com.opencsv.bean.concurrent.ProcessCsvBean;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
 import com.opencsv.bean.exceptionhandler.ExceptionHandlerThrow;
 import com.opencsv.bean.util.OpencsvUtils;
+import com.opencsv.bean.util.OrderedObject;
 import com.opencsv.exceptions.CsvDataTypeMismatchException;
 import com.opencsv.exceptions.CsvException;
 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
@@ -32,12 +32,14 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.collections4.iterators.PeekingIterator;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.Writer;
 import java.lang.reflect.Field;
 import java.util.*;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -52,7 +54,7 @@ import java.util.stream.StreamSupport;
  *
  * @param <T> Type of the bean to be written
  * @author Andrew Rucker Jones
- * @see OpencsvUtils#determineMappingStrategy(java.lang.Class, java.util.Locale)
+ * @see OpencsvUtils#determineMappingStrategy(java.lang.Class, java.util.Locale, java.lang.String)
  * @since 3.9
  */
 public class StatefulBeanToCsv<T> {
@@ -70,13 +72,14 @@ public class StatefulBeanToCsv<T> {
     private MappingStrategy<T> mappingStrategy;
     private final Writer writer;
     private ICSVWriter csvwriter;
-    private CsvExceptionHandler exceptionHandler;
+    private final CsvExceptionHandler exceptionHandler;
     private List<CsvException> capturedExceptions = new ArrayList<>();
     private boolean orderedResults = true;
     private BeanExecutor<T> executor = null;
     private Locale errorLocale = Locale.getDefault();
-    private boolean applyQuotesToAll;
+    private final boolean applyQuotesToAll;
     private final MultiValuedMap<Class<?>, Field> ignoredFields;
+    private final String profile;
 
     /**
      * Constructor used when supplying a Writer instead of a CsvWriter class.
@@ -91,11 +94,12 @@ public class StatefulBeanToCsv<T> {
      * @param writer           A {@link java.io.Writer} for writing the beans as a CSV to
      * @param applyQuotesToAll Whether all output fields should be quoted
      * @param ignoredFields The fields to ignore during processing. May be {@code null}.
+     * @param profile The profile to use when configuring how to interpret bean fields
      */
     StatefulBeanToCsv(char escapechar, String lineEnd,
                       MappingStrategy<T> mappingStrategy, char quotechar, char separator,
                       CsvExceptionHandler exceptionHandler, Writer writer, boolean applyQuotesToAll,
-                      MultiValuedMap<Class<?>, Field> ignoredFields) {
+                      MultiValuedMap<Class<?>, Field> ignoredFields, String profile) {
         this.escapechar = escapechar;
         this.lineEnd = lineEnd;
         this.mappingStrategy = mappingStrategy;
@@ -105,6 +109,7 @@ public class StatefulBeanToCsv<T> {
         this.writer = writer;
         this.applyQuotesToAll = applyQuotesToAll;
         this.ignoredFields = ignoredFields;
+        this.profile = StringUtils.defaultString(profile);
     }
 
     /**
@@ -116,11 +121,12 @@ public class StatefulBeanToCsv<T> {
      * @param applyQuotesToAll Whether all output fields should be quoted
      * @param csvWriter        An user-supplied {@link com.opencsv.ICSVWriter} for writing beans to a CSV output
      * @param ignoredFields The fields to ignore during processing. May be {@code null}.
+     * @param profile The profile to use when configuring how to interpret bean fields
      */
     public StatefulBeanToCsv(MappingStrategy<T> mappingStrategy,
                              CsvExceptionHandler exceptionHandler, boolean applyQuotesToAll,
                              ICSVWriter csvWriter,
-                             MultiValuedMap<Class<?>, Field> ignoredFields) {
+                             MultiValuedMap<Class<?>, Field> ignoredFields, String profile) {
         this.mappingStrategy = mappingStrategy;
         this.exceptionHandler = exceptionHandler;
         this.applyQuotesToAll = applyQuotesToAll;
@@ -132,6 +138,7 @@ public class StatefulBeanToCsv<T> {
         this.separator = NO_CHARACTER;
         this.writer = null;
         this.ignoredFields = ignoredFields;
+        this.profile = StringUtils.defaultString(profile);
     }
 
     /**
@@ -150,7 +157,8 @@ public class StatefulBeanToCsv<T> {
 
         // Determine mapping strategy
         if (mappingStrategy == null) {
-            mappingStrategy = OpencsvUtils.determineMappingStrategy((Class<T>) bean.getClass(), errorLocale);
+            mappingStrategy = OpencsvUtils.determineMappingStrategy(
+                    (Class<T>) bean.getClass(), errorLocale, profile);
         }
 
         // Ignore fields. It's possible the mapping strategy has already been
@@ -196,7 +204,7 @@ public class StatefulBeanToCsv<T> {
 
             // Process the bean
             BlockingQueue<OrderedObject<String[]>> resultantLineQueue = new ArrayBlockingQueue<>(1);
-            BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue = new ArrayBlockingQueue<>(1);
+            BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue = new LinkedBlockingQueue<>();
             ProcessCsvBean<T> proc = new ProcessCsvBean<>(++lineNumber,
                     mappingStrategy, bean, resultantLineQueue,
                     thrownExceptionsQueue, new TreeSet<>(), exceptionHandler);
@@ -223,8 +231,9 @@ public class StatefulBeanToCsv<T> {
             // Write out the result
             if (!thrownExceptionsQueue.isEmpty()) {
                 OrderedObject<CsvException> o = thrownExceptionsQueue.poll();
-                if (o != null && o.getElement() != null) {
+                while (o != null && o.getElement() != null) {
                     capturedExceptions.add(o.getElement());
+                    o = thrownExceptionsQueue.poll();
                 }
             } else {
                 // No exception, so there really must always be a string
@@ -383,6 +392,7 @@ public class StatefulBeanToCsv<T> {
     public List<CsvException> getCapturedExceptions() {
         List<CsvException> intermediate = capturedExceptions;
         capturedExceptions = new ArrayList<>();
+        intermediate.sort(Comparator.comparingLong(CsvException::getLineNumber));
         return intermediate;
     }
 
diff --git a/src/main/java/com/opencsv/bean/StatefulBeanToCsvBuilder.java b/src/main/java/com/opencsv/bean/StatefulBeanToCsvBuilder.java
index 5cca324..34e33a1 100644
--- a/src/main/java/com/opencsv/bean/StatefulBeanToCsvBuilder.java
+++ b/src/main/java/com/opencsv/bean/StatefulBeanToCsvBuilder.java
@@ -25,6 +25,7 @@ import org.apache.commons.collections4.ListValuedMap;
 import org.apache.commons.collections4.MultiValuedMap;
 import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.Writer;
 import java.lang.reflect.Field;
@@ -53,6 +54,7 @@ public class StatefulBeanToCsvBuilder<T> {
     private Locale errorLocale = Locale.getDefault();
     private boolean applyQuotesToAll = true;
     private final ListValuedMap<Class<?>, Field> ignoredFields = new ArrayListValuedHashMap<>();
+    private String profile = StringUtils.EMPTY;
     
     /**
      * Default constructor - Being stateful the writer is required by the builder at the start and not added in later.
@@ -249,7 +251,20 @@ public class StatefulBeanToCsvBuilder<T> {
         }
         return this;
     }
-    
+
+    /**
+     * Selects a profile for deciding which configurations to use for the bean
+     * fields.
+     *
+     * @param profile The name of the profile to be used
+     * @return {@code this}
+     * @since 5.4
+     */
+    public StatefulBeanToCsvBuilder<T> withProfile(String profile) {
+        this.profile = profile;
+        return this;
+    }
+
     /**
      * Builds a StatefulBeanToCsv from the information provided, filling in
      * default values where none have been specified.
@@ -260,10 +275,10 @@ public class StatefulBeanToCsvBuilder<T> {
         if (writer != null) {
             sbtcsv = new StatefulBeanToCsv<>(escapechar, lineEnd,
                     mappingStrategy, quotechar, separator, exceptionHandler,
-                    writer, applyQuotesToAll, ignoredFields);
+                    writer, applyQuotesToAll, ignoredFields, profile);
         } else {
             sbtcsv = new StatefulBeanToCsv<>(mappingStrategy, exceptionHandler,
-                    applyQuotesToAll, csvWriter, ignoredFields);
+                    applyQuotesToAll, csvWriter, ignoredFields, profile);
         }
 
         sbtcsv.setOrderedResults(orderedResults);
diff --git a/src/main/java/com/opencsv/bean/comparator/LiteralComparator.java b/src/main/java/com/opencsv/bean/comparator/LiteralComparator.java
index c142547..624e93e 100644
--- a/src/main/java/com/opencsv/bean/comparator/LiteralComparator.java
+++ b/src/main/java/com/opencsv/bean/comparator/LiteralComparator.java
@@ -35,6 +35,7 @@ import java.util.List;
  * @param <T> The type to be sorted
  *
  * @since 4.3
+ * @author Andrew Rucker Jones
  * @deprecated This exact behavior can be had using comparators from Apache
  * Commons Collections, which opencsv includes as a dependency. The following
  * code gives the same result:
diff --git a/src/main/java/com/opencsv/bean/concurrent/AccumulateCsvResults.java b/src/main/java/com/opencsv/bean/concurrent/AccumulateCsvResults.java
index 1e780f8..fd38f76 100644
--- a/src/main/java/com/opencsv/bean/concurrent/AccumulateCsvResults.java
+++ b/src/main/java/com/opencsv/bean/concurrent/AccumulateCsvResults.java
@@ -17,6 +17,7 @@ package com.opencsv.bean.concurrent;
 
 import com.opencsv.bean.util.OrderedObject;
 import com.opencsv.exceptions.CsvException;
+import org.apache.commons.collections4.ListValuedMap;
 
 import java.util.SortedSet;
 import java.util.concurrent.BlockingQueue;
@@ -39,9 +40,11 @@ class AccumulateCsvResults<T> extends Thread {
     private final BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue;
     private final SortedSet<Long> expectedRecords;
     private final ConcurrentMap<Long, T> resultantBeanMap;
-    private final ConcurrentMap<Long, CsvException> thrownExceptionsMap;
     private boolean mustStop = false;
 
+    /** <em>All access to this variable must be synchronized.</em> */
+    private final ListValuedMap<Long, CsvException> thrownExceptionsMap;
+
     /**
      * The only accepted constructor for the accumulator.
      * @param resultantBeansQueue A queue of beans coming out of the pool of
@@ -55,14 +58,15 @@ class AccumulateCsvResults<T> extends Thread {
      *                        while converting can be detected.
      * @param resultantBeanMap The (ordered) map of beans that have been
      *   created. The accumulator inserts into this map.
-     * @param thrownExceptionsMap The (ordered) map of suppressed exceptions
-     *   thrown during bean creation. The accumulator inserts into this map.
+     * @param thrownExceptionsMap The map of suppressed exceptions thrown
+     *   during bean creation. The accumulator inserts into this map. <em>All
+     *   access to this variable must be synchronized.</em>
      */
     AccumulateCsvResults(BlockingQueue<OrderedObject<T>> resultantBeansQueue,
                          BlockingQueue<OrderedObject<CsvException>> thrownExceptionsQueue,
                          SortedSet<Long> expectedRecords,
                          ConcurrentMap<Long, T> resultantBeanMap,
-                         ConcurrentMap<Long, CsvException> thrownExceptionsMap) {
+                         ListValuedMap<Long, CsvException> thrownExceptionsMap) {
         super();
         this.resultantBeansQueue = resultantBeansQueue;
         this.thrownExceptionsQueue = thrownExceptionsQueue;
@@ -123,7 +127,9 @@ class AccumulateCsvResults<T> extends Thread {
             while(!thrownExceptionsQueue.isEmpty()) {
                 OrderedObject<CsvException> capturedException = thrownExceptionsQueue.poll();
                 if(capturedException != null) {
-                    thrownExceptionsMap.put(capturedException.getOrdinal(), capturedException.getElement());
+                    synchronized (thrownExceptionsMap) {
+                        thrownExceptionsMap.put(capturedException.getOrdinal(), capturedException.getElement());
+                    }
                 }
             }
             Thread.yield();
diff --git a/src/main/java/com/opencsv/bean/concurrent/IntolerantThreadPoolExecutor.java b/src/main/java/com/opencsv/bean/concurrent/IntolerantThreadPoolExecutor.java
index 6dba817..bbdddd6 100644
--- a/src/main/java/com/opencsv/bean/concurrent/IntolerantThreadPoolExecutor.java
+++ b/src/main/java/com/opencsv/bean/concurrent/IntolerantThreadPoolExecutor.java
@@ -18,6 +18,8 @@ package com.opencsv.bean.concurrent;
 import com.opencsv.ICSVParser;
 import com.opencsv.bean.util.OrderedObject;
 import com.opencsv.exceptions.CsvException;
+import org.apache.commons.collections4.ListValuedMap;
+import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ObjectUtils;
 
@@ -73,8 +75,13 @@ class IntolerantThreadPoolExecutor<T> extends ThreadPoolExecutor implements Spli
     /** A sorted, concurrent map for the beans created. */
     private ConcurrentNavigableMap<Long, T> resultantBeansMap = null;
 
-    /** A sorted, concurrent map for any exceptions captured. */
-    private ConcurrentNavigableMap<Long, CsvException> thrownExceptionsMap = null;
+    /**
+     * A multi-valued map for any exceptions captured.
+     * <p>The multi-valued part is important because the same line can throw more
+     * than one exception.</p>
+     * <p><em>All access to this variable must be synchronized.</em></p>
+     * */
+    private ListValuedMap<Long, CsvException> thrownExceptionsMap = null;
 
     /** A separate thread that accumulates and orders results. */
     protected AccumulateCsvResults<T> accumulateThread = null;
@@ -122,7 +129,7 @@ class IntolerantThreadPoolExecutor<T> extends ThreadPoolExecutor implements Spli
         // processing.
         if(orderedResults) {
             resultantBeansMap = new ConcurrentSkipListMap<>();
-            thrownExceptionsMap = new ConcurrentSkipListMap<>();
+            thrownExceptionsMap = new ArrayListValuedHashMap<>();
 
             // Start the process for accumulating results and cleaning up
             accumulateThread = new AccumulateCsvResults<>(
@@ -167,12 +174,23 @@ class IntolerantThreadPoolExecutor<T> extends ThreadPoolExecutor implements Spli
      * @return All exceptions captured
      */
     public List<CsvException> getCapturedExceptions() {
-        return thrownExceptionsMap == null ?
-                thrownExceptionsQueue.stream()
-                        .filter(Objects::nonNull)
-                        .map(OrderedObject::getElement)
-                        .collect(Collectors.toList()) :
-                new ArrayList<>(thrownExceptionsMap.values());
+        List<CsvException> returnList = null;
+        if(thrownExceptionsMap == null) {
+            returnList = thrownExceptionsQueue.stream()
+                    .filter(Objects::nonNull)
+                    .map(OrderedObject::getElement)
+                    .collect(Collectors.toList());
+        }
+        else {
+            returnList = new LinkedList<>();
+            synchronized (thrownExceptionsMap) {
+                final List<CsvException> finalReturnList = returnList;
+                thrownExceptionsMap.keySet().stream()
+                        .sorted()
+                        .forEach(l -> finalReturnList.addAll(thrownExceptionsMap.get(l)));
+            }
+        }
+        return returnList;
     }
 
     @Override
@@ -230,7 +248,7 @@ class IntolerantThreadPoolExecutor<T> extends ThreadPoolExecutor implements Spli
             if(terminalException instanceof CsvException) {
                 CsvException csve = (CsvException) terminalException;
                 throw new RuntimeException(String.format(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("parsing.error.linenumber"),
-                        csve.getLineNumber(), String.join(",", ObjectUtils.defaultIfNull(csve.getLine(), ArrayUtils.EMPTY_STRING_ARRAY))), csve);
+                        csve.getLineNumber(), String.join(",", ArrayUtils.nullToEmpty(csve.getLine()))), csve);
             }
             throw new RuntimeException(terminalException);
         }
diff --git a/src/main/java/com/opencsv/bean/concurrent/ProcessCsvBean.java b/src/main/java/com/opencsv/bean/concurrent/ProcessCsvBean.java
index bc621dd..c2089e2 100644
--- a/src/main/java/com/opencsv/bean/concurrent/ProcessCsvBean.java
+++ b/src/main/java/com/opencsv/bean/concurrent/ProcessCsvBean.java
@@ -16,10 +16,13 @@
 package com.opencsv.bean.concurrent;
 
 import com.opencsv.bean.MappingStrategy;
-import com.opencsv.bean.util.OpencsvUtils;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
+import com.opencsv.bean.util.OpencsvUtils;
 import com.opencsv.bean.util.OrderedObject;
-import com.opencsv.exceptions.*;
+import com.opencsv.exceptions.CsvChainedException;
+import com.opencsv.exceptions.CsvException;
+import com.opencsv.exceptions.CsvFieldAssignmentException;
+import com.opencsv.exceptions.CsvRuntimeException;
 
 import java.util.SortedSet;
 import java.util.concurrent.BlockingQueue;
@@ -74,7 +77,7 @@ public class ProcessCsvBean<T> implements Runnable {
             OpencsvUtils.queueRefuseToAcceptDefeat(resultantLineQueue,
                     new OrderedObject<>(lineNumber, mappingStrategy.transmuteBean(bean)));
         }
-        catch (CsvException e) {
+        catch (CsvFieldAssignmentException | CsvChainedException e) {
             expectedRecords.remove(lineNumber);
             OpencsvUtils.handleException(e, lineNumber, exceptionHandler, thrownExceptionsQueue);
         }
diff --git a/src/main/java/com/opencsv/bean/concurrent/ProcessCsvLine.java b/src/main/java/com/opencsv/bean/concurrent/ProcessCsvLine.java
index d53e0d2..467af0b 100644
--- a/src/main/java/com/opencsv/bean/concurrent/ProcessCsvLine.java
+++ b/src/main/java/com/opencsv/bean/concurrent/ProcessCsvLine.java
@@ -18,8 +18,8 @@ package com.opencsv.bean.concurrent;
 import com.opencsv.bean.BeanVerifier;
 import com.opencsv.bean.CsvToBeanFilter;
 import com.opencsv.bean.MappingStrategy;
-import com.opencsv.bean.util.OpencsvUtils;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
+import com.opencsv.bean.util.OpencsvUtils;
 import com.opencsv.bean.util.OrderedObject;
 import com.opencsv.exceptions.*;
 import org.apache.commons.lang3.ArrayUtils;
@@ -118,20 +118,16 @@ public class ProcessCsvLine<T> implements Runnable {
      * @throws CsvBeanIntrospectionException Thrown on error creating bean.
      * @throws CsvBadConverterException If a custom converter cannot be
      *   initialized properly
-     * @throws CsvDataTypeMismatchException If the source data cannot be
-     *   converted to the type of the destination field
-     * @throws CsvRequiredFieldEmptyException If a mandatory field is empty in
-     *   the input file
-     * @throws CsvConstraintViolationException When the internal structure of
-     *   data would be violated by the data in the CSV file
-     * @throws CsvValidationException If a user-supplied validator declares the
-     *   data to be invalid
+     * @throws CsvFieldAssignmentException A more specific subclass of this
+     *   exception is thrown for any problem decoding and assigning a field
+     *   of the input to a bean field
+     * @throws CsvChainedException If multiple exceptions are thrown for the
+     * same input line
      */
     private T processLine()
             throws CsvBeanIntrospectionException,
-            CsvBadConverterException, CsvDataTypeMismatchException,
-            CsvRequiredFieldEmptyException, CsvConstraintViolationException,
-            CsvValidationException {
+            CsvBadConverterException, CsvFieldAssignmentException,
+            CsvChainedException {
         return mapper.populateNewBean(line);
     }
 }
diff --git a/src/main/java/com/opencsv/bean/concurrent/SingleLineReader.java b/src/main/java/com/opencsv/bean/concurrent/SingleLineReader.java
index 1ba7a1c..b6ddd17 100644
--- a/src/main/java/com/opencsv/bean/concurrent/SingleLineReader.java
+++ b/src/main/java/com/opencsv/bean/concurrent/SingleLineReader.java
@@ -3,6 +3,7 @@ package com.opencsv.bean.concurrent;
 import com.opencsv.CSVReader;
 import com.opencsv.bean.CsvToBean;
 import com.opencsv.exceptions.CsvValidationException;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.IOException;
 
@@ -31,7 +32,7 @@ public class SingleLineReader {
     }
 
     private boolean isCurrentLineEmpty() {
-        return line.length == 0 || (line.length == 1 && line[0].isEmpty());
+        return line.length == 0 || (line.length == 1 && StringUtils.isEmpty(line[0]));
     }
 
     /**
diff --git a/src/main/java/com/opencsv/bean/customconverter/ConvertFrenchToBoolean.java b/src/main/java/com/opencsv/bean/customconverter/ConvertFrenchToBoolean.java
new file mode 100644
index 0000000..5c9e96a
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/customconverter/ConvertFrenchToBoolean.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Andrew Rucker Jones.
+ *
+ * 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.opencsv.bean.customconverter;
+
+/**
+ * This class converts common French representations of boolean values into a
+ * {@link Boolean}.
+ * This class also demonstrates how to localize booleans for any other language.
+ *
+ * @param <T> Type of the bean to be manipulated
+ * @param <I> Type of the index into multivalued fields
+ * 
+ * @author Andrew Rucker Jones
+ * @since 5.4
+ */
+public class ConvertFrenchToBoolean<T, I> extends ConverterLanguageToBoolean<T, I> {
+
+    /**
+     * Silence code style checker by adding a useless constructor.
+     */
+    public ConvertFrenchToBoolean() {
+    }
+
+    private static final String VRAI = "vrai";
+    private static final String FAUX = "faux";
+    private static final String[] TRUE_STRINGS = {VRAI, "oui", "o", "1", "v"};
+    private static final String[] FALSE_STRINGS = {FAUX, "non", "n", "0", "f"};
+
+    @Override
+    protected String getLocalizedTrue() { return VRAI; }
+
+    @Override
+    protected String getLocalizedFalse() { return FAUX; }
+
+    @Override
+    protected String[] getAllLocalizedTrueValues() { return TRUE_STRINGS; }
+
+    @Override
+    protected String[] getAllLocalizedFalseValues() { return FALSE_STRINGS; }
+}
diff --git a/src/main/java/com/opencsv/bean/customconverter/ConvertGermanToBoolean.java b/src/main/java/com/opencsv/bean/customconverter/ConvertGermanToBoolean.java
index 0dfa811..3cdb754 100644
--- a/src/main/java/com/opencsv/bean/customconverter/ConvertGermanToBoolean.java
+++ b/src/main/java/com/opencsv/bean/customconverter/ConvertGermanToBoolean.java
@@ -15,17 +15,9 @@
  */
 package com.opencsv.bean.customconverter;
 
-import com.opencsv.bean.AbstractBeanField;
-import com.opencsv.exceptions.CsvDataTypeMismatchException;
-import java.util.ResourceBundle;
-import org.apache.commons.beanutils.ConversionException;
-import org.apache.commons.beanutils.Converter;
-import org.apache.commons.beanutils.converters.BooleanConverter;
-import org.apache.commons.lang3.StringUtils;
-
 /**
  * This class converts common German representations of boolean values into a
- * Boolean.
+ * {@link Boolean}.
  * This class also demonstrates how to localize booleans for any other language.
  *
  * @param <T> Type of the bean to be manipulated
@@ -34,10 +26,12 @@ import org.apache.commons.lang3.StringUtils;
  * @author Andrew Rucker Jones
  * @since 3.8
  */
-public class ConvertGermanToBoolean<T, I> extends AbstractBeanField<T, I> {
+public class ConvertGermanToBoolean<T, I> extends ConverterLanguageToBoolean<T, I> {
     
-    protected static final String WAHR = "wahr";
-    protected static final String FALSCH = "falsch";
+    private static final String WAHR = "wahr";
+    private static final String FALSCH = "falsch";
+    private static final String[] TRUE_STRINGS = {WAHR, "ja", "j", "1", "w"};
+    private static final String[] FALSE_STRINGS = {FALSCH, "nein", "n", "0", "f"};
 
     /**
      * Silence code style checker by adding a useless constructor.
@@ -45,65 +39,15 @@ public class ConvertGermanToBoolean<T, I> extends AbstractBeanField<T, I> {
     public ConvertGermanToBoolean() {
     }
 
-    /**
-     * Converts German text into a Boolean.
-     * The comparisons are case-insensitive. The recognized pairs are
-     * wahr/falsch, w/f, ja/nein, j/n, 1/0.
-     *
-     * @param value String that should represent a Boolean
-     * @return Boolean
-     * @throws CsvDataTypeMismatchException   If anything other than the
-     *                                        explicitly translated pairs is found
-     */
     @Override
-    protected Object convert(String value)
-            throws CsvDataTypeMismatchException {
-        if (StringUtils.isEmpty(value)) {
-            return null;
-        }
-        String[] trueStrings = {WAHR, "ja", "j", "1", "w"};
-        String[] falseStrings = {FALSCH, "nein", "n", "0", "f"};
-        Converter bc = new BooleanConverter(trueStrings, falseStrings);
-        try {
-            return bc.convert(Boolean.class, value.trim());
-        } catch (ConversionException e) {
-            CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
-                    value, field.getType(), ResourceBundle
-                            .getBundle("convertGermanToBoolean", errorLocale)
-                            .getString("input.not.boolean"));
-            csve.initCause(e);
-            throw csve;
-        }
-    }
-    
-    /**
-     * This method takes the current value of the field in question in the bean
-     * passed in and converts it to a string.
-     * This implementation returns true/false values in German.
-     * 
-     * @return "wahr" if true, "falsch" if false
-     * @throws CsvDataTypeMismatchException If the field is not a {@code boolean}
-     *   or {@link java.lang.Boolean}
-     */
+    protected String getLocalizedTrue() { return WAHR; }
+
     @Override
-    protected String convertToWrite(Object value)
-            throws CsvDataTypeMismatchException {
-        String result = "";
-        try {
-            if(value != null) {
-                Boolean b = (Boolean) value;
-                result = b?WAHR:FALSCH;
-            }
-        }
-        catch(ClassCastException e) {
-            CsvDataTypeMismatchException csve =
-                    new CsvDataTypeMismatchException(ResourceBundle
-                            .getBundle("convertGermanToBoolean", errorLocale)
-                            .getString("field.not.boolean"));
-            csve.initCause(e);
-            throw csve;
-        }
-        return result;
-    }
+    protected String getLocalizedFalse() { return FALSCH; }
 
+    @Override
+    protected String[] getAllLocalizedTrueValues() { return TRUE_STRINGS; }
+
+    @Override
+    protected String[] getAllLocalizedFalseValues() { return FALSE_STRINGS; }
 }
diff --git a/src/main/java/com/opencsv/bean/customconverter/ConverterLanguageToBoolean.java b/src/main/java/com/opencsv/bean/customconverter/ConverterLanguageToBoolean.java
new file mode 100644
index 0000000..3631017
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/customconverter/ConverterLanguageToBoolean.java
@@ -0,0 +1,114 @@
+package com.opencsv.bean.customconverter;
+
+import com.opencsv.bean.AbstractBeanField;
+import com.opencsv.exceptions.CsvDataTypeMismatchException;
+import org.apache.commons.beanutils.ConversionException;
+import org.apache.commons.beanutils.Converter;
+import org.apache.commons.beanutils.converters.BooleanConverter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ResourceBundle;
+
+/**
+ * A base class for any converter to and from booleans when the string
+ * values have been or should be localized to a specific language.
+ * @param <T> Type of the bean to be manipulated
+ * @param <I> Type of the index into multivalued fields
+ * @author Andrew Rucker Jones
+ */
+abstract public class ConverterLanguageToBoolean<T, I> extends AbstractBeanField<T, I> {
+
+    /**
+     * This is the string for "true" in the localized language.
+     * This value will be used on converting from a boolean to a string.
+     *
+     * @return The canonical name of {@code true} in this language
+     */
+    abstract protected String getLocalizedTrue();
+
+    /**
+     * This is the string for "false" in the localized language.
+     * This value will be used on converting from a boolean to a string.
+     *
+     * @return The canonical name of {@code false} in this language
+     */
+    abstract protected String getLocalizedFalse();
+
+    /**
+     * This represents a list of all values accepted as "true".
+     * Any language will have more than one way to say "true", such as
+     * "yes", "y", or "1". This array should list all possibilities.
+     * Comparison is done in a case-insensitive fashion.
+     *
+     * @return An array of all "true" strings
+     */
+    abstract protected String[] getAllLocalizedTrueValues();
+
+    /**
+     * This represents a list of all values accepted as "false".
+     * Any language will have more than one way to say "false", such as
+     * "no", "n", or "0". This array should list all possibilities.
+     * Comparison is done in a case-insensitive fashion.
+     *
+     * @return An array of all "false" strings
+     */
+    abstract protected String[] getAllLocalizedFalseValues();
+
+    /**
+     * Converts localized text into a {@link Boolean}.
+     * The comparisons are case-insensitive.
+     *
+     * @param value String that should represent a Boolean
+     * @return Boolean
+     * @throws CsvDataTypeMismatchException   If anything other than the
+     *                                        explicitly translated pairs is found
+     */
+    @Override
+    protected Object convert(String value)
+            throws CsvDataTypeMismatchException {
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+        Converter bc = new BooleanConverter(getAllLocalizedTrueValues(), getAllLocalizedFalseValues());
+        try {
+            return bc.convert(Boolean.class, value.trim());
+        } catch (ConversionException e) {
+            CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
+                    value, field.getType(), ResourceBundle
+                    .getBundle("convertLanguageToBoolean", errorLocale)
+                    .getString("input.not.boolean"));
+            csve.initCause(e);
+            throw csve;
+        }
+    }
+
+    /**
+     * This method takes the current value of the field in question in the bean
+     * passed in and converts it to a string.
+     * This implementation returns true/false values in the localized language.
+     *
+     * @return Localized text value for "true" or "false"
+     * @throws CsvDataTypeMismatchException If the field is not a {@code boolean}
+     *   or {@link Boolean}
+     */
+    @Override
+    protected String convertToWrite(Object value)
+            throws CsvDataTypeMismatchException {
+        String result = "";
+        try {
+            if(value != null) {
+                Boolean b = (Boolean) value;
+                result = b? getLocalizedTrue() : getLocalizedFalse();
+            }
+        }
+        catch(ClassCastException e) {
+            CsvDataTypeMismatchException csve =
+                    new CsvDataTypeMismatchException(ResourceBundle
+                            .getBundle("convertLanguageToBoolean", errorLocale)
+                            .getString("field.not.boolean"));
+            csve.initCause(e);
+            throw csve;
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java b/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java
new file mode 100644
index 0000000..d246f0e
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java
@@ -0,0 +1,39 @@
+package com.opencsv.bean.processor;
+
+/**
+ * StringProcessor that converts the empty or blank strings to a desired value string.
+ * This is useful when you want a default value.
+ * <p>
+ * A sample of this can be found in the unit test ProcessorTestBean and is annotated as follows.
+ * <p>
+ * <pre>
+ *     &#64;PreAssignmentProcessor(processor = ConvertEmptyOrBlankStringsToDefault.class, paramString = "-1")
+ *     &#64;CsvBindByName(column = "id")
+ *     private int beanId;
+ * </pre>
+ *
+ * @author Scott Conway
+ * @since 5.4
+ */
+public class ConvertEmptyOrBlankStringsToDefault implements StringProcessor {
+    String defaultValue;
+
+    /**
+     * Default constructor
+     */
+    public ConvertEmptyOrBlankStringsToDefault() {
+    }
+
+    @Override
+    public String processString(String value) {
+        if (value == null || value.trim().isEmpty()) {
+            return defaultValue;
+        }
+        return value;
+    }
+
+    @Override
+    public void setParameterString(String value) {
+        defaultValue = value;
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java b/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java
new file mode 100644
index 0000000..5d7caa5
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java
@@ -0,0 +1,44 @@
+package com.opencsv.bean.processor;
+
+/**
+ * StringProcessor that converts the empty or blank strings to a literal null string.
+ * This is useful when you prefer null in a particular variable.
+ * <p>
+ * A sample of this can be found in the unit test ProcessorTestBean and is annotated as follows.
+ * <p>
+ * <pre>
+ *     &#64;PreAssignmentProcessor(processor = ConvertEmptyOrBlankStringsToNull.class)
+ *     &#64;CsvBindByName(column = "name")
+ *     private String beanName;
+ *  </pre>
+ *
+ * @author Scott Conway
+ * @since 5.4
+ */
+public class ConvertEmptyOrBlankStringsToNull implements StringProcessor {
+
+    /**
+     * Default Constructor.
+     */
+    public ConvertEmptyOrBlankStringsToNull() {
+    }
+
+    @Override
+    public String processString(String value) {
+        if (value == null || value.trim().isEmpty()) {
+            return null;
+        }
+        return value;
+    }
+
+    /**
+     * This method is unused in this implementation as we are converting to null.
+     * Any calls to this method are ignored.
+     *
+     * @param value Information used by the processor to process the string
+     */
+    @Override
+    public void setParameterString(String value) {
+        // not needed
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/processor/ConvertWordNullToNull.java b/src/main/java/com/opencsv/bean/processor/ConvertWordNullToNull.java
new file mode 100644
index 0000000..5920683
--- /dev/null
+++ b/src/main/java/com/opencsv/bean/processor/ConvertWordNullToNull.java
@@ -0,0 +1,42 @@
+package com.opencsv.bean.processor;
+
+/**
+ * StringProcessor that converts the string value "null" to a literal null string.
+ * This is useful when you are dealing with csv files that actually use the word null.
+ * <p>
+ * A sample of this can be found in the Integration tests FR138MockBean and is annotated as follows.
+ * <p>
+ * <pre>
+ *     &#64;PreAssignmentProcessor(processor = ConvertWordNullToNull.class)
+ *     private int num;
+ * </pre>
+ * Without the annotation an CSVMalformedException is thrown when trying to conver the string "null" to an int.
+ * But with it is considered a null String and the int gets a default value of 0.
+ *
+ * @author Scott Conway
+ * @since 5.4
+ */
+public class ConvertWordNullToNull implements StringProcessor {
+
+    /**
+     * Default Constructor.
+     */
+    public ConvertWordNullToNull() {
+    }
+
+    @Override
+    public String processString(String value) {
+        return "null".equalsIgnoreCase(value) ? null : value;
+    }
+
+    /**
+     * This method is unused in this implementation as we are converting to null.
+     * Any calls to this method are ignored.
+     *
+     * @param value Information used by the processor to process the string
+     */
+    @Override
+    public void setParameterString(String value) {
+        // Unused in this case as all we care about is the word "null"
+    }
+}
diff --git a/src/main/java/com/opencsv/bean/util/OpencsvUtils.java b/src/main/java/com/opencsv/bean/util/OpencsvUtils.java
index e2aeff5..9951b2a 100644
--- a/src/main/java/com/opencsv/bean/util/OpencsvUtils.java
+++ b/src/main/java/com/opencsv/bean/util/OpencsvUtils.java
@@ -19,13 +19,12 @@ import com.opencsv.ICSVParser;
 import com.opencsv.bean.*;
 import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
 import com.opencsv.exceptions.CsvBadConverterException;
+import com.opencsv.exceptions.CsvChainedException;
 import com.opencsv.exceptions.CsvException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.reflect.FieldUtils;
 
-import java.util.IllegalFormatException;
-import java.util.Locale;
-import java.util.ResourceBundle;
+import java.util.*;
 import java.util.concurrent.BlockingQueue;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -59,9 +58,11 @@ public final class OpencsvUtils {
      * @param type The class of the bean for which the mapping strategy is sought
      * @param errorLocale The locale to use for all error messages. If null, the
      *   default locale is used.
+     * @param profile The profile to use when configuring bean fields
      * @return A functional mapping strategy for the bean in question
      */
-    public static <T> MappingStrategy<T> determineMappingStrategy(Class<? extends T> type, Locale errorLocale) {
+    public static <T> MappingStrategy<T> determineMappingStrategy(
+            Class<? extends T> type, Locale errorLocale, String profile) {
         // Check for annotations
         boolean positionAnnotationsPresent = Stream.of(FieldUtils.getAllFields(type)).anyMatch(
                 f -> f.isAnnotationPresent(CsvBindByPosition.class)
@@ -74,6 +75,7 @@ public final class OpencsvUtils {
                 new ColumnPositionMappingStrategy<>() :
                 new HeaderColumnNameMappingStrategy<>();
         mappingStrategy.setErrorLocale(errorLocale);
+        mappingStrategy.setProfile(profile);
         mappingStrategy.setType(type);
         return mappingStrategy;
     }
@@ -115,15 +117,20 @@ public final class OpencsvUtils {
             CsvExceptionHandler exceptionHandler, BlockingQueue<OrderedObject<CsvException>> queue) {
         e.setLineNumber(lineNumber);
         CsvException capturedException = null;
-        try {
-            capturedException = exceptionHandler.handleException(e);
-        } catch (CsvException csve) {
-            capturedException = csve;
-            throw new RuntimeException(csve);
-        } finally {
-            if (capturedException != null) {
-                queueRefuseToAcceptDefeat(queue,
-                        new OrderedObject<>(lineNumber, capturedException));
+        List<CsvException> exceptionList = e instanceof CsvChainedException ?
+                Collections.<CsvException>unmodifiableList(((CsvChainedException)e).getExceptionChain()) :
+                Collections.singletonList(e);
+        for (CsvException iteratedException : exceptionList) {
+            try {
+                capturedException = exceptionHandler.handleException(iteratedException);
+            } catch (CsvException csve) {
+                capturedException = csve;
+                throw new RuntimeException(csve);
+            } finally {
+                if (capturedException != null) {
+                    queueRefuseToAcceptDefeat(queue,
+                            new OrderedObject<>(lineNumber, capturedException));
+                }
             }
         }
     }
diff --git a/src/main/java/com/opencsv/exceptions/CsvChainedException.java b/src/main/java/com/opencsv/exceptions/CsvChainedException.java
new file mode 100644
index 0000000..7315e81
--- /dev/null
+++ b/src/main/java/com/opencsv/exceptions/CsvChainedException.java
@@ -0,0 +1,77 @@
+package com.opencsv.exceptions;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An exception class for collecting multiple exceptions.
+ * For internal use only.
+ *
+ * @author Andrew Rucker Jones
+ * @since 5.3
+ */
+public class CsvChainedException extends CsvException {
+
+    private final List<CsvFieldAssignmentException> exceptionChain = new LinkedList<>();
+
+    /**
+     * Constructor.
+     * @param csve The first exception for the list being collected.
+     *             Must not be {@code null}.
+     */
+    public CsvChainedException(CsvFieldAssignmentException csve) {
+        exceptionChain.add(csve);
+    }
+
+    /**
+     * Add an exception to the chain of collections.
+     * @param csve Exception to be added to this chain.
+     *             Must not be {@code null}.
+     */
+    public void add(CsvFieldAssignmentException csve) {
+        exceptionChain.add(csve);
+    }
+
+    /**
+     * @return A list of all exceptions collected
+     */
+    public List<CsvFieldAssignmentException> getExceptionChain() {
+        return exceptionChain;
+    }
+
+    /** Sets the line for all exceptions collected. */
+    // The rest of the Javadoc is inherited
+    @Override
+    public void setLine(String[] line) {
+        super.setLine(line);
+        exceptionChain.forEach(e -> e.setLine(line));
+    }
+
+    /** Sets the line number for all exceptions collected. */
+    // The rest of the Javadoc is inherited
+    @Override
+    public void setLineNumber(long lineNumber) {
+        super.setLineNumber(lineNumber);
+        exceptionChain.forEach(e -> e.setLineNumber(lineNumber));
+    }
+
+    /**
+     * Convenience method that checks if the chain only has a single exception.
+     *
+     * @return {@code true} if chain has only a single exception,
+     *   {@code false} otherwise
+     */
+    public boolean hasOnlyOneException() {
+        return exceptionChain.size() == 1;
+    }
+
+    /**
+     * Convenience method to return the first exception from the exception chain.
+     *
+     * @return {@link CsvFieldAssignmentException} at the first position in the
+     * list, {@code null} otherwise.
+     */
+    public CsvFieldAssignmentException getFirstException() {
+        return exceptionChain.isEmpty() ? null : exceptionChain.get(0);
+    }
+}
diff --git a/src/main/java/com/opencsv/exceptions/CsvConstraintViolationException.java b/src/main/java/com/opencsv/exceptions/CsvConstraintViolationException.java
index 390cc70..26b7d40 100644
--- a/src/main/java/com/opencsv/exceptions/CsvConstraintViolationException.java
+++ b/src/main/java/com/opencsv/exceptions/CsvConstraintViolationException.java
@@ -30,7 +30,7 @@ package com.opencsv.exceptions;
  * @author Andrew Rucker Jones
  * @since 3.8
  */
-public class CsvConstraintViolationException extends CsvException {
+public class CsvConstraintViolationException extends CsvFieldAssignmentException {
     private static final long serialVersionUID = 1L;
 
     private transient final Object sourceObject;
diff --git a/src/main/java/com/opencsv/exceptions/CsvDataTypeMismatchException.java b/src/main/java/com/opencsv/exceptions/CsvDataTypeMismatchException.java
index 563bda1..80b1bad 100644
--- a/src/main/java/com/opencsv/exceptions/CsvDataTypeMismatchException.java
+++ b/src/main/java/com/opencsv/exceptions/CsvDataTypeMismatchException.java
@@ -22,7 +22,7 @@ package com.opencsv.exceptions;
  * @author Andrew Rucker Jones
  * @since 3.8
  */
-public class CsvDataTypeMismatchException extends CsvException {
+public class CsvDataTypeMismatchException extends CsvFieldAssignmentException {
     private static final long serialVersionUID = 1L;
     
     private transient final Object sourceObject;
diff --git a/src/main/java/com/opencsv/exceptions/CsvFieldAssignmentException.java b/src/main/java/com/opencsv/exceptions/CsvFieldAssignmentException.java
new file mode 100644
index 0000000..33555bd
--- /dev/null
+++ b/src/main/java/com/opencsv/exceptions/CsvFieldAssignmentException.java
@@ -0,0 +1,22 @@
+package com.opencsv.exceptions;
+
+/**
+ * Superclass for checked exceptions that can be thrown while trying to decode
+ * and assign a single field.
+ *
+ * @author Andrew Rucker Jones
+ * @since 5.3
+ */
+public abstract class CsvFieldAssignmentException extends CsvException {
+
+    /** Nullary constructor. */
+    public CsvFieldAssignmentException() {}
+
+    /**
+     * Constructor for initializing an error message.
+     * @param message Human-readable error message
+     */
+    public CsvFieldAssignmentException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/com/opencsv/exceptions/CsvRequiredFieldEmptyException.java b/src/main/java/com/opencsv/exceptions/CsvRequiredFieldEmptyException.java
index 7812013..19e3b1a 100644
--- a/src/main/java/com/opencsv/exceptions/CsvRequiredFieldEmptyException.java
+++ b/src/main/java/com/opencsv/exceptions/CsvRequiredFieldEmptyException.java
@@ -28,7 +28,7 @@ import org.apache.commons.collections4.list.UnmodifiableList;
  * @author Andrew Rucker Jones
  * @since 3.8
  */
-public class CsvRequiredFieldEmptyException extends CsvException {
+public class CsvRequiredFieldEmptyException extends CsvFieldAssignmentException {
     private static final long serialVersionUID = 1L;
 
     private final Class<?> beanClass;
diff --git a/src/main/java/com/opencsv/exceptions/CsvValidationException.java b/src/main/java/com/opencsv/exceptions/CsvValidationException.java
index f016ad8..a7bbcdd 100644
--- a/src/main/java/com/opencsv/exceptions/CsvValidationException.java
+++ b/src/main/java/com/opencsv/exceptions/CsvValidationException.java
@@ -1,12 +1,13 @@
 package com.opencsv.exceptions;
 
 /**
- * Exception thrown by a LineValidator or LineValidatorAggregator when a single line is invalid.
+ * Exception thrown by a {@link com.opencsv.validators.LineValidator} or
+ * {@link com.opencsv.validators.LineValidatorAggregator} when a single line is invalid.
  *
  * @author Scott Conway
  * @since 5.0
  */
-public class CsvValidationException extends CsvException {
+public class CsvValidationException extends CsvFieldAssignmentException {
     private static final long serialVersionUID = 1L;
 
     /**
@@ -19,7 +20,7 @@ public class CsvValidationException extends CsvException {
     /**
      * Constructor that allows for a human readable message.
      *
-     * @param message - error text.
+     * @param message Error text.
      */
     public CsvValidationException(String message) {
         super(message);
diff --git a/src/main/java/com/opencsv/stream/reader/LineReader.java b/src/main/java/com/opencsv/stream/reader/LineReader.java
index 6f98545..40dafe1 100644
--- a/src/main/java/com/opencsv/stream/reader/LineReader.java
+++ b/src/main/java/com/opencsv/stream/reader/LineReader.java
@@ -12,7 +12,7 @@ import java.io.IOException;
  * This class allows the user to determine if they wish to keep or
  * remove them from the data being read.
  *
- * @author scott
+ * @author Scott Conway
  * @since 3.3
  */
 
diff --git a/src/main/resources/convertGermanToBoolean.properties b/src/main/resources/convertLanguageToBoolean.properties
similarity index 100%
rename from src/main/resources/convertGermanToBoolean.properties
rename to src/main/resources/convertLanguageToBoolean.properties
diff --git a/src/main/resources/convertGermanToBoolean_de.properties b/src/main/resources/convertLanguageToBoolean_de.properties
similarity index 100%
rename from src/main/resources/convertGermanToBoolean_de.properties
rename to src/main/resources/convertLanguageToBoolean_de.properties
diff --git a/src/main/resources/convertGermanToBoolean_en.properties b/src/main/resources/convertLanguageToBoolean_en.properties
similarity index 100%
rename from src/main/resources/convertGermanToBoolean_en.properties
rename to src/main/resources/convertLanguageToBoolean_en.properties
diff --git a/src/main/resources/convertGermanToBoolean_fr.properties b/src/main/resources/convertLanguageToBoolean_fr.properties
similarity index 100%
rename from src/main/resources/convertGermanToBoolean_fr.properties
rename to src/main/resources/convertLanguageToBoolean_fr.properties
diff --git a/src/main/resources/convertGermanToBoolean_pt_BR.properties b/src/main/resources/convertLanguageToBoolean_pt_BR.properties
similarity index 100%
rename from src/main/resources/convertGermanToBoolean_pt_BR.properties
rename to src/main/resources/convertLanguageToBoolean_pt_BR.properties
diff --git a/src/main/resources/opencsv.properties b/src/main/resources/opencsv.properties
index 6f5b9f2..0f3f765 100644
--- a/src/main/resources/opencsv.properties
+++ b/src/main/resources/opencsv.properties
@@ -22,6 +22,7 @@ conversion.impossible=Conversion of %1$s to %2$s failed.
 csvdate.not.date=@CsvDate annotation used on non-date field (%s).
 csvnumber.not.number=The annotation CsvNumber was used on a type not derived from java.lang.Number.
 csvreader.null=Unable to instantiate IterableCSVToBeanBuilder because there is no CSVReader defined.
+csvreaderheaderaware.impossible=It was not possible to initialize a CSVReaderHeaderAware.
 custom.converter.invalid=There was a problem instantiating the custom converter %s.
 define.separator=The separator character must be defined!
 error.introspecting.beans=There was an error while manipulating the bean to be written.
@@ -37,6 +38,8 @@ header.nonexistant=No column found for header [%s].
 header.required.field.absent=Header is missing required fields [%s]. The list of headers encountered is [%s].
 ignore.field.inconsistent=When specifying a field to ignore, both the type and the field must be non-null, and the field must be a member of the type, either directly or through inheritance.
 illegal.enum.value=The value [%1$s] is not a valid value for the enumeration type %2$s.
+invalid.currency.value=The value [%1$s] is not a valid ISO 4217 currency code.
+invalid.uuid.value=The value [%1$s] is not a valid pattern for UUID.
 invalid.collection.type=The specified type for the collection is either unknown or does not implement java.util.Collection: %s
 invalid.date.format.string=The specified format string does not parse properly or cannot be used with the supplied data. The format string is: %s
 invalid.multivaluedmap.type=The specified type for the map is either unknown or does not implement org.apache.commons.collections4.MultiValuedMap: %s
@@ -53,6 +56,8 @@ numberformat.not.decimalformat=A java.text.DecimalFormat is required in Converte
 parsing.error=Error parsing CSV.
 parsing.error.full=Error parsing CSV line: %1$d, values: %2$s
 parsing.error.linenumber=Error parsing CSV line: %1$d. [%2$s]
+profile.not.found.date=No CsvDate annotation was found for the profile "%s".
+profile.not.found.number=No CsvNumber annotation was found for the profile "%s".
 read.only.iterator=This is a read-only iterator.
 reader.null=The Reader must always be non-null.
 recursion.binding.mutually.exclusive=Recursion and binding annotations are mutually exclusive.
diff --git a/src/main/resources/opencsv_de.properties b/src/main/resources/opencsv_de.properties
index 0f17af4..98ade7d 100644
--- a/src/main/resources/opencsv_de.properties
+++ b/src/main/resources/opencsv_de.properties
@@ -22,6 +22,7 @@ conversion.impossible=Konvertierung von %1$s in %2$s fehlgeschlagen.
 csvdate.not.date=Annotation @CsvDate f\u00fcr ein Feld nicht zeitlichen Typs verwendet (%s).
 csvnumber.not.number=Die Annotation CsvNumber wurde für einen Typ angewendet, der nicht von java.lang.Number abgeleitet ist.
 csvreader.null=IterableCSVToBeanBuilder konnte deshalb nicht instatiiert werden, weil noch kein CSVReader definiert ist.
+csvreaderheaderaware.impossible=Es war unm\u00f6glich, CSVReaderHeaderAware zu instantiieren.
 custom.converter.invalid=Der Eigenkonvertierer %s konnte nicht instantiiert werden.
 define.separator=Das Trennzeichen muss definiert sein!
 error.introspecting.beans=Es gab beim Manipulieren der zu schreibenden Bean einen Fehler.
@@ -37,6 +38,8 @@ header.nonexistant=Keine Spalte f\u00fcr \u00dcberschrift [%s] gefunden.
 header.required.field.absent=Der \u00dcberschriftzeile fehlen die Pflichtfelder [%s]. Die Liste der gefundenen \u00dcberschriften enth\u00e4lt [%s].
 ignore.field.inconsistent=Bei Angabe eines zu ignorierenden Feldes dürfen weder Typ noch Feld Null sein, und das Feld muss entweder direkt oder indirekt über die Vererbung ein Mitglied vom Typ sein.
 illegal.enum.value=Der Wert [%1$s] ist für den Aufzählungstyp %2$s ungültig.
+invalid.currency.value=[%1$s] is kein gültiger ISO-4217-Code.
+invalid.uuid.value=The value [%1$s] is not a valid pattern for UUID.
 invalid.collection.type=Der angegebene Sammeltyp (Collection) ist entweder unbekannt oder implementiert java.util.Collection nicht: %s
 invalid.date.format.string=Die angegebene Formattierungszeichenkette ist ungültig oder passt nicht zu den gegebenen Daten. Die Formattierungszeichenkette lautet: %s
 invalid.multivaluedmap.type=Der angegebene \u00dcbersetzungstyp (Map) ist entweder unbekannt oder implementiert org.apache.commons.collections4.MultiValuedMap nicht: %s
@@ -53,6 +56,8 @@ numberformat.not.decimalformat=Ein java.text.DecimalFormat wird in ConverterNumb
 parsing.error=Fehler beim Parsen der CSV-Eingabe.
 parsing.error.full=Fehler beim Parsen der CSV-Zeile: %1$d, Werte: %2$s
 parsing.error.linenumber=Fehler beim Parsen der CSV-Zeile: %1$d. [%2$s]
+profile.not.found.date=Es wurde keine CsvDate-Annotation f\u00fcr das Profil "%s" gefunden.
+profile.not.found.number=Es wurde keine CsvNumber-Annotation f\u00fcr das Profil "%s" gefunden.
 read.only.iterator=Dieser Iterator darf nicht ver\u00e4ndert werden.
 reader.null=Der Reader darf nicht null sein.
 recursion.binding.mutually.exclusive=Recursion darf nicht zusammen mit den bindenden Annotationen verwendet werden.
diff --git a/src/main/resources/opencsv_en.properties b/src/main/resources/opencsv_en.properties
index 12f6952..2c98bc0 100644
--- a/src/main/resources/opencsv_en.properties
+++ b/src/main/resources/opencsv_en.properties
@@ -22,6 +22,7 @@ conversion.impossible=Conversion of %1$s to %2$s failed.
 csvdate.not.date=@CsvDate annotation used on non-date field (%s).
 csvnumber.not.number=The annotation CsvNumber was used on a type not derived from java.lang.Number.
 csvreader.null=Unable to instantiate IterableCSVToBeanBuilder because there is no CSVReader defined.
+csvreaderheaderaware.impossible=It was not possible to initialize a CSVReaderHeaderAware.
 custom.converter.invalid=There was a problem instantiating the custom converter %s.
 define.separator=The separator character must be defined!
 error.introspecting.beans=There was an error while manipulating the bean to be written.
@@ -37,6 +38,7 @@ header.nonexistant=No column found for header [%s].
 header.required.field.absent=Header is missing required fields [%s]. The list of headers encountered is [%s].
 ignore.field.inconsistent=When specifying a field to ignore, both the type and the field must be non-null, and the field must be a member of the type, either directly or through inheritance.
 illegal.enum.value=The value [%1$s] is not a valid value for the enumeration type %2$s.
+invalid.currency.value=The value [%1$s] is not a valid ISO 4217 currency code.
 invalid.collection.type=The specified type for the collection is either unknown or does not implement java.util.Collection: %s
 invalid.date.format.string=The specified format string does not parse properly or cannot be used with the supplied data. The format string is: %s
 invalid.multivaluedmap.type=The specified type for the map is either unknown or does not implement org.apache.commons.collections4.MultiValuedMap: %s
@@ -44,6 +46,7 @@ invalid.number.pattern=The pattern [%s] is not valid according to the rules of j
 invalid.one.parameter.format.string=The specified format string is not valid for one parameter of type string: %s
 invalid.range.definition=The specified range definition [%s] is invalid.
 invalid.regex=The specified regular expression is invalid: %s
+invalid.uuid.value=The value [%1$s] is not a valid pattern for UUID.
 map.cannot.be.instantiated=A multi-valued map of type [%s] cannot be instantiated with a nullary constructor.
 matching=Matches [%s]
 multiline.limit.broken=Encountered single record with more lines than the specified upper limit of %d (row %d). Context: %s
@@ -53,6 +56,8 @@ numberformat.not.decimalformat=A java.text.DecimalFormat is required in Converte
 parsing.error=Error parsing CSV.
 parsing.error.full=Error parsing CSV line: %1$d, values: %2$s
 parsing.error.linenumber=Error parsing CSV line: %1$d. [%2$s]
+profile.not.found.date=No CsvDate annotation was found for the profile "%s".
+profile.not.found.number=No CsvNumber annotation was found for the profile "%s".
 read.only.iterator=This is a read-only iterator.
 reader.null=The Reader must always be non-null.
 recursion.binding.mutually.exclusive=Recursion and binding annotations are mutually exclusive.
diff --git a/src/main/resources/opencsv_fr.properties b/src/main/resources/opencsv_fr.properties
index 7b465cf..349bce8 100644
--- a/src/main/resources/opencsv_fr.properties
+++ b/src/main/resources/opencsv_fr.properties
@@ -8,6 +8,7 @@ conversion.impossible=La conversion de %1$s vers %2$s a échoué.
 csvdate.not.date=Annotation @CsvDate utilisée sur un champ de type incompatible (%s).
 csvnumber.not.number=Annotation @CsvNumber utilisée sur un champ dont le type n''étend pas java.lang.Number.
 csvreader.null=Instantiation de IterableCSVToBeanBuilder impossible: aucun CSVReader défini.
+csvreaderheaderaware.impossible=Impossible d''initialiser un CSVReaderHeaderAware.
 custom.converter.invalid=Erreur lors de l''instantiation du convertisseur personnalisé %s.
 define.separator=Le caractère de séparation doit être défini.
 error.introspecting.beans=Erreur lors de l''introspection du bean à écrire.
@@ -23,6 +24,7 @@ header.nonexistant=Pas de colonne pour l''en-tête [%s].
 header.required.field.absent=En-tête manquant pour le champ [%s]. Liste des en-têtes trouvés [%s].
 ignore.field.inconsistent=Lors de la spécification d''un champ à ignorer, le type et le champ doivent être non nulls, et le champ doit être membre du type, directement ou par héritage.
 illegal.enum.value=La valeur [%1$s] n'est pas une valeur valide pour le type d'énumération %2$s. [Google Translate]
+invalid.currency.value=[%1$s] n'est pas un code ISO 4217 valide.
 invalid.collection.type=Le type spécifié pour la collection est inconnu, ou n''implémente pas java.util.Collection: %s.
 invalid.date.format.string=Le format spécifié pour la chaîne de caractères n''est pas parsé correctement ou ne peut être utilisé avec les données fournies. Le format est : %s.
 invalid.multivaluedmap.type=Le type spécifié pour la map est inconnu, ou n''implémente pas org.apache.commons.collections4.MultiValuedMap: %s.
@@ -30,6 +32,7 @@ invalid.number.pattern=Le pattern [%s] n''est pas valide d''après les règles d
 invalid.one.parameter.format.string=Le format spécifié pour la chaîne de caractères n''est pas valide pour le paramètre : %s
 invalid.range.definition=La spécification d''intervalle [%s] est invalide.
 invalid.regex=L''expression régulière spécifiée est invalide :%s.
+invalid.uuid.value=The value [%1$s] is not a valid pattern for UUID.
 map.cannot.be.instantiated=Une map multi-valuée de type [%s] ne peut pas être instanciée avec un constructeur sans paramètre.
 matching=correspond à [%s]
 multiline.limit.broken=Un enregistrment a plus de lignes que le maximum %d (ligne %d). Contexte : %s
@@ -39,6 +42,8 @@ numberformat.not.decimalformat=Un java.text.DecimalFormat est obligatoire dans @
 parsing.error=Erreur de parsing du CSV.
 parsing.error.full=Erreur de parsing du CSV ligne: %1$d, valeur: %2$s
 parsing.error.linenumber=Erreur de parsing du CSV ligne: %1$d. [%2$s]
+profile.not.found.date=Aucune annotation CsvDate n'a été trouvée pour le profil "%s". [Google Translate]
+profile.not.found.number=Aucune annotation CsvNumber n'a été trouvée pour le profil "%s". [Google Translate]
 read.only.iterator=L''itérateur est en lecture seule.
 reader.null=Le Reader ne doit jamais être null.
 recursion.binding.mutually.exclusive=Les annotations de récursion et de binding sont mutuellement exclusives.
diff --git a/src/main/resources/opencsv_pt_BR.properties b/src/main/resources/opencsv_pt_BR.properties
index 9afdc0e..fe4dbfc 100644
--- a/src/main/resources/opencsv_pt_BR.properties
+++ b/src/main/resources/opencsv_pt_BR.properties
@@ -22,6 +22,7 @@ conversion.impossible=Conversao de %1$s para %2$s falhou.
 csvdate.not.date=Anota\u00E7\u00E3o @CsvDate usada em um campo n\u00E3o-data (%s).
 csvnumber.not.number=A anota\u00E7\u00E3o CsvNumber foi usada em um tipo n\u00E3o derivado de java.lang.Number.
 csvreader.null=Incapaz de instanciar IterableCSVToBeanBuilder porque n\u00E3o h\u00E1 CSVReader definido.
+csvreaderheaderaware.impossible=N\u00E3o foi poss\u00EDvel inicializar um CSVReaderHeaderAware.
 custom.converter.invalid=Ocorreu um problema ao instanciar o conversor personlizado %s.
 define.separator=O caractere de separa\u00E7\u00E3o precisa ser definido.
 error.introspecting.beans=Houve um erro ao manipular o bean a ser escrito.
@@ -37,6 +38,7 @@ header.nonexistant=Coluna n\u00E3o encontrada para cabe\u00E7alho [%s].
 header.required.field.absent=Cabe\u00E7alho n\u00E2o cont\u00E9m campos obrigat\u00F3rios [%s]. A lista de cabe\u00E7alhos encontrados \u00E9 [%s].
 ignore.field.inconsistent=Quando especificado um campo a ser ignorado, tanto o tipo quanto o campo devem ser n\u00E3o-nulos. E o campo deve ser um membro do tipo, seja direta ou diretamente.
 illegal.enum.value=O valor [%1$s] não é válido para o enum %2$s.
+invalid.currency.value=[%1$s] não é um código ISO 4217 válido.
 invalid.collection.type=O tipo especificado para a cole\u00E7\u00E3o \u00E9 desconhecida ou n\u00E3o implementa java.util.Collection: %s
 invalid.date.format.string=O formato especificado para a string n\u00E3o pode ser corretamente interpretado ou n\u00E3o pode ser utilisado com os dados fornecidos. O formato da string \u00E9: %s
 invalid.multivaluedmap.type=O tipo especificado para o mapa \u00E9 desconhecido ou n\u00E3o implementa org.apache.commons.collections4.MultiValuedMap: %s
@@ -44,6 +46,7 @@ invalid.number.pattern=O padr\u00E3o [%s] n\u00E3o \u00E9 v\u00E1lido de acordo
 invalid.one.parameter.format.string=O formato de string especificado n\u00E3o \u00E9 v\u00E1lido para um par\u00E2metro do tipo string: %s
 invalid.range.definition=O intervalo definido [%s] \u00E9 inv\u00E1lido.
 invalid.regex=A express\u00E3o regular especificada \u00E9 inv\u00E1lida: %s
+invalid.uuid.value=The value [%1$s] is not a valid pattern for UUID.
 map.cannot.be.instantiated=Um mapa multi-valorado do tipo [%s] n\u00E3o pode ser instanciado com um construtor nulo.
 matching=Correspondentes [%s]
 multiline.limit.broken=Encontrado um \u00FAnico registro com mais linhas do que o limite superior especificado de %d (linha %d). contexto: %s.
@@ -53,6 +56,8 @@ numberformat.not.decimalformat=Um java.text.DecimalFormat \u00E9 requerido em Co
 parsing.error=Erro ao analisar entrada
 parsing.error.full=Erro ao analisar linha: %1$d, valor: %2$s
 parsing.error.linenumber=Erro ao analisar linha: %1$d. [%2$s]
+profile.not.found.date=Nenhuma anotação CsvDate foi encontrada para o perfil "%s". [Google Translate]
+profile.not.found.number=Nenhuma anotação CsvNumber foi encontrada para o perfil "%s". [Google Translate]
 read.only.iterator=Este \u00E9 um iterator somente leitura.
 reader.null=O leitor deve ser sempre n\u00E3o nulo.
 recursion.binding.mutually.exclusive=Anota\u00E7\u00F5es de recurs\u00E3o e de binding s\u00E3o mutualmente exclusivas.
diff --git a/src/site/asciidoc/dev-reading.adoc b/src/site/asciidoc/dev-reading.adoc
index daf9733..2688a10 100644
--- a/src/site/asciidoc/dev-reading.adoc
+++ b/src/site/asciidoc/dev-reading.adoc
@@ -152,13 +152,13 @@ Here we simply name the fields identically to the header names. After that,
 reading is a simple job:
 [source, java]
 ----
-     List<Visitors> beans = new CsvToBeanBuilder(FileReader("yourfile.csv"))
+     List<Visitors> beans = new CsvToBeanBuilder(new FileReader("yourfile.csv"))
        .withType(Visitors.class).build().parse();
 ----
 
 This will give you a list of the two beans as defined in the example input file.
 Note how type conversions to basic data types (wrapped and unwrapped primitives,
-enumerations, and Strings) occur automatically.
+enumerations, Strings, and java.util.Currency) occur automatically.
 
 Input can get more complicated, though, and opencsv gives you the tools to deal
 with that. Let's start with the possibility that the header names can't be
@@ -259,6 +259,11 @@ thing to say about them: input is checked against the declared values of the
 enumeration type **without regard to case**. On writing, the enumeration value
 will always be written exactly as declared.
 
+====== Currency
+
+Converting to and from ISO 4217 currency codes via java.util.Currency works
+exactly like regular primitive fields.
+
 ====== Locales, dates, numbers
 We've considered simple data types, but we haven't considered more complex yet
 common data types. We have also not considered locales other than the default
@@ -643,7 +648,7 @@ the index type to MultiValuedMap must be Integer. Values are saved under the
 index of the column position they were found in.
 
 The last thing to notice is that as long as new column positions are added to
-the end of the file and these are all new tracks, they will all be placed in the
+the end of the file, and these are all new tracks, they will all be placed in the
 variable "tracks" because the column position definition from the
 CsvBindAndJoinByPosition annotation defines an open range starting at index 7.
 
@@ -799,6 +804,125 @@ constructor, or they must be created by the enclosing bean.
 Access to subordinate beans is accomplished the same way it is in the rest of
 opencsv: accessor methods where available, and Reflection otherwise.
 
+====== Profiles
+
+There may be times when you receive differently formatted input files that
+nonetheless have the same data, and you will want to map them to the same
+bean. Here are two example inputs:
+
+This from customer 1:
+
+----
+last name,first name,middle initial,salary,height
+Jones,Andrew,R,50000.00,188
+----
+
+Compared to this from customer 2:
+
+----
+surname,given name,annual salary,height
+Jones,Andrew,5.0E5,188cm
+----
+
+As you can see, the inputs have mostly the same information, but the formats
+are incompatible for the purposes of using exactly one bean.
+
+Profiles allow you to resolve these superficial differences and use the same
+data bean for both inputs. All annotations save CsvRecurse include a "profiles"
+parameter for this purpose. CsvRecurse does not include the parameter because
+all annotations in recursively included beans are likewise subject to profile
+selection.
+
+The bean for our two inputs (and possibly more) could look like this:
+
+----
+public class Person {
+
+  @CsvBindByNames({
+    @CsvBindByName(column = "last name"),
+    @CsvBindByName(profiles = {"customer 2", "customer 5"})
+  })
+  private String surname;
+
+  @CsvBindByNames({
+    @CsvBindByName,
+    @CsvBindByName(column = "first name", profiles = "customer 1"),
+    @CsvBindByName(column = "given name", profiles = "customer 2")
+  })
+  private String name;
+
+  @CsvIgnore(profiles = "customer 2")
+  @CsvBindByName(column = "middle initial")
+  private char initial;
+
+  @CsvBindByName(column = "salary", profiles = "customer 1")
+  @CsvBindByName(column = "annual salary", profiles = "customer 2")
+  @CsvNumber(value = "#0.00", profiles = "customer 1")
+  @CsvNumber(value = "0.0#E0", profiles = "customer 2")
+  private float salaryInUSD;
+
+  @CsvBindByName(column = "height")
+  @CsvNumbers({
+    @CsvNumber("000"),
+    @CsvNumber(value = "000cm", profiles = "customer 2")
+  })
+  private int heightInCentimeters
+
+  // Accessor methods go here.
+}
+----
+
+To use this, your code might look like this:
+
+----
+List<Person> beans = new CsvToBeanBuilder<Person>(inputfile)
+  .withProfile("customer 1")
+  .withType(Person.class)
+  .build()
+  .parse();
+----
+
+The field "surname" is annotated with two CsvBindByName annotations, enclosed
+in a CsvBindByNames annotation, though the enclosure is optional. The first
+annotation does not specify any profiles, so it is used when no annotation
+for a specific profile is found, or when no profile is specified on
+parsing. It says that the default setting is to find a column named
+"last name" to bind to the field. The second annotation stipulates it is to
+be used with the profiles "customer 2" and "customer 5" (whose data we have
+not seen in this example). It does not name a column, so the typical fallback
+for header naming is used: the name of the field. In this case, that's
+"surname".
+
+The field "name" is annotated with three CsvBindByName annotations. The
+first does just what one would expect: it binds the field "name" to the
+column "name" from the input. We don't have an input like this in our
+example files, but perhaps such data come from other customers, like customer
+5. This annotation does not specify a profile, so it is the default profile.
+The second annotation is only for the profile "customer 1", and it binds
+the field to the input column named "first name". The third annotation is
+similar in function.
+
+The field "initial" is annotated with only one CsvBindByName, which binds the
+input column "middle initial" to the field. It uses the default profile. This
+field is also annotated with a CsvIgnore which says the field will be
+ignored for the profile "customer 2".
+
+The field "salaryInUSD" is annotated with two CsvBindByName annotations that
+should be self-explanatory by now. It is worth noting, though, that both are
+connected to one named profile each. If a different profile is specified
+for parsing, e.g. "customer 5", this field will simply not be bound at all
+— in other words, it will be ignored. The field is also annotated with two
+CsvNumber annotations: one each for the profiles specified in the
+CsvBindByName annotations, as it would happen. The two annotations simply
+provide different format strings for the input numbers.
+
+The field "heightInCentimeters" has only one CsvBindByName annotation to
+bind the field to the input column "height" independent of profile (since
+it is the default profile, and no other binding annotations exist for the
+field). After that come two CsvNumber annotations that demonstrate the
+same principle as the binding annotations: the first is for the default
+profile, the second is only for the profile "customer 2".
+
 ===== Reading into beans without annotations
 
 If annotations are anathema to you, you can bypass them with carefully
@@ -810,7 +934,7 @@ Here's how you can map to a bean based on the field positions in your CSV file:
 
 [source, java]
 ----
-    ColumnPositionMappingStrategy strat = new ColumnPositionMappingStrategy();
+    ColumnPositionMappingStrategy<YourOrderBean> strat = new ColumnPositionMappingStrategyBuilder<YourOrderBean>().build();
     strat.setType(YourOrderBean.class);
     String[] columns = new String[] {"name", "orderNumber", "id"}; // the fields to bind to in your bean
     strat.setColumnMapping(columns);
@@ -868,9 +992,9 @@ And use this code for reading:
 
 [source, java]
 ----
-MappingStrategy<MyBean> strategy = new FuzzyMappingStrategy<>();
+MappingStrategy<MyBean> strategy = new FuzzyMappingStrategyBuilder<MyBean>().build();
 strategy.setType(MyBean.class);
-List<MyBean> beans = new CsvToBeanBuilder(FileReader("yourfile.csv"))
+List<MyBean> beans = new CsvToBeanBuilder(new FileReader("yourfile.csv"))
     .withMappingStrategy(strategy)
     .build()
     .parse();
@@ -905,7 +1029,7 @@ variable, no matter how poor the match. If you need to get around this, the
 best way is to annotate the member variable to be skipped and map it to a
 fictitious but optional header.
 
-Nonetheless, if you know your data and a mismapping will not cause catastrophic
+Nonetheless, if you know your data, and a mismapping will not cause catastrophic
 failure of a critical system, this mapping strategy can save you some
 burdensome annotating for obvious mappings.
 
diff --git a/src/site/asciidoc/dev-writing.adoc b/src/site/asciidoc/dev-writing.adoc
index 6f7ab46..c33136b 100644
--- a/src/site/asciidoc/dev-writing.adoc
+++ b/src/site/asciidoc/dev-writing.adoc
@@ -73,7 +73,7 @@ to name for header name-based mappings.You can change this order, if you must.
 ----
       // List<MyBean> beans comes from somewhere earlier in your code.
       Writer writer = new FileWriter("yourfile.csv");
-      HeaderColumnNameMappingStrategy<MyBean> strategy = new HeaderColumnNameMappingStrategy<>();
+      HeaderColumnNameMappingStrategy<MyBean> strategy = new HeaderColumnNameMappingStrategyBuilder<MyBean>().build();
       strategy.setType(MyBean.class);
       strategy.setColumnOrderOnWrite(new MyComparator());
       StatefulBeanToCsv beanToCsv = StatefulBeanToCsvBuilder(writer)
@@ -83,8 +83,8 @@ to name for header name-based mappings.You can change this order, if you must.
       writer.close();
 ----
 The same method exists for ColumnPositionMappingStrategy.If you wish to use
-your own ordering, you must instantiate your own mapping strategy and pass it
-in to StatefulBeanToCsvBuilder.
+your own ordering, you must instantiate your own mapping strategy (through
+the appropriate builder) and pass it in to StatefulBeanToCsvBuilder.
 
 We expect there will be plenty of people who find using a Comparator
 uncomfortable, because they have an exact order that they need that has nothing
diff --git a/src/site/asciidoc/developer-documentation.adoc b/src/site/asciidoc/developer-documentation.adoc
index a98d71a..5e6f397 100644
--- a/src/site/asciidoc/developer-documentation.adoc
+++ b/src/site/asciidoc/developer-documentation.adoc
@@ -220,11 +220,11 @@ the pieces fit together for reading:
 . The MappingStrategy and the Reader or CSVReader and optionally the CsvToBeanFilter or BeanVerifier are passed to a CsvToBean, which uses them to parse input and populate beans.
 . If you have any custom converters, they are called for each bean field as CsvToBean is populating the bean fields.
 
-For writing it's a little simpler:
+For writing, it's a little simpler:
 
 . You must provide a Writer. This can be any Writer, but a FileWriter or a StringWriter are the most common options.
 . The Writer is wrapped in a CSVWriter. This is always done for you.
-. Create a MappingStrategy if you need to. Otherwise opencsv will automatically determine one.
+. Create a MappingStrategy if you need to. (Use the appropriate builder.) Otherwise opencsv will automatically determine one.
 . Create a StatefulBeanToCsv, give it the MappingStrategy and the Writer.
 . If you have any custom converters, they are called for each bean field as the field is written out to the CSV file.
 
@@ -233,10 +233,10 @@ Opencsv has the concept of a mapping strategy. This is what translates a column
 a field in a bean or vice versa. As we have already implied in the documentation of the
 annotations, there are two basic mapping strategies: Mapping by header name and
 mapping by column position. These are incarnated in HeaderColumnNameMappingStrategy
-and ColumnPositionMappingStrategy respectively. There is one more addendum to
-the header name mapping strategy: If you need to translate names from the input
-file to field names and you are not using annotations, you will need to use
-HeaderColumnNameTranslateMappingStrategy.
+and ColumnPositionMappingStrategy respectively. If you need to translate names from the input
+file to field names, and you are not using annotations, you will need to use
+HeaderColumnNameTranslateMappingStrategy. FuzzyMappingStrategy maps from input column
+names to bean fields as intelligently as possible based on name.
 
 If you use annotations and CsvToBeanBuilder (for reading) or StatefulBeanToCsv(Builder)
 (for writing), an appropriate mapping strategy is automatically determined, and
diff --git a/src/site/asciidoc/faq.adoc b/src/site/asciidoc/faq.adoc
index 1aa1739..f0f19bf 100644
--- a/src/site/asciidoc/faq.adoc
+++ b/src/site/asciidoc/faq.adoc
@@ -29,7 +29,7 @@ Add a dependency element to your pom:
   <dependency>
      <groupId>com.opencsv</groupId>
      <artifactId>opencsv</artifactId>
-     <version>4.5</version>
+     <version>5.5</version>
   </dependency>
 ----
 
diff --git a/src/test/java/com/opencsv/CSVIteratorTest.java b/src/test/java/com/opencsv/CSVIteratorTest.java
index 4b9288f..491c740 100644
--- a/src/test/java/com/opencsv/CSVIteratorTest.java
+++ b/src/test/java/com/opencsv/CSVIteratorTest.java
@@ -7,7 +7,7 @@ import java.io.IOException;
 import java.util.Locale;
 import java.util.NoSuchElementException;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -39,13 +39,11 @@ public class CSVIteratorTest {
     @Test
     public void readerExceptionCausesRunTimeException() throws IOException, CsvValidationException {
         when(mockReader.readNext()).thenThrow(new IOException("reader threw test exception"));
-        Assertions.assertThrows(NoSuchElementException.class, () -> {
-            iterator.next();
-        });
+        Assertions.assertThrows(NoSuchElementException.class, () -> iterator.next());
     }
 
     @Test
-    public void removethrowsUnsupportedOperationException() {
+    public void removeThrowsUnsupportedOperationException() {
         String englishErrorMessage = null;
         try {
             iterator.remove();
diff --git a/src/test/java/com/opencsv/CSVParserBuilderTest.java b/src/test/java/com/opencsv/CSVParserBuilderTest.java
index a519c12..d9efb01 100644
--- a/src/test/java/com/opencsv/CSVParserBuilderTest.java
+++ b/src/test/java/com/opencsv/CSVParserBuilderTest.java
@@ -4,7 +4,7 @@ import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class CSVParserBuilderTest {
 
diff --git a/src/test/java/com/opencsv/CSVParserTest.java b/src/test/java/com/opencsv/CSVParserTest.java
index f7a72ef..b7c759c 100644
--- a/src/test/java/com/opencsv/CSVParserTest.java
+++ b/src/test/java/com/opencsv/CSVParserTest.java
@@ -3,14 +3,14 @@ package com.opencsv;
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import com.opencsv.exceptions.CsvMultilineLimitBrokenException;
 import com.opencsv.exceptions.CsvValidationException;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.*;
 
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.Locale;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CSVParserTest {
 
@@ -85,6 +85,37 @@ public class CSVParserTest {
         assertFalse(csvParser.isPending());
     }
 
+    @Test
+    public void parseEscapingTheFirstCharacter() throws IOException {
+
+        // Escaping a separator
+        String[] nextLine = csvParser.parseLine("\\,a,b,c,d");
+        assertEquals(4, nextLine.length);
+        assertEquals(",a", nextLine[0]);
+        assertEquals("b", nextLine[1]);
+        assertEquals("c", nextLine[2]);
+        assertEquals("d", nextLine[3]);
+        assertFalse(csvParser.isPending());
+
+        // Escaping an escape
+        nextLine = csvParser.parseLine("\\\\a,b,c,d");
+        assertEquals(4, nextLine.length);
+        assertEquals("\\a", nextLine[0]);
+        assertEquals("b", nextLine[1]);
+        assertEquals("c", nextLine[2]);
+        assertEquals("d", nextLine[3]);
+        assertFalse(csvParser.isPending());
+
+        // Escaping a quotation character
+        nextLine = csvParser.parseLine("\\\"a,b,c,d");
+        assertEquals(4, nextLine.length);
+        assertEquals("\"a", nextLine[0]);
+        assertEquals("b", nextLine[1]);
+        assertEquals("c", nextLine[2]);
+        assertEquals("d", nextLine[3]);
+        assertFalse(csvParser.isPending());
+    }
+
     @Test
     public void parseSimpleQuotedString() throws IOException {
 
@@ -117,7 +148,7 @@ public class CSVParserTest {
      * @throws IOException if bad things happen
      */
     @Test
-    public void testParsedLineWithInternalQuota() throws IOException {
+    public void testParsedLineWithInternalQuote() throws IOException {
 
         String[] nextLine = csvParser.parseLine("a,123\"4\"567,c");
         assertEquals(3, nextLine.length);
@@ -349,15 +380,13 @@ public class CSVParserTest {
     }
 
     @Test
-    public void testFalseIgnoreQuotations() throws IOException {
+    public void testFalseIgnoreQuotations() {
         csvParser = new CSVParserBuilder()
                 .withIgnoreQuotations(false)
                 .build();
         String testString = "Bob,test\",Beaumont,TX";
 
-        Assertions.assertThrows(IOException.class, () -> {
-            csvParser.parseLine(testString);
-        });
+        Assertions.assertThrows(IOException.class, () -> csvParser.parseLine(testString));
     }
 
     /**
@@ -604,9 +633,7 @@ public class CSVParserTest {
 
     @Test
     public void separatorCharacterCannotBeNull() {
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            new CSVParserBuilder().withSeparator(ICSVParser.NULL_CHARACTER).build();
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> new CSVParserBuilder().withSeparator(ICSVParser.NULL_CHARACTER).build());
     }
 
     @Test
@@ -663,7 +690,7 @@ public class CSVParserTest {
             fail("UnsupportedOperationException should have been thrown.");
         }
         catch(UnsupportedOperationException e) {
-            assertThat(e.getLocalizedMessage(), is("O separador, delimitador de texto e caractere de escape precisam ser diferentes!"));
+            assertEquals("O separador, delimitador de texto e caractere de escape precisam ser diferentes!", e.getLocalizedMessage());
         }
     }
 
diff --git a/src/test/java/com/opencsv/CSVReaderBuilderTest.java b/src/test/java/com/opencsv/CSVReaderBuilderTest.java
index e65afae..bd9567f 100644
--- a/src/test/java/com/opencsv/CSVReaderBuilderTest.java
+++ b/src/test/java/com/opencsv/CSVReaderBuilderTest.java
@@ -6,7 +6,7 @@ import org.junit.jupiter.api.*;
 import java.io.Reader;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.mock;
 
 public class CSVReaderBuilderTest {
@@ -54,9 +54,7 @@ public class CSVReaderBuilderTest {
 
     @Test
    public void testNullReader() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder = new CSVReaderBuilder(null);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new CSVReaderBuilder(null));
    }
 
    @Test
diff --git a/src/test/java/com/opencsv/CSVReaderHeaderAwareBuilderTest.java b/src/test/java/com/opencsv/CSVReaderHeaderAwareBuilderTest.java
index faea081..97c50a1 100644
--- a/src/test/java/com/opencsv/CSVReaderHeaderAwareBuilderTest.java
+++ b/src/test/java/com/opencsv/CSVReaderHeaderAwareBuilderTest.java
@@ -1,15 +1,14 @@
 package com.opencsv;
 
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import com.opencsv.enums.CSVReaderNullFieldIndicator;
+import org.junit.jupiter.api.*;
 
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Locale;
 
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -22,23 +21,132 @@ import static org.mockito.Mockito.when;
 public class CSVReaderHeaderAwareBuilderTest {
 
     private CSVReaderHeaderAwareBuilder builder;
+    private final Reader reader = new StringReader("header");
+
+    private static Locale systemLocale;
+
+    @BeforeAll
+    public static void storeSystemLocale() {
+        systemLocale = Locale.getDefault();
+    }
+
+    @AfterEach
+    public void setSystemLocaleBackToDefault() {
+        Locale.setDefault(systemLocale);
+    }
 
     @BeforeEach
-    public void setup() {
-        this.builder = new CSVReaderHeaderAwareBuilder(new StringReader("header"));
+    public void setUp() {
+        Locale.setDefault(Locale.US);
+        builder = new CSVReaderHeaderAwareBuilder(reader);
+    }
+
+    @Test
+    public void testDefaultBuilder() {
+        assertSame(reader, builder.getReader());
+        assertNull(builder.getCsvParser());
+        assertEquals(
+                CSVReader.DEFAULT_SKIP_LINES,
+                builder.getSkipLines());
+        assertEquals(0, builder.getMultilineLimit());
+
+        final CSVReader csvReader = builder.build();
+        assertEquals(
+                CSVReader.DEFAULT_SKIP_LINES,
+                csvReader.getSkipLines());
+        assertEquals(CSVReader.DEFAULT_KEEP_CR, csvReader.keepCarriageReturns());
+        assertEquals(CSVReader.DEFAULT_VERIFY_READER, csvReader.verifyReader());
+        assertEquals(CSVReader.DEFAULT_MULTILINE_LIMIT, csvReader.getMultilineLimit());
+        assertEquals(Locale.getDefault(), csvReader.errorLocale);
+    }
+
+    @Test
+    public void testNullReader() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new CSVReaderBuilder(null));
+    }
+
+    @Test
+    public void testWithCSVParserNull() {
+        builder.withCSVParser(mock(CSVParser.class));
+        builder.withCSVParser(null);
+        assertNull(builder.getCsvParser());
+    }
+
+    @Test
+    public void testWithCSVParser() {
+        final ICSVParser csvParser = mock(CSVParser.class);
+
+        builder.withCSVParser(csvParser);
+
+        assertSame(csvParser, builder.getCsvParser());
+    }
+
+    @Test
+    public void testWithSkipLines() {
+        builder.withSkipLines(99);
+
+        assertEquals(99, builder.getSkipLines());
+    }
+
+    @Test
+    public void testWithKeepCR() {
+        builder.withKeepCarriageReturn(true);
+        assertTrue(builder.keepCarriageReturn());
+
+        final CSVReader actual = builder.build();
+        assertTrue(actual.keepCarriageReturns());
+    }
+
+    @Test
+    public void testWithSkipLinesZero() {
+        builder.withSkipLines(0);
+
+        assertEquals(0, builder.getSkipLines());
+
+        final CSVReader actual = builder.build();
+        assertSame(0, actual.getSkipLines());
+    }
+
+    @Test
+    public void testWithSkipLinesNegative() {
+        builder.withSkipLines(-1);
+
+        assertEquals(0, builder.getSkipLines());
+
+        final CSVReader actual = builder.build();
+        assertSame(0, actual.getSkipLines());
+    }
+
+    @Test
+    public void testWithVerifyReader() {
+        final CSVReader r = builder.withVerifyReader(false).build();
+        assertFalse(r.verifyReader());
+    }
+
+    @Test
+    public void builderWithNullFieldIndicator() {
+        final CSVReader r = builder.withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS).build();
+
+        assertEquals(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS, r.getParser().nullFieldIndicator());
+    }
+
+    @Test
+    public void builderWithMultilineLimit() {
+        final CSVReader r = builder.withMultilineLimit(Integer.MAX_VALUE).build();
+
+        assertEquals(Integer.MAX_VALUE, r.getMultilineLimit());
     }
 
     @Test
-    public void shouldCreateCsvReaderHeaderAwareInstance() {
-        assertThat(builder.build(), instanceOf(CSVReaderHeaderAware.class));
+    public void builderWithErrorLocale() {
+        final CSVReader r = builder.withErrorLocale(Locale.KOREAN).build();
+        assertEquals(Locale.KOREAN, r.errorLocale);
     }
 
     @Test
     public void shouldThrowExceptionWhenCannotReadHeader() throws IOException {
         Reader reader = mock(Reader.class);
         when(reader.read(any((char[].class)), eq(0), eq(8192))).thenThrow(new IOException());
-        Assertions.assertThrows(RuntimeException.class, () -> {
-            new CSVReaderHeaderAwareBuilder(reader).build();
-        });
+        Assertions.assertThrows(RuntimeException.class, () -> new CSVReaderHeaderAwareBuilder(reader).build());
     }
 }
\ No newline at end of file
diff --git a/src/test/java/com/opencsv/CSVReaderHeaderAwareWithRowProcessorTest.java b/src/test/java/com/opencsv/CSVReaderHeaderAwareWithRowProcessorTest.java
new file mode 100644
index 0000000..fa7d8ce
--- /dev/null
+++ b/src/test/java/com/opencsv/CSVReaderHeaderAwareWithRowProcessorTest.java
@@ -0,0 +1,66 @@
+package com.opencsv;
+
+import com.opencsv.exceptions.CsvException;
+import com.opencsv.processor.BlankColumnsBecomeNull;
+import com.opencsv.processor.RowProcessor;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class CSVReaderHeaderAwareWithRowProcessorTest {
+    private static final RowProcessor ROW_PROCESSOR = new BlankColumnsBecomeNull();
+    private static final String LINES = "one,two,three\na,, \n, ,\n";
+
+    @DisplayName("CSVReaderHeaderAware with RowProcessor with good string")
+    @Test
+    public void readerWithRowProcessor() throws IOException, CsvException {
+
+        StringReader stringReader = new StringReader(LINES);
+        CSVReaderHeaderAware csvReader = new CSVReaderHeaderAwareBuilder(stringReader)
+                .withRowProcessor(ROW_PROCESSOR)
+                .build();
+
+        List<String[]> rows = csvReader.readAll();
+        assertEquals(2, rows.size());
+
+        String[] row1 = rows.get(0);
+        assertEquals(3, row1.length);
+        assertEquals("a", row1[0]);
+        assertNull(row1[1]);
+        assertEquals(" ", row1[2]);
+
+        String[] row2 = rows.get(1);
+        assertEquals(3, row2.length);
+        assertNull(row2[0]);
+        assertEquals(" ", row2[1]);
+        assertNull(row2[2]);
+    }
+
+    @DisplayName("CSVReaderHeaderAware without RowProcessor with good string")
+    @Test
+    public void readerWithoutRowProcessor() throws IOException, CsvException {
+
+        StringReader stringReader = new StringReader(LINES);
+        CSVReaderHeaderAware csvReader = new CSVReaderHeaderAwareBuilder(stringReader).build();
+
+        List<String[]> rows = csvReader.readAll();
+        assertEquals(2, rows.size());
+
+        String[] row1 = rows.get(0);
+        assertEquals(3, row1.length);
+        assertEquals("a", row1[0]);
+        assertTrue(row1[1].isEmpty());
+        assertEquals(" ", row1[2]);
+
+        String[] row2 = rows.get(1);
+        assertEquals(3, row2.length);
+        assertTrue(row2[0].isEmpty());
+        assertEquals(" ", row2[1]);
+        assertTrue(row2[2].isEmpty());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/opencsv/CSVReaderHeaderAwareWithValidatorsTest.java b/src/test/java/com/opencsv/CSVReaderHeaderAwareWithValidatorsTest.java
new file mode 100644
index 0000000..3edcc98
--- /dev/null
+++ b/src/test/java/com/opencsv/CSVReaderHeaderAwareWithValidatorsTest.java
@@ -0,0 +1,144 @@
+package com.opencsv;
+
+import com.opencsv.exceptions.CsvException;
+import com.opencsv.exceptions.CsvValidationException;
+import com.opencsv.validators.LineDoesNotHaveForbiddenString;
+import com.opencsv.validators.RowFunctionValidator;
+import com.opencsv.validators.RowValidator;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class CSVReaderHeaderAwareWithValidatorsTest {
+    private static final String BAD = "bad";
+    private static final String AWFUL = "awful";
+    private LineDoesNotHaveForbiddenString lineDoesNotHaveBadString;
+    private LineDoesNotHaveForbiddenString lineDoesNotHaveAwfulString;
+
+    private static final Function<String[], Boolean> ROW_MUST_HAVE_THREE_COLUMNS = (x) -> x.length == 3;
+    private static final RowValidator THREE_COLUMNS_ROW_VALIDATOR = new RowFunctionValidator(ROW_MUST_HAVE_THREE_COLUMNS, "Row must have three columns!");
+
+    @BeforeEach
+    public void setup() {
+        lineDoesNotHaveBadString = new LineDoesNotHaveForbiddenString(BAD);
+        lineDoesNotHaveAwfulString = new LineDoesNotHaveForbiddenString(AWFUL);
+    }
+
+    @DisplayName("CSVReaderHeaderAware with LineValidator with good string")
+    @Test
+    public void readerWithLineValidatorWithValidString() throws IOException, CsvException {
+        String lines = "one,two.three\na,b,c\nd,e,f\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReaderHeaderAware csvReader = builder
+                .withLineValidator(lineDoesNotHaveAwfulString)
+                .withLineValidator(lineDoesNotHaveBadString)
+                .withRowValidator(THREE_COLUMNS_ROW_VALIDATOR)
+                .build();
+
+        List<String[]> rows = csvReader.readAll();
+        assertEquals(2, rows.size());
+    }
+
+    @DisplayName("CSVReaderHeaderAware with LineValidator with bad string")
+    @Test
+    public void readerWithLineValidatorWithBadString() {
+        String lines = "a,b,c\nd,bad,f\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReaderHeaderAware csvReader = builder
+                .withLineValidator(lineDoesNotHaveAwfulString)
+                .withLineValidator(lineDoesNotHaveBadString)
+                .build();
+
+        assertThrows(CsvValidationException.class, csvReader::readAll);
+    }
+
+    @DisplayName("CSVReaderHeaderAware with LineValidator with bad first string")
+    @Test
+    public void readerWithLineValidatorWithBadFirstString() {
+        String lines = "one,two,three\nd,bad,f\na,b,c\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReaderHeaderAware csvReader = builder
+                .withLineValidator(lineDoesNotHaveAwfulString)
+                .withLineValidator(lineDoesNotHaveBadString)
+                .build();
+
+        try {
+            csvReader.readAll();
+            fail("Expected a CsvValidationException to be thrown!");
+        } catch (CsvValidationException cve) {
+            assertEquals(2, cve.getLineNumber());
+        } catch (Exception e) {
+            fail("Caught an exception other than CsvValidationException!", e);
+        }
+    }
+
+    @DisplayName("CSVReaderHeaderAware populates line number of exception thrown by LineValidatorAggregator")
+    @Test
+    public void readerWithLineValidatorExceptionContainsLineNumber() {
+        String lines = "a,b,c\nd,bad,f\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReaderHeaderAware csvReader = builder
+                .withLineValidator(lineDoesNotHaveAwfulString)
+                .withLineValidator(lineDoesNotHaveBadString)
+                .build();
+
+        try {
+            csvReader.readAll();
+            fail("Expected a CsvValidationException to be thrown!");
+        } catch (CsvValidationException cve) {
+            assertEquals(2, cve.getLineNumber());
+        } catch (Exception e) {
+            fail("Caught an exception other than CsvValidationException!", e);
+        }
+    }
+
+    @DisplayName("CSVReaderHeaderAware with RowValidator with bad row")
+    @Test
+    public void readerWithRowValidatorWithBadRow() {
+        String lines = "a,b,c\nd,f\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReaderHeaderAware csvReader = builder
+                .withRowValidator(THREE_COLUMNS_ROW_VALIDATOR)
+                .build();
+
+        assertThrows(CsvValidationException.class, csvReader::readAll);
+    }
+
+    @DisplayName("CSVReaderHeaderAware populates line number of exception thrown by RowValidatorAggregator")
+    @Test
+    public void readerWithRowValidatorExceptionContainsLineNumber() {
+        String lines = "a,b,c\nd,f\n";
+        StringReader stringReader = new StringReader(lines);
+        CSVReaderHeaderAwareBuilder builder = new CSVReaderHeaderAwareBuilder(stringReader);
+
+        CSVReader csvReader = builder
+                .withRowValidator(THREE_COLUMNS_ROW_VALIDATOR)
+                .build();
+
+        try {
+            csvReader.readAll();
+            fail("Expected a CsvValidationException to be thrown!");
+        } catch (CsvValidationException cve) {
+            assertEquals(2, cve.getLineNumber());
+        } catch (Exception e) {
+            fail("Caught an exception other than CsvValidationException!", e);
+        }
+    }
+}
diff --git a/src/test/java/com/opencsv/CSVReaderTest.java b/src/test/java/com/opencsv/CSVReaderTest.java
index 18a548b..7cc1567 100644
--- a/src/test/java/com/opencsv/CSVReaderTest.java
+++ b/src/test/java/com/opencsv/CSVReaderTest.java
@@ -19,19 +19,21 @@ package com.opencsv;
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import com.opencsv.exceptions.CsvException;
 import com.opencsv.exceptions.CsvValidationException;
+import com.opencsv.util.MockDataBuilder;
 import org.junit.jupiter.api.*;
 
 import java.io.*;
 import java.nio.channels.Channels;
 import java.nio.channels.ReadableByteChannel;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CSVReaderTest {
 
     CSVReader csvr;
+    MockDataBuilder mockDataBuilder;
 
     private static Locale systemLocale;
 
@@ -48,15 +50,18 @@ public class CSVReaderTest {
     @BeforeEach
     public void setUp() {
         Locale.setDefault(Locale.US);
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("a,b,c").append("\n");   // standard case
-        sb.append("a,\"b,b,b\",c").append("\n");  // quoted elements
-        sb.append(",,").append("\n"); // empty elements
-        sb.append("a,\"PO Box 123,\nKippax,ACT. 2615.\nAustralia\",d.\n");
-        sb.append("\"Glen \"\"The Man\"\" Smith\",Athlete,Developer\n"); // Test quoted quote chars
-        sb.append("\"\"\"\"\"\",\"test\"\n"); // """""","test"  representing:  "", test
-        sb.append("\"a\nb\",b,\"\nd\",e\n");
-        csvr = new CSVReader(new StringReader(sb.toString()));
+        MockDataBuilder builder = new MockDataBuilder();
+
+        builder.addDataRow("a,b,c");   // standard case
+        builder.addDataRow("a,\"b,b,b\",c");  // quoted elements
+        builder.addDataRow(",,"); // empty elements
+        builder.addDataRow("a,\"PO Box 123,\nKippax,ACT. 2615.\nAustralia\",d.");
+        builder.addDataRow("\"Glen \"\"The Man\"\" Smith\",Athlete,Developer"); // Test quoted quote chars
+        builder.addDataRow("\"\"\"\"\"\",\"test\""); // """""","test"  representing:  "", test
+        builder.addDataRow("\"a\nb\",b,\"\nd\",e");
+        csvr = new CSVReader(builder.buildStringReader());
+
+        mockDataBuilder = new MockDataBuilder();
     }
 
 
@@ -105,12 +110,10 @@ public class CSVReaderTest {
 
     @Test
     public void readerCanHandleNullInString() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("a,\0b,c");
 
-        StringReader reader = new StringReader(sb.toString());
+        mockDataBuilder.addDataRow("a,\0b,c");
 
-        CSVReaderBuilder builder = new CSVReaderBuilder(reader);
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
         CSVReader defaultReader = builder.build();
 
         String[] nextLine = defaultReader.readNext();
@@ -123,15 +126,15 @@ public class CSVReaderTest {
 
     @Test
     public void testParseLineStrictQuote() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("a,b,c").append("\n");   // standard case
-        sb.append("a,\"b,b,b\",c").append("\n");  // quoted elements
-        sb.append(",,").append("\n"); // empty elements
-        sb.append("a,\"PO Box 123,\nKippax,ACT. 2615.\nAustralia\",d.\n");
-        sb.append("\"Glen \"\"The Man\"\" Smith\",Athlete,Developer\n"); // Test quoted quote chars
-        sb.append("\"\"\"\"\"\",\"test\"\n"); // """""","test"  representing:  "", test
-        sb.append("\"a\nb\",b,\"\nd\",e\n");
-        csvr = new CSVReaderBuilder(new StringReader(sb.toString()))
+
+        mockDataBuilder.addDataRow("a,b,c");   // standard case
+        mockDataBuilder.addDataRow("a,\"b,b,b\",c");  // quoted elements
+        mockDataBuilder.addDataRow(",,"); // empty elements
+        mockDataBuilder.addDataRow("a,\"PO Box 123,\nKippax,ACT. 2615.\nAustralia\",d.");
+        mockDataBuilder.addDataRow("\"Glen \"\"The Man\"\" Smith\",Athlete,Developer"); // Test quoted quote chars
+        mockDataBuilder.addDataRow("\"\"\"\"\"\",\"test\""); // """""","test"  representing:  "", test
+        mockDataBuilder.addDataRow("\"a\nb\",b,\"\nd\",e");
+        csvr = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withStrictQuotes(true)
                         .build())
@@ -162,8 +165,8 @@ public class CSVReaderTest {
         assertEquals("Glen \"The Man\" Smith", nextLine[0]);
 
         nextLine = csvr.readNext();
-        assertTrue(nextLine[0].equals("\"\"")); // check the tricky situation
-        assertTrue(nextLine[1].equals("test")); // make sure we didn't ruin the next field..
+        assertEquals("\"\"", nextLine[0]); // check the tricky situation
+        assertEquals("test", nextLine[1]); // make sure we didn't ruin the next field..
 
         nextLine = csvr.readNext();
         assertEquals(4, nextLine.length);
@@ -195,10 +198,9 @@ public class CSVReaderTest {
     @Test
     public void testOptionalConstructors() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("a\tb\tc").append("\n");   // tab separated case
-        sb.append("a\t'b\tb\tb'\tc").append("\n");  // single quoted elements
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        mockDataBuilder.addDataRow("a\tb\tc");   // tab separated case
+        mockDataBuilder.addDataRow("a\t'b\tb\tb'\tc");  // single quoted elements
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withQuoteChar('\'')
                         .withSeparator('\t')
@@ -214,10 +216,10 @@ public class CSVReaderTest {
 
     @Test
     public void parseQuotedStringWithDefinedSeparator() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("a\tb\tc").append("\n");   // tab separated case
 
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        mockDataBuilder.addDataRow("a\tb\tc");   // tab separated case
+
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withSeparator('\t')
                         .build())
@@ -235,11 +237,10 @@ public class CSVReaderTest {
     @Test
     public void testSkippingLines() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("Skip this line\t with tab").append("\n");   // should skip this
-        sb.append("And this line too").append("\n");   // and this
-        sb.append("a\t'b\tb\tb'\tc").append("\n");  // single quoted elements
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        mockDataBuilder.addDataRow("Skip this line\t with tab");   // should skip this
+        mockDataBuilder.addDataRow("And this line too");   // and this
+        mockDataBuilder.addDataRow("a\t'b\tb\tb'\tc");  // single quoted elements
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withQuoteChar('\'')
                         .withSeparator('\t')
@@ -261,14 +262,13 @@ public class CSVReaderTest {
     @Test
     public void linesAndRecordsRead() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("Skip this line\t with tab").append("\n");   // should skip this
-        sb.append("And this line too").append("\n");   // and this
-        sb.append("a,b,c").append("\n");  // second line
-        sb.append("\n");                  // no data here just a blank line
-        sb.append("a,\"b\nb\",c");
+        mockDataBuilder.addDataRow("Skip this line\t with tab");   // should skip this
+        mockDataBuilder.addDataRow("And this line too");   // and this
+        mockDataBuilder.addDataRow("a,b,c");  // second line
+        mockDataBuilder.addDataRow("");                  // no data here just a blank line
+        mockDataBuilder.addDataRow("a,\"b\nb\",c");
 
-        CSVReaderBuilder builder = new CSVReaderBuilder(new StringReader(sb.toString()));
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
         CSVReader c = builder.withCSVParser(new CSVParser())
                 .withSkipLines(2)
                 .build();
@@ -310,11 +310,10 @@ public class CSVReaderTest {
     @Test
     public void testSkippingLinesWithDifferentEscape() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-        sb.append("Skip this line?t with tab").append("\n");   // should skip this
-        sb.append("And this line too").append("\n");   // and this
-        sb.append("a\t'b\tb\tb'\t'c'").append("\n");  // single quoted elements
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        mockDataBuilder.addDataRow("Skip this line?t with tab");   // should skip this
+        mockDataBuilder.addDataRow("And this line too");   // and this
+        mockDataBuilder.addDataRow("a\t'b\tb\tb'\t'c'");  // single quoted elements
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withQuoteChar('\'')
                         .withSeparator('\t')
@@ -339,12 +338,9 @@ public class CSVReaderTest {
      */
     @Test
     public void testNormalParsedLine() throws IOException, CsvValidationException {
+        mockDataBuilder.addDataRow("a,1234567,c");// a,1234,c
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,1234567,c").append("\n");// a,1234,c
-
-        CSVReader c = new CSVReader(new StringReader(sb.toString()));
+        CSVReader c = new CSVReader(mockDataBuilder.buildStringReader());
 
         String[] nextLine = c.readNext();
         assertEquals(3, nextLine.length);
@@ -364,11 +360,9 @@ public class CSVReaderTest {
     @Test
     public void testASingleQuoteAsDataElement() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,'''',c").append("\n");// a,',c
+        mockDataBuilder.addDataRow("a,'''',c");// a,',c
 
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withQuoteChar('\'')
                         .withSeparator(',')
@@ -380,7 +374,7 @@ public class CSVReaderTest {
 
         assertEquals("a", nextLine[0]);
         assertEquals(1, nextLine[1].length());
-        assertEquals("\'", nextLine[1]);
+        assertEquals("'", nextLine[1]);
         assertEquals("c", nextLine[2]);
     }
 
@@ -393,11 +387,9 @@ public class CSVReaderTest {
     @Test
     public void testASingleQuoteAsDataElementWithEmptyField() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
+        mockDataBuilder.addDataRow("a,'',c");// a,,c
 
-        sb.append("a,'',c").append("\n");// a,,c
-
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withQuoteChar('\'')
                         .withSeparator(',')
@@ -415,11 +407,9 @@ public class CSVReaderTest {
 
     @Test
     public void testSpacesAtEndOfString() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("\"a\",\"b\",\"c\"   ");
+        mockDataBuilder.addDataRow("\"a\",\"b\",\"c\"   ");
 
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withStrictQuotes(true)
                         .build())
@@ -436,12 +426,9 @@ public class CSVReaderTest {
 
     @Test
     public void testEscapedQuote() throws IOException, CsvValidationException {
+        mockDataBuilder.addDataRow("a,\"123\\\"4567\",c");// a,123"4",c
 
-        StringBuilder sb = new StringBuilder();
-
-        sb.append("a,\"123\\\"4567\",c").append("\n");// a,123"4",c
-
-        CSVReader c = new CSVReader(new StringReader(sb.toString()));
+        CSVReader c = new CSVReader(mockDataBuilder.buildStringReader());
 
         String[] nextLine = c.readNext();
         assertEquals(3, nextLine.length);
@@ -451,12 +438,9 @@ public class CSVReaderTest {
 
     @Test
     public void testEscapedEscape() throws IOException, CsvValidationException {
+        mockDataBuilder.addDataRow("a,\"123\\\\4567\",c");// a,123"4",c
 
-        StringBuilder sb = new StringBuilder();
-
-        sb.append("a,\"123\\\\4567\",c").append("\n");// a,123"4",c
-
-        CSVReader c = new CSVReader(new StringReader(sb.toString()));
+        CSVReader c = new CSVReader(mockDataBuilder.buildStringReader());
 
         String[] nextLine = c.readNext();
         assertEquals(3, nextLine.length);
@@ -474,12 +458,9 @@ public class CSVReaderTest {
      */
     @Test
     public void testSingleQuoteWhenDoubleQuoteIsQuoteChar() throws IOException, CsvValidationException {
+        mockDataBuilder.addDataRow("a,'',c");// a,'',c
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,'',c").append("\n");// a,'',c
-
-        CSVReader c = new CSVReader(new StringReader(sb.toString()));
+        CSVReader c = new CSVReader(mockDataBuilder.buildStringReader());
 
         String[] nextLine = c.readNext();
         assertEquals(3, nextLine.length);
@@ -497,12 +478,9 @@ public class CSVReaderTest {
      */
     @Test
     public void testQuotedParsedLine() throws IOException, CsvValidationException {
+        mockDataBuilder.addDataRow("\"a\",\"1234567\",\"c\""); // "a","1234567","c"
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("\"a\",\"1234567\",\"c\"").append("\n"); // "a","1234567","c"
-
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withStrictQuotes(true)
                         .build())
@@ -520,14 +498,11 @@ public class CSVReaderTest {
 
     @Test
     public void bug106ParseLineWithCarriageReturnNewLineStrictQuotes() throws IOException, CsvValidationException {
-
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("\"a\",\"123\r\n4567\",\"c\"").append("\n"); // "a","123\r\n4567","c"
+        mockDataBuilder.addDataRow("\"a\",\"123\r\n4567\",\"c\""); // "a","123\r\n4567","c"
 
         // public CSVReader(Reader reader, char separator, char quotechar, char escape, int line, boolean strictQuotes,
         // boolean ignoreLeadingWhiteSpace, boolean keepCarriageReturn)
-        CSVReader c = new CSVReaderBuilder(new StringReader(sb.toString()))
+        CSVReader c = new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                 .withCSVParser(new CSVParserBuilder()
                         .withStrictQuotes(true)
                         .build())
@@ -546,11 +521,9 @@ public class CSVReaderTest {
 
     @Test
     public void testIssue2992134OutOfPlaceQuotes() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
+        mockDataBuilder.addDataRow("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
 
-        CSVReader c = new CSVReader(new StringReader(sb.toString()));
+        CSVReader c = new CSVReader(mockDataBuilder.buildStringReader());
 
         String[] nextLine = c.readNext();
 
@@ -562,12 +535,10 @@ public class CSVReaderTest {
 
     @Test
     public void quoteAndEscapeMustBeDifferent() {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
+        mockDataBuilder.addDataRow("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
 
         Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            new CSVReaderBuilder(new StringReader(sb.toString()))
+            new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                     .withCSVParser(new CSVParserBuilder()
                             .withEscapeChar(ICSVParser.DEFAULT_QUOTE_CHARACTER)
                             .build())
@@ -577,12 +548,10 @@ public class CSVReaderTest {
 
     @Test
     public void separatorAndEscapeMustBeDifferent() {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
+        mockDataBuilder.addDataRow("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
 
         Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            new CSVReaderBuilder(new StringReader(sb.toString()))
+            new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                     .withCSVParser(new CSVParserBuilder()
                             .withEscapeChar(ICSVParser.DEFAULT_SEPARATOR)
                             .build())
@@ -592,12 +561,10 @@ public class CSVReaderTest {
 
     @Test
     public void separatorAndQuoteMustBeDifferent() {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
+        mockDataBuilder.addDataRow("a,b,c,ddd\\\"eee\nf,g,h,\"iii,jjj\"");
 
         Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            new CSVReaderBuilder(new StringReader(sb.toString()))
+            new CSVReaderBuilder(mockDataBuilder.buildStringReader())
                     .withCSVParser(new CSVParserBuilder()
                             .withQuoteChar(ICSVParser.DEFAULT_SEPARATOR)
                             .build())
@@ -689,11 +656,11 @@ public class CSVReaderTest {
 
     @Test
     public void issue108ReaderPlaysWellWithChannels() throws IOException, CsvException {
-        byte[] bytes = "name\r\nvalue\r\n".getBytes("UTF-8");
+        byte[] bytes = "name\r\nvalue\r\n".getBytes(StandardCharsets.UTF_8);
         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
         ReadableByteChannel ch = Channels.newChannel(bais);
         InputStream in = Channels.newInputStream(ch);
-        InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
+        InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
         CSVReaderBuilder builder = new CSVReaderBuilder(reader);
         CSVReader csv = builder.withVerifyReader(false).build();
         assertEquals(2, csv.readAll().size());
@@ -701,13 +668,9 @@ public class CSVReaderTest {
 
     @Test
     public void featureRequest60ByDefaultEmptyFieldsAreBlank() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append(",,,\"\",");
-
-        StringReader stringReader = new StringReader(sb.toString());
+        mockDataBuilder.addDataRow(",,,\"\",");
 
-        CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
         CSVReader csvReader = builder.build();
 
         String[] row = csvReader.readNext();
@@ -723,17 +686,12 @@ public class CSVReaderTest {
     @Test
     public void featureRequest60TreatEmptyFieldsAsNull() throws IOException, CsvValidationException {
 
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append(",,,\"\",");
-
-        StringReader stringReader = new StringReader(sb.toString());
-
-        CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
+        mockDataBuilder.addDataRow(",,,\"\",");
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
 
         CSVReader csvReader = builder.withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS).build();
 
-        String item[] = csvReader.readNext();
+        String[] item = csvReader.readNext();
 
         assertEquals(5, item.length);
         assertNull(item[0]);
@@ -746,16 +704,12 @@ public class CSVReaderTest {
 
     @Test
     public void featureRequest60TreatEmptyDelimitedFieldsAsNull() throws IOException, CsvValidationException {
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append(",,,\"\",");
-
-        StringReader stringReader = new StringReader(sb.toString());
+        mockDataBuilder.addDataRow(",,,\"\",");
 
-        CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
         CSVReader csvReader = builder.withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_QUOTES).build();
 
-        String item[] = csvReader.readNext();
+        String[] item = csvReader.readNext();
 
         assertEquals(5, item.length);
         assertEquals("", item[0]);
@@ -767,17 +721,11 @@ public class CSVReaderTest {
 
     @Test
     public void featureRequest60TreatEmptyFieldsDelimitedOrNotAsNull() throws IOException, CsvValidationException {
-
-        StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
-
-        sb.append(",,,\"\",");
-
-        StringReader stringReader = new StringReader(sb.toString());
-
-        CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
+        mockDataBuilder.addDataRow(",,,\"\",");
+        CSVReaderBuilder builder = new CSVReaderBuilder(mockDataBuilder.buildStringReader());
         CSVReader csvReader = builder.withFieldAsNull(CSVReaderNullFieldIndicator.BOTH).build();
 
-        String item[] = csvReader.readNext();
+        String[] item = csvReader.readNext();
 
         assertEquals(5, item.length);
         assertNull(item[0]);
@@ -791,9 +739,7 @@ public class CSVReaderTest {
     public void testMultilineLimit() {
         CSVReader r = new CSVReaderBuilder(new StringReader("This,is,a,\"test\na\",test"))
                 .withMultilineLimit(1).build();
-        Assertions.assertThrows(IOException.class, () -> {
-            r.readNext();
-        });
+        Assertions.assertThrows(IOException.class, () -> r.readNext());
     }
 
     @Test
diff --git a/src/test/java/com/opencsv/CSVReaderWithRowProcessorTest.java b/src/test/java/com/opencsv/CSVReaderWithRowProcessorTest.java
index 2dfa245..55a84c0 100644
--- a/src/test/java/com/opencsv/CSVReaderWithRowProcessorTest.java
+++ b/src/test/java/com/opencsv/CSVReaderWithRowProcessorTest.java
@@ -13,7 +13,7 @@ import java.util.List;
 import static org.junit.jupiter.api.Assertions.*;
 
 public class CSVReaderWithRowProcessorTest {
-    private static RowProcessor ROW_PROCESSOR = new BlankColumnsBecomeNull();
+    private static final RowProcessor ROW_PROCESSOR = new BlankColumnsBecomeNull();
     private static final String LINES = "a,, \n, ,\n";
 
     @DisplayName("CSVReader with RowProcessor with good string")
diff --git a/src/test/java/com/opencsv/CSVReaderWithValidatorsTest.java b/src/test/java/com/opencsv/CSVReaderWithValidatorsTest.java
index dd7a901..db18eca 100644
--- a/src/test/java/com/opencsv/CSVReaderWithValidatorsTest.java
+++ b/src/test/java/com/opencsv/CSVReaderWithValidatorsTest.java
@@ -22,9 +22,7 @@ public class CSVReaderWithValidatorsTest {
     private LineDoesNotHaveForbiddenString lineDoesNotHaveBadString;
     private LineDoesNotHaveForbiddenString lineDoesNotHaveAwfulString;
 
-    private static final Function<String[], Boolean> ROW_MUST_HAVE_THREE_COLUMNS = (x) -> {
-        return x.length == 3;
-    };
+    private static final Function<String[], Boolean> ROW_MUST_HAVE_THREE_COLUMNS = (x) -> x.length == 3;
     private static final RowValidator THREE_COLUMNS_ROW_VALIDATOR = new RowFunctionValidator(ROW_MUST_HAVE_THREE_COLUMNS, "Row must have three columns!");
 
     @BeforeEach
@@ -52,7 +50,7 @@ public class CSVReaderWithValidatorsTest {
 
     @DisplayName("CSVReader with LineValidator with bad string")
     @Test
-    public void readerWithLineValidatorWithBadString() throws IOException {
+    public void readerWithLineValidatorWithBadString() {
         String lines = "a,b,c\nd,bad,f\n";
         StringReader stringReader = new StringReader(lines);
         CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
@@ -62,14 +60,12 @@ public class CSVReaderWithValidatorsTest {
                 .withLineValidator(lineDoesNotHaveBadString)
                 .build();
 
-        assertThrows(CsvValidationException.class, () -> {
-            List<String[]> rows = csvReader.readAll();
-        });
+        assertThrows(CsvValidationException.class, csvReader::readAll);
     }
 
     @DisplayName("CSVReader with LineValidator with bad first string")
     @Test
-    public void readerWithLineValidatorWithBadFirstString() throws IOException {
+    public void readerWithLineValidatorWithBadFirstString() {
         String lines = "d,bad,f\na,b,c\n";
         StringReader stringReader = new StringReader(lines);
         CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
@@ -91,7 +87,7 @@ public class CSVReaderWithValidatorsTest {
 
     @DisplayName("CSVReader populates line number of exception thrown by LineValidatorAggregator")
     @Test
-    public void readerWithLineValidatorExceptionContainsLineNumber() throws IOException {
+    public void readerWithLineValidatorExceptionContainsLineNumber() {
         String lines = "a,b,c\nd,bad,f\n";
         StringReader stringReader = new StringReader(lines);
         CSVReaderBuilder builder = new CSVReaderBuilder(stringReader);
@@ -122,9 +118,7 @@ public class CSVReaderWithValidatorsTest {
                 .withRowValidator(THREE_COLUMNS_ROW_VALIDATOR)
                 .build();
 
-        assertThrows(CsvValidationException.class, () -> {
-            List<String[]> rows = csvReader.readAll();
-        });
+        assertThrows(CsvValidationException.class, csvReader::readAll);
     }
 
     @DisplayName("CSVReader populates line number of exception thrown by RowValidatorAggregator")
@@ -139,7 +133,7 @@ public class CSVReaderWithValidatorsTest {
                 .build();
 
         try {
-            List<String[]> rows = csvReader.readAll();
+            csvReader.readAll();
             fail("Expected a CsvValidationException to be thrown!");
         } catch (CsvValidationException cve) {
             assertEquals(2, cve.getLineNumber());
diff --git a/src/test/java/com/opencsv/CSVWriterBuilderTest.java b/src/test/java/com/opencsv/CSVWriterBuilderTest.java
index 8b7c439..891e1b0 100644
--- a/src/test/java/com/opencsv/CSVWriterBuilderTest.java
+++ b/src/test/java/com/opencsv/CSVWriterBuilderTest.java
@@ -9,13 +9,13 @@ import java.io.StringWriter;
 import java.io.Writer;
 import java.lang.reflect.Field;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.mock;
 
 public class CSVWriterBuilderTest {
     private CSVWriterBuilder builder;
     private Writer writer;
-    private ICSVParser mockParser = mock(ICSVParser.class);
+    private final ICSVParser mockParser = mock(ICSVParser.class);
 
     @BeforeEach
     public void setup() {
@@ -47,16 +47,12 @@ public class CSVWriterBuilderTest {
 
     @Test
     public void withSeparatorFailsIfParserSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withParser(mockParser).withSeparator(ICSVParser.DEFAULT_SEPARATOR);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withParser(mockParser).withSeparator(ICSVParser.DEFAULT_SEPARATOR));
     }
 
     @Test
     public void withParserFailsIfSeparatorSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withSeparator(ICSVParser.DEFAULT_SEPARATOR).withParser(mockParser);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withSeparator(ICSVParser.DEFAULT_SEPARATOR).withParser(mockParser));
     }
 
     @Test
@@ -69,16 +65,12 @@ public class CSVWriterBuilderTest {
 
     @Test
     public void withQuoteCharFailsIfParserSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withParser(mockParser).withQuoteChar(ICSVParser.DEFAULT_SEPARATOR);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withParser(mockParser).withQuoteChar(ICSVParser.DEFAULT_SEPARATOR));
     }
 
     @Test
     public void withParserFailsIfQuoteCharSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withQuoteChar(ICSVParser.DEFAULT_SEPARATOR).withParser(mockParser);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withQuoteChar(ICSVParser.DEFAULT_SEPARATOR).withParser(mockParser));
     }
 
     @Test
@@ -91,16 +83,12 @@ public class CSVWriterBuilderTest {
 
     @Test
     public void withEscapeCharFailsIfParserSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withParser(mockParser).withEscapeChar(ICSVParser.DEFAULT_ESCAPE_CHARACTER);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withParser(mockParser).withEscapeChar(ICSVParser.DEFAULT_ESCAPE_CHARACTER));
     }
 
     @Test
     public void withParserFailsIfEscapeCharSet() {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            builder.withEscapeChar(ICSVParser.DEFAULT_ESCAPE_CHARACTER).withParser(mockParser);
-        });
+        Assertions.assertThrows(IllegalArgumentException.class, () -> builder.withEscapeChar(ICSVParser.DEFAULT_ESCAPE_CHARACTER).withParser(mockParser));
     }
 
     @Test
diff --git a/src/test/java/com/opencsv/CSVWriterTest.java b/src/test/java/com/opencsv/CSVWriterTest.java
index 0d2fedf..5034577 100644
--- a/src/test/java/com/opencsv/CSVWriterTest.java
+++ b/src/test/java/com/opencsv/CSVWriterTest.java
@@ -17,7 +17,6 @@ package com.opencsv;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
-import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.io.*;
@@ -25,7 +24,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.*;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.*;
@@ -211,17 +210,14 @@ public class CSVWriterTest {
       @SuppressWarnings("unchecked")
       Iterable<String[]> iterable = mock(Iterable.class);
 
-      Answer<Iterator> iteratorAnswer = new Answer<Iterator>() {
-         @Override
-         public Iterator answer(InvocationOnMock invocationOnMock) {
-            @SuppressWarnings("unchecked")
-            Iterator<String[]> iterator = mock(Iterator.class);
-            when(iterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true)
-                    .thenReturn(false);
-            when(iterator.next()).thenReturn(line1).thenReturn(line2).thenReturn(line3)
-                    .thenThrow(NoSuchElementException.class);
-            return iterator;
-         }
+      Answer<Iterator<String[]>> iteratorAnswer = invocationOnMock -> {
+         @SuppressWarnings("unchecked")
+         Iterator<String[]> iterator = mock(Iterator.class);
+         when(iterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true)
+                 .thenReturn(false);
+         when(iterator.next()).thenReturn(line1).thenReturn(line2).thenReturn(line3)
+                 .thenThrow(NoSuchElementException.class);
+         return iterator;
       };
       when(iterable.iterator()).then(iteratorAnswer);
 
@@ -361,14 +357,12 @@ public class CSVWriterTest {
    }
 
    @Test
-   public void flushWillThrowIOException() throws IOException {
+   public void flushWillThrowIOException() {
       String[] line = {"Foo", "bar's"};
       StringWriter sw = new StringWriter();
       ICSVWriter csvw = new CSVWriterExceptionThrower(sw);
       csvw.writeNext(line);
-      Assertions.assertThrows(IOException.class, () -> {
-         csvw.flush();
-      });
+      Assertions.assertThrows(IOException.class, csvw::flush);
    }
 
    @Test
@@ -759,7 +753,7 @@ public class CSVWriterTest {
       verify(csvWriter).writeNext(any(String[].class), anyBoolean(), any(StringBuilder.class));
 
       IOException storedException = csvWriter.getException();
-      assertEquals("Expected Exception is not returned by getException", ioException, storedException);
+      assertEquals(ioException, storedException, "Expected Exception is not returned by getException");
    }
 
    @Test
@@ -779,7 +773,7 @@ public class CSVWriterTest {
       verify(csvWriter).writeNext(any(String[].class), anyBoolean(), any(StringBuilder.class));
 
       IOException storedException = csvWriter.getException();
-      assertEquals("Expected Exception is not returned by getException", ioException, storedException);
+      assertEquals(ioException, storedException, "Expected Exception is not returned by getException");
    }
 
    @Test
@@ -795,11 +789,11 @@ public class CSVWriterTest {
       csvWriter.writeNext(SIMPLE_STRING_ARRAY);
 
       IOException storedException = csvWriter.getException();
-      assertEquals("Expected Exception is not returned by getException", ioException, storedException);
+      assertEquals(ioException, storedException, "Expected Exception is not returned by getException");
 
       csvWriter.resetError();
 
-      assertNull("Exception has not been removed", csvWriter.getException());
+      assertNull(csvWriter.getException(), "Exception has not been removed");
 
       csvWriter.close();
 
@@ -818,11 +812,11 @@ public class CSVWriterTest {
 
       csvWriter.writeNext(SIMPLE_STRING_ARRAY);
 
-      assertTrue("Error has not occurred initially", csvWriter.checkError());
+      assertTrue(csvWriter.checkError(), "Error has not occurred initially");
 
       csvWriter.resetError();
 
-      assertFalse("Error has not been removed", csvWriter.checkError());
+      assertFalse(csvWriter.checkError(), "Error has not been removed");
 
       csvWriter.close();
 
diff --git a/src/test/java/com/opencsv/CsvReaderHeaderAwareTest.java b/src/test/java/com/opencsv/CsvReaderHeaderAwareTest.java
index 19768d3..6368be6 100644
--- a/src/test/java/com/opencsv/CsvReaderHeaderAwareTest.java
+++ b/src/test/java/com/opencsv/CsvReaderHeaderAwareTest.java
@@ -2,6 +2,7 @@ package com.opencsv;
 
 import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import com.opencsv.exceptions.CsvValidationException;
+import com.opencsv.processor.RowProcessor;
 import com.opencsv.validators.LineValidatorAggregator;
 import com.opencsv.validators.RowValidatorAggregator;
 import org.junit.jupiter.api.Assertions;
@@ -106,33 +107,25 @@ public class CsvReaderHeaderAwareTest {
     }
 
     @Test
-    public void shouldFailForInvalidColumn() throws IOException {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            csvr.readNext("fourth");
-        });
+    public void shouldFailForInvalidColumn() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> csvr.readNext("fourth"));
     }
 
     @Test
-    public void shouldFailForInvalidColumnEvenAmongstValidOnes() throws IOException {
-        Assertions.assertThrows(IllegalArgumentException.class, () -> {
-            csvr.readNext("first", "third", "fourth");
-        });
+    public void shouldFailForInvalidColumnEvenAmongstValidOnes() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> csvr.readNext("first", "third", "fourth"));
     }
 
     @Test
     public void shouldFailWhenNumberOfDataItemsIsLessThanHeader() throws IOException {
         csvr.skip(7);
-        Assertions.assertThrows(IOException.class, () -> {
-            csvr.readNext("second");
-        });
+        Assertions.assertThrows(IOException.class, () -> csvr.readNext("second"));
     }
 
     @Test
     public void shouldFailWhenNumberOfDataItemsIsGreaterThanHeader() throws IOException {
         csvr.skip(6);
-        Assertions.assertThrows(IOException.class, () -> {
-            csvr.readNext("second");
-        });
+        Assertions.assertThrows(IOException.class, () -> csvr.readNext("second"));
     }
 
     @Test
@@ -159,9 +152,7 @@ public class CsvReaderHeaderAwareTest {
 
         csvr.skip(5);
 
-        Assertions.assertThrows(IOException.class, () -> {
-            csvr.readMap();
-        });
+        Assertions.assertThrows(IOException.class, () -> csvr.readMap());
     }
 
     @Test
@@ -173,9 +164,7 @@ public class CsvReaderHeaderAwareTest {
 
         csvr.skip(6);
 
-        Assertions.assertThrows(IOException.class, () -> {
-            csvr.readMap();
-        });
+        Assertions.assertThrows(IOException.class, () -> csvr.readMap());
     }
 
     @Test
@@ -195,7 +184,13 @@ public class CsvReaderHeaderAwareTest {
         ICSVParser parser = mock(ICSVParser.class);
         when(parser.parseLineMulti(anyString())).thenReturn(new String[]{"myHeader"});
         CSVReaderHeaderAware reader = new CSVReaderHeaderAware(createReader(), 0, parser, false, false, 1, Locale.getDefault(),
-                new LineValidatorAggregator(), new RowValidatorAggregator());
+                new LineValidatorAggregator(), new RowValidatorAggregator(), new RowProcessor() {
+            @Override
+            public String processColumnItem(String column) {return column;}
+
+            @Override
+            public void processRow(String[] row) {}
+        });
         assertThat(reader.readMap().keySet().iterator().next(), is("myHeader"));
     }
 
diff --git a/src/test/java/com/opencsv/MockClob.java b/src/test/java/com/opencsv/MockClob.java
index fd80625..75d91df 100644
--- a/src/test/java/com/opencsv/MockClob.java
+++ b/src/test/java/com/opencsv/MockClob.java
@@ -17,7 +17,6 @@ package com.opencsv;
 
 import java.io.*;
 import java.sql.Clob;
-import java.sql.SQLException;
 
 public class MockClob implements Clob {
 
diff --git a/src/test/java/com/opencsv/MockResultSetBuilder.java b/src/test/java/com/opencsv/MockResultSetBuilder.java
index 217c291..e20886d 100644
--- a/src/test/java/com/opencsv/MockResultSetBuilder.java
+++ b/src/test/java/com/opencsv/MockResultSetBuilder.java
@@ -1,10 +1,14 @@
 package com.opencsv;
 
 import javax.sql.rowset.serial.SerialClob;
+import java.io.InputStream;
+import java.io.Reader;
 import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.SQLXML;
 import java.sql.*;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Calendar;
+import java.util.Map;
 
 import static java.sql.Types.*;
 import static org.mockito.Mockito.mock;
@@ -14,37 +18,16 @@ public class MockResultSetBuilder {
 
    public static ResultSet buildResultSet(ResultSetMetaData metaData, String[] columnValues, int[] columnTypes) throws SQLException {
       ResultSet resultSet = mock(ResultSet.class);
-      List<Boolean> wnrl = new ArrayList<>();
       when(resultSet.getMetaData()).thenReturn(metaData);
 
       for (int i = 0; i < columnValues.length; i++) {
-         setExpectToGetColumnValue(resultSet, i + 1, columnValues[i], columnTypes[i], wnrl);
+         setExpectToGetColumnValue(resultSet, i + 1, columnValues[i], columnTypes[i]);
       }
 
-      if (!wnrl.isEmpty()) {
-         // Why I have to do it this way I have no idea. but I cannot pass in just an array of Boolean I
-         // have to break it up into a first value and the rest of the values.
-
-         Boolean firstValue = wnrl.get(0);
-         wnrl.remove(0);
-
-         Boolean[] values = new Boolean[wnrl.size()];
-         int i = 0;
-         for (Boolean b : wnrl) {
-            values[i++] = b;
-         }
-
-         if (!wnrl.isEmpty()) {
-            when(resultSet.wasNull()).thenReturn(firstValue, values);
-         } else {
-            when(resultSet.wasNull()).thenReturn(firstValue);
-         }
-      }
-
-      return resultSet;
+      return wrapResultsetWasNull(columnValues, resultSet);
    }
 
-   private static void setExpectToGetColumnValue(ResultSet rs, int index, String value, int type, List<Boolean> wnrl) throws SQLException {
+   private static void setExpectToGetColumnValue(ResultSet rs, int index, String value, int type) throws SQLException {
 
       switch (type) {
          case BIT:
@@ -56,7 +39,6 @@ public class MockResultSetBuilder {
             break;
          case BIGINT:
             when(rs.getBigDecimal(index)).thenReturn(value != null ? new BigDecimal(value) : null);
-            wnrl.add(value == null);
             break;
          case Types.DECIMAL:
          case Types.REAL:
@@ -64,16 +46,15 @@ public class MockResultSetBuilder {
             when(rs.getBigDecimal(index)).thenReturn(value != null ? new BigDecimal(value) : null);
             break;
          case Types.DOUBLE:
-            when(rs.getDouble(index)).thenReturn(value != null ? new Double(value) : null);
+            when(rs.getDouble(index)).thenReturn(value != null ? Double.valueOf(value) : null);
             break;
          case Types.FLOAT:
-            when(rs.getFloat(index)).thenReturn(value != null ? new Float(value) : null);
+            when(rs.getFloat(index)).thenReturn(value != null ? Float.valueOf(value) : null);
             break;
          case Types.INTEGER:
          case Types.TINYINT:
          case Types.SMALLINT:
-            when(rs.getInt(index)).thenReturn(value != null ? new Integer(value) : 0);
-            wnrl.add(value == null);
+            when(rs.getInt(index)).thenReturn(value != null ? Integer.parseInt(value) : 0);
             break;
          case Types.NVARCHAR:
          case Types.NCHAR:
@@ -124,7 +105,7 @@ public class MockResultSetBuilder {
       if (value == null) {
          date = null;
       } else {
-         Long milliseconds = Long.valueOf(value);
+         long milliseconds = Long.parseLong(value);
          date = new Date(milliseconds);
       }
       return date;
@@ -136,7 +117,7 @@ public class MockResultSetBuilder {
       if (value == null) {
          time = null;
       } else {
-         Long milliseconds = Long.valueOf(value);
+         long milliseconds = Long.parseLong(value);
          time = new Time(milliseconds);
       }
       return time;
@@ -148,7 +129,7 @@ public class MockResultSetBuilder {
       if (value == null) {
          timestamp = null;
       } else {
-         Long milliseconds = Long.valueOf(value);
+         long milliseconds = Long.parseLong(value);
          timestamp = new Timestamp(milliseconds);
       }
       return timestamp;
@@ -191,4 +172,813 @@ public class MockResultSetBuilder {
          when(rs.next()).thenReturn(true, nextArray);
       }
    }
+
+   private static ResultSet wrapResultsetWasNull(String[] columnValues, ResultSet resultSet) {
+      return new ResultSet()  {
+         int lastColumn = 0;
+
+         public boolean wasNull() throws SQLException {
+            return columnValues[lastColumn - 1] == null;
+         }
+          
+         public boolean absolute(int row) throws SQLException {
+            return resultSet.absolute(row);
+         }
+
+         public void afterLast() throws SQLException {
+            resultSet.afterLast();
+         }
+
+         public void beforeFirst() throws SQLException {
+            resultSet.beforeFirst();
+         }
+
+         public void cancelRowUpdates() throws SQLException {
+            resultSet.cancelRowUpdates();
+         }
+
+         public void clearWarnings() throws SQLException {
+            resultSet.clearWarnings();
+         }
+
+         public void close() throws SQLException {
+            resultSet.close();
+         }
+
+         public void deleteRow() throws SQLException {
+            resultSet.deleteRow();
+         }
+
+         public int findColumn(String columnLabel) throws SQLException {
+            return resultSet.findColumn(columnLabel);
+         }
+
+         public boolean first() throws SQLException {
+            return resultSet.first();
+         }
+
+         public Array getArray(int columnIndex) throws SQLException {
+            return resultSet.getArray(columnIndex);
+         }
+
+         public Array getArray(String columnLabel) throws SQLException {
+            return resultSet.getArray(columnLabel);
+         }
+
+         public InputStream getAsciiStream(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getAsciiStream(columnIndex);
+         }
+
+         public InputStream getAsciiStream(String columnLabel) throws SQLException {
+            return resultSet.getAsciiStream(columnLabel);
+         }
+
+         @Deprecated
+         public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBigDecimal(columnIndex, scale);
+         }
+
+         public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBigDecimal(columnIndex);
+         }
+
+         @Deprecated
+         public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
+            return resultSet.getBigDecimal(columnLabel, scale);
+         }
+
+         public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
+            return resultSet.getBigDecimal(columnLabel);
+         }
+
+         public InputStream getBinaryStream(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBinaryStream(columnIndex);
+         }
+
+         public InputStream getBinaryStream(String columnLabel) throws SQLException {
+            return resultSet.getBinaryStream(columnLabel);
+         }
+
+         public Blob getBlob(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBlob(columnIndex);
+         }
+
+         public Blob getBlob(String columnLabel) throws SQLException {
+            return resultSet.getBlob(columnLabel);
+         }
+
+         public boolean getBoolean(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBoolean(columnIndex);
+         }
+
+         public boolean getBoolean(String columnLabel) throws SQLException {
+            return resultSet.getBoolean(columnLabel);
+         }
+
+         public byte getByte(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getByte(columnIndex);
+         }
+
+         public byte getByte(String columnLabel) throws SQLException {
+            return resultSet.getByte(columnLabel);
+         }
+
+         public byte[] getBytes(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getBytes(columnIndex);
+         }
+
+         public byte[] getBytes(String columnLabel) throws SQLException {
+            return resultSet.getBytes(columnLabel);
+         }
+
+         public Reader getCharacterStream(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getCharacterStream(columnIndex);
+         }
+
+         public Reader getCharacterStream(String columnLabel) throws SQLException {
+            return resultSet.getCharacterStream(columnLabel);
+         }
+
+         public Clob getClob(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getClob(columnIndex);
+         }
+
+         public Clob getClob(String columnLabel) throws SQLException {
+            return resultSet.getClob(columnLabel);
+         }
+
+         public int getConcurrency() throws SQLException {
+            return resultSet.getConcurrency();
+         }
+
+         public String getCursorName() throws SQLException {
+            return resultSet.getCursorName();
+         }
+
+         public Date getDate(int columnIndex, Calendar cal) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getDate(columnIndex, cal);
+         }
+
+         public Date getDate(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getDate(columnIndex);
+         }
+
+         public Date getDate(String columnLabel, Calendar cal) throws SQLException {
+            return resultSet.getDate(columnLabel, cal);
+         }
+
+         public Date getDate(String columnLabel) throws SQLException {
+            return resultSet.getDate(columnLabel);
+         }
+
+         public double getDouble(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getDouble(columnIndex);
+         }
+
+         public double getDouble(String columnLabel) throws SQLException {
+            return resultSet.getDouble(columnLabel);
+         }
+
+         public int getFetchDirection() throws SQLException {
+            return resultSet.getFetchDirection();
+         }
+
+         public int getFetchSize() throws SQLException {
+            return resultSet.getFetchSize();
+         }
+
+         public float getFloat(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getFloat(columnIndex);
+         }
+
+         public float getFloat(String columnLabel) throws SQLException {
+            return resultSet.getFloat(columnLabel);
+         }
+
+         public int getHoldability() throws SQLException {
+            return resultSet.getHoldability();
+         }
+
+         public int getInt(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getInt(columnIndex);
+         }
+
+         public int getInt(String columnLabel) throws SQLException {
+            return resultSet.getInt(columnLabel);
+         }
+
+         public long getLong(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getLong(columnIndex);
+         }
+
+         public long getLong(String columnLabel) throws SQLException {
+            return resultSet.getLong(columnLabel);
+         }
+
+         public ResultSetMetaData getMetaData() throws SQLException {
+            return resultSet.getMetaData();
+         }
+
+         public Reader getNCharacterStream(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getNCharacterStream(columnIndex);
+         }
+
+         public Reader getNCharacterStream(String columnLabel) throws SQLException {
+            return resultSet.getNCharacterStream(columnLabel);
+         }
+
+         public NClob getNClob(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getNClob(columnIndex);
+         }
+
+         public NClob getNClob(String columnLabel) throws SQLException {
+            return resultSet.getNClob(columnLabel);
+         }
+
+         public String getNString(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getNString(columnIndex);
+         }
+
+         public String getNString(String columnLabel) throws SQLException {
+            return resultSet.getNString(columnLabel);
+         }
+
+         public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getObject(columnIndex, type);
+         }
+
+         public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getObject(columnIndex, map);
+         }
+
+         public Object getObject(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getObject(columnIndex);
+         }
+
+         public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
+            return resultSet.getObject(columnLabel, type);
+         }
+
+         public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
+            return resultSet.getObject(columnLabel, map);
+         }
+
+         public Object getObject(String columnLabel) throws SQLException {
+            return resultSet.getObject(columnLabel);
+         }
+
+         public Ref getRef(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getRef(columnIndex);
+         }
+
+         public Ref getRef(String columnLabel) throws SQLException {
+            return resultSet.getRef(columnLabel);
+         }
+
+         public int getRow() throws SQLException {
+            return resultSet.getRow();
+         }
+
+         public RowId getRowId(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getRowId(columnIndex);
+         }
+
+         public RowId getRowId(String columnLabel) throws SQLException {
+            return resultSet.getRowId(columnLabel);
+         }
+
+         public SQLXML getSQLXML(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getSQLXML(columnIndex);
+         }
+
+         public SQLXML getSQLXML(String columnLabel) throws SQLException {
+            return resultSet.getSQLXML(columnLabel);
+         }
+
+         public short getShort(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getShort(columnIndex);
+         }
+
+         public short getShort(String columnLabel) throws SQLException {
+            return resultSet.getShort(columnLabel);
+         }
+
+         public Statement getStatement() throws SQLException {
+            return resultSet.getStatement();
+         }
+
+         public String getString(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getString(columnIndex);
+         }
+
+         public String getString(String columnLabel) throws SQLException {
+            return resultSet.getString(columnLabel);
+         }
+
+         public Time getTime(int columnIndex, Calendar cal) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getTime(columnIndex, cal);
+         }
+
+         public Time getTime(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getTime(columnIndex);
+         }
+
+         public Time getTime(String columnLabel, Calendar cal) throws SQLException {
+            return resultSet.getTime(columnLabel, cal);
+         }
+
+         public Time getTime(String columnLabel) throws SQLException {
+            return resultSet.getTime(columnLabel);
+         }
+
+         public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getTimestamp(columnIndex, cal);
+         }
+
+         public Timestamp getTimestamp(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getTimestamp(columnIndex);
+         }
+
+         public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
+            return resultSet.getTimestamp(columnLabel, cal);
+         }
+
+         public Timestamp getTimestamp(String columnLabel) throws SQLException {
+            return resultSet.getTimestamp(columnLabel);
+         }
+
+         public int getType() throws SQLException {
+            return resultSet.getType();
+         }
+
+         public URL getURL(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getURL(columnIndex);
+         }
+
+         public URL getURL(String columnLabel) throws SQLException {
+            return resultSet.getURL(columnLabel);
+         }
+
+         @Deprecated
+         public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+            lastColumn = columnIndex;
+            return resultSet.getUnicodeStream(columnIndex);
+         }
+
+         @Deprecated
+         public InputStream getUnicodeStream(String columnLabel) throws SQLException {
+            return resultSet.getUnicodeStream(columnLabel);
+         }
+
+         public SQLWarning getWarnings() throws SQLException {
+            return resultSet.getWarnings();
+         }
+
+         public void insertRow() throws SQLException {
+            resultSet.insertRow();
+         }
+
+         public boolean isAfterLast() throws SQLException {
+            return resultSet.isAfterLast();
+         }
+
+         public boolean isBeforeFirst() throws SQLException {
+            return resultSet.isBeforeFirst();
+         }
+
+         public boolean isClosed() throws SQLException {
+            return resultSet.isClosed();
+         }
+
+         public boolean isFirst() throws SQLException {
+            return resultSet.isFirst();
+         }
+
+         public boolean isLast() throws SQLException {
+            return resultSet.isLast();
+         }
+
+         public boolean isWrapperFor(Class<?> arg0) throws SQLException {
+            return resultSet.isWrapperFor(arg0);
+         }
+
+         public boolean last() throws SQLException {
+            return resultSet.last();
+         }
+
+         public void moveToCurrentRow() throws SQLException {
+            resultSet.moveToCurrentRow();
+         }
+
+         public void moveToInsertRow() throws SQLException {
+            resultSet.moveToInsertRow();
+         }
+
+         public boolean next() throws SQLException {
+            return resultSet.next();
+         }
+
+         public boolean previous() throws SQLException {
+            return resultSet.previous();
+         }
+
+         public void refreshRow() throws SQLException {
+            resultSet.refreshRow();
+         }
+
+         public boolean relative(int rows) throws SQLException {
+            return resultSet.relative(rows);
+         }
+
+         public boolean rowDeleted() throws SQLException {
+            return resultSet.rowDeleted();
+         }
+
+         public boolean rowInserted() throws SQLException {
+            return resultSet.rowInserted();
+         }
+
+         public boolean rowUpdated() throws SQLException {
+            return resultSet.rowUpdated();
+         }
+
+         public void setFetchDirection(int direction) throws SQLException {
+            resultSet.setFetchDirection(direction);
+         }
+
+         public void setFetchSize(int rows) throws SQLException {
+            resultSet.setFetchSize(rows);
+         }
+
+         public <T> T unwrap(Class<T> arg0) throws SQLException {
+            return resultSet.unwrap(arg0);
+         }
+
+         public void updateArray(int columnIndex, Array x) throws SQLException {
+            resultSet.updateArray(columnIndex, x);
+         }
+
+         public void updateArray(String columnLabel, Array x) throws SQLException {
+            resultSet.updateArray(columnLabel, x);
+         }
+
+         public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
+            resultSet.updateAsciiStream(columnIndex, x, length);
+         }
+
+         public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
+            resultSet.updateAsciiStream(columnIndex, x, length);
+         }
+
+         public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
+            resultSet.updateAsciiStream(columnIndex, x);
+         }
+
+         public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException {
+            resultSet.updateAsciiStream(columnLabel, x, length);
+         }
+
+         public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
+            resultSet.updateAsciiStream(columnLabel, x, length);
+         }
+
+         public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
+            resultSet.updateAsciiStream(columnLabel, x);
+         }
+
+         public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+            resultSet.updateBigDecimal(columnIndex, x);
+         }
+
+         public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException {
+            resultSet.updateBigDecimal(columnLabel, x);
+         }
+
+         public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
+            resultSet.updateBinaryStream(columnIndex, x, length);
+         }
+
+         public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
+            resultSet.updateBinaryStream(columnIndex, x, length);
+         }
+
+         public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
+            resultSet.updateBinaryStream(columnIndex, x);
+         }
+
+         public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException {
+            resultSet.updateBinaryStream(columnLabel, x, length);
+         }
+
+         public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
+            resultSet.updateBinaryStream(columnLabel, x, length);
+         }
+
+         public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
+            resultSet.updateBinaryStream(columnLabel, x);
+         }
+
+         public void updateBlob(int columnIndex, Blob x) throws SQLException {
+            resultSet.updateBlob(columnIndex, x);
+         }
+
+         public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
+            resultSet.updateBlob(columnIndex, inputStream, length);
+         }
+
+         public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+            resultSet.updateBlob(columnIndex, inputStream);
+         }
+
+         public void updateBlob(String columnLabel, Blob x) throws SQLException {
+            resultSet.updateBlob(columnLabel, x);
+         }
+
+         public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
+            resultSet.updateBlob(columnLabel, inputStream, length);
+         }
+
+         public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
+            resultSet.updateBlob(columnLabel, inputStream);
+         }
+
+         public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+            resultSet.updateBoolean(columnIndex, x);
+         }
+
+         public void updateBoolean(String columnLabel, boolean x) throws SQLException {
+            resultSet.updateBoolean(columnLabel, x);
+         }
+
+         public void updateByte(int columnIndex, byte x) throws SQLException {
+            resultSet.updateByte(columnIndex, x);
+         }
+
+         public void updateByte(String columnLabel, byte x) throws SQLException {
+            resultSet.updateByte(columnLabel, x);
+         }
+
+         public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+            resultSet.updateBytes(columnIndex, x);
+         }
+
+         public void updateBytes(String columnLabel, byte[] x) throws SQLException {
+            resultSet.updateBytes(columnLabel, x);
+         }
+
+         public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
+            resultSet.updateCharacterStream(columnIndex, x, length);
+         }
+
+         public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+            resultSet.updateCharacterStream(columnIndex, x, length);
+         }
+
+         public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
+            resultSet.updateCharacterStream(columnIndex, x);
+         }
+
+         public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException {
+            resultSet.updateCharacterStream(columnLabel, reader, length);
+         }
+
+         public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+            resultSet.updateCharacterStream(columnLabel, reader, length);
+         }
+
+         public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
+            resultSet.updateCharacterStream(columnLabel, reader);
+         }
+
+         public void updateClob(int columnIndex, Clob x) throws SQLException {
+            resultSet.updateClob(columnIndex, x);
+         }
+
+         public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+            resultSet.updateClob(columnIndex, reader, length);
+         }
+
+         public void updateClob(int columnIndex, Reader reader) throws SQLException {
+            resultSet.updateClob(columnIndex, reader);
+         }
+
+         public void updateClob(String columnLabel, Clob x) throws SQLException {
+            resultSet.updateClob(columnLabel, x);
+         }
+
+         public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
+            resultSet.updateClob(columnLabel, reader, length);
+         }
+
+         public void updateClob(String columnLabel, Reader reader) throws SQLException {
+            resultSet.updateClob(columnLabel, reader);
+         }
+
+         public void updateDate(int columnIndex, Date x) throws SQLException {
+            resultSet.updateDate(columnIndex, x);
+         }
+
+         public void updateDate(String columnLabel, Date x) throws SQLException {
+            resultSet.updateDate(columnLabel, x);
+         }
+
+         public void updateDouble(int columnIndex, double x) throws SQLException {
+            resultSet.updateDouble(columnIndex, x);
+         }
+
+         public void updateDouble(String columnLabel, double x) throws SQLException {
+            resultSet.updateDouble(columnLabel, x);
+         }
+
+         public void updateFloat(int columnIndex, float x) throws SQLException {
+            resultSet.updateFloat(columnIndex, x);
+         }
+
+         public void updateFloat(String columnLabel, float x) throws SQLException {
+            resultSet.updateFloat(columnLabel, x);
+         }
+
+         public void updateInt(int columnIndex, int x) throws SQLException {
+            resultSet.updateInt(columnIndex, x);
+         }
+
+         public void updateInt(String columnLabel, int x) throws SQLException {
+            resultSet.updateInt(columnLabel, x);
+         }
+
+         public void updateLong(int columnIndex, long x) throws SQLException {
+            resultSet.updateLong(columnIndex, x);
+         }
+
+         public void updateLong(String columnLabel, long x) throws SQLException {
+            resultSet.updateLong(columnLabel, x);
+         }
+
+         public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+            resultSet.updateNCharacterStream(columnIndex, x, length);
+         }
+
+         public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+            resultSet.updateNCharacterStream(columnIndex, x);
+         }
+
+         public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+            resultSet.updateNCharacterStream(columnLabel, reader, length);
+         }
+
+         public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
+            resultSet.updateNCharacterStream(columnLabel, reader);
+         }
+
+         public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+            resultSet.updateNClob(columnIndex, nClob);
+         }
+
+         public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+            resultSet.updateNClob(columnIndex, reader, length);
+         }
+
+         public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+            resultSet.updateNClob(columnIndex, reader);
+         }
+
+         public void updateNClob(String columnLabel, NClob nClob) throws SQLException {
+            resultSet.updateNClob(columnLabel, nClob);
+         }
+
+         public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
+            resultSet.updateNClob(columnLabel, reader, length);
+         }
+
+         public void updateNClob(String columnLabel, Reader reader) throws SQLException {
+            resultSet.updateNClob(columnLabel, reader);
+         }
+
+         public void updateNString(int columnIndex, String nString) throws SQLException {
+            resultSet.updateNString(columnIndex, nString);
+         }
+
+         public void updateNString(String columnLabel, String nString) throws SQLException {
+            resultSet.updateNString(columnLabel, nString);
+         }
+
+         public void updateNull(int columnIndex) throws SQLException {
+            resultSet.updateNull(columnIndex);
+         }
+
+         public void updateNull(String columnLabel) throws SQLException {
+            resultSet.updateNull(columnLabel);
+         }
+
+         public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException {
+            resultSet.updateObject(columnIndex, x, scaleOrLength);
+         }
+
+         public void updateObject(int columnIndex, Object x) throws SQLException {
+            resultSet.updateObject(columnIndex, x);
+         }
+
+         public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException {
+            resultSet.updateObject(columnLabel, x, scaleOrLength);
+         }
+
+         public void updateObject(String columnLabel, Object x) throws SQLException {
+            resultSet.updateObject(columnLabel, x);
+         }
+
+         public void updateRef(int columnIndex, Ref x) throws SQLException {
+            resultSet.updateRef(columnIndex, x);
+         }
+
+         public void updateRef(String columnLabel, Ref x) throws SQLException {
+            resultSet.updateRef(columnLabel, x);
+         }
+
+         public void updateRow() throws SQLException {
+            resultSet.updateRow();
+         }
+
+         public void updateRowId(int columnIndex, RowId x) throws SQLException {
+            resultSet.updateRowId(columnIndex, x);
+         }
+
+         public void updateRowId(String columnLabel, RowId x) throws SQLException {
+            resultSet.updateRowId(columnLabel, x);
+         }
+
+         public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+            resultSet.updateSQLXML(columnIndex, xmlObject);
+         }
+
+         public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
+            resultSet.updateSQLXML(columnLabel, xmlObject);
+         }
+
+         public void updateShort(int columnIndex, short x) throws SQLException {
+            resultSet.updateShort(columnIndex, x);
+         }
+
+         public void updateShort(String columnLabel, short x) throws SQLException {
+            resultSet.updateShort(columnLabel, x);
+         }
+
+         public void updateString(int columnIndex, String x) throws SQLException {
+            resultSet.updateString(columnIndex, x);
+         }
+
+         public void updateString(String columnLabel, String x) throws SQLException {
+            resultSet.updateString(columnLabel, x);
+         }
+
+         public void updateTime(int columnIndex, Time x) throws SQLException {
+            resultSet.updateTime(columnIndex, x);
+         }
+
+         public void updateTime(String columnLabel, Time x) throws SQLException {
+            resultSet.updateTime(columnLabel, x);
+         }
+
+         public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+            resultSet.updateTimestamp(columnIndex, x);
+         }
+
+         public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException {
+            resultSet.updateTimestamp(columnLabel, x);
+         }
+
+      };
+   }
+
 }
diff --git a/src/test/java/com/opencsv/OpencsvTest.java b/src/test/java/com/opencsv/OpencsvTest.java
index 4105263..ff64c63 100644
--- a/src/test/java/com/opencsv/OpencsvTest.java
+++ b/src/test/java/com/opencsv/OpencsvTest.java
@@ -24,7 +24,7 @@ import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class OpencsvTest {
 
@@ -54,13 +54,13 @@ public class OpencsvTest {
 
       String[] line;
       for (int row = 0; (line = reader.readNext()) != null; row++) {
-         assertTrue(line.length == data[row].length);
+         assertEquals(line.length, data[row].length);
 
          for (int col = 0; col < line.length; col++) {
             if (data[row][col] == null) {
-               assertTrue(line[col].equals(""));
+               assertEquals("", line[col]);
             } else {
-               assertTrue(line[col].equals(data[row][col]));
+               assertEquals(line[col], data[row][col]);
             }
          }
       }
diff --git a/src/test/java/com/opencsv/RFC4180ParserBuilderTest.java b/src/test/java/com/opencsv/RFC4180ParserBuilderTest.java
index 9c85fba..c4fc157 100644
--- a/src/test/java/com/opencsv/RFC4180ParserBuilderTest.java
+++ b/src/test/java/com/opencsv/RFC4180ParserBuilderTest.java
@@ -5,7 +5,7 @@ import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class RFC4180ParserBuilderTest {
 
diff --git a/src/test/java/com/opencsv/ResultSetColumnNameHelperServiceTest.java b/src/test/java/com/opencsv/ResultSetColumnNameHelperServiceTest.java
index 864d9ef..ea011b7 100644
--- a/src/test/java/com/opencsv/ResultSetColumnNameHelperServiceTest.java
+++ b/src/test/java/com/opencsv/ResultSetColumnNameHelperServiceTest.java
@@ -5,10 +5,11 @@ import org.junit.jupiter.api.*;
 import java.io.IOException;
 import java.sql.*;
 import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -123,9 +124,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", "Column Name 2", "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -134,9 +133,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", "Column Name 2", "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -145,9 +142,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", "Column Name 2", "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -156,9 +151,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", null, "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -167,9 +160,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", "", "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -178,9 +169,7 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnHeaders = {"Column Name 1", "     ", "Column Name 3"};
 
         ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.setColumnNames(desiredColumnNames, columnHeaders);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.setColumnNames(desiredColumnNames, columnHeaders));
     }
 
     @Test
@@ -200,16 +189,14 @@ public class ResultSetColumnNameHelperServiceTest {
 
         // end expects
 
-        Assertions.assertThrows(UnsupportedOperationException.class, () -> {
-            service.getColumnNames(resultSet);
-        });
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> service.getColumnNames(resultSet));
     }
 
     @Test
     public void getBooleanFromResultSet() throws SQLException, IOException {
         String[] expectedNames = {"true", "false", "TRUE", "FALSE", "Null"};
         String[] realValues = {"true", "false", "TRUE", "FALSE", null};
-        String[] expectedValues = {"true", "false", "true", "false", "false"};
+        String[] expectedValues = {"true", "false", "true", "false", ""};
         int[] expectedTypes = {Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN};
 
         ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
@@ -282,7 +269,7 @@ public class ResultSetColumnNameHelperServiceTest {
 
     @Test
     public void getTimestampFromResultSetWithCustomFormat() throws SQLException, IOException {
-        Timestamp date = new Timestamp(new GregorianCalendar(2009, 11, 15, 12, 0, 0).getTimeInMillis());
+        Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
         long dateInMilliSeconds = date.getTime();
         String customFormat = "mm/dd/yy HH:mm:ss";
         SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
@@ -303,7 +290,7 @@ public class ResultSetColumnNameHelperServiceTest {
 
     @Test
     public void getSubsetFromResultSetWithCustomFormat() throws SQLException, IOException {
-        Timestamp date = new Timestamp(new GregorianCalendar(2009, 11, 15, 12, 0, 0).getTimeInMillis());
+        Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
         long dateInMilliSeconds = date.getTime();
         String customFormat = "mm/dd/yy HH:mm:ss";
         SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
@@ -326,4 +313,60 @@ public class ResultSetColumnNameHelperServiceTest {
         String[] columnValues = service.getColumnValues(resultSet, false, null, customFormat);
         assertArrayEquals(expectedValues, columnValues);
     }
+
+    @DisplayName("Bug#215: DateTimeFormat that are set are not being used.")
+    @Test
+    public void setDateTimeFormat() throws SQLException, IOException {
+        Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
+        long dateInMilliSeconds = date.getTime();
+        String customFormat = "mm/dd/yy HH:mm:ss";
+        SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
+
+        String[] realColumnNames = {"Timestamp", "Null"};
+        String[] realValues = {Long.toString(dateInMilliSeconds), null};
+
+        String[] desiredColumnNames = {"Timestamp"};
+        String[] desiredColumnHeaders = {"A timestamp"};
+
+        String[] expectedValues = {timeFormat.format(date)};
+        int[] expectedTypes = {Types.TIMESTAMP, Types.TIMESTAMP};
+
+        ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(realColumnNames, expectedTypes);
+        ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+        ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
+        service.setDateTimeFormat(customFormat);
+        service.setColumnNames(desiredColumnNames, desiredColumnHeaders);
+
+        String[] columnValues = service.getColumnValues(resultSet, false);
+        assertArrayEquals(expectedValues, columnValues);
+    }
+
+    @DisplayName("Bug#215: DateTime that are set are not being used.")
+    @Test
+    public void setDateFormat() throws SQLException, IOException {
+        Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15).getTimeInMillis());
+        long dateInMilliSeconds = date.getTime();
+        String customFormat = "mm/dd/yy";
+        SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
+
+        String[] realColumnNames = {"Timestamp", "Null"};
+        String[] realValues = {Long.toString(dateInMilliSeconds), null};
+
+        String[] desiredColumnNames = {"Timestamp"};
+        String[] desiredColumnHeaders = {"A timestamp"};
+
+        String[] expectedValues = {timeFormat.format(date)};
+        int[] expectedTypes = {Types.DATE, Types.DATE};
+
+        ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(realColumnNames, expectedTypes);
+        ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+        ResultSetColumnNameHelperService service = new ResultSetColumnNameHelperService();
+        service.setDateFormat(customFormat);
+        service.setColumnNames(desiredColumnNames, desiredColumnHeaders);
+
+        String[] columnValues = service.getColumnValues(resultSet, false);
+        assertArrayEquals(expectedValues, columnValues);
+    }
 }
diff --git a/src/test/java/com/opencsv/ResultSetHelperServiceTest.java b/src/test/java/com/opencsv/ResultSetHelperServiceTest.java
index 4b1231d..c204493 100644
--- a/src/test/java/com/opencsv/ResultSetHelperServiceTest.java
+++ b/src/test/java/com/opencsv/ResultSetHelperServiceTest.java
@@ -19,10 +19,14 @@ import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.sql.*;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
 import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.GregorianCalendar;
+import java.util.Locale;
 
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -86,7 +90,7 @@ public class ResultSetHelperServiceTest {
    public void getBooleanFromResultSet() throws SQLException, IOException {
       String[] expectedNames = {"true", "false", "TRUE", "FALSE", "Null"};
       String[] realValues = {"true", "false", "TRUE", "FALSE", null};
-      String[] expectedValues = {"true", "false", "true", "false", "false"};
+      String[] expectedValues = {"true", "false", "true", "false", ""};
       int[] expectedTypes = {Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN, Types.BOOLEAN};
 
       ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
@@ -284,7 +288,7 @@ public class ResultSetHelperServiceTest {
    @Test
    public void getDateFromResultSet() throws SQLException, IOException {
 
-      Date date = new Date(new GregorianCalendar(2009, 11, 15).getTimeInMillis());
+      Date date = new Date(new GregorianCalendar(2009, Calendar.DECEMBER, 15).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       SimpleDateFormat dateFormat = new SimpleDateFormat(ResultSetHelperService.DEFAULT_DATE_FORMAT);
 
@@ -306,7 +310,7 @@ public class ResultSetHelperServiceTest {
    public void getDateFromResultSetUsingCustomFormat() throws SQLException, IOException {
 
       String customDateFormat = "mm/dd/yy";
-      Date date = new Date(new GregorianCalendar(2009, 11, 15).getTimeInMillis());
+      Date date = new Date(new GregorianCalendar(2009, Calendar.DECEMBER, 15).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       SimpleDateFormat dateFormat = new SimpleDateFormat(customDateFormat);
 
@@ -328,7 +332,7 @@ public class ResultSetHelperServiceTest {
    public void setDateFormat() throws SQLException, IOException {
 
       String customDateFormat = "mm/dd/yy";
-      Date date = new Date(new GregorianCalendar(2009, 11, 15).getTimeInMillis());
+      Date date = new Date(new GregorianCalendar(2009, Calendar.DECEMBER, 15).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       SimpleDateFormat dateFormat = new SimpleDateFormat(customDateFormat);
 
@@ -370,7 +374,7 @@ public class ResultSetHelperServiceTest {
 
    @Test
    public void getTimestampFromResultSet() throws SQLException, IOException {
-      Timestamp date = new Timestamp(new GregorianCalendar(2009, 11, 15, 12, 0, 0).getTimeInMillis());
+      Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       SimpleDateFormat timeFormat = new SimpleDateFormat(ResultSetHelperService.DEFAULT_TIMESTAMP_FORMAT);
 
@@ -390,7 +394,7 @@ public class ResultSetHelperServiceTest {
 
    @Test
    public void getTimestampFromResultSetWithCustomFormat() throws SQLException, IOException {
-      Timestamp date = new Timestamp(new GregorianCalendar(2009, 11, 15, 12, 0, 0).getTimeInMillis());
+      Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       String customFormat = "mm/dd/yy HH:mm:ss";
       SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
@@ -411,7 +415,7 @@ public class ResultSetHelperServiceTest {
 
    @Test
    public void setDateTimeFormat() throws SQLException, IOException {
-      Timestamp date = new Timestamp(new GregorianCalendar(2009, 11, 15, 12, 0, 0).getTimeInMillis());
+      Timestamp date = new Timestamp(new GregorianCalendar(2009, Calendar.DECEMBER, 15, 12, 0, 0).getTimeInMillis());
       long dateInMilliSeconds = date.getTime();
       String customFormat = "mm/dd/yy HH:mm:ss";
       SimpleDateFormat timeFormat = new SimpleDateFormat(customFormat);
@@ -549,9 +553,81 @@ public class ResultSetHelperServiceTest {
       }
 
       if (substrsize > 0) {
-         sb.append(BUILDSTRING.substring(0, substrsize));
+         sb.append(BUILDSTRING, 0, substrsize);
       }
 
       return sb.toString();
    }
+
+   @Test
+   public void formatNumbersIntPrimitives() throws SQLException, IOException {
+      String[] expectedNames = {"Integer", "tinyint", "smallint", "Null"};
+      String[] realValues = {"1", "2", "3", null};
+      String[] expectedValues = {"001", "002", "003", ""};
+      int[] expectedTypes = {Types.INTEGER, Types.TINYINT, Types.SMALLINT, Types.INTEGER};
+
+      ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
+      ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+      ResultSetHelperService service = new ResultSetHelperService();
+      service.setIntegerFormat(new DecimalFormat("000"));
+      service.setFloatingPointFormat(new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.ENGLISH)));
+
+      String[] columnValues = service.getColumnValues(resultSet);
+      assertArrayEquals(expectedValues, columnValues);
+   }
+
+   @Test
+   public void formatNumbersFloatPrimitives() throws SQLException, IOException {
+      String[] expectedNames = {"double", "float"};
+      String[] realValues = {"2.2", "3.3"};
+      String[] expectedValues = {"2.20", "3.30"};
+      int[] expectedTypes = {Types.DOUBLE, Types.FLOAT};
+
+      ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
+      ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+      ResultSetHelperService service = new ResultSetHelperService();
+      service.setIntegerFormat(new DecimalFormat("000"));
+      service.setFloatingPointFormat(new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.ENGLISH)));
+
+      String[] columnValues = service.getColumnValues(resultSet);
+      assertArrayEquals(expectedValues, columnValues);
+   }
+
+   @Test
+   public void formatNumbersObjects() throws SQLException, IOException {
+      String[] expectedNames = {"Decimal", "real", "numeric", "Null"};
+      String[] realValues = {"1.1", "4.4", "5.5", null};
+      String[] expectedValues = {"1.10", "4.40", "5.50", ""};
+      int[] expectedTypes = {Types.DECIMAL, Types.REAL, Types.NUMERIC, Types.DECIMAL};
+
+      ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
+      ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+      ResultSetHelperService service = new ResultSetHelperService();
+      service.setIntegerFormat(new DecimalFormat("000"));
+      service.setFloatingPointFormat(new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.ENGLISH)));
+
+      String[] columnValues = service.getColumnValues(resultSet);
+      assertArrayEquals(expectedValues, columnValues);
+   }
+
+   @Test
+   public void formatBigInt() throws SQLException, IOException {
+      String[] expectedNames = {"BigInt", "Null BigInt"};
+      String[] realValues = {"11772935803167061222", null};
+      String[] expectedValues = {"1.177E19", ""};
+      int[] expectedTypes = {Types.BIGINT, Types.BIGINT};
+
+      ResultSetMetaData metaData = MockResultSetMetaDataBuilder.buildMetaData(expectedNames, expectedTypes);
+      ResultSet resultSet = MockResultSetBuilder.buildResultSet(metaData, realValues, expectedTypes);
+
+      ResultSetHelperService service = new ResultSetHelperService();
+      service.setIntegerFormat(new DecimalFormat("0.###E0", new DecimalFormatSymbols(Locale.ENGLISH)));
+
+      String[] columnValues = service.getColumnValues(resultSet);
+      assertArrayEquals(expectedValues, columnValues);
+   }
+   
 }
diff --git a/src/test/java/com/opencsv/TestUtilitiesTest.java b/src/test/java/com/opencsv/TestUtilitiesTest.java
index 859a0c6..34e4405 100644
--- a/src/test/java/com/opencsv/TestUtilitiesTest.java
+++ b/src/test/java/com/opencsv/TestUtilitiesTest.java
@@ -2,7 +2,7 @@ package com.opencsv;
 
 import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class TestUtilitiesTest {
 
diff --git a/src/test/java/com/opencsv/UniCodeTest.java b/src/test/java/com/opencsv/UniCodeTest.java
index e1b53ee..d12c9b5 100644
--- a/src/test/java/com/opencsv/UniCodeTest.java
+++ b/src/test/java/com/opencsv/UniCodeTest.java
@@ -10,8 +10,8 @@ import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.List;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class UniCodeTest {
    private static final String COMPOUND_STRING = "??,??";
@@ -27,8 +27,7 @@ public class UniCodeTest {
    @Test
    public void canParseUnicode() throws IOException {
       csvParser = new CSVParser();
-      String simpleString = COMPOUND_STRING;
-      String[] items = csvParser.parseLine(simpleString);
+      String[] items = csvParser.parseLine(COMPOUND_STRING);
       assertEquals(2, items.length);
       assertEquals(FIRST_STRING, items[0]);
       assertEquals(SECOND_STRING, items[1]);
diff --git a/src/test/java/com/opencsv/bean/AnnotationTest.java b/src/test/java/com/opencsv/bean/AnnotationTest.java
index fad9ba9..b73abc2 100644
--- a/src/test/java/com/opencsv/bean/AnnotationTest.java
+++ b/src/test/java/com/opencsv/bean/AnnotationTest.java
@@ -22,10 +22,7 @@ import com.opencsv.enums.CSVReaderNullFieldIndicator;
 import com.opencsv.exceptions.*;
 import org.apache.commons.beanutils.ConversionException;
 import org.apache.commons.lang3.StringUtils;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.*;
 
 import java.io.*;
 import java.math.BigDecimal;
@@ -33,7 +30,7 @@ import java.math.BigInteger;
 import java.text.ParseException;
 import java.util.*;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * This class tests all annotation-based mapping except
@@ -122,6 +119,26 @@ public class AnnotationTest {
         testGoodData(strat, fin, true);
     }
 
+    /**
+     * "Anonymous headers" is a term someone came up with for empty headers.
+     * If empty fields are converted to {@code null}, these null headers can't
+     * break opencsv. They are not supported, but they can't cause a crash.
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testAnonymousHeaders() throws FileNotFoundException {
+        HeaderColumnNameMappingStrategy<AnnotatedMockBeanFull> strat =
+                new HeaderColumnNameMappingStrategy<>();
+        strat.setType(AnnotatedMockBeanFull.class);
+        FileReader fin = new FileReader("src/test/resources/testAnonymousHeaders.csv");
+        List<AnnotatedMockBeanFull> beanList = new CsvToBeanBuilder<AnnotatedMockBeanFull>(fin)
+                .withSeparator(';')
+                .withMappingStrategy(strat)
+                .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
+                .build().parse();
+        assertEquals(2, beanList.size());
+    }
+
     @Test
     public void testCaptureWithNullField() throws FileNotFoundException {
         HeaderColumnNameMappingStrategy<AnnotatedMockBeanFull> strat =
@@ -206,7 +223,7 @@ public class AnnotationTest {
     @Test
     public void testGoodDerivedDataByPosition() throws IOException {
         ColumnPositionMappingStrategy<AnnotatedMockBeanFullDerived> stratd =
-                new ColumnPositionMappingStrategy<>();
+                new ColumnPositionMappingStrategyBuilder<AnnotatedMockBeanFullDerived>().build();
         stratd.setType(AnnotatedMockBeanFullDerived.class);
         FileReader fin = new FileReader("src/test/resources/testinputposderivedgood.csv");
         List<AnnotatedMockBeanFull> beanList = testGoodData(stratd, fin, true);
@@ -299,10 +316,11 @@ public class AnnotationTest {
             assertEquals("1978-01-15", bean.getSqldateDefaultLocale().toString());
             assertEquals("1978-01-15", bean.getSqldateSetLocale().toString());
             assertEquals("test string", bean.getStringClass());
-            assertEquals(new GregorianCalendar(1978, 0, 15).getTimeInMillis(), bean.getGcalFormatDefaultLocale().getTimeInMillis());
-            assertEquals(new GregorianCalendar(2018, 11, 13).getTimeInMillis(), bean.getGcalFormatSetLocale().getTimeInMillis());
+            assertEquals(new GregorianCalendar(1978, Calendar.JANUARY, 15).getTimeInMillis(), bean.getGcalFormatDefaultLocale().getTimeInMillis());
+            assertEquals(new GregorianCalendar(2018, Calendar.DECEMBER, 13).getTimeInMillis(), bean.getGcalFormatSetLocale().getTimeInMillis());
             assertEquals(1.01, bean.getFloatBadLocale(), 0.001);
             assertEquals(TestEnum.TEST1, bean.getTestEnum());
+            assertEquals(Currency.getInstance("EUR"), bean.getTestCurrency());
             assertNull(bean.getColumnDoesntExist());
             assertNull(bean.getUnmapped());
 
@@ -461,7 +479,7 @@ public class AnnotationTest {
         assertEquals("inside custom converter", bean.getRequiredWithCustom());
 
         bean = beanList.get(1);
-        assertEquals(Arrays.asList("really"), bean.getComplexString());
+        assertEquals(Collections.singletonList("really"), bean.getComplexString());
         assertTrue(bean.getComplexClass2() instanceof ComplexClassForCustomAnnotation);
         assertTrue(bean.getComplexClass2() instanceof ComplexDerivedClassForCustomAnnotation);
         assertEquals(Integer.MAX_VALUE - 5, bean.getComplexClass2().i);
@@ -611,8 +629,8 @@ public class AnnotationTest {
         auxiliaryTestUnbindableField(strat, read, 1);
     }
 
-    private void auxiliaryTestUnbindableField(MappingStrategy strat, CSVReader read, long expectedLineNumber) {
-        CsvToBean ctb = new CsvToBeanBuilder(read).withMappingStrategy(strat).build();
+    private <T> void auxiliaryTestUnbindableField(MappingStrategy<T> strat, CSVReader read, long expectedLineNumber) {
+        CsvToBean<T> ctb = new CsvToBeanBuilder<T>(read).withMappingStrategy(strat).build();
         try {
             ctb.parse();
             fail("The parse should have thrown an Exception.");
@@ -649,6 +667,29 @@ public class AnnotationTest {
         assertEquals(String.class, innere.getDestinationClass());
     }
 
+    @Test
+    public void testMultipleExceptionsPerLine() throws FileNotFoundException {
+        ColumnPositionMappingStrategy<AnnotatedMockBeanFull> strat =
+                new ColumnPositionMappingStrategy<>();
+        strat.setType(AnnotatedMockBeanFull.class);
+        Reader fin = new FileReader("src/test/resources/testMultipleExceptionsPerLine.csv");
+        CsvToBean<AnnotatedMockBeanFull> ctb = new CsvToBeanBuilder<AnnotatedMockBeanFull>(fin)
+                .withMappingStrategy(strat)
+                .withSeparator(';')
+                .withThrowExceptions(false)
+                .build();
+        ctb.parse();
+        List<CsvException> exceptionList = ctb.getCapturedExceptions();
+        assertNotNull(exceptionList);
+        assertEquals(6, exceptionList.size()); // Two lines, three mistakes per line
+        assertEquals(1, exceptionList.get(0).getLineNumber());
+        assertEquals(1, exceptionList.get(1).getLineNumber());
+        assertEquals(1, exceptionList.get(2).getLineNumber());
+        assertEquals(2, exceptionList.get(3).getLineNumber());
+        assertEquals(2, exceptionList.get(4).getLineNumber());
+        assertEquals(2, exceptionList.get(5).getLineNumber());
+    }
+
     @Test
     public void testRequiredDateEmptyInput() throws IOException {
         for(String fn : Arrays.asList(
@@ -666,15 +707,15 @@ public class AnnotationTest {
                 ctb.parse();
                 fail("Expected parse to throw exception. Input filename: " + fn);
             } catch (RuntimeException e) {
-                assertTrue("Input filename: " + fn,
-                        e.getCause() instanceof CsvRequiredFieldEmptyException);
+                assertTrue(e.getCause() instanceof CsvRequiredFieldEmptyException,
+                        "Input filename: " + fn);
                 CsvRequiredFieldEmptyException csve = (CsvRequiredFieldEmptyException) e.getCause();
-                assertEquals("Input filename: " + fn, 2, csve.getLineNumber());
+                assertEquals(2, csve.getLineNumber(), "Input filename: " + fn);
                 assertNotNull(csve.getLine());
-                assertEquals("Input filename: " + fn,
-                        AnnotatedMockBeanFull.class, csve.getBeanClass());
-                assertEquals("Input filename: " + fn, "dateDefaultLocale",
-                        csve.getDestinationField().getName());
+                assertEquals(AnnotatedMockBeanFull.class, csve.getBeanClass(),
+                        "Input filename: " + fn);
+                assertEquals("dateDefaultLocale",
+                        csve.getDestinationField().getName(), "Input filename: " + fn);
             }
         }
     }
@@ -1150,4 +1191,51 @@ public class AnnotationTest {
             assertFalse(StringUtils.isEmpty(csve.getMessage()));
         }
     }
+
+    @Test
+    public void testIllegalCurrency() throws IOException {
+        try {
+            new CsvToBeanBuilder<AnnotatedMockBeanFull>(new FileReader("src/test/resources/testIllegalCurrency.csv"))
+                    .withType(AnnotatedMockBeanFull.class)
+                    .withSeparator(';')
+                    .build().parse();
+        }
+        catch (RuntimeException e) {
+            assertTrue(e.getCause() instanceof CsvDataTypeMismatchException);
+            CsvDataTypeMismatchException csve = (CsvDataTypeMismatchException) e.getCause();
+            assertEquals(Currency.class, csve.getDestinationClass());
+            assertEquals("illegalCurrency", csve.getSourceObject());
+            assertEquals(1L, csve.getLineNumber());
+            assertFalse(StringUtils.isEmpty(csve.getMessage()));
+        }
+    }
+
+    /**
+     * Checks that columns are padded or lopped off if checking is turned off.
+     *
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testWrongNumberOfColumnsAllowedByName() throws FileNotFoundException {
+        HeaderColumnNameMappingStrategy<AnnotatedMockBeanFull> strat =
+                new HeaderColumnNameMappingStrategyBuilder<AnnotatedMockBeanFull>()
+                        .withForceCorrectRecordLength(true)
+                        .build();
+        strat.setType(AnnotatedMockBeanFull.class);
+        FileReader fin = new FileReader("src/test/resources/testinputfullwrongcolumnnumber.csv");
+        List<AnnotatedMockBeanFull> beanList = new CsvToBeanBuilder<AnnotatedMockBeanFull>(fin)
+                .withSeparator(';')
+                .withOrderedResults(true)
+                .withMappingStrategy(strat)
+                .build().parse();
+        assertEquals(4, beanList.size());
+        assertEquals(TestEnum.TEST1, beanList.get(0).getTestEnum());
+        assertEquals(Currency.getInstance("EUR"), beanList.get(0).getTestCurrency());
+        assertEquals(TestEnum.TEST1, beanList.get(2).getTestEnum());
+        assertEquals(Currency.getInstance("EUR"), beanList.get(2).getTestCurrency());
+        assertNull(beanList.get(1).getTestEnum());
+        assertNull(beanList.get(1).getTestCurrency());
+        assertNull(beanList.get(3).getTestEnum());
+        assertNull(beanList.get(3).getTestCurrency());
+    }
 }
diff --git a/src/test/java/com/opencsv/bean/CollectionSplitTest.java b/src/test/java/com/opencsv/bean/CollectionSplitTest.java
index 0cc3e0d..9cb61e6 100644
--- a/src/test/java/com/opencsv/bean/CollectionSplitTest.java
+++ b/src/test/java/com/opencsv/bean/CollectionSplitTest.java
@@ -42,7 +42,7 @@ import java.io.StringWriter;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  *
@@ -352,7 +352,7 @@ public class CollectionSplitTest {
     
     @Test
     public void testUnknownElementType() {
-        final String input = "$45";
+        final String input = "America/New_York";
         CsvToBean<UnknownElementType> csv2b = new CsvToBeanBuilder<UnknownElementType>(new StringReader(input))
                 .withType(UnknownElementType.class)
                 .withThrowExceptions(false)
@@ -365,7 +365,7 @@ public class CollectionSplitTest {
         CsvDataTypeMismatchException dtme = (CsvDataTypeMismatchException) csve;
         assertEquals(1, dtme.getLineNumber());
         assertNotNull(dtme.getLine());
-        assertEquals(Currency.class, dtme.getDestinationClass());
+        assertEquals(TimeZone.class, dtme.getDestinationClass());
         assertEquals(input, dtme.getSourceObject());
     }
     
@@ -476,13 +476,12 @@ public class CollectionSplitTest {
         StatefulBeanToCsv<AnnotatedMockBeanCollectionSplit> b2csv =
                 new StatefulBeanToCsvBuilder<AnnotatedMockBeanCollectionSplit>(writer).build();
         AnnotatedMockBeanCollectionSplit bean = new AnnotatedMockBeanCollectionSplit();
-        SortedSet<Integer> sortedSet = new TreeSet<>();
-        sortedSet.addAll(Arrays.asList(2, 2, 1, 3, 3, 3));
+        SortedSet<Integer> sortedSet = new TreeSet<>(Arrays.asList(2, 2, 1, 3, 3, 3));
         bean.setSortedSetType(sortedSet);
         Set<Date> set = new HashSet<>();
-        Calendar cal = new GregorianCalendar(1978, 0, 15);
+        Calendar cal = new GregorianCalendar(1978, Calendar.JANUARY, 15);
         set.add(cal.getTime());
-        cal = new GregorianCalendar(2018, 0, 1);
+        cal = new GregorianCalendar(2018, Calendar.JANUARY, 1);
         set.add(cal.getTime());
         bean.setSetType(set);
         b2csv.write(bean);
diff --git a/src/test/java/com/opencsv/bean/ColumnPositionMappingStrategyTest.java b/src/test/java/com/opencsv/bean/ColumnPositionMappingStrategyTest.java
index 2216922..9c22087 100644
--- a/src/test/java/com/opencsv/bean/ColumnPositionMappingStrategyTest.java
+++ b/src/test/java/com/opencsv/bean/ColumnPositionMappingStrategyTest.java
@@ -27,7 +27,7 @@ import java.io.StringReader;
 import java.util.List;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class ColumnPositionMappingStrategyTest {
    private ColumnPositionMappingStrategy<MockBean> strat;
diff --git a/src/test/java/com/opencsv/bean/ConverterUUIDTest.java b/src/test/java/com/opencsv/bean/ConverterUUIDTest.java
new file mode 100644
index 0000000..649469b
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/ConverterUUIDTest.java
@@ -0,0 +1,101 @@
+package com.opencsv.bean;
+
+import com.opencsv.exceptions.CsvDataTypeMismatchException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.NullAndEmptySource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.Locale;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ConverterUUIDTest {
+    private static final String UUID_STRING = "7F804F85-0064-4E96-8260-3FD47EA6A8BB";
+    private static final UUID EXPECTED_UUID = UUID.fromString(UUID_STRING);
+
+    private ConverterUUID converter;
+
+    @BeforeEach
+    public void setUp() {
+        converter = new ConverterUUID(Locale.getDefault());
+    }
+
+    public static Stream<Arguments> buildLegalUUIDValues() {
+        return Stream.of(
+                Arguments.of(UUID_STRING),
+                Arguments.of(UUID_STRING.toLowerCase()),
+                Arguments.of(UUID_STRING.toUpperCase()),
+                Arguments.of(UUID_STRING + " "),
+                Arguments.of(" " + UUID_STRING),
+                Arguments.of(" " + UUID_STRING + " ")
+        );
+    }
+
+    @DisplayName("Can convert UUID values to an java.util.UUID object.")
+    @ParameterizedTest
+    @MethodSource("buildLegalUUIDValues")
+    public void convertToRead(String uuidValue) throws CsvDataTypeMismatchException {
+        assertEquals(EXPECTED_UUID, converter.convertToRead(uuidValue));
+    }
+
+    @DisplayName("Convert uuid to a string")
+    @Test
+    public void convertToWrite() throws CsvDataTypeMismatchException {
+        assertEquals(UUID_STRING.toUpperCase(), converter.convertToWrite(EXPECTED_UUID).toUpperCase());
+    }
+
+    @DisplayName("convertToWrite handles null UUID object.")
+    @Test
+    public void convertToWriteWithNull() throws CsvDataTypeMismatchException {
+        assertEquals("", converter.convertToWrite(null));
+    }
+
+    public static Stream<Arguments> buildIllegalUUIDValues() {
+        return Stream.of(
+                Arguments.of("17F804F85-0064-4E96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-10064-4E96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-0064-14E96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-0064-4E96-18260-13FD47EA6A8BB"),
+                Arguments.of("7G804F85-0064-4E96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-006G-4E96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-0064-4G96-8260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-0064-4E96-G260-3FD47EA6A8BB"),
+                Arguments.of("7F804F85-0064-4E96-8260-3GD47EA6A8BB")
+        );
+    }
+
+    @DisplayName("convertToRead handles illegal values.")
+    @ParameterizedTest
+    @MethodSource("buildIllegalUUIDValues")
+    public void convertToReadWithBadValues(String uuidValue) {
+        UUID uuid = null;
+        boolean pass = false;
+        try {
+            uuid = (UUID) converter.convertToRead(uuidValue);
+        } catch (CsvDataTypeMismatchException exception) {
+            pass = true;
+        } catch (Throwable t) {
+            fail(String.format("Expected a CsvDataTypeMismatch exception when converting %s but a %s was thrown with a message of %s", uuidValue, t.getClass(), t.getMessage()));
+        }
+
+        if (!pass) {
+            fail(String.format("Expected a CsvDataTypeMismatch exception when converting %s but got a UUID with a value of %s", uuidValue, Objects.toString(uuid, "null")));
+        }
+    }
+
+    @DisplayName("convertToRead handles null and empty string values")
+    @ParameterizedTest
+    @NullAndEmptySource
+    @ValueSource(strings = {"  ", "\t", "\n"})
+    public void convertToReadWithNullEmptyOrWhiteSpace(String uuidValue) throws CsvDataTypeMismatchException {
+        assertNull(converter.convertToRead(uuidValue));
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/CsvToBeanAsIteratorTest.java b/src/test/java/com/opencsv/bean/CsvToBeanAsIteratorTest.java
index 5ee13b8..553a305 100644
--- a/src/test/java/com/opencsv/bean/CsvToBeanAsIteratorTest.java
+++ b/src/test/java/com/opencsv/bean/CsvToBeanAsIteratorTest.java
@@ -26,14 +26,12 @@ import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
+import java.io.*;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvToBeanAsIteratorTest {
 
@@ -42,11 +40,7 @@ public class CsvToBeanAsIteratorTest {
             + "jimmy,def098765,456 ";
 
     private CSVReader createReader() {
-        return createReader(TEST_STRING);
-    }
-
-    private CSVReader createReader(String testString) {
-        StringReader reader = new StringReader(testString);
+        StringReader reader = new StringReader(TEST_STRING);
         return new CSVReader(reader);
     }
 
@@ -281,4 +275,28 @@ public class CsvToBeanAsIteratorTest {
             assertTrue(re.getCause() instanceof IOException);
         }
     }
+
+    @Test
+    public void testMultipleExceptionsPerLine() throws FileNotFoundException {
+        ColumnPositionMappingStrategy<AnnotatedMockBeanFull> strat =
+                new ColumnPositionMappingStrategy<>();
+        strat.setType(AnnotatedMockBeanFull.class);
+        Reader fin = new FileReader("src/test/resources/testMultipleExceptionsPerLine.csv");
+        CsvToBean<AnnotatedMockBeanFull> ctb = new CsvToBeanBuilder<AnnotatedMockBeanFull>(fin)
+                .withMappingStrategy(strat)
+                .withSeparator(';')
+                .withThrowExceptions(false)
+                .build();
+        Iterator<AnnotatedMockBeanFull> it = ctb.iterator();
+        while(it.hasNext()) { it.next(); }
+        List<CsvException> exceptionList = ctb.getCapturedExceptions();
+        assertNotNull(exceptionList);
+        assertEquals(6, exceptionList.size()); // Two lines, three mistakes per line
+        assertEquals(1, exceptionList.get(0).getLineNumber());
+        assertEquals(1, exceptionList.get(1).getLineNumber());
+        assertEquals(1, exceptionList.get(2).getLineNumber());
+        assertEquals(2, exceptionList.get(3).getLineNumber());
+        assertEquals(2, exceptionList.get(4).getLineNumber());
+        assertEquals(2, exceptionList.get(5).getLineNumber());
+    }
 }
diff --git a/src/test/java/com/opencsv/bean/CsvToBeanBuilderTest.java b/src/test/java/com/opencsv/bean/CsvToBeanBuilderTest.java
new file mode 100644
index 0000000..5a9f457
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/CsvToBeanBuilderTest.java
@@ -0,0 +1,53 @@
+package com.opencsv.bean;
+
+import com.opencsv.bean.exceptionhandler.CsvExceptionHandler;
+import com.opencsv.bean.mocks.MockBean;
+import com.opencsv.exceptions.CsvException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.StringReader;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CsvToBeanBuilderTest {
+
+    private static final String TEST_STRING = "Some string not really parsed but I needed a reader.";
+
+    private CsvToBean<MockBean> throwsExceptionFirst;
+    private CsvToBean<MockBean> withExceptionHandlerFirst;
+
+    private final CsvExceptionHandler exceptionHandler = new JunkExceptionHandler();
+
+    @BeforeEach
+    public void setUp() {
+        HeaderColumnNameMappingStrategy<MockBean> strategy = new HeaderColumnNameMappingStrategy<>();
+        strategy.setType(MockBean.class);
+        throwsExceptionFirst = new CsvToBeanBuilder<MockBean>(new StringReader(TEST_STRING))
+                .withMappingStrategy(strategy)
+                .withThrowExceptions(true)
+                .withExceptionHandler(exceptionHandler)
+                .build();
+        withExceptionHandlerFirst = new CsvToBeanBuilder<MockBean>(new StringReader(TEST_STRING))
+                .withMappingStrategy(strategy)
+                .withExceptionHandler(exceptionHandler)
+                .withThrowExceptions(true)
+                .build();
+    }
+
+    @DisplayName("If both withExceptionHandler and withThrowsException are called in the same builder then the withThrowsException is used.")
+    @Test
+    public void precedenceOfExceptionHandlers() {
+        assertTrue(throwsExceptionFirst.getExceptionHandler() instanceof JunkExceptionHandler);
+        assertTrue(withExceptionHandlerFirst.getExceptionHandler() instanceof JunkExceptionHandler);
+    }
+
+    private static class JunkExceptionHandler implements CsvExceptionHandler {
+
+        @Override
+        public CsvException handleException(CsvException e) {
+            return null;
+        }
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/CsvToBeanDoubleTest.java b/src/test/java/com/opencsv/bean/CsvToBeanDoubleTest.java
index 2eed676..8cf2574 100644
--- a/src/test/java/com/opencsv/bean/CsvToBeanDoubleTest.java
+++ b/src/test/java/com/opencsv/bean/CsvToBeanDoubleTest.java
@@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test;
 import java.io.StringReader;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * This test was created based on an question posted on stack overflow
@@ -24,11 +24,7 @@ public class CsvToBeanDoubleTest {
             "jimmy,def098765,1.0023E+13 ";
 
     private CSVReader createReader() {
-        return createReader(TEST_STRING);
-    }
-
-    private CSVReader createReader(String testString) {
-        StringReader reader = new StringReader(testString);
+        StringReader reader = new StringReader(TEST_STRING);
         return new CSVReader(reader);
     }
 
diff --git a/src/test/java/com/opencsv/bean/CsvToBeanFilterTest.java b/src/test/java/com/opencsv/bean/CsvToBeanFilterTest.java
index 1d12608..cdf70c2 100644
--- a/src/test/java/com/opencsv/bean/CsvToBeanFilterTest.java
+++ b/src/test/java/com/opencsv/bean/CsvToBeanFilterTest.java
@@ -9,8 +9,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class CsvToBeanFilterTest {
 
@@ -67,7 +67,7 @@ public class CsvToBeanFilterTest {
    }
 
    // This would be better done with the BeanVerifier.
-   private class NonProductionFilter implements CsvToBeanFilter {
+   private static class NonProductionFilter implements CsvToBeanFilter {
 
       @Override
       public boolean allowLine(String[] line) {
@@ -84,11 +84,11 @@ public class CsvToBeanFilterTest {
               .withFilter(new NonProductionFilter())
               .build();
       List<Feature> list = csvToBean.parse();
-      assertEquals("Parsing resulted in the wrong number of items.", 2, list.size());
-      assertEquals("The first item has the wrong name.", "calc age", list.get(0).getName());
-      assertEquals("The first item has the wrong state.", "beta", list.get(0).getState());
-      assertEquals("The second item has the wrong name.", "wash dishes", list.get(1).getName());
-      assertEquals("The second item has the wrong state.", "alpha", list.get(1).getState());
+      assertEquals(2, list.size(), "Parsing resulted in the wrong number of items.");
+      assertEquals("calc age", list.get(0).getName(), "The first item has the wrong name.");
+      assertEquals("beta", list.get(0).getState(), "The first item has the wrong state.");
+      assertEquals("wash dishes", list.get(1).getName(), "The second item has the wrong name.");
+      assertEquals("alpha", list.get(1).getState(), "The second item has the wrong state.");
    }
 
    @Test
@@ -111,11 +111,11 @@ public class CsvToBeanFilterTest {
               .withMappingStrategy(strategy)
               .withFilter(new NonProductionFilter())
               .build().parse();
-      assertEquals("Parsing resulted in the wrong number of items.", 2, list.size());
-      assertEquals("The first item has the wrong name.", "calc age", list.get(0).getName());
-      assertEquals("The first item has the wrong state.", "beta", list.get(0).getState());
-      assertEquals("The second item has the wrong name.", "wash dishes", list.get(1).getName());
-      assertEquals("The second item has the wrong state.", "alpha", list.get(1).getState());
+      assertEquals(2, list.size(), "Parsing resulted in the wrong number of items.");
+      assertEquals("calc age", list.get(0).getName(), "The first item has the wrong name.");
+      assertEquals("beta", list.get(0).getState(), "The first item has the wrong state.");
+      assertEquals("wash dishes", list.get(1).getName(), "The second item has the wrong name.");
+      assertEquals("alpha", list.get(1).getState(), "The second item has the wrong state.");
    }
 
    @Test
@@ -128,10 +128,10 @@ public class CsvToBeanFilterTest {
               .build();
       List<Feature> list = new ArrayList<>(2);
       for(Feature f : ctb) { list.add(f); }
-      assertEquals("Parsing resulted in the wrong number of items.", 2, list.size());
-      assertEquals("The first item has the wrong name.", "calc age", list.get(0).getName());
-      assertEquals("The first item has the wrong state.", "beta", list.get(0).getState());
-      assertEquals("The second item has the wrong name.", "wash dishes", list.get(1).getName());
-      assertEquals("The second item has the wrong state.", "alpha", list.get(1).getState());
+      assertEquals(2, list.size(), "Parsing resulted in the wrong number of items.");
+      assertEquals("calc age", list.get(0).getName(), "The first item has the wrong name.");
+      assertEquals("beta", list.get(0).getState(), "The first item has the wrong state.");
+      assertEquals("wash dishes", list.get(1).getName(), "The second item has the wrong name.");
+      assertEquals("alpha", list.get(1).getState(), "The second item has the wrong state.");
    }
 }
diff --git a/src/test/java/com/opencsv/bean/CsvToBeanTest.java b/src/test/java/com/opencsv/bean/CsvToBeanTest.java
index 7fc5512..efb9f8a 100644
--- a/src/test/java/com/opencsv/bean/CsvToBeanTest.java
+++ b/src/test/java/com/opencsv/bean/CsvToBeanTest.java
@@ -19,7 +19,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvToBeanTest {
     private static final String TEST_STRING = "name,orderNumber,num\n" +
@@ -105,6 +105,22 @@ public class CsvToBeanTest {
         assertTrue(beanList.contains(new MockBean("jimmy", null, "def098765", 456, 0.0)));
     }
 
+    @DisplayName("Blank lines are ignored when withIgnoreEmptyLine is set to true and withFieldAsNull is set to EMPTY_SEPARATORS.")
+    @Test
+    public void parseBeanWithIgnoreEmptyLinesAndEmptyIsNull() {
+        HeaderColumnNameMappingStrategy<MockBean> strategy = new HeaderColumnNameMappingStrategy<>();
+        strategy.setType(MockBean.class);
+        List<MockBean> beanList = new CsvToBeanBuilder<MockBean>(new StringReader(TEST_STRING_WITH_BLANK_LINES))
+                .withMappingStrategy(strategy)
+                .withIgnoreEmptyLine(true)
+                .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
+                .build().parse();
+
+        assertEquals(2, beanList.size());
+        assertTrue(beanList.contains(new MockBean("kyle", null, "abc123456", 123, 0.0)));
+        assertTrue(beanList.contains(new MockBean("jimmy", null, "def098765", 456, 0.0)));
+    }
+
     @Test
     public void bug133ShouldNotThrowNullPointerExceptionWhenProcessingEmptyWithNoAnnotations() {
         HeaderColumnNameMappingStrategy<Bug133Bean> strategy = new HeaderColumnNameMappingStrategy<>();
@@ -233,7 +249,7 @@ public class CsvToBeanTest {
         assertEquals(resultList, resultStream);
     }
 
-    private class BegToBeFiltered implements CsvToBeanFilter {
+    private static class BegToBeFiltered implements CsvToBeanFilter {
 
         @Override
         public boolean allowLine(String[] line) {
@@ -382,10 +398,10 @@ public class CsvToBeanTest {
         StringBuilder sb = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
         String dateString = "19780115T063209";
         sb.append("BYTE1,BYTE2,BYTE3,DATE1\n");
-        sb.append("1,2,3," + dateString + "\n");
-        sb.append("4,5,6," + dateString + "\n");
+        sb.append("1,2,3,").append(dateString).append("\n");
+        sb.append("4,5,6,").append(dateString).append("\n");
         sb.append("7\n");
-        sb.append("8,9,10," + dateString + "\n");
+        sb.append("8,9,10,").append(dateString).append("\n");
         try {
             new CsvToBeanBuilder<AnnotatedMockBeanFull>(new StringReader(sb.toString()))
                     .withType(AnnotatedMockBeanFull.class)
diff --git a/src/test/java/com/opencsv/bean/ExceptionHandlerTest.java b/src/test/java/com/opencsv/bean/ExceptionHandlerTest.java
index d7505e5..c6dd164 100644
--- a/src/test/java/com/opencsv/bean/ExceptionHandlerTest.java
+++ b/src/test/java/com/opencsv/bean/ExceptionHandlerTest.java
@@ -18,7 +18,7 @@ import java.nio.file.Files;
 import java.util.LinkedList;
 import java.util.List;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class ExceptionHandlerTest {
 
@@ -32,7 +32,7 @@ public class ExceptionHandlerTest {
      * @throws IOException Never
      */
     @Test
-    public synchronized void testReadWithExceptionHandler() throws IOException {
+    public void testReadWithExceptionHandler() throws IOException {
         CsvToBean<AnnotatedMockBeanFull> ctb = new CsvToBeanBuilder<AnnotatedMockBeanFull>(new FileReader("src/test/resources/testinputcase7.csv"))
                 .withSeparator(';')
                 .withType(AnnotatedMockBeanFull.class)
@@ -47,7 +47,7 @@ public class ExceptionHandlerTest {
     }
 
     @Test
-    public synchronized void testLambdaExceptionHandler() throws IOException {
+    public void testLambdaExceptionHandler() throws IOException {
         final String testString = "test";
         CsvToBean<AnnotatedMockBeanFull> ctb = new CsvToBeanBuilder<AnnotatedMockBeanFull>(new FileReader("src/test/resources/testinputcase7.csv"))
                 .withSeparator(';')
@@ -67,7 +67,7 @@ public class ExceptionHandlerTest {
     }
 
     @Test
-    public synchronized void testReadWithQueueThenThrowHandler() throws IOException {
+    public void testReadWithQueueThenThrowHandler() throws IOException {
         BufferedReader inFile = Files.newBufferedReader(FileSystems.getDefault().getPath("src/test/resources/testinputcase85.csv"));
         String goodLine = inFile.readLine();
         String badLine = inFile.readLine();
@@ -96,7 +96,7 @@ public class ExceptionHandlerTest {
             assertEquals(1, csve.getLineNumber() % 10);
             List<CsvException> capturedExceptions = ctb.getCapturedExceptions();
             assertNotNull(capturedExceptions);
-            assertFalse("Expected exceptions in the captured exceptions but there were none.", capturedExceptions.isEmpty());
+            assertFalse(capturedExceptions.isEmpty(), "Expected exceptions in the captured exceptions but there were none.");
         }
     }
 
@@ -112,7 +112,7 @@ public class ExceptionHandlerTest {
      */
     @DisplayName("Test ExceptionHandlerIgnoreThenThrowAfter when the max number of exceptions is less than the actual number of exceptions.")
     @Test
-    public synchronized void testWriteWithIgnoreExceptionHandlerSmallNumberOfExceptions() throws IOException, CsvDataTypeMismatchException {
+    public void testWriteWithIgnoreExceptionHandlerSmallNumberOfExceptions() throws IOException, CsvDataTypeMismatchException {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         AnnotatedMockBeanFull goodBean = beans.left;
         AnnotatedMockBeanFull badBean = beans.right;
@@ -157,7 +157,7 @@ public class ExceptionHandlerTest {
      */
     @DisplayName("Test ExceptionHandlerIgnoreThenThrowAfter when the max number of exceptions is greater than the actual number of exceptions.")
     @Test
-    public synchronized void testWriteWithIgnoreExceptionHandlerLargeMaxExceptions() throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+    public void testWriteWithIgnoreExceptionHandlerLargeMaxExceptions() throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         AnnotatedMockBeanFull goodBean = beans.left;
         AnnotatedMockBeanFull badBean = beans.right;
@@ -195,7 +195,7 @@ public class ExceptionHandlerTest {
      */
     @DisplayName("Test ExceptionHandlerQueueThenThrowAfter when the max number of exceptions is less than the actual number of exceptions.")
     @Test
-    public synchronized void testWriteWithQueueExceptionHandlerSmallNumberOfExceptions() throws IOException, CsvDataTypeMismatchException {
+    public void testWriteWithQueueExceptionHandlerSmallNumberOfExceptions() throws IOException, CsvDataTypeMismatchException {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         AnnotatedMockBeanFull goodBean = beans.left;
         AnnotatedMockBeanFull badBean = beans.right;
@@ -242,7 +242,7 @@ public class ExceptionHandlerTest {
      */
     @DisplayName("Test ExceptionHandlerQueueThenThrowAfter when the max number of exceptions is greater than the actual number of exceptions.")
     @Test
-    public synchronized void testWriteWithQueueExceptionHandlerLargeMaxExceptions() throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+    public void testWriteWithQueueExceptionHandlerLargeMaxExceptions() throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         AnnotatedMockBeanFull goodBean = beans.left;
         AnnotatedMockBeanFull badBean = beans.right;
@@ -270,7 +270,7 @@ public class ExceptionHandlerTest {
     }
 
     @Test
-    public synchronized void testQueueThenThrowExceptionHandler() throws IOException, CsvDataTypeMismatchException {
+    public void testQueueThenThrowExceptionHandler() throws IOException, CsvDataTypeMismatchException {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         AnnotatedMockBeanFull goodBean = beans.left;
         AnnotatedMockBeanFull badBean = beans.right;
diff --git a/src/test/java/com/opencsv/bean/FuzzyMappingTest.java b/src/test/java/com/opencsv/bean/FuzzyMappingTest.java
index 5380ea7..9b3418e 100644
--- a/src/test/java/com/opencsv/bean/FuzzyMappingTest.java
+++ b/src/test/java/com/opencsv/bean/FuzzyMappingTest.java
@@ -71,7 +71,7 @@ public class FuzzyMappingTest {
      */
     @Test
     public void testReadingFuzzyWithAnnotations() {
-        MappingStrategy<FuzzyMock> strategy = new FuzzyMappingStrategy<>();
+        MappingStrategy<FuzzyMock> strategy = new FuzzyMappingStrategyBuilder<FuzzyMock>().build();
         strategy.setType(FuzzyMock.class);
         StringReader input = new StringReader(HEADERS + DATA);
         List<FuzzyMock> beans = new CsvToBeanBuilder<FuzzyMock>(input)
@@ -86,7 +86,9 @@ public class FuzzyMappingTest {
 
     @Test
     public void testHeadersUnmatched() {
-        MappingStrategy<FuzzyMock> strategy = new FuzzyMappingStrategy<>();
+        MappingStrategy<FuzzyMock> strategy = new FuzzyMappingStrategyBuilder<FuzzyMock>()
+                .withForceCorrectRecordLength(true)
+                .build();
         strategy.setType(FuzzyMock.class);
         final String data = "potentially unmatched data";
         StringReader input = new StringReader("potentially unmatched header," + HEADERS + data + "," + DATA);
diff --git a/src/test/java/com/opencsv/bean/HeaderColumnNameMappingStrategyTest.java b/src/test/java/com/opencsv/bean/HeaderColumnNameMappingStrategyTest.java
index 94415a0..29e77ef 100644
--- a/src/test/java/com/opencsv/bean/HeaderColumnNameMappingStrategyTest.java
+++ b/src/test/java/com/opencsv/bean/HeaderColumnNameMappingStrategyTest.java
@@ -26,7 +26,7 @@ import java.io.StringReader;
 import java.util.List;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class HeaderColumnNameMappingStrategyTest {
 
@@ -147,10 +147,8 @@ public class HeaderColumnNameMappingStrategyTest {
    }
 
     @Test
-   public void throwsIllegalStateExceptionIfTypeNotSetBeforeGenerateHeaders() throws CsvRequiredFieldEmptyException {
+   public void throwsIllegalStateExceptionIfTypeNotSetBeforeGenerateHeaders() {
       strat = new HeaderColumnNameMappingStrategy<>();
-        Assertions.assertThrows(IllegalStateException.class, () -> {
-            strat.generateHeader(new MockBean());
-        });
+        Assertions.assertThrows(IllegalStateException.class, () -> strat.generateHeader(new MockBean()));
    }
 }
diff --git a/src/test/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyTest.java b/src/test/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyTest.java
index 43a9c7d..3c4968f 100644
--- a/src/test/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyTest.java
+++ b/src/test/java/com/opencsv/bean/HeaderColumnNameTranslateMappingStrategyTest.java
@@ -25,7 +25,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class HeaderColumnNameTranslateMappingStrategyTest {
 
@@ -59,7 +59,7 @@ public class HeaderColumnNameTranslateMappingStrategyTest {
       String s = "n,o,foo\n" +
               "kyle,123456,emp123\n" +
               "jimmy,abcnum,cust09878";
-      HeaderColumnNameTranslateMappingStrategy<MockBean> strat = new HeaderColumnNameTranslateMappingStrategy<>();
+      HeaderColumnNameTranslateMappingStrategy<MockBean> strat = new HeaderColumnNameTranslateMappingStrategyBuilder<MockBean>().build();
       strat.setType(MockBean.class);
       Map<String, String> map = new HashMap<>();
       map.put("n", "name");
@@ -83,7 +83,9 @@ public class HeaderColumnNameTranslateMappingStrategyTest {
         String s = "n,o,foo,name,id,orderNumber,num,doubleNum\n" +
                 "kyle,123456,emp123,aName,aId,aOrderNumber,22,3.14\n" +
                 "jimmy,abcnum,cust09878,bName,bId,bOrderNumber,44,8.3";
-        HeaderColumnNameTranslateMappingStrategy<MockBean> strat = new HeaderColumnNameTranslateMappingStrategy<>();
+        HeaderColumnNameTranslateMappingStrategy<MockBean> strat = new HeaderColumnNameTranslateMappingStrategyBuilder<MockBean>()
+                .withForceCorrectRecordLength(true)
+                .build();
         strat.setType(MockBean.class);
         Map<String, String> map = new HashMap<>();
         map.put("n", "name");
diff --git a/src/test/java/com/opencsv/bean/IgnoreTest.java b/src/test/java/com/opencsv/bean/IgnoreTest.java
index 9438892..3cf1a02 100644
--- a/src/test/java/com/opencsv/bean/IgnoreTest.java
+++ b/src/test/java/com/opencsv/bean/IgnoreTest.java
@@ -381,7 +381,7 @@ public class IgnoreTest {
      *     <li>Calling {@link MappingStrategy#setType(Class)} before
      *     {@link MappingStrategy#ignoreFields(MultiValuedMap)}</li>
      * </ul></p>
-     * @throws NoSuchFieldException
+     * @throws NoSuchFieldException Never
      */
     @Test
     public void testRemappingOnIgnore() throws NoSuchFieldException {
diff --git a/src/test/java/com/opencsv/bean/JoinTest.java b/src/test/java/com/opencsv/bean/JoinTest.java
index e8f358c..45133b1 100644
--- a/src/test/java/com/opencsv/bean/JoinTest.java
+++ b/src/test/java/com/opencsv/bean/JoinTest.java
@@ -39,7 +39,7 @@ import java.util.*;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  *
@@ -128,39 +128,39 @@ public class JoinTest {
         assertEquals(3, map.keySet().size());
         Collection<Date> values = map.get("date1");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 15).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 15).getTime()));
         values = map.get("date2");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 1, 27).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.FEBRUARY, 27).getTime()));
         values = map.get("date3");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 13).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 13).getTime()));
         
         map = beans.get(1).getMap2();
         assertNotNull(map);
         assertEquals(3, map.keySet().size());
         values = map.get("date1");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 16).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 16).getTime()));
         values = map.get("date2");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 1, 28).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.FEBRUARY, 28).getTime()));
         values = map.get("date3");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 14).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 14).getTime()));
         
         map = beans.get(2).getMap2();
         assertNotNull(map);
         assertEquals(3, map.keySet().size());
         values = map.get("date1");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 17).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 17).getTime()));
         values = map.get("date2");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 2, 1).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.MARCH, 1).getTime()));
         values = map.get("date3");
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 15).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 15).getTime()));
     }
     
     // Test with CsvBindByName taking the same name as CsvBindAndJoinByName
@@ -528,39 +528,39 @@ public class JoinTest {
         assertEquals(3, map.keySet().size());
         Collection<Date> values = map.get(1);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 15).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 15).getTime()));
         values = map.get(16);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 1, 27).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.FEBRUARY, 27).getTime()));
         values = map.get(17);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 13).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 13).getTime()));
         
         map = beans.get(1).showMeTheSecondMap();
         assertNotNull(map);
         assertEquals(3, map.keySet().size());
         values = map.get(1);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 16).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 16).getTime()));
         values = map.get(16);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 1, 28).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.FEBRUARY, 28).getTime()));
         values = map.get(17);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 14).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 14).getTime()));
         
         map = beans.get(2).showMeTheSecondMap();
         assertNotNull(map);
         assertEquals(3, map.keySet().size());
         values = map.get(1);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1978, 11, 17).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1978, Calendar.DECEMBER, 17).getTime()));
         values = map.get(16);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(1974, 2, 1).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(1974, Calendar.MARCH, 1).getTime()));
         values = map.get(17);
         assertEquals(1, values.size());
-        assertTrue(values.contains(new GregorianCalendar(2013, 3, 15).getTime()));
+        assertTrue(values.contains(new GregorianCalendar(2013, Calendar.APRIL, 15).getTime()));
     }
     
     /**
@@ -878,8 +878,8 @@ public class JoinTest {
         map1.put("index", 3);
         bean.setMap1(map1);
         MultiValuedMap<String, Date> map2 = new ArrayListValuedHashMap<>();
-        map2.put("date1", new GregorianCalendar(1978, 0, 15).getTime());
-        map2.put("date2", new GregorianCalendar(2018, 1, 7).getTime());
+        map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 15).getTime());
+        map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 7).getTime());
         bean.setMap2(map2);
         MultiValuedMap<String, String> map3 = new ArrayListValuedHashMap<>();
         map3.put("test", "string1");
@@ -897,8 +897,8 @@ public class JoinTest {
         // Third value is missing; "required" applies to the bean field, not every column
         bean.setMap1(map1);
         map2 = new ArrayListValuedHashMap<>();
-        map2.put("date1", new GregorianCalendar(1978, 0, 16).getTime());
-        map2.put("date2", new GregorianCalendar(2018, 1, 8).getTime());
+        map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 16).getTime());
+        map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 8).getTime());
         bean.setMap2(map2);
         map3 = new ArrayListValuedHashMap<>();
         map3.put("test", "string2");
@@ -918,8 +918,8 @@ public class JoinTest {
         map1.put("unknown header", -1); // Different headers from first bean will be ignored
         bean.setMap1(map1);
         map2 = new ArrayListValuedHashMap<>();
-        map2.put("date1", new GregorianCalendar(1978, 0, 17).getTime());
-        map2.put("date2", new GregorianCalendar(2018, 1, 9).getTime());
+        map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 17).getTime());
+        map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 9).getTime());
         bean.setMap2(map2);
         map3 = new ArrayListValuedHashMap<>();
         map3.put("test", "string3");
@@ -934,8 +934,8 @@ public class JoinTest {
         map1.put("index", 12);
         bean.setMap1(map1);
         map2 = new ArrayListValuedHashMap<>();
-        map2.put("date1", new GregorianCalendar(1978, 0, 18).getTime());
-        map2.put("date2", new GregorianCalendar(2018, 1, 10).getTime());
+        map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 18).getTime());
+        map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 10).getTime());
         bean.setMap2(map2);
         map3 = new ArrayListValuedHashMap<>();
         map3.put("test", "string4");
@@ -953,13 +953,13 @@ public class JoinTest {
         map1.put("index", 15);
         bean.setMap1(map1);
         map2 = new ArrayListValuedHashMap<>();
-        map2.put("date1", new GregorianCalendar(1978, 0, 19).getTime());
-        map2.put("date2", new GregorianCalendar(2018, 1, 11).getTime());
+        map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 19).getTime());
+        map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 11).getTime());
         bean.setMap2(map2);
         map3 = new ArrayListValuedHashMap<>();
         map3.put("test", "string5");
         bean.setMap3(map3);
-        bean.setMap4(new ArrayListValuedHashMap<String, Integer>());
+        bean.setMap4(new ArrayListValuedHashMap<>());
         beanList.add(bean);
         
         StringWriter w = new StringWriter();
@@ -998,8 +998,8 @@ public class JoinTest {
         map1.put(0, 11); // Two values for one position can never work
         bean.setMap1(map1);
         MultiValuedMap<Integer, Date> map2 = new HashSetValuedHashMap<>();
-        map2.put(1, new GregorianCalendar(1978, 0, 15).getTime());
-        map2.put(16, new GregorianCalendar(2018, 2, 6).getTime());
+        map2.put(1, new GregorianCalendar(1978, Calendar.JANUARY, 15).getTime());
+        map2.put(16, new GregorianCalendar(2018, Calendar.MARCH, 6).getTime());
         bean.setMap2(map2);
         ArrayListValuedHashMap<Integer, String> map4 = new ArrayListValuedHashMap<>();
         map4.put(4, "string4");
@@ -1025,8 +1025,8 @@ public class JoinTest {
         map1.put(0, 12);
         bean.setMap1(map1);
         map2 = new HashSetValuedHashMap<>();
-        map2.put(1, new GregorianCalendar(1978, 0, 16).getTime());
-        map2.put(16, new GregorianCalendar(2018, 2, 7).getTime());
+        map2.put(1, new GregorianCalendar(1978, Calendar.JANUARY, 16).getTime());
+        map2.put(16, new GregorianCalendar(2018, Calendar.MARCH, 7).getTime());
         bean.setMap2(map2);
         map4 = new ArrayListValuedHashMap<>();
         map4.put(4, "string42");
@@ -1060,8 +1060,8 @@ public class JoinTest {
             GoodJoinByNameAnnotations bean = new GoodJoinByNameAnnotations();
             bean.setMap1(map1); // Required
             MultiValuedMap<String, Date> map2 = new ArrayListValuedHashMap<>();
-            map2.put("date1", new GregorianCalendar(1978, 0, 15).getTime());
-            map2.put("date2", new GregorianCalendar(2018, 1, 7).getTime());
+            map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 15).getTime());
+            map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 7).getTime());
             bean.setMap2(map2);
             MultiValuedMap<String, String> map3 = new ArrayListValuedHashMap<>();
             map3.put("test", "string1");
@@ -1097,8 +1097,8 @@ public class JoinTest {
             map1.put("index", 3);
             bean.setMap1(map1);
             MultiValuedMap<String, Date> map2 = new ArrayListValuedHashMap<>();
-            map2.put("date1", new GregorianCalendar(1978, 0, 15).getTime());
-            map2.put("date2", new GregorianCalendar(2018, 1, 7).getTime());
+            map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 15).getTime());
+            map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 7).getTime());
             bean.setMap2(map2);
             MultiValuedMap<String, Integer> map4 = new ArrayListValuedHashMap<>();
             map4.put("conversion", 10000);
@@ -1109,8 +1109,8 @@ public class JoinTest {
             bean = new GoodJoinByNameAnnotations();
             bean.setMap1(map1version2);
             map2 = new ArrayListValuedHashMap<>();
-            map2.put("date1", new GregorianCalendar(1978, 0, 16).getTime());
-            map2.put("date2", new GregorianCalendar(2018, 1, 8).getTime());
+            map2.put("date1", new GregorianCalendar(1978, Calendar.JANUARY, 16).getTime());
+            map2.put("date2", new GregorianCalendar(2018, Calendar.FEBRUARY, 8).getTime());
             bean.setMap2(map2);
             map4 = new ArrayListValuedHashMap<>();
             map4.put("conversion", 10001);
@@ -1139,9 +1139,9 @@ public class JoinTest {
         map1.put(0, Integer.MIN_VALUE);
         bean.setMap1(map1);
         ArrayListValuedHashMap<Integer, Date> map2 = new ArrayListValuedHashMap<>();
-        map2.put(1, new GregorianCalendar(1974, 1, 27).getTime());
-        map2.put(16, new GregorianCalendar(1978, 0, 15).getTime());
-        map2.put(17, new GregorianCalendar(2003, 3, 13).getTime());
+        map2.put(1, new GregorianCalendar(1974, Calendar.FEBRUARY, 27).getTime());
+        map2.put(16, new GregorianCalendar(1978, Calendar.JANUARY, 15).getTime());
+        map2.put(17, new GregorianCalendar(2003, Calendar.APRIL, 13).getTime());
         bean.setMap2(map2);
         
         StringWriter w = new StringWriter();
diff --git a/src/test/java/com/opencsv/bean/NonPrimitiveConversionTest.java b/src/test/java/com/opencsv/bean/NonPrimitiveConversionTest.java
new file mode 100644
index 0000000..c1b0013
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/NonPrimitiveConversionTest.java
@@ -0,0 +1,48 @@
+package com.opencsv.bean;
+
+import com.opencsv.bean.mocks.MockBean;
+import com.opencsv.util.MockDataBuilder;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class NonPrimitiveConversionTest {
+    private static final String UUID_STRING = "7F804F85-0064-4E96-8260-3FD47EA6A8BB";
+    private static final UUID EXPECTED_UUID = UUID.fromString(UUID_STRING);
+
+    public static Stream<Arguments> buildLegalUUIDValues() {
+        return Stream.of(
+                Arguments.of(UUID_STRING),
+                Arguments.of(UUID_STRING.toLowerCase()),
+                Arguments.of(UUID_STRING.toUpperCase()),
+                Arguments.of(UUID_STRING + " "),
+                Arguments.of(" " + UUID_STRING),
+                Arguments.of(" " + UUID_STRING + " ")
+        );
+    }
+
+    @DisplayName("Can convert UUID values.")
+    @ParameterizedTest
+    @MethodSource("buildLegalUUIDValues")
+    public void convertUUID(String uuidValue) {
+        MockDataBuilder builder = new MockDataBuilder();
+        builder.setHeaderString("id,uuid");
+        builder.addColumns("2", uuidValue);
+        HeaderColumnNameMappingStrategy<MockBean> strategy = new HeaderColumnNameMappingStrategy<>();
+        strategy.setType(MockBean.class);
+        List<MockBean> beanList = new CsvToBeanBuilder<MockBean>(builder.buildStringReader())
+                .withMappingStrategy(strategy)
+                .withFilter(null)
+                .build().parse(); // Extra arguments for code coverage
+
+        assertEquals(1, beanList.size());
+        assertEquals(EXPECTED_UUID, beanList.get(0).getUuid());
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/NumberTest.java b/src/test/java/com/opencsv/bean/NumberTest.java
index a72e232..51b6c06 100644
--- a/src/test/java/com/opencsv/bean/NumberTest.java
+++ b/src/test/java/com/opencsv/bean/NumberTest.java
@@ -15,7 +15,7 @@
  */
 package com.opencsv.bean;
 
-import com.opencsv.bean.mocks.*;
+import com.opencsv.bean.mocks.number.*;
 import com.opencsv.exceptions.CsvBadConverterException;
 import com.opencsv.exceptions.CsvDataTypeMismatchException;
 import com.opencsv.exceptions.CsvException;
@@ -34,7 +34,7 @@ import java.math.BigInteger;
 import java.util.List;
 import java.util.Locale;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * Tests of the {@link CsvNumber} annotation.
@@ -326,12 +326,14 @@ public class NumberTest {
     /**
      * Tests writing numerical values using {@link CsvNumber} and the column
      * position mapping strategy.
-     * <p>Also incidentally tests:
-     * <ul><li>Using a different format string for writing than reading</li>
+     * Also incidentally tests:
+     * <ul>
+     * <li>Using a different format string for writing than reading</li>
      * <li>Using a different format string for writing, but leaving
-     * {@link CsvNumber#writeFormatEqualsReadFormat()} {@code true}</li></ul></p>
+     * {@link CsvNumber#writeFormatEqualsReadFormat()} {@code true}</li>
+     * </ul>
      *
-     * @throws IOException Never thrown
+     * @throws IOException  Never thrown
      * @throws CsvException Never thrown
      */
     @Test
@@ -352,7 +354,7 @@ public class NumberTest {
     @Test
     public void testNonNumber() {
         try {
-            CsvToBean<NumberNonNumber> csvToBean = new CsvToBeanBuilder<NumberNonNumber>(new StringReader("test\\nteststring"))
+            new CsvToBeanBuilder<NumberNonNumber>(new StringReader("test\\nteststring"))
                     .withType(NumberNonNumber.class)
                     .build();
             fail("Exception should have been thrown");
diff --git a/src/test/java/com/opencsv/bean/OptionalTest.java b/src/test/java/com/opencsv/bean/OptionalTest.java
index ca09506..c081cfc 100644
--- a/src/test/java/com/opencsv/bean/OptionalTest.java
+++ b/src/test/java/com/opencsv/bean/OptionalTest.java
@@ -51,7 +51,7 @@ public class OptionalTest {
     @Test
     public void testWriteWithOptionalNull() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
         OptionalMock b = new OptionalMock();
-        b.setField(Optional.ofNullable(null));
+        b.setField(Optional.empty());
         Writer w = new StringWriter();
         StatefulBeanToCsv<OptionalMock> b2csv = new StatefulBeanToCsvBuilder<OptionalMock>(w)
                 .withApplyQuotesToAll(false)
diff --git a/src/test/java/com/opencsv/bean/ProfileTest.java b/src/test/java/com/opencsv/bean/ProfileTest.java
new file mode 100644
index 0000000..6a62e4d
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/ProfileTest.java
@@ -0,0 +1,755 @@
+package com.opencsv.bean;
+
+import com.opencsv.bean.mocks.profile.*;
+import com.opencsv.exceptions.CsvBadConverterException;
+import com.opencsv.exceptions.CsvDataTypeMismatchException;
+import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
+import org.apache.commons.collections4.MultiValuedMap;
+import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.StringWriter;
+import java.time.LocalDate;
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests profile functionality.
+ *
+ * @author Andrew Rucker Jones
+ */
+public class ProfileTest {
+
+    private static Locale systemLocale;
+
+    @BeforeAll
+    public static void storeSystemLocale() {
+        systemLocale = Locale.getDefault();
+    }
+
+    @BeforeEach
+    public void setSystemLocaleToValueNotGerman() {
+        Locale.setDefault(Locale.US);
+    }
+
+    @AfterEach
+    public void setSystemLocaleBackToDefault() {
+        Locale.setDefault(systemLocale);
+    }
+
+    /**
+     * Tests that reading with profiles by header name works.
+     * <p>Also incidentally tests:
+     * <ul>
+     *     <li>Use of CsvBindByName with non-default profiles</li>
+     *     <li>Use of CsvCustomBindByName with non-default profiles</li>
+     *     <li>Use of CsvBindAndSplitByName with non-default profiles</li>
+     *     <li>Use of CsvBindAndJoinByName with non-default profiles</li>
+     *     <li>Use of @CsvNumber with non-default profiles</li>
+     *     <li>Use of @CsvDate with non-default profiles</li>
+     *     <li>One explicit profile in the profile list</li>
+     *     <li>Multiple profiles in the profile list</li>
+     *     <li>With the explicit default profile</li>
+     *     <li>Multiple annotations with no enclosing annotation</li>
+     *     <li>Multiple annotations with an enclosing annotation</li>
+     *     <li>Multiple annotations, mixed not enclosed / enclosed</li>
+     *     <li>Request of a non-existent profile name with the standard profile present in annotations</li>
+     *     <li>Request of a non-existent profile name with no standard profile present in annotations</li>
+     *     <li>Ignoring one profile</li>
+     *     <li>Ignoring all profiles with names profiles specified in binding annotations</li>
+     * </ul></p>
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testReadingByName() throws FileNotFoundException {
+        ProfileNameMock b;
+        List<ProfileNameMock> beans;
+        List<Float> floats;
+        Collection<LocalDate> dates;
+
+        //Default profile (not explicitly stated)
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfileDefault.csv"))
+                .withType(ProfileNameMock.class)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Default profile (empty string)
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfileDefault.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile(StringUtils.EMPTY)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Default profile (null)
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfileDefault.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile(null)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 1
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfile1.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile("profile 1")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof LinkedList);
+        assertEquals(2, floats.size());
+        assertEquals(1.234f, floats.get(0));
+        assertEquals(2.345f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 2
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfile2.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile("profile 2")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof Stack);
+        assertEquals(2, floats.size());
+        assertEquals(12.34f, floats.get(0));
+        assertEquals(23.45f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 3
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfile3.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile("profile 3")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertEquals("test string", b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 4
+        beans = new CsvToBeanBuilder<ProfileNameMock>(new FileReader("src/test/resources/testNameProfile4.csv"))
+                .withType(ProfileNameMock.class)
+                .withProfile("profile 4")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+    }
+
+    /**
+     * Tests that reading with profiles by header name works.
+     * <p>Also incidentally tests:
+     * <ul>
+     *     <li>Use of CsvBindByPosition with non-default profiles</li>
+     *     <li>Use of CsvCustomBindByPosition with non-default profiles</li>
+     *     <li>Use of CsvBindAndSplitByPosition with non-default profiles</li>
+     *     <li>Use of CsvBindAndJoinByPosition with non-default profiles</li>
+     *     <li>Use of @CsvNumber with non-default profiles</li>
+     *     <li>Use of @CsvDate with non-default profiles</li>
+     *     <li>One explicit profile in the profile list</li>
+     *     <li>Multiple profiles in the profile list</li>
+     *     <li>With the explicit default profile</li>
+     *     <li>Multiple annotations with no enclosing annotation</li>
+     *     <li>Multiple annotations with an enclosing annotation</li>
+     *     <li>Multiple annotations, mixed not enclosed / enclosed</li>
+     *     <li>Request of a non-existent profile name with the standard profile present in annotations</li>
+     *     <li>Request of a non-existent profile name with no standard profile present in annotations</li>
+     *     <li>Ignoring one profile</li>
+     *     <li>Ignoring all profiles with names profiles specified in binding annotations</li>
+     * </ul></p>
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testReadingByPosition() throws FileNotFoundException {
+        ProfilePositionMock b;
+        List<ProfilePositionMock> beans;
+        List<Float> floats;
+        Collection<LocalDate> dates;
+
+        //Default profile (not explicitly stated)
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfileDefault.csv"))
+                .withType(ProfilePositionMock.class)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Default profile (empty string)
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfileDefault.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile(StringUtils.EMPTY)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Default profile (null)
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfileDefault.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile(null)
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 1
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfile1.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile("profile 1")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof LinkedList);
+        assertEquals(2, floats.size());
+        assertEquals(1.234f, floats.get(0));
+        assertEquals(2.345f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 2
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfile2.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile("profile 2")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof Stack);
+        assertEquals(2, floats.size());
+        assertEquals(12.34f, floats.get(0));
+        assertEquals(23.45f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 3
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfile3.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile("profile 3")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertEquals("test string", b.getString1());
+        assertNull(b.getString2());
+
+        //Profile 4
+        beans = new CsvToBeanBuilder<ProfilePositionMock>(new FileReader("src/test/resources/testPositionProfile4.csv"))
+                .withType(ProfilePositionMock.class)
+                .withProfile("profile 4")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(12, b.getInt1());
+        assertTrue(b.isBool1());
+        floats = b.getFloats();
+        assertNotNull(floats);
+        assertTrue(floats instanceof ArrayList);
+        assertEquals(2, floats.size());
+        assertEquals(1.2f, floats.get(0));
+        assertEquals(2.3f, floats.get(1));
+        dates = b.getDates().values();
+        assertNotNull(dates);
+        assertEquals(2, dates.size());
+        assertTrue(dates.contains(LocalDate.of(1978,1,15)));
+        assertTrue(dates.contains(LocalDate.of(1974,2,27)));
+        assertNull(b.getString1());
+        assertNull(b.getString2());
+    }
+
+    /**
+     * Tests that writing with profiles by header name works.
+     * <p>Also incidentally tests:
+     * <ul>
+     *     <li>Use of CsvBindByName with non-default profiles</li>
+     *     <li>Use of CsvCustomBindByName with non-default profiles</li>
+     *     <li>Use of CsvBindAndSplitByName with non-default profiles</li>
+     *     <li>Use of CsvBindAndJoinByName with non-default profiles</li>
+     *     <li>Use of @CsvNumber with non-default profiles</li>
+     *     <li>Use of @CsvDate with non-default profiles</li>
+     *     <li>One explicit profile in the profile list</li>
+     *     <li>Multiple profiles in the profile list</li>
+     *     <li>With the explicit default profile</li>
+     *     <li>Multiple annotations with no enclosing annotation</li>
+     *     <li>Multiple annotations with an enclosing annotation</li>
+     *     <li>Multiple annotations, mixed not enclosed / enclosed</li>
+     *     <li>Request of a non-existent profile name with the standard profile present in annotations</li>
+     *     <li>Request of a non-existent profile name with no standard profile present in annotations</li>
+     *     <li>Ignoring one profile</li>
+     *     <li>Ignoring all profiles with names profiles specified in binding annotations</li>
+     * </ul></p>
+     * @throws CsvDataTypeMismatchException Never
+     * @throws CsvRequiredFieldEmptyException Never
+     */
+    @Test
+    public void testWritingByName() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+        StringWriter w;
+        StatefulBeanToCsv<ProfileNameMock> beanToCsv;
+
+        // Initiate bean
+        ProfileNameMock b = new ProfileNameMock();
+        b.setInt1(12);
+        b.setBool1(true);
+        b.setFloats(Arrays.asList(1.234f, 2.345f));
+        MultiValuedMap<String, LocalDate> mvm = new ArrayListValuedHashMap<>();
+        mvm.put("dates", LocalDate.of(1978,1,15));
+        mvm.put("dates", LocalDate.of(1974,2,27));
+        b.setDates(mvm);
+        b.setString1("test string 1");
+        b.setString2("test string 2");
+
+        // Default profile (not explicitly stated)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nvrai,1.23% 2.35%,12,01/15/1978,02/27/1974\n", w.toString());
+
+        // Default profile (empty string)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile(StringUtils.EMPTY)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nvrai,1.23% 2.35%,12,01/15/1978,02/27/1974\n", w.toString());
+
+        // Default profile (null)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile(null)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nvrai,1.23% 2.35%,12,01/15/1978,02/27/1974\n", w.toString());
+
+        // Profile 1
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile("profile 1")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nwahr,1.234 2.345,integer: 12,15. January 1978,27. February 1974\n", w.toString());
+
+        // Profile 2
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile("profile 2")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nwahr,1.23E0 2.35E0,int 12 value,15. January 1978,27. February 1974\n", w.toString());
+
+        // Profile 3
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile("profile 3")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,STRING1,dates,dates\nvrai,1.23% 2.35%,12,test string 1,15. January 1978,27. February 1974\n", w.toString());
+
+        // Profile 4
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfileNameMock>(w)
+                .withProfile("profile 4")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("BOOL1,FLOATS,INT1,dates,dates\nvrai,1.23% 2.35%,12,01/15/1978,02/27/1974\n", w.toString());
+    }
+
+    /**
+     * Tests that writing with profiles by header name works.
+     * <p>Also incidentally tests:
+     * <ul>
+     *     <li>Use of CsvBindByPosition with non-default profiles</li>
+     *     <li>Use of CsvCustomBindByPosition with non-default profiles</li>
+     *     <li>Use of CsvBindAndSplitByPosition with non-default profiles</li>
+     *     <li>Use of CsvBindAndJoinByPosition with non-default profiles</li>
+     *     <li>Use of @CsvNumber with non-default profiles</li>
+     *     <li>Use of @CsvDate with non-default profiles</li>
+     *     <li>One explicit profile in the profile list</li>
+     *     <li>Multiple profiles in the profile list</li>
+     *     <li>With the explicit default profile</li>
+     *     <li>Multiple annotations with no enclosing annotation</li>
+     *     <li>Multiple annotations with an enclosing annotation</li>
+     *     <li>Multiple annotations, mixed not enclosed / enclosed</li>
+     *     <li>Request of a non-existent profile name with the standard profile present in annotations</li>
+     *     <li>Request of a non-existent profile name with no standard profile present in annotations</li>
+     *     <li>Ignoring one profile</li>
+     *     <li>Ignoring all profiles with names profiles specified in binding annotations</li>
+     * </ul></p>
+     * @throws CsvDataTypeMismatchException Never
+     * @throws CsvRequiredFieldEmptyException Never
+     */
+    @Test
+    public void testWritingByPosition() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+        StringWriter w;
+        StatefulBeanToCsv<ProfilePositionMock> beanToCsv;
+
+        // Initiate bean
+        ProfilePositionMock b = new ProfilePositionMock();
+        b.setInt1(12);
+        b.setBool1(true);
+        b.setFloats(Arrays.asList(1.234f, 2.345f));
+        MultiValuedMap<Integer, LocalDate> mvm = new ArrayListValuedHashMap<>();
+        mvm.put(3, LocalDate.of(1978,1,15));
+        mvm.put(4, LocalDate.of(1974,2,27));
+        b.setDates(mvm);
+        b.setString1("test string 1");
+        b.setString2("test string 2");
+
+        // Default profile (not explicitly stated)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("12,vrai,1.23% 2.35%,01/15/1978,02/27/1974\n", w.toString());
+
+        // Default profile (empty string)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile(StringUtils.EMPTY)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("12,vrai,1.23% 2.35%,01/15/1978,02/27/1974\n", w.toString());
+
+        // Default profile (null)
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile(null)
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("12,vrai,1.23% 2.35%,01/15/1978,02/27/1974\n", w.toString());
+
+        // Profile 1
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile("profile 1")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("integer: 12,wahr,1.234 2.345,15. January 1978,27. February 1974\n", w.toString());
+
+        // Profile 2
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile("profile 2")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("int 12 value,wahr,1.23E0 2.35E0,15. January 1978,27. February 1974\n", w.toString());
+
+        // Profile 3
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile("profile 3")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("12,vrai,1.23% 2.35%,15. January 1978,27. February 1974,test string 1\n", w.toString());
+
+        // Profile 4
+        w = new StringWriter();
+        beanToCsv = new StatefulBeanToCsvBuilder<ProfilePositionMock>(w)
+                .withProfile("profile 4")
+                .withApplyQuotesToAll(false)
+                .build();
+        beanToCsv.write(b);
+        assertEquals("12,vrai,1.23% 2.35%,01/15/1978,02/27/1974\n", w.toString());
+    }
+
+    /**
+     * Tests that a profile valid for the binding annotation, but with no
+     * matching {@link CsvNumber} annotation throws an error.
+     *
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testCsvNumberMismatch() throws FileNotFoundException {
+        try {
+            // The file used is irrelevant, because the error is thrown
+            // before the file is read.
+            new CsvToBeanBuilder<ProfileMismatch>(new FileReader("src/test/resources/testNameProfileDefault.csv"))
+                    .withProfile("number")
+                    .withType(ProfileMismatch.class)
+                    .build();
+            fail("Exception should have been thrown.");
+        } catch (CsvBadConverterException e) {
+            assertEquals(CsvNumber.class, e.getConverterClass());
+        }
+    }
+
+    /**
+     * Tests that a profile valid for the binding annotation, but with no
+     * matching {@link CsvDate} annotation throws an error.
+     *
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testCsvDateMismatch() throws FileNotFoundException {
+        try {
+            // The file used is irrelevant, because the error is thrown
+            // before the file is read.
+            new CsvToBeanBuilder<ProfileMismatch>(new FileReader("src/test/resources/testNameProfileDefault.csv"))
+                    .withProfile("date")
+                    .withType(ProfileMismatch.class)
+                    .build();
+        } catch (CsvBadConverterException e) {
+            assertEquals(CsvDate.class, e.getConverterClass());
+        }
+    }
+
+    /**
+     * Tests all name-based annotations for exclusion based on profile.
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testNoDefaultsName() throws FileNotFoundException {
+        ProfileNameNoDefault b;
+        List<ProfileNameNoDefault> beans;
+        List<Float> floats;
+
+        beans = new CsvToBeanBuilder<ProfileNameNoDefault>(new FileReader("src/test/resources/testNameProfile1.csv"))
+                .withType(ProfileNameNoDefault.class)
+                .withProfile("profile 1")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(0, b.getInt1());
+        assertFalse(b.isBool1());
+        floats = b.getFloats();
+        assertNull(floats);
+        assertNull(b.getDates());
+    }
+
+    /**
+     * Tests all position-based annotations for exclusion based on profile.
+     * @throws FileNotFoundException Never
+     */
+    @Test
+    public void testNoDefaultsPosition() throws FileNotFoundException {
+        ProfilePositionNoDefault b;
+        List<ProfilePositionNoDefault> beans;
+        List<Float> floats;
+
+        beans = new CsvToBeanBuilder<ProfilePositionNoDefault>(new FileReader("src/test/resources/testPositionProfile1.csv"))
+                .withType(ProfilePositionNoDefault.class)
+                .withProfile("profile 1")
+                .build()
+                .parse();
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+        b = beans.get(0);
+        assertEquals(0, b.getInt1());
+        assertFalse(b.isBool1());
+        floats = b.getFloats();
+        assertNull(floats);
+        assertNull(b.getDates());
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/RecursionTest.java b/src/test/java/com/opencsv/bean/RecursionTest.java
index c15e35d..20da3c4 100644
--- a/src/test/java/com/opencsv/bean/RecursionTest.java
+++ b/src/test/java/com/opencsv/bean/RecursionTest.java
@@ -35,6 +35,25 @@ public class RecursionTest {
         return bean;
     }
 
+    private RecursionMockLevelZeroNoAnnotations oneGoodMockNoAnnotations() {
+        RecursionMockLevelZeroNoAnnotations bean = new RecursionMockLevelZeroNoAnnotations();
+        bean.setIntLevelZero(10);
+        RecursionMockLevelOneNoAnnotations l1 = new RecursionMockLevelOneNoAnnotations();
+        l1.setStringLevelOne("11");
+        bean.setLevelOne(l1);
+        RecursionMockLevelTwoNoAnnotations l2 = new RecursionMockLevelTwoNoAnnotations();
+        l2.setCharLevelTwo('c');
+        l1.setLevelTwo(l2);
+        l2.procureTheThirdLevelPointZero().setFloatLevelThree(4.0f);
+        RecursionMockLevelThreePointOneNoAnnotations l3point1 = new RecursionMockLevelThreePointOneNoAnnotations();
+        l3point1.setBooleanLevelThree(true);
+        l2.setLevelThreePointOne(l3point1);
+        RecursionMockLevelThreePointTwoNoAnnotations l3point2 = new RecursionMockLevelThreePointTwoNoAnnotations();
+        l3point2.setShortLevelThree((short)32);
+        l2.setLevelThreePointTwo(l3point2);
+        return bean;
+    }
+
     private void checkReadingResults(List<RecursionMockLevelZero> beans) {
         assertNotNull(beans);
         assertEquals(1, beans.size());
@@ -58,6 +77,29 @@ public class RecursionTest {
         assertEquals((short)32, b3point2.getShortLevelThree());
     }
 
+    private void checkReadingResultsNoAnnotations(List<RecursionMockLevelZeroNoAnnotations> beans) {
+        assertNotNull(beans);
+        assertEquals(1, beans.size());
+
+        RecursionMockLevelZeroNoAnnotations b0 = beans.get(0);
+        assertEquals(10, b0.getIntLevelZero());
+
+        RecursionMockLevelOneNoAnnotations b1 = b0.getLevelOne();
+        assertEquals("11", b1.getStringLevelOne());
+
+        RecursionMockLevelTwoNoAnnotations b2 = b1.getLevelTwo();
+        assertEquals('c', b2.getCharLevelTwo());
+
+        RecursionMockLevelThreePointZeroNoAnnotations b3point0 = b2.procureTheThirdLevelPointZero();
+        assertEquals(4.0f, b3point0.getFloatLevelThree());
+
+        RecursionMockLevelThreePointOneNoAnnotations b3point1 = b2.getLevelThreePointOne();
+        assertTrue(b3point1.isBooleanLevelThree());
+
+        RecursionMockLevelThreePointTwoNoAnnotations b3point2 = b2.getLevelThreePointTwo();
+        assertEquals((short)32, b3point2.getShortLevelThree());
+    }
+
     @Test
     public void testPrimitives() {
 
@@ -214,6 +256,20 @@ public class RecursionTest {
         checkReadingResults(beans);
     }
 
+    @Test
+    public void testReadingHeaderNamesNoAnnotations() {
+        CsvToBean<RecursionMockLevelZeroNoAnnotations> csvToBean =
+                new CsvToBeanBuilder<RecursionMockLevelZeroNoAnnotations>(new StringReader(
+                        HEADER + DATA))
+                        .withType(RecursionMockLevelZeroNoAnnotations.class)
+                        .build();
+        List<RecursionMockLevelZeroNoAnnotations> beans = csvToBean.parse();
+        List<CsvException> exceptions = csvToBean.getCapturedExceptions();
+        assertNotNull(exceptions);
+        assertTrue(exceptions.isEmpty());
+        checkReadingResultsNoAnnotations(beans);
+    }
+
     @Test
     public void testReadingColumnPositions() {
         CsvToBean<RecursionMockLevelZero> csvToBean =
@@ -260,6 +316,17 @@ public class RecursionTest {
                 "true,c,4.0,10,32,11\n", w.toString());
     }
 
+    @Test
+    public void testWritingHeaderNamesNoAnnotations() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+        StringWriter w = new StringWriter();
+        StatefulBeanToCsv<RecursionMockLevelZeroNoAnnotations> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZeroNoAnnotations>(w)
+                .withApplyQuotesToAll(false)
+                .build();
+        b2c.write(oneGoodMockNoAnnotations());
+        assertEquals("BOOLEANLEVELTHREE,CHARLEVELTWO,FLOATLEVELTHREE,INTLEVELZERO,SHORTLEVELTHREE,STRINGLEVELONE\n" +
+                "true,c,4.0,10,32,11\n", w.toString());
+    }
+
     @Test
     public void testWritingColumnPositions() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
         StringWriter w = new StringWriter();
@@ -283,7 +350,7 @@ public class RecursionTest {
     }
 
     @Test
-    public void testNullMemberVariableRequired() throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
+    public void testNullMemberVariableRequired() throws CsvDataTypeMismatchException {
         StringWriter w = new StringWriter();
         StatefulBeanToCsv<RecursionMockLevelZero> b2c = new StatefulBeanToCsvBuilder<RecursionMockLevelZero>(w)
                 .withApplyQuotesToAll(false)
diff --git a/src/test/java/com/opencsv/bean/StatefulBeanToCsvPerformanceTest.java b/src/test/java/com/opencsv/bean/StatefulBeanToCsvPerformanceTest.java
index d629d6c..c23b3ba 100644
--- a/src/test/java/com/opencsv/bean/StatefulBeanToCsvPerformanceTest.java
+++ b/src/test/java/com/opencsv/bean/StatefulBeanToCsvPerformanceTest.java
@@ -16,7 +16,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class StatefulBeanToCsvPerformanceTest {
     private static final String SEPARATOR_LINE = "===============================================================================";
@@ -87,7 +87,7 @@ public class StatefulBeanToCsvPerformanceTest {
         btcsv.write(beanList);
         watch.stop();
         if (displayData) {
-            System.out.println("Time taken to write " + numBeans + " beans, ordered: " + watch.toString());
+            System.out.println("Time taken to write " + numBeans + " beans, ordered: " + watch);
         }
 
         // Writing, unordered
@@ -105,7 +105,7 @@ public class StatefulBeanToCsvPerformanceTest {
         btcsv.write(beanList);
         watch.stop();
         if (displayData) {
-            System.out.println("Time taken to write " + numBeans + " beans, unordered: " + watch.toString());
+            System.out.println("Time taken to write " + numBeans + " beans, unordered: " + watch);
         }
 
         // Reading, ordered
@@ -121,7 +121,7 @@ public class StatefulBeanToCsvPerformanceTest {
         watch.stop();
         assertEquals(numBeans, beans.size());
         if (displayData) {
-            System.out.println("Time taken to read " + numBeans + " beans, ordered: " + watch.toString());
+            System.out.println("Time taken to read " + numBeans + " beans, ordered: " + watch);
         }
 
         // Reading, ordered
@@ -138,7 +138,7 @@ public class StatefulBeanToCsvPerformanceTest {
         watch.stop();
         assertEquals(numBeans, beans.size());
         if (displayData) {
-            System.out.println("Time taken to read " + numBeans + " beans, unordered: " + watch.toString());
+            System.out.println("Time taken to read " + numBeans + " beans, unordered: " + watch);
         }
     }
 
@@ -168,7 +168,7 @@ public class StatefulBeanToCsvPerformanceTest {
         btcsv.write(beanList);
         watch.stop();
         if (displayData) {
-            System.out.println("Time taken to write " + numBeans + " beans, ordered: " + watch.toString());
+            System.out.println("Time taken to write " + numBeans + " beans, ordered: " + watch);
         }
 
         // Writing, unordered
@@ -183,7 +183,7 @@ public class StatefulBeanToCsvPerformanceTest {
         btcsv.write(beanList);
         watch.stop();
         if (displayData) {
-            System.out.println("Time taken to write " + numBeans + " beans, unordered: " + watch.toString());
+            System.out.println("Time taken to write " + numBeans + " beans, unordered: " + watch);
         }
 
         // Reading, ordered
@@ -196,7 +196,7 @@ public class StatefulBeanToCsvPerformanceTest {
         watch.stop();
         assertEquals(numBeans, beans.size());
         if (displayData) {
-            System.out.println("Time taken to read " + numBeans + " beans, ordered: " + watch.toString());
+            System.out.println("Time taken to read " + numBeans + " beans, ordered: " + watch);
         }
 
         // Reading, ordered
@@ -210,7 +210,7 @@ public class StatefulBeanToCsvPerformanceTest {
         watch.stop();
         assertEquals(numBeans, beans.size());
         if (displayData) {
-            System.out.println("Time taken to read " + numBeans + " beans, unordered: " + watch.toString());
+            System.out.println("Time taken to read " + numBeans + " beans, unordered: " + watch);
         }
     }
 
diff --git a/src/test/java/com/opencsv/bean/StatefulBeanToCsvTest.java b/src/test/java/com/opencsv/bean/StatefulBeanToCsvTest.java
index b87c718..0dac03c 100644
--- a/src/test/java/com/opencsv/bean/StatefulBeanToCsvTest.java
+++ b/src/test/java/com/opencsv/bean/StatefulBeanToCsvTest.java
@@ -20,6 +20,7 @@ import com.opencsv.TestUtils;
 import com.opencsv.bean.mocks.*;
 import com.opencsv.exceptions.CsvDataTypeMismatchException;
 import com.opencsv.exceptions.CsvException;
+import com.opencsv.exceptions.CsvFieldAssignmentException;
 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.junit.jupiter.api.AfterEach;
@@ -34,7 +35,7 @@ import java.text.RuleBasedCollator;
 import java.util.*;
 import java.util.regex.Pattern;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * Tests {@link StatefulBeanToCsv}.
@@ -48,21 +49,21 @@ public class StatefulBeanToCsvTest {
     // handling of the same locale in different Java versions. There was a break from
     // Java 8 to Java 9, and another from Java 12 to Java 13.
     private static final String EXTRA_STRING_FOR_WRITING = "extrastringforwritinghowcreative";
-    private static final String GOOD_DATA_1 = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
-    private static final String GOOD_DATA_2 = "test string;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
-    private static final String GOOD_DATA_OPTIONALS_NULL = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_1 = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_2 = "test string;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;CHF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_OPTIONALS_NULL = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
     private static final String GOOD_DATA_CUSTOM_1 = "inside custom converter;wahr;falsch;127;127;127;;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;32767;32767;32767;32767;\uFFFF;\uFFFF;10;10;10;10;;;;;;;;;;;;;falsch;wahr;really long test string, yeah!;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;inside custom converter";
-    private static final String HEADER_NAME_FULL = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
-    private static final String GOOD_DATA_NAME_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String HEADER_NAME_FULL = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
+    private static final String GOOD_DATA_NAME_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
     private static final String HEADER_NAME_FULL_CUSTOM = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOL2;BOOL3;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;CHAR1;CHAR2;COMPLEX1;COMPLEX2;COMPLEX3;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;FLOAT1;FLOAT2;FLOAT3;FLOAT4;INTEGER1;INTEGER2;INTEGER3;INTEGER4;LONG1;LONG2;LONG3;LONG4;REQUIREDWITHCUSTOM;SHORT1;SHORT2;SHORT3;SHORT4;STRING1;STRING2";
     private static final String GOOD_DATA_NAME_CUSTOM_1 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really long test string, yeah!";
     private static final String GOOD_DATA_NAME_CUSTOM_2 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;4.d.long,long.string4;2147483642.z.Inserted in setter methodlong,long.derived.string5;6.f.long,long.string6;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really";
-    private static final String HEADER_NAME_FULL_DERIVED = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INT IN SUBCLASS;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
-    private static final String GOOD_DATA_NAME_DERIVED_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;7;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
-    private static final String GOOD_DATA_NAME_DERIVED_SUB_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
-    private static final String REVERSE_GOOD_DATA_1 = ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;TEST1;1.01;19780115T063209;19780115T063209;13. Dezember 2018;01/15/1978;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;102;101;123.102,102;123101.101;b;a;16.000;15000;14.000;13000;12.000;11000;10.000;9000;8.000;2147476647;6.000;5000;3.000,4;2000.3;1.000,2;123101.1;123(\u00A0|\u202F)404,404;123303.303;123.202,202;123,101.101;4;3;2;1;false;value: true;test string";
-    private static final String COLLATED_HEADER_NAME_FULL = "SHORT1;SHORT2;SHORT3;SHORT4;STRING1;BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOLPRIMITIVE;BOOL1;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4";
-    private static final String COLLATED_GOOD_DATA_NAME_1 = "13000;14.000;15000;16.000;test string;123101.101;123.102,102;101;102;false;value: true;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000";
+    private static final String HEADER_NAME_FULL_DERIVED = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INT IN SUBCLASS;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
+    private static final String GOOD_DATA_NAME_DERIVED_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;7;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String GOOD_DATA_NAME_DERIVED_SUB_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String REVERSE_GOOD_DATA_1 = ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EUR;TEST1;1.01;19780115T063209;19780115T063209;13. Dezember 2018;01/15/1978;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;102;101;123.102,102;123101.101;b;a;16.000;15000;14.000;13000;12.000;11000;10.000;9000;8.000;2147476647;6.000;5000;3.000,4;2000.3;1.000,2;123101.1;123[\u00A0\u202F]404,404;123303.303;123.202,202;123,101.101;4;3;2;1;false;value: true;test string";
+    private static final String COLLATED_HEADER_NAME_FULL = "SHORT1;SHORT2;SHORT3;SHORT4;STRING1;BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOLPRIMITIVE;BOOL1;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4";
+    private static final String COLLATED_GOOD_DATA_NAME_1 = "13000;14.000;15000;16.000;test string;123101.101;123.102,102;101;102;false;value: true;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000";
 
     @BeforeAll
     public static void storeSystemLocale() {
@@ -129,7 +130,7 @@ public class StatefulBeanToCsvTest {
         btcsv.write(beans.left);
         String output = writer.toString();
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123(\u00A0|\u202F)404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
+                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
                 output));
     }
 
@@ -144,7 +145,7 @@ public class StatefulBeanToCsvTest {
         beans.left.setStringClass("Quoted \"air quotes\" string");
         btcsv.write(beans.left);
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";value: true;false;1;2;3;4;123,101\\.101;123\\.202,202;123303\\.303;123(\u00A0|\u202F)404,404;123101\\.1;1\\.000,2;2000\\.3;3\\.000,4;5000;6\\.000;2147476647;8\\.000;9000;10\\.000;11000;12\\.000;13000;14\\.000;15000;16\\.000;a;b;123101\\.101;123\\.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13\\. Dezember 2018;19780115T063209;19780115T063209;1\\.01;TEST1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n",
+                "\"Quoted \"\"air quotes\"\" string\";value: true;false;1;2;3;4;123,101\\.101;123\\.202,202;123303\\.303;123[\u00A0\u202F]404,404;123101\\.1;1\\.000,2;2000\\.3;3\\.000,4;5000;6\\.000;2147476647;8\\.000;9000;10\\.000;11000;12\\.000;13000;14\\.000;15000;16\\.000;a;b;123101\\.101;123\\.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13\\. Dezember 2018;19780115T063209;19780115T063209;1\\.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n",
                 writer.toString()));
     }
 
@@ -316,6 +317,7 @@ public class StatefulBeanToCsvTest {
         beans.left.setFloatWrappedDefaultLocale(null);
         beans.left.setCalDefaultLocale(null);
         beans.left.setTestEnum(null);
+        beans.left.setTestCurrency(null);
         StringWriter writer = new StringWriter();
         StatefulBeanToCsv<AnnotatedMockBeanFull> btcsv = new StatefulBeanToCsvBuilder<AnnotatedMockBeanFull>(writer)
                 .withQuotechar(ICSVWriter.NO_QUOTE_CHARACTER)
@@ -1000,6 +1002,62 @@ public class StatefulBeanToCsvTest {
         assertTrue(Pattern.matches(HEADER_NAME_FULL + "\n" + GOOD_DATA_NAME_1 + "\n", writer.toString()));
     }
 
+    @Test
+    public void writeMultipleExceptionsPerBean() throws CsvFieldAssignmentException {
+        StringWriter writer = new StringWriter();
+        HeaderColumnNameMappingStrategy<WriteLocale> strat = new HeaderColumnNameMappingStrategy<>();
+        strat.setColumnOrderOnWrite(null);
+        strat.setType(WriteLocale.class);
+        StatefulBeanToCsv<WriteLocale> btcsv = new StatefulBeanToCsvBuilder<WriteLocale>(writer)
+                .withQuotechar(ICSVWriter.NO_QUOTE_CHARACTER)
+                .withSeparator(';')
+                .withMappingStrategy(strat)
+                .withThrowExceptions(false)
+                .build();
+
+        // Broken beans. Each has multiple required fields.
+        List<WriteLocale> beans = Arrays.asList(new WriteLocale(), new WriteLocale(), new WriteLocale());
+        btcsv.write(beans);
+        final int errorsPerLine = 4;
+        final int totalLines = 3;
+        List<CsvException> thrownExceptions = btcsv.getCapturedExceptions();
+        assertNotNull(thrownExceptions);
+        assertEquals(errorsPerLine*totalLines, thrownExceptions.size());
+        for(int line = 0; line < totalLines ; line++) {
+            for(int mistake = 0; mistake < errorsPerLine; mistake++) {
+                CsvException e = thrownExceptions.get(line*errorsPerLine+mistake);
+                assertTrue(e instanceof CsvRequiredFieldEmptyException);
+                assertEquals(line+1, e.getLineNumber());
+            }
+        }
+    }
+
+    @Test
+    public void writeMultipleExceptionsOneBean() throws CsvFieldAssignmentException {
+        StringWriter writer = new StringWriter();
+        HeaderColumnNameMappingStrategy<WriteLocale> strat = new HeaderColumnNameMappingStrategy<>();
+        strat.setColumnOrderOnWrite(null);
+        strat.setType(WriteLocale.class);
+        StatefulBeanToCsv<WriteLocale> btcsv = new StatefulBeanToCsvBuilder<WriteLocale>(writer)
+                .withQuotechar(ICSVWriter.NO_QUOTE_CHARACTER)
+                .withSeparator(';')
+                .withMappingStrategy(strat)
+                .withThrowExceptions(false)
+                .build();
+
+        // Broken beans. Each has multiple required fields.
+        btcsv.write(new WriteLocale());
+        List<CsvException> thrownExceptions = btcsv.getCapturedExceptions();
+        assertNotNull(thrownExceptions);
+        final int errorsPerLine = 4;
+        assertEquals(errorsPerLine, thrownExceptions.size());
+        for(int mistake = 0; mistake < errorsPerLine; mistake++) {
+            CsvException e = thrownExceptions.get(mistake);
+            assertTrue(e instanceof CsvRequiredFieldEmptyException);
+            assertEquals(1, e.getLineNumber());
+        }
+    }
+
     private static class SFirstCollator implements Comparator<String> {
         private final Comparator<Object> c;
 
diff --git a/src/test/java/com/opencsv/bean/StatefulBeanToCsvWithCSVWriterTest.java b/src/test/java/com/opencsv/bean/StatefulBeanToCsvWithCSVWriterTest.java
index c32b25c..d39262e 100644
--- a/src/test/java/com/opencsv/bean/StatefulBeanToCsvWithCSVWriterTest.java
+++ b/src/test/java/com/opencsv/bean/StatefulBeanToCsvWithCSVWriterTest.java
@@ -26,7 +26,6 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
@@ -35,7 +34,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.regex.Pattern;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * Tests {@link StatefulBeanToCsv}.
@@ -50,18 +49,18 @@ public class StatefulBeanToCsvWithCSVWriterTest {
     // handling of the same locale in different Java versions. There was a break from
     // Java 8 to Java 9, and another from Java 12 to Java 13.
     private static final String EXTRA_STRING_FOR_WRITING = "extrastringforwritinghowcreative";
-    private static final String GOOD_DATA_1 = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
-    private static final String GOOD_DATA_2 = "test string;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
-    private static final String GOOD_DATA_OPTIONALS_NULL = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_1 = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_2 = "test string;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;123101.1;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;CHF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
+    private static final String GOOD_DATA_OPTIONALS_NULL = "test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;;1.000,2;2000.3;3.000,4;5000;6.000;2147476647;8.000;9000;10.000;11000;12.000;13000;14.000;15000;16.000;a;b;123101.101;123.102,102;101;102;19780115T063209;19780115T063209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;";
     private static final String GOOD_DATA_CUSTOM_1 = "inside custom converter;wahr;falsch;127;127;127;;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;32767;32767;32767;32767;\uFFFF;\uFFFF;10;10;10;10;;;;;;;;;;;;;falsch;wahr;really long test string, yeah!;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;inside custom converter";
-    private static final String HEADER_NAME_FULL = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
-    private static final String GOOD_DATA_NAME_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String HEADER_NAME_FULL = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
+    private static final String GOOD_DATA_NAME_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;1.000,2;2000.3;3.000,4;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
     private static final String HEADER_NAME_FULL_CUSTOM = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOL2;BOOL3;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;CHAR1;CHAR2;COMPLEX1;COMPLEX2;COMPLEX3;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;FLOAT1;FLOAT2;FLOAT3;FLOAT4;INTEGER1;INTEGER2;INTEGER3;INTEGER4;LONG1;LONG2;LONG3;LONG4;REQUIREDWITHCUSTOM;SHORT1;SHORT2;SHORT3;SHORT4;STRING1;STRING2";
     private static final String GOOD_DATA_NAME_CUSTOM_1 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;1.a.long,long.string1;2147483645.z.Inserted in setter methodlong,long.string2;3.c.long,long.derived.string3;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really long test string, yeah!";
     private static final String GOOD_DATA_NAME_CUSTOM_2 = "10;10;10;10;wahr;falsch;wahr;falsch;127;127;127;\uFFFF;\uFFFF;4.d.long,long.string4;2147483642.z.Inserted in setter methodlong,long.derived.string5;6.f.long,long.string6;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;1.7976931348623157E308;3.4028235E38;3.4028235E38;3.4028235E38;3.4028235E38;2147483647;2147483647;2147483647;2147483647;9223372036854775807;9223372036854775807;9223372036854775807;9223372036854775807;inside custom converter;32767;32767;32767;32767;inside custom converter;really";
-    private static final String HEADER_NAME_FULL_DERIVED = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INT IN SUBCLASS;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
-    private static final String GOOD_DATA_NAME_DERIVED_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;7;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
-    private static final String GOOD_DATA_NAME_DERIVED_SUB_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123(\u00A0|\u202F)404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String HEADER_NAME_FULL_DERIVED = "BIGDECIMAL1;BIGDECIMAL2;BIGINTEGER1;BIGINTEGER2;BOOL1;BOOLPRIMITIVE;BYTE1;BYTE2;BYTE3;BYTE4;CHAR1;CHAR2;CURRENCY1;DATE1;DATE10;DATE11;DATE12;DATE13;DATE14;DATE15;DATE16;DATE2;DATE3;DATE4;DATE5;DATE6;DATE7;DATE8;DATE9;DOUBLE1;DOUBLE2;DOUBLE3;DOUBLE4;ENUM1;FLOAT1;FLOAT2;FLOAT3;FLOAT4;FLOAT5;INT IN SUBCLASS;INTEGER1;INTEGER2;INTEGER3;INTEGER4;ITNOGOODCOLUMNITVERYBAD;LONG1;LONG2;LONG3;LONG4;SHORT1;SHORT2;SHORT3;SHORT4;STRING1";
+    private static final String GOOD_DATA_NAME_DERIVED_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;7;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
+    private static final String GOOD_DATA_NAME_DERIVED_SUB_1 = "123101.101;123.102,102;101;102;value: true;false;1;2;3;4;a;b;EUR;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;123,101.101;123.202,202;123303.303;123[\u00A0\u202F]404,404;TEST1;123101.1;123.202,203;123303.305;123.404,406;1.01;5000;6.000;2147476647;8.000;;9000;10.000;11000;12.000;13000;14.000;15000;16.000;test string";
 
 
     private StringWriter writer;
@@ -133,7 +132,7 @@ public class StatefulBeanToCsvWithCSVWriterTest {
         btcsv.write(beans.left);
         String output = writer.toString();
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123(\u00A0|\u202F)404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
+                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
                 output));
     }
 
@@ -151,7 +150,7 @@ public class StatefulBeanToCsvWithCSVWriterTest {
         btcsv.write(beans.left);
         String output = writer.toString();
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123(\u00A0|\u202F)404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
+                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
                 output));
     }
 
@@ -168,7 +167,7 @@ public class StatefulBeanToCsvWithCSVWriterTest {
         btcsv.write(beans.left);
         String output = writer.toString();
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123(\u00A0|\u202F)404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
+                "\"Quoted \"\"air quotes\"\" string\";\"value: true\";\"false\";\"1\";\"2\";\"3\";\"4\";\"123,101\\.101\";\"123\\.202,202\";\"123303\\.303\";\"123[\u00A0\u202F]404,404\";\"123101\\.1\";\"1\\.000,2\";\"2000\\.3\";\"3\\.000,4\";\"5000\";\"6\\.000\";\"2147476647\";\"8\\.000\";\"9000\";\"10\\.000\";\"11000\";\"12\\.000\";\"13000\";\"14\\.000\";\"15000\";\"16\\.000\";\"a\";\"b\";\"123101\\.101\";\"123\\.102,102\";\"101\";\"102\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"19780115T063209\";\"01/15/1978\";\"13\\. Dezember 2018\";\"19780115T063209\";\"19780115T063209\";\"1\\.01\";\"TEST1\";\"EUR\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\";\"\"\n",
                 output));
     }
 
@@ -184,7 +183,7 @@ public class StatefulBeanToCsvWithCSVWriterTest {
         beans.left.setStringClass("Quoted \"air quotes\" string");
         btcsv.write(beans.left);
         assertTrue(Pattern.matches(
-                "\"Quoted \"\"air quotes\"\" string\";value: true;false;1;2;3;4;123,101\\.101;123\\.202,202;123303\\.303;123(\u00A0|\u202F)404,404;123101\\.1;1\\.000,2;2000\\.3;3\\.000,4;5000;6\\.000;2147476647;8\\.000;9000;10\\.000;11000;12\\.000;13000;14\\.000;15000;16\\.000;a;b;123101\\.101;123\\.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13\\. Dezember 2018;19780115T063209;19780115T063209;1\\.01;TEST1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n",
+                "\"Quoted \"\"air quotes\"\" string\";value: true;false;1;2;3;4;123,101\\.101;123\\.202,202;123303\\.303;123[\u00A0\u202F]404,404;123101\\.1;1\\.000,2;2000\\.3;3\\.000,4;5000;6\\.000;2147476647;8\\.000;9000;10\\.000;11000;12\\.000;13000;14\\.000;15000;16\\.000;a;b;123101\\.101;123\\.102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13\\. Dezember 2018;19780115T063209;19780115T063209;1\\.01;TEST1;EUR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n",
                 writer.toString()));
     }
 
@@ -274,6 +273,8 @@ public class StatefulBeanToCsvWithCSVWriterTest {
         ImmutablePair<AnnotatedMockBeanFull, AnnotatedMockBeanFull> beans = TestUtils.createTwoGoodBeans();
         beans.left.setFloatWrappedDefaultLocale(null);
         beans.left.setCalDefaultLocale(null);
+        beans.left.setTestEnum(null);
+        beans.left.setTestCurrency(null);
         ICSVWriter csvWriter = csvWriterBuilder
                 .withQuoteChar(ICSVWriter.NO_QUOTE_CHARACTER)
                 .withEscapeChar('|')
diff --git a/src/test/java/com/opencsv/bean/TemporalTest.java b/src/test/java/com/opencsv/bean/TemporalTest.java
index 460fe51..b4790ef 100644
--- a/src/test/java/com/opencsv/bean/TemporalTest.java
+++ b/src/test/java/com/opencsv/bean/TemporalTest.java
@@ -19,12 +19,11 @@ import java.io.StringWriter;
 import java.time.*;
 import java.time.chrono.*;
 import java.time.temporal.Temporal;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class TemporalTest {
 
@@ -220,27 +219,21 @@ public class TemporalTest {
 
         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
         assertNotNull(exceptions);
-        assertEquals(5, exceptions.size());
 
-        // The exception we're looking for could be the third or fourth
-        assertTrue(verifyEraException(exceptions.get(2), 4, era)
-                || verifyEraException(exceptions.get(3), 5, era)
-                || verifyEraException(exceptions.get(4), 6, era));
-    }
+        // Five lines must show errors
+        assertEquals(5, exceptions.stream()
+                .map(CsvException::getLineNumber)
+                .collect(Collectors.toSet()).size());
 
-    private boolean verifyEraException(CsvException csve, long lineNumber, Class<? extends Era> era) {
-        boolean assertionPassed = true;
-        assertTrue(csve instanceof CsvDataTypeMismatchException);
-        CsvDataTypeMismatchException dtmm = (CsvDataTypeMismatchException) csve;
-        assertNotNull(dtmm.getCause());
-        if(lineNumber != dtmm.getLineNumber()) {
-            assertionPassed = false;
-        }
-        assertNotNull(dtmm.getSourceObject());
-        if(!era.equals(dtmm.getDestinationClass())) {
-            assertionPassed = false;
-        }
-        return assertionPassed;
+        // The exception we're looking for could be in one of three lines
+        Set<Long> possibleLines = new HashSet<>();
+        possibleLines.add(4L); possibleLines.add(5L); possibleLines.add(6L);
+        assertTrue(exceptions.stream()
+                .filter(e -> e.getCause() != null)
+                .filter(e -> e instanceof CsvDataTypeMismatchException)
+                .filter(e -> era.equals(((CsvDataTypeMismatchException)e).getDestinationClass()))
+                .filter(e -> ((CsvDataTypeMismatchException) e).getSourceObject() != null)
+                .anyMatch(e -> possibleLines.contains(e.getLineNumber())));
     }
 
     /**
@@ -272,8 +265,11 @@ public class TemporalTest {
         //   Japanese eras = "Shōwa" and "Taishō"
         // Thus we have three lines of input for every combination and any
         // given version of Java will accept three and throw exceptions for the
-        // other six.
-        assertEquals(6, csvToBean.getCapturedExceptions().size());
+        // other six. But there are either four or six errors per line, as well.
+        Set<Integer> numErrors = new HashSet<>();
+        numErrors.add(24); // Java 9 through 12
+        numErrors.add(36); // Java 8 and 13+
+        assertTrue(numErrors.contains(csvToBean.getCapturedExceptions().size()));
         verifyBeans(beans);
     }
 
@@ -297,8 +293,11 @@ public class TemporalTest {
         //   Japanese eras = "Shōwa" and "Taishō"
         // Thus we have three lines of input for every combination and any
         // given version of Java will accept three and throw exceptions for the
-        // other six.
-        assertEquals(6, csvToBean.getCapturedExceptions().size());
+        // other six. But there are either four or six errors per line, as well.
+        Set<Integer> numErrors = new HashSet<>();
+        numErrors.add(24); // Java 9 through 12
+        numErrors.add(36); // Java 8 and 13+
+        assertTrue(numErrors.contains(csvToBean.getCapturedExceptions().size()));
         verifyBeans(beans);
     }
 
@@ -321,8 +320,8 @@ public class TemporalTest {
                 .build();
         beanToCsv.write(Arrays.asList(pair.left, pair.right));
         Pattern p = Pattern.compile("CHRONOLOCALDATE,CHRONOLOCALDATELOCALE,CHRONOLOCALDATETIME,CHRONOLOCALDATETIMELOCALE,CHRONOZONEDDATETIME,CHRONOZONEDDATETIMELOCALE,DAYOFWEEK,DAYOFWEEKLOCALE,ERA,ERALOCALE,HIJRAHDATE,HIJRAHDATELOCALE,HIJRAHERA,HIJRAHERALOCALE,INSTANT,INSTANTLOCALE,ISOERA,ISOERALOCALE,JAPANESEDATE,JAPANESEDATELOCALE,JAPANESEERA,JAPANESEERALOCALE,LOCALDATE,LOCALDATELOCALE,LOCALDATETIME,LOCALDATETIMELOCALE,LOCALTIME,LOCALTIMELOCALE,MINGUODATE,MINGUODATELOCALE,MINGUOERA,MINGUOERALOCALE,MONTH,MONTHDAY,MONTHDAYLOCALE,MONTHLOCALE,OFFSETDATETIME,OFFSETDATETIMELOCALE,OFFSETTIME,OFFSETTIMELOCALE,TEMPORAL,TEMPORALACCESSOR,TEMPORALACCESSORLOCALE,TEMPORALLOCALE,THAIBUDDHISTDATE,THAIBUDDHISTDATELOCALE,THAIBUDDHISTERA,THAIBUDDHISTERALOCALE,YEAR,YEARLOCALE,YEARMONTH,YEARMONTHLOCALE,ZONEDDATETIME,ZONEDDATETIMELOCALE,ZONEOFFSET,ZONEOFFSETLOCALE\n" +
-                "AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,Tue,Di\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,AD,n\\. Chr\\.,Sh(o|ō)wa 53 January 15,Sh(o|ō)wa 53 Januar 15,AD,n\\. Chr\\.,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,28,28,Januar,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 January 21 06 02 35 EST,Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,Sh(o|ō)wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z,\\+01:00,\\+01:00\n" +
-                "AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,Wed,Mi\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,AD,n\\. Chr\\.,Sh(o|ō)wa 53 May 15,Sh(o|ō)wa 53 Mai 15,AD,n\\. Chr\\.,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,28,28,Mai,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 May 21 06 02 35 EDT,Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,Sh(o|ō)wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z,\\+01:00,\\+01:00\n");
+                "AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,Tue,Di\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,AD,n\\. Chr\\.,Sh[oō]wa 53 January 15,Sh[oō]wa 53 Januar 15,AD,n\\. Chr\\.,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,28,28,Januar,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 January 21 06 02 35 EST,Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,Sh[oō]wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z,\\+01:00,\\+01:00\n" +
+                "AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,Wed,Mi\\.?,AD,n\\. Chr\\.,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,AD,n\\. Chr\\.,Sh[oō]wa 53 May 15,Sh[oō]wa 53 Mai 15,AD,n\\. Chr\\.,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,28,28,Mai,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,AD 1978 May 21 06 02 35 EDT,Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,Sh[oō]wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z,\\+01:00,\\+01:00\n");
         assertTrue(p.matcher(w.toString()).matches());
     }
 
@@ -334,8 +333,8 @@ public class TemporalTest {
                 .withApplyQuotesToAll(false)
                 .build();
         beanToCsv.write(Arrays.asList(pair.left, pair.right));
-        Pattern p = Pattern.compile("Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,AD 1978 January 21 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Tue,Di\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,Sh(o|ō)wa 53 January 15,Sh(o|ō)wa 53 Januar 15,AD,n\\. Chr\\.,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,Januar,28,28,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,\\+01:00,\\+01:00,Sh(o|ō)wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z\n" +
-                "Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,AD 1978 May 21 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Wed,Mi\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,Sh(o|ō)wa 53 May 15,Sh(o|ō)wa 53 Mai 15,AD,n\\. Chr\\.,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,Mai,28,28,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,\\+01:00,\\+01:00,Sh(o|ō)wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z\n");
+        Pattern p = Pattern.compile("Anno Domini 1978 January 15 06 02 35 EST,n\\. Chr\\. 1978 Januar 15 06 02 35 EST,AD 1978 January 16,n\\. Chr\\. 1978 Januar 16,AD 1978 January 17,n\\. Chr\\. 1978 Januar 17,AD 1978 January 18 06 02 35,n\\. Chr\\. 1978 Januar 18 06 02 35,AD 1978 January 19 06 02 35,n\\. Chr\\. 1978 Januar 19 06 02 35,AD 1978 January 20 06 02 35 EST,n\\. Chr\\. 1978 Januar 20 06 02 35 EST,AD 1978 January 21 06 02 35 EST,n\\. Chr\\. 1978 Januar 21 06 02 35 EST,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Tue,Di\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 January 25 11 02 35,1978 1月 25 11 02 35,Sh[oō]wa 53 January 15,Sh[oō]wa 53 Januar 15,AD,n\\. Chr\\.,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 January 15,Before R\\.O\\.C\\. 67 Januar 15,BC,v\\. Chr\\.,January,Januar,28,28,1978-January-29T06:02:35\\+00:30,1978-Januar-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 January 15,B\\.?E\\.? 2521 Januar 15,AD,n\\. Chr\\.,1978,1978,1978 January,1978 Januar,\\+01:00,\\+01:00,Sh[oō]wa 0053 January 03 06 02 35 Z,n\\. Chr\\. 1978 Januar 03 06 02 35 Z\n" +
+                "Anno Domini 1978 May 15 06 02 35 EDT,n\\. Chr\\. 1978 Mai 15 06 02 35 EDT,AD 1978 May 16,n\\. Chr\\. 1978 Mai 16,AD 1978 May 17,n\\. Chr\\. 1978 Mai 17,AD 1978 May 18 06 02 35,n\\. Chr\\. 1978 Mai 18 06 02 35,AD 1978 May 19 06 02 35,n\\. Chr\\. 1978 Mai 19 06 02 35,AD 1978 May 20 06 02 35 EDT,n\\. Chr\\. 1978 Mai 20 06 02 35 EDT,AD 1978 May 21 06 02 35 EDT,n\\. Chr\\. 1978 Mai 21 06 02 35 EDT,AD,n\\. Chr\\.,AD,n\\. Chr\\.,Wed,Mi\\.?,AH 1398 Safar 06,AH 1398 Safar 06,AD,n\\. Chr\\.,1978 May 25 10 02 35,1978 5月 25 10 02 35,Sh[oō]wa 53 May 15,Sh[oō]wa 53 Mai 15,AD,n\\. Chr\\.,06 02 35,06 02 35,Before R\\.O\\.C\\. 67 May 15,Before R\\.O\\.C\\. 67 Mai 15,BC,v\\. Chr\\.,May,Mai,28,28,1978-May-29T06:02:35\\+00:30,1978-Mai-29T06:02:35\\+00:30,06:02:35\\+00:30,06:02:35\\+00:30,B\\.?E\\.? 2521 May 15,B\\.?E\\.? 2521 Mai 15,AD,n\\. Chr\\.,1978,1978,1978 May,1978 Mai,\\+01:00,\\+01:00,Sh[oō]wa 0053 May 03 06 02 35 Z,n\\. Chr\\. 1978 Mai 03 06 02 35 Z\n");
         assertTrue(p.matcher(w.toString()).matches());
     }
 
@@ -395,15 +394,13 @@ public class TemporalTest {
         csvToBean.parse();
         List<CsvException> exceptions = csvToBean.getCapturedExceptions();
         assertNotNull(exceptions);
-        assertEquals(2, exceptions.size());
-        for(CsvException e : exceptions) {
-            assertNotNull(e.getCause());
-            assertNotNull(e.getLine());
-            assertTrue(e instanceof CsvDataTypeMismatchException);
-            CsvDataTypeMismatchException csve = (CsvDataTypeMismatchException) e;
-            assertEquals(input.split(",", 2)[0], csve.getSourceObject());
-            assertEquals(IsoEra.class, csve.getDestinationClass());
-        }
+        assertEquals(2, exceptions.stream()
+                .filter(e -> e.getCause() != null)
+                .filter(e -> e.getLine() != null)
+                .filter(e -> e instanceof CsvDataTypeMismatchException)
+                .filter(e -> IsoEra.class.equals(((CsvDataTypeMismatchException)e).getDestinationClass()))
+                .filter(e -> input.split(",", 2)[0].equals(((CsvDataTypeMismatchException)e).getSourceObject()))
+                .count());
     }
 
     @Test
diff --git a/src/test/java/com/opencsv/bean/comparator/ComparatorTest.java b/src/test/java/com/opencsv/bean/comparator/ComparatorTest.java
index c21fbd2..106f32d 100644
--- a/src/test/java/com/opencsv/bean/comparator/ComparatorTest.java
+++ b/src/test/java/com/opencsv/bean/comparator/ComparatorTest.java
@@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
 
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 
 public class ComparatorTest {
 
diff --git a/src/test/java/com/opencsv/bean/customconverter/ConvertSplitOnWhitespace.java b/src/test/java/com/opencsv/bean/customconverter/ConvertSplitOnWhitespace.java
index d690358..a4e94f3 100644
--- a/src/test/java/com/opencsv/bean/customconverter/ConvertSplitOnWhitespace.java
+++ b/src/test/java/com/opencsv/bean/customconverter/ConvertSplitOnWhitespace.java
@@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.ResourceBundle;
 
 /**
  * This class takes a string and splits it on whitespace into a list of strings.
diff --git a/src/test/java/com/opencsv/bean/customconverter/ErrorCodeConverter.java b/src/test/java/com/opencsv/bean/customconverter/ErrorCodeConverter.java
index 42baa5c..5eb486f 100644
--- a/src/test/java/com/opencsv/bean/customconverter/ErrorCodeConverter.java
+++ b/src/test/java/com/opencsv/bean/customconverter/ErrorCodeConverter.java
@@ -59,7 +59,7 @@ public class ErrorCodeConverter extends AbstractCsvConverter {
     @Override
     public String convertToWrite(Object value) {
         ErrorCode ec = (ErrorCode) value;
-        StringBuffer sb = new StringBuffer();
+        StringBuilder sb = new StringBuilder();
         sb.append(ec.errorCode);
         sb.append("default.error");
         return sb.toString();
diff --git a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFull.java b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFull.java
index 43d9297..4ae3a93 100644
--- a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFull.java
+++ b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFull.java
@@ -25,6 +25,7 @@ import java.math.BigInteger;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.Calendar;
+import java.util.Currency;
 import java.util.Date;
 import java.util.GregorianCalendar;
 
@@ -735,10 +736,22 @@ public class AnnotatedMockBeanFull {
      * <li>{@link com.opencsv.bean.AnnotationTest#testGoodDataByName()}</li>
      * </ul>
      */
-    @CsvBindByName(column = "enum1", required = false)
-    @CsvBindByPosition(position = 50, required = false)
+    @CsvBindByName(column = "enum1")
+    @CsvBindByPosition(position = 50)
     private TestEnum testEnum;
 
+    /**
+     * Field for annotation tests.
+     * <p>Used for the following test cases, reading:</p>
+     * <ul>
+     * <li>{@link com.opencsv.bean.AnnotationTest#testGoodDataByName()}</li>
+     * <li>{@link com.opencsv.bean.AnnotationTest#testGoodDataByPosition()}</li>
+     * </ul>
+     */
+    @CsvBindByName(column = "currency1")
+    @CsvBindByPosition(position = 51)
+    private Currency testCurrency;
+
     /**
      * Field for annotation tests.
      * <p>Used for the following test cases, reading:</p>
@@ -1192,4 +1205,8 @@ public class AnnotatedMockBeanFull {
     public TestEnum getTestEnum() {return testEnum;}
 
     public void setTestEnum(TestEnum testEnum) {this.testEnum = testEnum;}
+
+    public Currency getTestCurrency() {return testCurrency;}
+
+    public void setTestCurrency(Currency testCurrency) {this.testCurrency = testCurrency;}
 }
diff --git a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFullDerived.java b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFullDerived.java
index 0f9e3a2..4b3ba23 100644
--- a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFullDerived.java
+++ b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanFullDerived.java
@@ -38,7 +38,7 @@ public class AnnotatedMockBeanFullDerived extends AnnotatedMockBeanFull {
      * </ul>
      */
     @CsvBindByName(required = true, column = "int in subclass")
-    @CsvBindByPosition(required = true, position = 51)
+    @CsvBindByPosition(required = true, position = 52)
     private int intInSubclass;
 
     public int getIntInSubclass() {
diff --git a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanTemporal.java b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanTemporal.java
index 9127cc3..6bd84dd 100644
--- a/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanTemporal.java
+++ b/src/test/java/com/opencsv/bean/mocks/AnnotatedMockBeanTemporal.java
@@ -155,7 +155,7 @@ public class AnnotatedMockBeanTemporal {
             value = "G yyyy MMMM dd HH mm ss z",
             writeFormatEqualsReadFormat = false,
             writeFormat = "G yyyy MMMM dd HH mm ss")
-    private ChronoLocalDateTime chronoLocalDateTime;
+    private ChronoLocalDateTime<LocalDate> chronoLocalDateTime;
 
     /**
      * Field for {@link java.time.temporal.TemporalAccessor}-based annotation
@@ -175,7 +175,7 @@ public class AnnotatedMockBeanTemporal {
             value = "G yyyy MMMM dd HH mm ss z",
             writeFormatEqualsReadFormat = false,
             writeFormat = "G yyyy MMMM dd HH mm ss")
-    private ChronoLocalDateTime chronoLocalDateTimeLocale;
+    private ChronoLocalDateTime<LocalDate> chronoLocalDateTimeLocale;
 
     /**
      * Field for {@link java.time.temporal.TemporalAccessor}-based annotation
@@ -232,7 +232,7 @@ public class AnnotatedMockBeanTemporal {
     @CsvBindByName
     @CsvBindByPosition(position = 10)
     @CsvDate("G yyyy MMMM dd HH mm ss z")
-    private ChronoZonedDateTime chronoZonedDateTime;
+    private ChronoZonedDateTime<LocalDate> chronoZonedDateTime;
 
     /**
      * Field for {@link java.time.temporal.TemporalAccessor}-based annotation
@@ -249,7 +249,7 @@ public class AnnotatedMockBeanTemporal {
     @CsvBindByName(locale = "de")
     @CsvBindByPosition(position = 11, locale = "de")
     @CsvDate("G yyyy MMMM dd HH mm ss z")
-    private ChronoZonedDateTime chronoZonedDateTimeLocale;
+    private ChronoZonedDateTime<LocalDate> chronoZonedDateTimeLocale;
 
     /**
      * Field for {@link java.time.temporal.TemporalAccessor}-based annotation
@@ -1167,19 +1167,19 @@ public class AnnotatedMockBeanTemporal {
         this.localDateLocale = localDateLocale;
     }
 
-    public ChronoLocalDateTime getChronoLocalDateTime() {
+    public ChronoLocalDateTime<LocalDate> getChronoLocalDateTime() {
         return chronoLocalDateTime;
     }
 
-    public void setChronoLocalDateTime(ChronoLocalDateTime chronoLocalDateTime) {
+    public void setChronoLocalDateTime(ChronoLocalDateTime<LocalDate> chronoLocalDateTime) {
         this.chronoLocalDateTime = chronoLocalDateTime;
     }
 
-    public ChronoLocalDateTime getChronoLocalDateTimeLocale() {
+    public ChronoLocalDateTime<LocalDate> getChronoLocalDateTimeLocale() {
         return chronoLocalDateTimeLocale;
     }
 
-    public void setChronoLocalDateTimeLocale(ChronoLocalDateTime chronoLocalDateTimeLocale) {
+    public void setChronoLocalDateTimeLocale(ChronoLocalDateTime<LocalDate> chronoLocalDateTimeLocale) {
         this.chronoLocalDateTimeLocale = chronoLocalDateTimeLocale;
     }
 
@@ -1199,19 +1199,19 @@ public class AnnotatedMockBeanTemporal {
         this.localDateTimeLocale = localDateTimeLocale;
     }
 
-    public ChronoZonedDateTime getChronoZonedDateTime() {
+    public ChronoZonedDateTime<LocalDate> getChronoZonedDateTime() {
         return chronoZonedDateTime;
     }
 
-    public void setChronoZonedDateTime(ChronoZonedDateTime chronoZonedDateTime) {
+    public void setChronoZonedDateTime(ChronoZonedDateTime<LocalDate> chronoZonedDateTime) {
         this.chronoZonedDateTime = chronoZonedDateTime;
     }
 
-    public ChronoZonedDateTime getChronoZonedDateTimeLocale() {
+    public ChronoZonedDateTime<LocalDate> getChronoZonedDateTimeLocale() {
         return chronoZonedDateTimeLocale;
     }
 
-    public void setChronoZonedDateTimeLocale(ChronoZonedDateTime chronoZonedDateTimeLocale) {
+    public void setChronoZonedDateTimeLocale(ChronoZonedDateTime<LocalDate> chronoZonedDateTimeLocale) {
         this.chronoZonedDateTimeLocale = chronoZonedDateTimeLocale;
     }
 
diff --git a/src/test/java/com/opencsv/bean/mocks/BindUnknownType.java b/src/test/java/com/opencsv/bean/mocks/BindUnknownType.java
index 0df9edb..b54b5a6 100644
--- a/src/test/java/com/opencsv/bean/mocks/BindUnknownType.java
+++ b/src/test/java/com/opencsv/bean/mocks/BindUnknownType.java
@@ -31,7 +31,7 @@ public class BindUnknownType {
             new BindByNameUnknownTypeInnerClass(TOSTRING);
     public BindByNameUnknownTypeInnerClass getTest() {return test;}
     
-    private class BindByNameUnknownTypeInnerClass {
+    private static class BindByNameUnknownTypeInnerClass {
         private final String s;
         public BindByNameUnknownTypeInnerClass(String s) {
             this.s = s;
diff --git a/src/test/java/com/opencsv/bean/mocks/ConverterComplexClassForCustomAnnotation.java b/src/test/java/com/opencsv/bean/mocks/ConverterComplexClassForCustomAnnotation.java
index af91812..8266063 100644
--- a/src/test/java/com/opencsv/bean/mocks/ConverterComplexClassForCustomAnnotation.java
+++ b/src/test/java/com/opencsv/bean/mocks/ConverterComplexClassForCustomAnnotation.java
@@ -37,7 +37,7 @@ public class ConverterComplexClassForCustomAnnotation<T, I> extends AbstractBean
         } else {
             o = new ComplexClassForCustomAnnotation();
         }
-        o.i = Integer.valueOf(sa[0]);
+        o.i = Integer.parseInt(sa[0]);
         o.c = sa[1].charAt(0);
         o.s = sa[2];
         return o;
diff --git a/src/test/java/com/opencsv/bean/mocks/CustomTestMapper.java b/src/test/java/com/opencsv/bean/mocks/CustomTestMapper.java
index 12d00c9..95eafa4 100644
--- a/src/test/java/com/opencsv/bean/mocks/CustomTestMapper.java
+++ b/src/test/java/com/opencsv/bean/mocks/CustomTestMapper.java
@@ -33,7 +33,7 @@ public class CustomTestMapper<T, I> extends AbstractBeanField<T, I> {
     @Override
     protected Object convert(String value)
             throws CsvDataTypeMismatchException {
-        Class fieldType = field.getType();
+        Class<?> fieldType = field.getType();
         if (fieldType.equals(Boolean.TYPE) || fieldType.equals(Boolean.class))
             return Boolean.TRUE;
         if (fieldType.equals(Byte.TYPE) || fieldType.equals(Byte.class))
diff --git a/src/test/java/com/opencsv/bean/mocks/EmptyAsSplitOn.java b/src/test/java/com/opencsv/bean/mocks/EmptyAsSplitOn.java
deleted file mode 100644
index 47d4aff..0000000
--- a/src/test/java/com/opencsv/bean/mocks/EmptyAsSplitOn.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2017 Andrew Rucker Jones.
- *
- * 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.opencsv.bean.mocks;
-
-import com.opencsv.bean.CsvBindAndSplitByName;
-import java.util.List;
-
-/**
- *
- * @author Andrew Rucker Jones
- */
-public class EmptyAsSplitOn {
-    
-    @CsvBindAndSplitByName(elementType = Integer.class, splitOn = "")
-    private List<Integer> l;
-
-    public List<Integer> getL() {
-        return l;
-    }
-
-    public void setL(List<Integer> l) {
-        this.l = l;
-    }
-}
diff --git a/src/test/java/com/opencsv/bean/mocks/ErrorLineMappingStrategy.java b/src/test/java/com/opencsv/bean/mocks/ErrorLineMappingStrategy.java
index 0c2236e..e06e9a4 100644
--- a/src/test/java/com/opencsv/bean/mocks/ErrorLineMappingStrategy.java
+++ b/src/test/java/com/opencsv/bean/mocks/ErrorLineMappingStrategy.java
@@ -16,15 +16,11 @@
 package com.opencsv.bean.mocks;
 
 import com.opencsv.CSVReader;
-import com.opencsv.bean.BeanField;
 import com.opencsv.bean.MappingStrategy;
 import com.opencsv.exceptions.CsvBadConverterException;
 import com.opencsv.exceptions.CsvBeanIntrospectionException;
 import org.apache.commons.lang3.ArrayUtils;
 
-import java.beans.PropertyDescriptor;
-import java.util.Locale;
-
 public class ErrorLineMappingStrategy<T> implements MappingStrategy<T> {
     @Override
     public void captureHeader(CSVReader reader) {
@@ -36,7 +32,7 @@ public class ErrorLineMappingStrategy<T> implements MappingStrategy<T> {
     }
     
     @Override
-    public void setType(Class type) throws CsvBadConverterException {}
+    public void setType(Class<? extends T> type) throws CsvBadConverterException {}
 
     @Override
     public T populateNewBean(String[] line) throws CsvBeanIntrospectionException {
diff --git a/src/test/java/com/opencsv/bean/mocks/MockBean.java b/src/test/java/com/opencsv/bean/mocks/MockBean.java
index a73ff35..c2eccb6 100644
--- a/src/test/java/com/opencsv/bean/mocks/MockBean.java
+++ b/src/test/java/com/opencsv/bean/mocks/MockBean.java
@@ -1,14 +1,16 @@
 package com.opencsv.bean.mocks;
 
+import java.util.UUID;
+
 /*
  * Copyright 2007 Kyle Miller.
- * 
+ *
  * 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.
@@ -21,6 +23,7 @@ public class MockBean {
    private String orderNumber;
    private int num;
    private double doubleNum;
+   private UUID uuid;
 
    public MockBean() {}
 
@@ -72,6 +75,14 @@ public class MockBean {
       this.doubleNum = doubleNum;
    }
 
+   public UUID getUuid() {
+      return uuid;
+   }
+
+   public void setUuid(UUID uuid) {
+      this.uuid = uuid;
+   }
+
    @Override
    public boolean equals(Object o) {
       if (this == o) return true;
@@ -83,6 +94,7 @@ public class MockBean {
       if (Double.compare(mockBean.getDoubleNum(), getDoubleNum()) != 0) return false;
       if (getName() != null ? !getName().equals(mockBean.getName()) : mockBean.getName() != null) return false;
       if (getId() != null ? !getId().equals(mockBean.getId()) : mockBean.getId() != null) return false;
+      if (getUuid() != null ? !getUuid().equals(mockBean.getUuid()) : mockBean.getUuid() != null) return false;
       return !(getOrderNumber() != null ? !getOrderNumber().equals(mockBean.getOrderNumber()) : mockBean.getOrderNumber() != null);
 
    }
@@ -95,6 +107,7 @@ public class MockBean {
       result = 31 * result + (getId() != null ? getId().hashCode() : 0);
       result = 31 * result + (getOrderNumber() != null ? getOrderNumber().hashCode() : 0);
       result = 31 * result + getNum();
+      result = 31 * result + (getUuid() != null ? getUuid().hashCode() : 0);
       temp = Double.doubleToLongBits(getDoubleNum());
       result = 31 * result + (int) (temp ^ (temp >>> 32));
       return result;
@@ -108,6 +121,7 @@ public class MockBean {
               ", orderNumber='" + orderNumber + '\'' +
               ", num=" + num +
               ", doubleNum=" + doubleNum +
+              ", uuid=" + uuid +
               '}';
    }
 }
diff --git a/src/test/java/com/opencsv/bean/mocks/TestEnum.java b/src/test/java/com/opencsv/bean/mocks/TestEnum.java
index 7445e5f..d128870 100644
--- a/src/test/java/com/opencsv/bean/mocks/TestEnum.java
+++ b/src/test/java/com/opencsv/bean/mocks/TestEnum.java
@@ -1,5 +1,5 @@
 package com.opencsv.bean.mocks;
 
 public enum TestEnum {
-    TEST1, Test2, test3;
+    TEST1, Test2, test3
 }
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberEmptyPattern.java b/src/test/java/com/opencsv/bean/mocks/number/NumberEmptyPattern.java
similarity index 89%
rename from src/test/java/com/opencsv/bean/mocks/NumberEmptyPattern.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberEmptyPattern.java
index 7dd92e9..a2850d8 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberEmptyPattern.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberEmptyPattern.java
@@ -1,4 +1,4 @@
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByName;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternReading.java b/src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternReading.java
similarity index 92%
rename from src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternReading.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternReading.java
index 16026f2..39fcf37 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternReading.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternReading.java
@@ -1,4 +1,4 @@
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByName;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternWriting.java b/src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternWriting.java
similarity index 93%
rename from src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternWriting.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternWriting.java
index ab7d842..143d2a6 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberInvalidPatternWriting.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberInvalidPatternWriting.java
@@ -1,4 +1,4 @@
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByName;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberMockColumn.java b/src/test/java/com/opencsv/bean/mocks/number/NumberMockColumn.java
similarity index 95%
rename from src/test/java/com/opencsv/bean/mocks/NumberMockColumn.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberMockColumn.java
index 3d5721e..133f815 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberMockColumn.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberMockColumn.java
@@ -1,4 +1,4 @@
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByPosition;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberMockHeader.java b/src/test/java/com/opencsv/bean/mocks/number/NumberMockHeader.java
similarity index 99%
rename from src/test/java/com/opencsv/bean/mocks/NumberMockHeader.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberMockHeader.java
index 69c002b..c45b255 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberMockHeader.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberMockHeader.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByName;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/NumberNonNumber.java b/src/test/java/com/opencsv/bean/mocks/number/NumberNonNumber.java
similarity index 88%
rename from src/test/java/com/opencsv/bean/mocks/NumberNonNumber.java
rename to src/test/java/com/opencsv/bean/mocks/number/NumberNonNumber.java
index d0c18d5..d0993de 100644
--- a/src/test/java/com/opencsv/bean/mocks/NumberNonNumber.java
+++ b/src/test/java/com/opencsv/bean/mocks/number/NumberNonNumber.java
@@ -1,4 +1,4 @@
-package com.opencsv.bean.mocks;
+package com.opencsv.bean.mocks.number;
 
 import com.opencsv.bean.CsvBindByName;
 import com.opencsv.bean.CsvNumber;
diff --git a/src/test/java/com/opencsv/bean/mocks/profile/ProfileMismatch.java b/src/test/java/com/opencsv/bean/mocks/profile/ProfileMismatch.java
new file mode 100644
index 0000000..6a6fe7f
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/profile/ProfileMismatch.java
@@ -0,0 +1,34 @@
+package com.opencsv.bean.mocks.profile;
+
+import com.opencsv.bean.*;
+
+import java.time.LocalDate;
+
+public class ProfileMismatch {
+
+    @CsvBindByName(profiles = "number")
+    @CsvBindByPosition(position = 0, profiles = "number")
+    @CsvNumbers(@CsvNumber(value = "0.0", profiles = "mismatch"))
+    private float float1;
+
+    @CsvBindByName(profiles = "date")
+    @CsvBindByPosition(position = 1, profiles = "date")
+    @CsvDate(value = "MM/dd/yyyy", profiles = "mismatch")
+    private LocalDate localDate1;
+
+    public float getFloat1() {
+        return float1;
+    }
+
+    public void setFloat1(float float1) {
+        this.float1 = float1;
+    }
+
+    public LocalDate getLocalDate1() {
+        return localDate1;
+    }
+
+    public void setLocalDate1(LocalDate localDate1) {
+        this.localDate1 = localDate1;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameMock.java b/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameMock.java
new file mode 100644
index 0000000..fc101f6
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameMock.java
@@ -0,0 +1,108 @@
+package com.opencsv.bean.mocks.profile;
+
+import com.opencsv.bean.*;
+import com.opencsv.bean.customconverter.ConvertFrenchToBoolean;
+import com.opencsv.bean.customconverter.ConvertGermanToBoolean;
+import org.apache.commons.collections4.MultiValuedMap;
+
+import java.time.LocalDate;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+public class ProfileNameMock {
+
+    @CsvBindByName
+    @CsvBindByName(capture = "integer: ([0-9]+)", format = "integer: %s", profiles = {"profile 1"})
+    @CsvBindByName(capture = "int ([0-9]+) value", format = "int %s value", profiles = {"profile 2"})
+    private int int1;
+
+    @CsvCustomBindByNames({
+            @CsvCustomBindByName(converter = ConvertGermanToBoolean.class, profiles = {"profile 1", "profile 2"}),
+            @CsvCustomBindByName(converter = ConvertFrenchToBoolean.class, profiles = {"", "profile 3"})
+    })
+    private boolean bool1;
+
+    @CsvBindAndSplitByName(elementType = Float.class)
+    @CsvNumber("#0.0#'%'")
+    @CsvBindAndSplitByNames({
+            @CsvBindAndSplitByName(elementType = Float.class, collectionType = LinkedList.class, profiles = "profile 1"),
+            @CsvBindAndSplitByName(elementType = Float.class, collectionType = Stack.class, profiles = "profile 2")
+    })
+    @CsvNumbers({
+            @CsvNumber(value = "#0.000#", profiles = "profile 1"),
+            @CsvNumber(value = "0.0#E0", profiles = "profile 2")
+    })
+    private List<Float> floats;
+
+    @CsvBindAndJoinByNames({
+            @CsvBindAndJoinByName(elementType = LocalDate.class),
+            @CsvBindAndJoinByName(
+                    elementType = LocalDate.class,
+                    profiles = {"profile 1", "profile 2", "profile 3"},
+                    column = "date.*")
+    })
+    @CsvDates({
+            @CsvDate("MM/dd/yyyy"),
+            @CsvDate(value = "dd. MMMM yyyy", profiles = {"profile 1", "profile 2", "profile 3"})
+    })
+    private MultiValuedMap<String, LocalDate> dates;
+
+    @CsvIgnore(profiles = "profile 2")
+    @CsvBindByName(column = "stringy string", profiles = "profile 2")
+    @CsvBindByName(profiles = "profile 3")
+    private String string1;
+
+    @CsvIgnore
+    @CsvBindByName(column = "stringy string", profiles = "profile 2")
+    @CsvBindByName(profiles = "profile 3")
+    private String string2;
+
+    public int getInt1() {
+        return int1;
+    }
+
+    public void setInt1(int int1) {
+        this.int1 = int1;
+    }
+
+    public boolean isBool1() {
+        return bool1;
+    }
+
+    public void setBool1(boolean bool1) {
+        this.bool1 = bool1;
+    }
+
+    public List<Float> getFloats() {
+        return floats;
+    }
+
+    public void setFloats(List<Float> floats) {
+        this.floats = floats;
+    }
+
+    public MultiValuedMap<String, LocalDate> getDates() {
+        return dates;
+    }
+
+    public void setDates(MultiValuedMap<String, LocalDate> dates) {
+        this.dates = dates;
+    }
+
+    public String getString1() {
+        return string1;
+    }
+
+    public void setString1(String string1) {
+        this.string1 = string1;
+    }
+
+    public String getString2() {
+        return string2;
+    }
+
+    public void setString2(String string2) {
+        this.string2 = string2;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameNoDefault.java b/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameNoDefault.java
new file mode 100644
index 0000000..c8dc285
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/profile/ProfileNameNoDefault.java
@@ -0,0 +1,58 @@
+package com.opencsv.bean.mocks.profile;
+
+import com.opencsv.bean.*;
+import com.opencsv.bean.customconverter.ConvertGermanToBoolean;
+import org.apache.commons.collections4.MultiValuedMap;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Stack;
+
+public class ProfileNameNoDefault {
+
+    @CsvBindByName(capture = "int ([0-9]+) value", format = "int %s value", profiles = "profile 2")
+    private int int1;
+
+    @CsvCustomBindByName(converter = ConvertGermanToBoolean.class, profiles = "profile 2")
+    private boolean bool1;
+
+    @CsvBindAndSplitByNames(@CsvBindAndSplitByName(elementType = Float.class, collectionType = Stack.class, profiles = "profile 2"))
+    @CsvNumber(value = "0.0#E0", profiles = "profile 2")
+    private List<Float> floats;
+
+    @CsvBindAndJoinByName(elementType = LocalDate.class, profiles = "profile 2", column = "date.*")
+    @CsvDate(value = "dd. MMMM yyyy", profiles = "profile 2")
+    private MultiValuedMap<String, LocalDate> dates;
+
+    public int getInt1() {
+        return int1;
+    }
+
+    public void setInt1(int int1) {
+        this.int1 = int1;
+    }
+
+    public boolean isBool1() {
+        return bool1;
+    }
+
+    public void setBool1(boolean bool1) {
+        this.bool1 = bool1;
+    }
+
+    public List<Float> getFloats() {
+        return floats;
+    }
+
+    public void setFloats(List<Float> floats) {
+        this.floats = floats;
+    }
+
+    public MultiValuedMap<String, LocalDate> getDates() {
+        return dates;
+    }
+
+    public void setDates(MultiValuedMap<String, LocalDate> dates) {
+        this.dates = dates;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionMock.java b/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionMock.java
new file mode 100644
index 0000000..2f711b2
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionMock.java
@@ -0,0 +1,108 @@
+package com.opencsv.bean.mocks.profile;
+
+import com.opencsv.bean.*;
+import com.opencsv.bean.customconverter.ConvertFrenchToBoolean;
+import com.opencsv.bean.customconverter.ConvertGermanToBoolean;
+import org.apache.commons.collections4.MultiValuedMap;
+
+import java.time.LocalDate;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+public class ProfilePositionMock {
+
+    @CsvBindByPosition(position = 0)
+    @CsvBindByPosition(position = 0, capture = "integer: ([0-9]+)", format = "integer: %s", profiles = {"profile 1"})
+    @CsvBindByPosition(position = 0, capture = "int ([0-9]+) value", format = "int %s value", profiles = {"profile 2"})
+    private int int1;
+
+    @CsvCustomBindByPositions({
+            @CsvCustomBindByPosition(position = 1, converter = ConvertGermanToBoolean.class, profiles = {"profile 1", "profile 2"}),
+            @CsvCustomBindByPosition(position = 1, converter = ConvertFrenchToBoolean.class, profiles = {"", "profile 3"})
+    })
+    private boolean bool1;
+
+    @CsvBindAndSplitByPosition(position = 2, elementType = Float.class)
+    @CsvNumber("#0.0#'%'")
+    @CsvBindAndSplitByPositions({
+            @CsvBindAndSplitByPosition(position = 2, elementType = Float.class, collectionType = LinkedList.class, profiles = "profile 1"),
+            @CsvBindAndSplitByPosition(position = 2, elementType = Float.class, collectionType = Stack.class, profiles = "profile 2")
+    })
+    @CsvNumbers({
+            @CsvNumber(value = "#0.000#", profiles = "profile 1"),
+            @CsvNumber(value = "0.0#E0", profiles = "profile 2")
+    })
+    private List<Float> floats;
+
+    @CsvBindAndJoinByPositions({
+            @CsvBindAndJoinByPosition(position = "3-4", elementType = LocalDate.class),
+            @CsvBindAndJoinByPosition(
+                    elementType = LocalDate.class,
+                    profiles = {"profile 1", "profile 2", "profile 3"},
+                    position = "3-4")
+    })
+    @CsvDates({
+            @CsvDate("MM/dd/yyyy"),
+            @CsvDate(value = "dd. MMMM yyyy", profiles = {"profile 1", "profile 2", "profile 3"})
+    })
+    private MultiValuedMap<Integer, LocalDate> dates;
+
+    @CsvIgnore(profiles = "profile 2")
+    @CsvBindByPosition(position = 5, profiles = "profile 2")
+    @CsvBindByPosition(position = 5, profiles = "profile 3")
+    private String string1;
+
+    @CsvIgnore
+    @CsvBindByPosition(position = 6, profiles = "profile 2")
+    @CsvBindByPosition(position = 6, profiles = "profile 3")
+    private String string2;
+
+    public int getInt1() {
+        return int1;
+    }
+
+    public void setInt1(int int1) {
+        this.int1 = int1;
+    }
+
+    public boolean isBool1() {
+        return bool1;
+    }
+
+    public void setBool1(boolean bool1) {
+        this.bool1 = bool1;
+    }
+
+    public List<Float> getFloats() {
+        return floats;
+    }
+
+    public void setFloats(List<Float> floats) {
+        this.floats = floats;
+    }
+
+    public MultiValuedMap<Integer, LocalDate> getDates() {
+        return dates;
+    }
+
+    public void setDates(MultiValuedMap<Integer, LocalDate> dates) {
+        this.dates = dates;
+    }
+
+    public String getString1() {
+        return string1;
+    }
+
+    public void setString1(String string1) {
+        this.string1 = string1;
+    }
+
+    public String getString2() {
+        return string2;
+    }
+
+    public void setString2(String string2) {
+        this.string2 = string2;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionNoDefault.java b/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionNoDefault.java
new file mode 100644
index 0000000..0a369d2
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/profile/ProfilePositionNoDefault.java
@@ -0,0 +1,58 @@
+package com.opencsv.bean.mocks.profile;
+
+import com.opencsv.bean.*;
+import com.opencsv.bean.customconverter.ConvertGermanToBoolean;
+import org.apache.commons.collections4.MultiValuedMap;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Stack;
+
+public class ProfilePositionNoDefault {
+
+    @CsvBindByPosition(position = 0, capture = "int ([0-9]+) value", format = "int %s value", profiles = "profile 2")
+    private int int1;
+
+    @CsvCustomBindByPosition(position = 1, converter = ConvertGermanToBoolean.class, profiles = "profile 2")
+    private boolean bool1;
+
+    @CsvBindAndSplitByPositions(@CsvBindAndSplitByPosition(position = 2, elementType = Float.class, collectionType = Stack.class, profiles = "profile 2"))
+    @CsvNumber(value = "0.0#E0", profiles = "profile 2")
+    private List<Float> floats;
+
+    @CsvBindAndJoinByPosition(position = "3-4", elementType = LocalDate.class, profiles = "profile 2")
+    @CsvDate(value = "dd. MMMM yyyy", profiles = "profile 2")
+    private MultiValuedMap<Integer, LocalDate> dates;
+
+    public int getInt1() {
+        return int1;
+    }
+
+    public void setInt1(int int1) {
+        this.int1 = int1;
+    }
+
+    public boolean isBool1() {
+        return bool1;
+    }
+
+    public void setBool1(boolean bool1) {
+        this.bool1 = bool1;
+    }
+
+    public List<Float> getFloats() {
+        return floats;
+    }
+
+    public void setFloats(List<Float> floats) {
+        this.floats = floats;
+    }
+
+    public MultiValuedMap<Integer, LocalDate> getDates() {
+        return dates;
+    }
+
+    public void setDates(MultiValuedMap<Integer, LocalDate> dates) {
+        this.dates = dates;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelOneNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelOneNoAnnotations.java
new file mode 100644
index 0000000..e4fded6
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelOneNoAnnotations.java
@@ -0,0 +1,27 @@
+package com.opencsv.bean.mocks.recurse;
+
+import com.opencsv.bean.CsvRecurse;
+
+public class RecursionMockLevelOneNoAnnotations {
+
+    private String stringLevelOne;
+
+    @CsvRecurse
+    private RecursionMockLevelTwoNoAnnotations levelTwo;
+
+    public String getStringLevelOne() {
+        return stringLevelOne;
+    }
+
+    public void setStringLevelOne(String stringLevelOne) {
+        this.stringLevelOne = stringLevelOne;
+    }
+
+    public RecursionMockLevelTwoNoAnnotations getLevelTwo() {
+        return levelTwo;
+    }
+
+    public void setLevelTwo(RecursionMockLevelTwoNoAnnotations levelTwo) {
+        this.levelTwo = levelTwo;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointOneNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointOneNoAnnotations.java
new file mode 100644
index 0000000..e951340
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointOneNoAnnotations.java
@@ -0,0 +1,13 @@
+package com.opencsv.bean.mocks.recurse;
+
+public class RecursionMockLevelThreePointOneNoAnnotations {
+    private boolean booleanLevelThree;
+
+    public boolean isBooleanLevelThree() {
+        return booleanLevelThree;
+    }
+
+    public void setBooleanLevelThree(boolean booleanLevelThree) {
+        this.booleanLevelThree = booleanLevelThree;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointTwoNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointTwoNoAnnotations.java
new file mode 100644
index 0000000..d8d89e4
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointTwoNoAnnotations.java
@@ -0,0 +1,13 @@
+package com.opencsv.bean.mocks.recurse;
+
+public class RecursionMockLevelThreePointTwoNoAnnotations {
+    private short shortLevelThree;
+
+    public short getShortLevelThree() {
+        return shortLevelThree;
+    }
+
+    public void setShortLevelThree(short shortLevelThree) {
+        this.shortLevelThree = shortLevelThree;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointZeroNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointZeroNoAnnotations.java
new file mode 100644
index 0000000..4256228
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelThreePointZeroNoAnnotations.java
@@ -0,0 +1,25 @@
+package com.opencsv.bean.mocks.recurse;
+
+public class RecursionMockLevelThreePointZeroNoAnnotations {
+
+    /**
+     * This constructor exists to ensure that opencsv cannot create this
+     * bean. This bean must be created and initialized by the encapsulating
+     * bean.
+     *
+     * @param f The floating point number to initialize the bean with
+     */
+    public RecursionMockLevelThreePointZeroNoAnnotations(float f) {
+        floatLevelThree = f;
+    }
+
+    private float floatLevelThree;
+
+    public float getFloatLevelThree() {
+        return floatLevelThree;
+    }
+
+    public void setFloatLevelThree(float floatLevelThree) {
+        this.floatLevelThree = floatLevelThree;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelTwoNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelTwoNoAnnotations.java
new file mode 100644
index 0000000..7ad6f07
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelTwoNoAnnotations.java
@@ -0,0 +1,60 @@
+package com.opencsv.bean.mocks.recurse;
+
+import com.opencsv.bean.CsvRecurse;
+
+import java.util.Random;
+
+public class RecursionMockLevelTwoNoAnnotations {
+
+    public RecursionMockLevelTwoNoAnnotations() {
+        Random r = new Random();
+        levelThreePointZero = new RecursionMockLevelThreePointZeroNoAnnotations(r.nextFloat());
+    }
+
+    private char charLevelTwo;
+
+    @CsvRecurse
+    private final RecursionMockLevelThreePointZeroNoAnnotations levelThreePointZero;
+
+    @CsvRecurse
+    private RecursionMockLevelThreePointOneNoAnnotations levelThreePointOne;
+
+    @CsvRecurse
+    private RecursionMockLevelThreePointTwoNoAnnotations levelThreePointTwo;
+
+    public char getCharLevelTwo() {
+        return charLevelTwo;
+    }
+
+    public void setCharLevelTwo(char charLevelTwo) {
+        this.charLevelTwo = charLevelTwo;
+    }
+
+    /**
+     * Accessor method for the third subordinate bean.
+     * Their is no assignment method and the accessor method is named so it
+     * will not be found by opencsv. This ensures reflection is used to get
+     * and set the value.
+     *
+     * @return The bean at the third level of subordination
+     */
+    public RecursionMockLevelThreePointZeroNoAnnotations procureTheThirdLevelPointZero() {
+        return levelThreePointZero;
+    }
+
+    public RecursionMockLevelThreePointOneNoAnnotations getLevelThreePointOne() {
+        return levelThreePointOne;
+    }
+
+    public void setLevelThreePointOne(RecursionMockLevelThreePointOneNoAnnotations levelThreePointOne) {
+        this.levelThreePointOne = levelThreePointOne;
+    }
+
+    public RecursionMockLevelThreePointTwoNoAnnotations getLevelThreePointTwo() {
+        return levelThreePointTwo;
+    }
+
+    public void setLevelThreePointTwo(RecursionMockLevelThreePointTwoNoAnnotations levelThreePointTwo) {
+        this.levelThreePointTwo = levelThreePointTwo;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelZeroNoAnnotations.java b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelZeroNoAnnotations.java
new file mode 100644
index 0000000..0e1dc7c
--- /dev/null
+++ b/src/test/java/com/opencsv/bean/mocks/recurse/RecursionMockLevelZeroNoAnnotations.java
@@ -0,0 +1,26 @@
+package com.opencsv.bean.mocks.recurse;
+
+import com.opencsv.bean.CsvRecurse;
+
+public class RecursionMockLevelZeroNoAnnotations {
+    private int intLevelZero;
+
+    @CsvRecurse
+    private RecursionMockLevelOneNoAnnotations levelOne;
+
+    public int getIntLevelZero() {
+        return intLevelZero;
+    }
+
+    public void setIntLevelZero(int intLevelZero) {
+        this.intLevelZero = intLevelZero;
+    }
+
+    public RecursionMockLevelOneNoAnnotations getLevelOne() {
+        return levelOne;
+    }
+
+    public void setLevelOne(RecursionMockLevelOneNoAnnotations levelOne) {
+        this.levelOne = levelOne;
+    }
+}
diff --git a/src/test/java/com/opencsv/bean/mocks/split/IntegerSetSortedToString.java b/src/test/java/com/opencsv/bean/mocks/split/IntegerSetSortedToString.java
index 41e17ef..2bb6d53 100644
--- a/src/test/java/com/opencsv/bean/mocks/split/IntegerSetSortedToString.java
+++ b/src/test/java/com/opencsv/bean/mocks/split/IntegerSetSortedToString.java
@@ -28,7 +28,7 @@ public class IntegerSetSortedToString extends HashSet<Integer> {
     @Override
     public String toString() {
         TextStringBuilder sb = new TextStringBuilder("[");
-        Integer[] intArray = this.toArray(new Integer[this.size()]);
+        Integer[] intArray = this.toArray(new Integer[0]);
         Arrays.sort(intArray);
         sb.appendWithSeparators(intArray, ",");
         sb.append(']');
diff --git a/src/test/java/com/opencsv/bean/mocks/split/SplitEnum.java b/src/test/java/com/opencsv/bean/mocks/split/SplitEnum.java
index e4bb96f..070fd56 100644
--- a/src/test/java/com/opencsv/bean/mocks/split/SplitEnum.java
+++ b/src/test/java/com/opencsv/bean/mocks/split/SplitEnum.java
@@ -1,5 +1,5 @@
 package com.opencsv.bean.mocks.split;
 
 public enum SplitEnum {
-    SPLIT1, Split2, split3;
+    SPLIT1, Split2, split3
 }
diff --git a/src/test/java/com/opencsv/bean/mocks/split/UnknownElementType.java b/src/test/java/com/opencsv/bean/mocks/split/UnknownElementType.java
index 814a7dd..4dc7a03 100644
--- a/src/test/java/com/opencsv/bean/mocks/split/UnknownElementType.java
+++ b/src/test/java/com/opencsv/bean/mocks/split/UnknownElementType.java
@@ -16,7 +16,7 @@
 package com.opencsv.bean.mocks.split;
 
 import com.opencsv.bean.CsvBindAndSplitByPosition;
-import java.util.Currency;
+import java.util.TimeZone;
 import java.util.List;
 
 /**
@@ -25,14 +25,14 @@ import java.util.List;
  */
 public class UnknownElementType {
     
-    @CsvBindAndSplitByPosition(elementType = Currency.class, position = 0)
-    private List<Currency> l;
+    @CsvBindAndSplitByPosition(elementType = TimeZone.class, position = 0)
+    private List<TimeZone> l;
 
-    public List<Currency> getL() {
+    public List<TimeZone> getL() {
         return l;
     }
 
-    public void setL(List<Currency> l) {
+    public void setL(List<TimeZone> l) {
         this.l = l;
     }
 }
diff --git a/src/test/java/com/opencsv/bean/processor/BeanFieldProcessorTest.java b/src/test/java/com/opencsv/bean/processor/BeanFieldProcessorTest.java
index 8a82965..a911350 100644
--- a/src/test/java/com/opencsv/bean/processor/BeanFieldProcessorTest.java
+++ b/src/test/java/com/opencsv/bean/processor/BeanFieldProcessorTest.java
@@ -30,7 +30,7 @@ public class BeanFieldProcessorTest {
     private CsvToBean<ProcessorTestBean> createBuilderForString(String csvStrings) {
         Reader stringReader = new StringReader(csvStrings);
 
-        CsvToBeanBuilder<ProcessorTestBean> builder = new CsvToBeanBuilder(stringReader);
+        CsvToBeanBuilder<ProcessorTestBean> builder = new CsvToBeanBuilder<>(stringReader);
 
         return builder.withType(ProcessorTestBean.class).build();
     }
@@ -43,7 +43,7 @@ public class BeanFieldProcessorTest {
         return builder.toString();
     }
 
-    private static Stream<Arguments> createArguements() {
+    private static Stream<Arguments> createArguments() {
         return Stream.of(
                 Arguments.of("1,able,300", 1, "able", 300),
                 Arguments.of(",,", DEFAULT_ID, null, DEFAULT_BIG_NUMBER),
@@ -53,7 +53,7 @@ public class BeanFieldProcessorTest {
 
     @DisplayName("Test out preassignment processor")
     @ParameterizedTest
-    @MethodSource("createArguements")
+    @MethodSource("createArguments")
     public void testProcessor(String line, int id, String name, long bigNumber) {
         String testString = createTestString(line);
         CsvToBean<ProcessorTestBean> csvToBean = createBuilderForString(testString);
diff --git a/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java b/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java
deleted file mode 100644
index 5ad4d6a..0000000
--- a/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToDefault.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.opencsv.bean.processor;
-
-public class ConvertEmptyOrBlankStringsToDefault implements StringProcessor {
-    String defaultValue;
-
-    @Override
-    public String processString(String value) {
-        if (value == null || value.trim().isEmpty()) {
-            return defaultValue;
-        }
-        return value;
-    }
-
-    @Override
-    public void setParameterString(String value) {
-        defaultValue = value;
-    }
-}
diff --git a/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java b/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java
deleted file mode 100644
index 015e6b0..0000000
--- a/src/test/java/com/opencsv/bean/processor/ConvertEmptyOrBlankStringsToNull.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.opencsv.bean.processor;
-
-public class ConvertEmptyOrBlankStringsToNull implements StringProcessor {
-    @Override
-    public String processString(String value) {
-        if (value == null || value.trim().isEmpty()) {
-            return null;
-        }
-        return value;
-    }
-
-    @Override
-    public void setParameterString(String value) {
-        // not needed
-    }
-}
diff --git a/src/test/java/com/opencsv/exceptions/CsvBadConverterExceptionTest.java b/src/test/java/com/opencsv/exceptions/CsvBadConverterExceptionTest.java
index bb8781c..ffdd875 100644
--- a/src/test/java/com/opencsv/exceptions/CsvBadConverterExceptionTest.java
+++ b/src/test/java/com/opencsv/exceptions/CsvBadConverterExceptionTest.java
@@ -21,14 +21,14 @@ import org.junit.jupiter.api.Test;
 
 import java.io.*;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvBadConverterExceptionTest {
     private static final String TEST_MESSAGE = "some test message";
     
     @Test
     public void codeCoverageConstructors() {
-        Class c = TestCase80.class;
+        Class<TestCase80> c = TestCase80.class;
 
         CsvBadConverterException e3 = new CsvBadConverterException();
         assertNull(e3.getConverterClass());
diff --git a/src/test/java/com/opencsv/exceptions/CsvBeanIntrospectionExceptionTest.java b/src/test/java/com/opencsv/exceptions/CsvBeanIntrospectionExceptionTest.java
index 494deb6..cbcd303 100644
--- a/src/test/java/com/opencsv/exceptions/CsvBeanIntrospectionExceptionTest.java
+++ b/src/test/java/com/opencsv/exceptions/CsvBeanIntrospectionExceptionTest.java
@@ -1,13 +1,12 @@
 package com.opencsv.exceptions;
 
-
 import com.opencsv.bean.mocks.MockBean;
 import org.junit.jupiter.api.Test;
 
 import java.io.*;
 import java.lang.reflect.Field;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvBeanIntrospectionExceptionTest {
     private static final String TEST_MESSAGE = "some test message";
diff --git a/src/test/java/com/opencsv/exceptions/CsvConstraintViolationExceptionTest.java b/src/test/java/com/opencsv/exceptions/CsvConstraintViolationExceptionTest.java
index e1d1b78..980d15d 100644
--- a/src/test/java/com/opencsv/exceptions/CsvConstraintViolationExceptionTest.java
+++ b/src/test/java/com/opencsv/exceptions/CsvConstraintViolationExceptionTest.java
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
 import java.io.*;
 import java.lang.reflect.Field;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvConstraintViolationExceptionTest {
     private static final String TEST_MESSAGE = "some test message";
diff --git a/src/test/java/com/opencsv/exceptions/CsvDataTypeMismatchExceptionTest.java b/src/test/java/com/opencsv/exceptions/CsvDataTypeMismatchExceptionTest.java
index 0a4b2ca..e1ddc93 100644
--- a/src/test/java/com/opencsv/exceptions/CsvDataTypeMismatchExceptionTest.java
+++ b/src/test/java/com/opencsv/exceptions/CsvDataTypeMismatchExceptionTest.java
@@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test;
 import java.io.*;
 import java.lang.reflect.Field;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvDataTypeMismatchExceptionTest {
     private static final String TEST_MESSAGE = "some test message";
diff --git a/src/test/java/com/opencsv/exceptions/CsvRequiredFieldEmptyExceptionTest.java b/src/test/java/com/opencsv/exceptions/CsvRequiredFieldEmptyExceptionTest.java
index fd005d8..0390b03 100644
--- a/src/test/java/com/opencsv/exceptions/CsvRequiredFieldEmptyExceptionTest.java
+++ b/src/test/java/com/opencsv/exceptions/CsvRequiredFieldEmptyExceptionTest.java
@@ -22,14 +22,14 @@ import org.junit.jupiter.api.Test;
 import java.io.*;
 import java.lang.reflect.Field;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class CsvRequiredFieldEmptyExceptionTest {
     private static final String TEST_MESSAGE = "some test message";
     
     @Test
     public void codeCoverageConstructors() throws NoSuchFieldException {
-        Class c = TestCase80.class;
+        Class<TestCase80> c = TestCase80.class;
         Field f = c.getField("test");
         CsvRequiredFieldEmptyException e1 = new CsvRequiredFieldEmptyException(c, f);
         assertEquals(TestCase80.class, e1.getBeanClass());
diff --git a/src/test/java/com/opencsv/stream/reader/LineReaderTest.java b/src/test/java/com/opencsv/stream/reader/LineReaderTest.java
index 28e7489..80e7a2c 100644
--- a/src/test/java/com/opencsv/stream/reader/LineReaderTest.java
+++ b/src/test/java/com/opencsv/stream/reader/LineReaderTest.java
@@ -6,7 +6,7 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class LineReaderTest {
     private static final String ORIGINAL = "This is the original string\r\n";
diff --git a/src/test/java/com/opencsv/util/MockDataBuilder.java b/src/test/java/com/opencsv/util/MockDataBuilder.java
new file mode 100644
index 0000000..ff06c56
--- /dev/null
+++ b/src/test/java/com/opencsv/util/MockDataBuilder.java
@@ -0,0 +1,62 @@
+package com.opencsv.util;
+
+import com.opencsv.ICSVParser;
+import com.opencsv.RFC4180Parser;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The purpose of this class is to make it easier to build test data strings for tests.
+ *
+ * @author scott
+ */
+public class MockDataBuilder {
+
+    private String headerString;
+    private final List<String> dataRows = new ArrayList<>();
+    RFC4180Parser parser = new RFC4180Parser();
+
+    public void setHeaderString(String headerString) {
+        this.headerString = headerString;
+    }
+
+    public void addDataRow(String row) {
+        dataRows.add(row);
+    }
+
+    public void addColumns(String... values) {
+        dataRows.add(parser.parseToLine(values, true));
+    }
+
+    public String buildDataString() {
+        StringBuilder builder = new StringBuilder(ICSVParser.INITIAL_READ_SIZE);
+        if (headerString != null && !headerString.trim().isEmpty()) {
+            builder.append(headerString);
+            builder.append(ICSVParser.NEWLINE);
+        }
+        for (String row : dataRows) {
+            builder.append(row);
+            builder.append(ICSVParser.NEWLINE);
+        }
+        return builder.toString();
+    }
+
+    /**
+     * @return a StringReader that contains the data input from the MockDataBuilder.
+     * @see StringReader
+     */
+    public StringReader buildStringReader() {
+        return new StringReader(buildDataString());
+    }
+
+    /**
+     * @return a BufferedReader that contains the data input from the MockDataBuilder.
+     * @see BufferedReader
+     */
+    public BufferedReader buildBufferedReader() {
+        return new BufferedReader(buildStringReader());
+    }
+}
diff --git a/src/test/java/com/opencsv/validators/LineValidatorAggregatorTest.java b/src/test/java/com/opencsv/validators/LineValidatorAggregatorTest.java
index 7274a0c..501fc67 100644
--- a/src/test/java/com/opencsv/validators/LineValidatorAggregatorTest.java
+++ b/src/test/java/com/opencsv/validators/LineValidatorAggregatorTest.java
@@ -53,7 +53,7 @@ public class LineValidatorAggregatorTest {
         );
 
 
-        Assertions.assertTrue(aggregator.isValid(line) == "valid".equals(valid));
+        assertEquals(aggregator.isValid(line), "valid".equals(valid));
 
         String testLine = preprocessLine(line);
 
@@ -68,8 +68,8 @@ public class LineValidatorAggregatorTest {
             }
             String exceptionMessage = ex.getMessage();
             Assertions.assertAll("Exception message is incorrect",
-                    () -> assertTrue("will".equals(willHaveAwful) == exceptionMessage.contains(lineDoesNotHaveAwfulString.getMessage()), "Supposed to have Awful message."),
-                    () -> assertTrue("will".equals(willHaveBadString) == exceptionMessage.contains(lineDoesNotHaveBadString.getMessage()), "Supposed to have Bad message."));
+                    () -> assertEquals("will".equals(willHaveAwful), exceptionMessage.contains(lineDoesNotHaveAwfulString.getMessage()), "Supposed to have Awful message."),
+                    () -> assertEquals("will".equals(willHaveBadString), exceptionMessage.contains(lineDoesNotHaveBadString.getMessage()), "Supposed to have Bad message."));
         }
     }
 
diff --git a/src/test/java/com/opencsv/validators/RowFunctionValidatorTest.java b/src/test/java/com/opencsv/validators/RowFunctionValidatorTest.java
index 5f486b9..585069f 100644
--- a/src/test/java/com/opencsv/validators/RowFunctionValidatorTest.java
+++ b/src/test/java/com/opencsv/validators/RowFunctionValidatorTest.java
@@ -12,37 +12,31 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class RowFunctionValidatorTest {
     private static final String[] GOOD_ROW = {"8675309", "Firstname", "M", "Lastname", "Dec 06, 1951"};
-    private static final String[] BAD_ROW = {"not a number", "not capitialized", "not an initial", "Not Single word", "12/06/51"};
+    private static final String[] BAD_ROW = {"not a number", "not capitalized", "not an initial", "Not Single word", "12/06/51"};
     private static final String[] LONG_ROW = {"8675309", "Firstname", "M", "Lastname", "Dec 06, 1951", "More data"};
     private static final String[] SHORT_ROW = {"8675309", "Firstname", "Lastname", "Dec 06, 1951"};
     private static final String[] EMPTY_ROW = {};
     private static final String FAILURE_MESSAGE = "The first element of the row must be a number!";
 
     private RowValidator validator;
-    private static final Function<String[], Boolean> FIRST_ELEMENT_IS_A_NUMBER = (x) -> {
-        return x[0].matches("^[0-9]+$");
-    };
+    private static final Function<String[], Boolean> FIRST_ELEMENT_IS_A_NUMBER =
+            (x) -> x[0].matches("^[0-9]+$");
     private static final RowValidator FIRST_ELEMENT_VALIDATOR = new RowFunctionValidator(FIRST_ELEMENT_IS_A_NUMBER, FAILURE_MESSAGE);
 
-    private static final Function<String[], Boolean> SECOND_ELEMENT_IS_SIMPLE_NAME = (x) -> {
-        return x[1].matches("^[A-Z]+([a-z]*)*$");
-    };
+    private static final Function<String[], Boolean> SECOND_ELEMENT_IS_SIMPLE_NAME =
+            (x) -> x[1].matches("^[A-Z]+([a-z]*)*$");
 
-    private static final Function<String[], Boolean> THIRD_ELEMENT_IS_MIDDLE_INITIAL = (x) -> {
-        return x[2].matches("^[A-Z]$");
-    };
+    private static final Function<String[], Boolean> THIRD_ELEMENT_IS_MIDDLE_INITIAL =
+            (x) -> x[2].matches("^[A-Z]$");
 
-    private static final Function<String[], Boolean> FOURTH_ELEMENT_IS_SIMPLE_NAME = (x) -> {
-        return x[3].matches("^[A-Z]+([a-z]*)*$");
-    };
+    private static final Function<String[], Boolean> FOURTH_ELEMENT_IS_SIMPLE_NAME =
+            (x) -> x[3].matches("^[A-Z]+([a-z]*)*$");
 
-    private static final Function<String[], Boolean> FIFTH_ELEMENT_IS_SPECIFIC_DATE_FOMRAT = (x) -> {
-        return x[4].matches("^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+(0?[1-9]|[1-2][0-9]|3[01]),\\s*(19[0-9]{2}|[2-9][0-9]{3}|[0-9]{2})$");
-    };
+    private static final Function<String[], Boolean> FIFTH_ELEMENT_IS_SPECIFIC_DATE_FORMAT =
+            (x) -> x[4].matches("^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+(0?[1-9]|[1-2][0-9]|3[01]),\\s*(19[0-9]{2}|[2-9][0-9]{3}|[0-9]{2})$");
 
-    private static final Function<String[], Boolean> ROW_MUST_HAME_FIVE_ELEMENTS = (x) -> {
-        return (x.length == 5);
-    };
+    private static final Function<String[], Boolean> ROW_MUST_HAVE_FIVE_ELEMENTS =
+            (x) -> (x.length == 5);
 
     @Test
     @DisplayName("isValid with a function to ensure that the first element is a number.")
@@ -102,7 +96,7 @@ public class RowFunctionValidatorTest {
     @Test
     @DisplayName("Fifth element is a very specific date format")
     public void fifthElementIsADate() {
-        validator = new RowFunctionValidator(FIFTH_ELEMENT_IS_SPECIFIC_DATE_FOMRAT, "The fifth element must be a date with the MMM dd, yyyy format.");
+        validator = new RowFunctionValidator(FIFTH_ELEMENT_IS_SPECIFIC_DATE_FORMAT, "The fifth element must be a date with the MMM dd, yyyy format.");
 
         assertTrue(validator.isValid(GOOD_ROW));
         assertFalse(validator.isValid(BAD_ROW));
@@ -111,7 +105,7 @@ public class RowFunctionValidatorTest {
     @Test
     @DisplayName("The row must have a specific number of elements in order to be valid.")
     public void numberOfElementsInARow() {
-        validator = new RowFunctionValidator(ROW_MUST_HAME_FIVE_ELEMENTS, "A Row can have only five elements.");
+        validator = new RowFunctionValidator(ROW_MUST_HAVE_FIVE_ELEMENTS, "A Row can have only five elements.");
 
         assertTrue(validator.isValid(GOOD_ROW));
         assertFalse(validator.isValid(LONG_ROW));
diff --git a/src/test/java/com/opencsv/validators/RowMustHaveSameNumberOfColumnsAsFirstRowValidatorTest.java b/src/test/java/com/opencsv/validators/RowMustHaveSameNumberOfColumnsAsFirstRowValidatorTest.java
index 2bb1750..3c38a3d 100644
--- a/src/test/java/com/opencsv/validators/RowMustHaveSameNumberOfColumnsAsFirstRowValidatorTest.java
+++ b/src/test/java/com/opencsv/validators/RowMustHaveSameNumberOfColumnsAsFirstRowValidatorTest.java
@@ -36,7 +36,7 @@ public class RowMustHaveSameNumberOfColumnsAsFirstRowValidatorTest {
 
     @Test
     @DisplayName("RowValidator validate with three rows first")
-    public void validateThreeRowsFirst() throws CsvValidationException {
+    public void validateThreeRowsFirst() {
         Assertions.assertDoesNotThrow(() -> validator.validate(THREE_COLUMNS));
         Assertions.assertDoesNotThrow(() -> validator.validate(THREE_COLUMNS));
         Assertions.assertThrows(CsvValidationException.class, () -> validator.validate(FOUR_COLUMNS));
diff --git a/src/test/java/com/opencsv/validators/RowValidatorAggregatorTest.java b/src/test/java/com/opencsv/validators/RowValidatorAggregatorTest.java
index 9afc88f..64710a2 100644
--- a/src/test/java/com/opencsv/validators/RowValidatorAggregatorTest.java
+++ b/src/test/java/com/opencsv/validators/RowValidatorAggregatorTest.java
@@ -31,16 +31,14 @@ public class RowValidatorAggregatorTest {
 
     private static final String FIRST_COLUMN_FAILURE_MESSAGE = "The first element of the row must be a seven digit number!";
 
-    private static final Function<String[], Boolean> FIRST_ELEMENT_IS_A_NUMBER = (x) -> {
-        return x.length > 0 && x[0].matches("^[0-9]{7}$");
-    };
+    private static final Function<String[], Boolean> FIRST_ELEMENT_IS_A_NUMBER =
+            (x) -> x.length > 0 && x[0].matches("^[0-9]{7}$");
     private static final RowValidator FIRST_ELEMENT_VALIDATOR = new RowFunctionValidator(FIRST_ELEMENT_IS_A_NUMBER, FIRST_COLUMN_FAILURE_MESSAGE);
 
     private static final String SECOND_COLUMN_FAILURE_MESSAGE = "The second element of the row must be an email address!";
 
-    private static final Function<String[], Boolean> SECOND_ELEMENT_IS_AN_EMAIL_ADDRESS = (x) -> {
-        return x.length > 1 && x[1].matches("^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,5})$");
-    };
+    private static final Function<String[], Boolean> SECOND_ELEMENT_IS_AN_EMAIL_ADDRESS =
+            (x) -> x.length > 1 && x[1].matches("^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,5})$");
     private static final RowValidator SECOND_ELEMENT_VALIDATOR = new RowFunctionValidator(SECOND_ELEMENT_IS_AN_EMAIL_ADDRESS, SECOND_COLUMN_FAILURE_MESSAGE);
 
 
@@ -56,7 +54,7 @@ public class RowValidatorAggregatorTest {
     // LONG_ROW has true for the column count validator result
     // because for each arguement it IS the first row.  The SHORT_ROW fails
     // because there is not a second row to validate which is part of the validation regex.
-    private static Stream<Arguments> createIsValidArguements() {
+    private static Stream<Arguments> createIsValidArguments() {
         return Stream.of(
                 Arguments.of(GOOD_ROW, true),
                 Arguments.of(null, false),
@@ -71,7 +69,7 @@ public class RowValidatorAggregatorTest {
 
     @DisplayName("RowValidatorAggregator isValid")
     @ParameterizedTest
-    @MethodSource("createIsValidArguements")
+    @MethodSource("createIsValidArguments")
     public void lineIsValid(String[] row, boolean valid) {
         aggregator.addValidator(null);
         aggregator.addValidator(columnCountValidator);
@@ -86,7 +84,7 @@ public class RowValidatorAggregatorTest {
     // recreated on each run each run has a new validator.  That is why the
     // LONG_ROW and SHORT_ROW have true for the column count validator result
     // because for each arguement it IS the first row.
-    private static Stream<Arguments> createValidateArguements() {
+    private static Stream<Arguments> createValidateArguments() {
         return Stream.of(
                 Arguments.of(GOOD_ROW, true, true, true),
                 Arguments.of(null, false, false, false),
@@ -101,7 +99,7 @@ public class RowValidatorAggregatorTest {
 
     @DisplayName("RowValidatorAggregator validate")
     @ParameterizedTest
-    @MethodSource("createValidateArguements")
+    @MethodSource("createValidateArguments")
     public void lineValidate(String[] row, boolean columnCountValid, boolean firstElementValid, boolean secondElementValid) {
         aggregator.addValidator(null);
         aggregator.addValidator(columnCountValidator);
@@ -123,9 +121,9 @@ public class RowValidatorAggregatorTest {
             String exceptionMessage = cve.getMessage();
 
             Assertions.assertAll("Exception message is incorrect",
-                    () -> assertTrue(!columnCountValid == exceptionMessageContainsColumnCountMessage(exceptionMessage), "Supposed to have column count message."),
-                    () -> assertTrue(!firstElementValid == exceptionMessage.contains(FIRST_COLUMN_FAILURE_MESSAGE), "Exception message did not mention first column error."),
-                    () -> assertTrue(!secondElementValid == exceptionMessage.contains(SECOND_COLUMN_FAILURE_MESSAGE), "Exception message did not mention second column error."));
+                    () -> assertEquals(!columnCountValid, exceptionMessageContainsColumnCountMessage(exceptionMessage), "Supposed to have column count message."),
+                    () -> assertEquals(!firstElementValid, exceptionMessage.contains(FIRST_COLUMN_FAILURE_MESSAGE), "Exception message did not mention first column error."),
+                    () -> assertEquals(!secondElementValid, exceptionMessage.contains(SECOND_COLUMN_FAILURE_MESSAGE), "Exception message did not mention second column error."));
         }
     }
 
diff --git a/src/test/java/integrationTest/BeanTests/CsvToBeanShouldUseNullFieldIndicatorForStrings.java b/src/test/java/integrationTest/BeanTests/CsvToBeanShouldUseNullFieldIndicatorForStrings.java
index edff467..acbe748 100644
--- a/src/test/java/integrationTest/BeanTests/CsvToBeanShouldUseNullFieldIndicatorForStrings.java
+++ b/src/test/java/integrationTest/BeanTests/CsvToBeanShouldUseNullFieldIndicatorForStrings.java
@@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test;
 import java.io.StringReader;
 import java.util.List;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * This is for Bug #158 in sourceforge.  When creating beans from openCSV the Parser null field indicator
diff --git a/src/test/java/integrationTest/FR138/FR138MockBean.java b/src/test/java/integrationTest/FR138/FR138MockBean.java
new file mode 100644
index 0000000..05743e9
--- /dev/null
+++ b/src/test/java/integrationTest/FR138/FR138MockBean.java
@@ -0,0 +1,104 @@
+package integrationTest.FR138;
+
+import com.opencsv.bean.processor.ConvertWordNullToNull;
+import com.opencsv.bean.processor.PreAssignmentProcessor;
+
+public class FR138MockBean {
+
+    private String name;
+    private String id;
+    private String orderNumber;
+    @PreAssignmentProcessor(processor = ConvertWordNullToNull.class)
+    private int num;
+    private double doubleNum;
+
+    public FR138MockBean() {}
+
+    public FR138MockBean(String name, String id, String orderNumber, int num, double doubleNum) {
+        this.name = name;
+        this.id = id;
+        this.orderNumber = orderNumber;
+        this.num = num;
+        this.doubleNum = doubleNum;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getOrderNumber() {
+        return orderNumber;
+    }
+
+    public void setOrderNumber(String orderNumber) {
+        this.orderNumber = orderNumber;
+    }
+
+    public int getNum() {
+        return num;
+    }
+
+    public void setNum(int num) {
+        this.num = num;
+    }
+
+    public double getDoubleNum() {
+        return doubleNum;
+    }
+
+    public void setDoubleNum(double doubleNum) {
+        this.doubleNum = doubleNum;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FR138MockBean)) return false;
+
+        FR138MockBean mockBean = (FR138MockBean) o;
+
+        if (getNum() != mockBean.getNum()) return false;
+        if (Double.compare(mockBean.getDoubleNum(), getDoubleNum()) != 0) return false;
+        if (getName() != null ? !getName().equals(mockBean.getName()) : mockBean.getName() != null) return false;
+        if (getId() != null ? !getId().equals(mockBean.getId()) : mockBean.getId() != null) return false;
+        return !(getOrderNumber() != null ? !getOrderNumber().equals(mockBean.getOrderNumber()) : mockBean.getOrderNumber() != null);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        long temp;
+        result = getName() != null ? getName().hashCode() : 0;
+        result = 31 * result + (getId() != null ? getId().hashCode() : 0);
+        result = 31 * result + (getOrderNumber() != null ? getOrderNumber().hashCode() : 0);
+        result = 31 * result + getNum();
+        temp = Double.doubleToLongBits(getDoubleNum());
+        result = 31 * result + (int) (temp ^ (temp >>> 32));
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "FR138MockBean{" +
+                "name='" + name + '\'' +
+                ", id='" + id + '\'' +
+                ", orderNumber='" + orderNumber + '\'' +
+                ", num=" + num +
+                ", doubleNum=" + doubleNum +
+                '}';
+    }
+
+}
diff --git a/src/test/java/integrationTest/FR138/FR138Test.java b/src/test/java/integrationTest/FR138/FR138Test.java
new file mode 100644
index 0000000..930a6dc
--- /dev/null
+++ b/src/test/java/integrationTest/FR138/FR138Test.java
@@ -0,0 +1,32 @@
+package integrationTest.FR138;
+
+import com.opencsv.bean.CsvToBeanBuilder;
+import com.opencsv.bean.HeaderColumnNameMappingStrategy;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.StringReader;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FR138Test {
+    @DisplayName("Parse a Bean that has the word null for a primitive value.")
+    @Test
+    public void parseBeanThatHasNullForInt() {
+        String testString = "name,num,orderNumber\n" +
+                "kyle,123,abc123456\n" +
+                "jimmy,null,def098765";
+
+        HeaderColumnNameMappingStrategy<FR138MockBean> strategy = new HeaderColumnNameMappingStrategy<>();
+        strategy.setType(FR138MockBean.class);
+        List<FR138MockBean> beanList = new CsvToBeanBuilder<FR138MockBean>(new StringReader(testString))
+                .withMappingStrategy(strategy)
+                .build().parse(); // Extra arguments for code coverage
+
+        assertEquals(2, beanList.size());
+        assertTrue(beanList.contains(new FR138MockBean("kyle", null, "abc123456", 123, 0.0)));
+        assertTrue(beanList.contains(new FR138MockBean("jimmy", null, "def098765", 0, 0.0)));
+    }
+}
diff --git a/src/test/java/integrationTest/FR143/UUIDTest.java b/src/test/java/integrationTest/FR143/UUIDTest.java
new file mode 100644
index 0000000..78442d7
--- /dev/null
+++ b/src/test/java/integrationTest/FR143/UUIDTest.java
@@ -0,0 +1,19 @@
+package integrationTest.FR143;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class UUIDTest {
+    public static String TEST_ID_STRING = " 7F804F85-0064-4E96-8260-3FD47EA6A8BB ";
+
+    @DisplayName("Convert String to UUID and back to String")
+    @Test
+    public void convert() {
+        UUID uuid = UUID.fromString(TEST_ID_STRING.trim());
+        assertTrue(TEST_ID_STRING.trim().equalsIgnoreCase(uuid.toString()));
+    }
+}
diff --git a/src/test/java/integrationTest/isClosed/isClosedTest.java b/src/test/java/integrationTest/isClosed/isClosedTest.java
index 03ddc20..c8f6ae0 100644
--- a/src/test/java/integrationTest/isClosed/isClosedTest.java
+++ b/src/test/java/integrationTest/isClosed/isClosedTest.java
@@ -11,7 +11,7 @@ import java.io.Reader;
 import java.io.StringReader;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * I created this class to test out all the issues that is being seen with the isClosed
diff --git a/src/test/java/integrationTest/issue3402853/HeaderColumnNameMappingStrategyUserTest.java b/src/test/java/integrationTest/issue3402853/HeaderColumnNameMappingStrategyUserTest.java
index a2e202d..e0c849b 100644
--- a/src/test/java/integrationTest/issue3402853/HeaderColumnNameMappingStrategyUserTest.java
+++ b/src/test/java/integrationTest/issue3402853/HeaderColumnNameMappingStrategyUserTest.java
@@ -26,8 +26,8 @@ import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 public class HeaderColumnNameMappingStrategyUserTest {
 
diff --git a/src/test/java/integrationTest/issue3402853/MockUserBean.java b/src/test/java/integrationTest/issue3402853/MockUserBean.java
index 6165b82..708d939 100644
--- a/src/test/java/integrationTest/issue3402853/MockUserBean.java
+++ b/src/test/java/integrationTest/issue3402853/MockUserBean.java
@@ -3,9 +3,8 @@ package integrationTest.issue3402853;
 import org.apache.commons.lang3.StringUtils;
 
 public class MockUserBean {
-    public static final String DEFAULT_BLANK_FIELD = "";
-    private String first_name = DEFAULT_BLANK_FIELD;
-    private String last_name = DEFAULT_BLANK_FIELD;
+    private String first_name = StringUtils.EMPTY;
+    private String last_name = StringUtils.EMPTY;
     private String profile_id = "";
     private String email = "";
     private String secondary_email = "";
@@ -58,7 +57,7 @@ public class MockUserBean {
     }
 
     public boolean equals(Object obj) {
-        if ((null == obj) || !(obj instanceof MockUserBean)) {
+        if (!(obj instanceof MockUserBean)) {
             return false;
         }
 
@@ -78,7 +77,7 @@ public class MockUserBean {
     }
 
     private boolean isNameEmpty(String name) {
-        return name == null || name.length() == 0 || DEFAULT_BLANK_FIELD.equals(name);
+        return name == null || name.length() == 0 || StringUtils.EMPTY.equals(name);
 
     }
 
diff --git a/src/test/java/integrationTest/writeThenRead/WriteThenReadTest.java b/src/test/java/integrationTest/writeThenRead/WriteThenReadTest.java
new file mode 100644
index 0000000..683b8b6
--- /dev/null
+++ b/src/test/java/integrationTest/writeThenRead/WriteThenReadTest.java
@@ -0,0 +1,70 @@
+package integrationTest.writeThenRead;
+
+import com.opencsv.CSVReader;
+import com.opencsv.CSVReaderBuilder;
+import com.opencsv.CSVWriterBuilder;
+import com.opencsv.ICSVWriter;
+import com.opencsv.exceptions.CsvException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The purpose of these tests is to write an array of data using the CSVWriter then read
+ * it with the CSVReader to ensure the result is the original data.
+ */
+public class WriteThenReadTest {
+
+    private static Stream<Arguments> singleArrayItem() {
+        return Stream.of(
+                Arguments.of("1", "\\Escape at beginning."),
+                Arguments.of("2", "Escape in\\ middle."),
+                Arguments.of("3", "Escape at end.\\")
+        );
+    }
+
+    @DisplayName("To have the CSVWriter and CSVReader work together they must have the same escape character.")
+    @ParameterizedTest
+    @MethodSource("singleArrayItem")
+    public void defaultWriterAndReader(String id, String description) throws IOException, CsvException {
+        String[] originalArray = {id, description};
+
+        Writer writer = new StringWriter();
+        CSVWriterBuilder writerBuilder = new CSVWriterBuilder(writer);
+        ICSVWriter icsvWriter = writerBuilder.withEscapeChar('\\').build();
+
+        List<String[]> rows = new ArrayList<>();
+        rows.add(originalArray);
+
+        System.out.println(listToString(rows));
+        icsvWriter.writeAll(rows);
+        System.out.println(writer);
+
+        Reader reader = new StringReader(writer.toString());
+        CSVReaderBuilder readerBuilder = new CSVReaderBuilder(reader);
+        CSVReader csvReader = readerBuilder.build();
+
+        List<String[]> readRows = csvReader.readAll();
+        String originalString = listToString(rows);
+        String readString = listToString(readRows);
+        assertEquals(originalString, readString, String.format("Expected: %s     | Actual: %s    |", originalString, readString));
+    }
+
+    private String listToString(List<String[]> rows) {
+        StringBuilder builder = new StringBuilder(1024);
+        rows.forEach(a -> {
+            builder.append(Arrays.toString(a));
+            builder.append("\n");
+        });
+        return builder.toString();
+    }
+}
diff --git a/src/test/resources/testAnonymousHeaders.csv b/src/test/resources/testAnonymousHeaders.csv
new file mode 100644
index 0000000..439df1f
--- /dev/null
+++ b/src/test/resources/testAnonymousHeaders.csv
@@ -0,0 +1,3 @@
+string1;;bool1;;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1;currency1
+test string;;value: true;;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;tESt1;EUR
++test string;;;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;
diff --git a/src/test/resources/testIllegalCurrency.csv b/src/test/resources/testIllegalCurrency.csv
new file mode 100644
index 0000000..86ccb17
--- /dev/null
+++ b/src/test/resources/testIllegalCurrency.csv
@@ -0,0 +1 @@
+test string;Truth value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;Test1;illegalCurrency
diff --git a/src/test/resources/testIllegalEnumValue.csv b/src/test/resources/testIllegalEnumValue.csv
index 27960c9..3ca43f4 100644
--- a/src/test/resources/testIllegalEnumValue.csv
+++ b/src/test/resources/testIllegalEnumValue.csv
@@ -1 +1 @@
-test string;Truth value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;bogusEnumValue
+test string;Truth value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;bogusEnumValue;EUR
diff --git a/src/test/resources/testMultipleExceptionsPerLine.csv b/src/test/resources/testMultipleExceptionsPerLine.csv
new file mode 100644
index 0000000..b99eb8e
--- /dev/null
+++ b/src/test/resources/testMultipleExceptionsPerLine.csv
@@ -0,0 +1,2 @@
+test string;Truth vlue: true;bogus;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Jaener 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR
+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115XXX063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01321978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;bogus
diff --git a/src/test/resources/testNameProfile1.csv b/src/test/resources/testNameProfile1.csv
new file mode 100644
index 0000000..0d60870
--- /dev/null
+++ b/src/test/resources/testNameProfile1.csv
@@ -0,0 +1,2 @@
+int1,bool1,floats,date1,date2
+integer: 12,wahr,1.234 2.345,15. January 1978,27. February 1974
diff --git a/src/test/resources/testNameProfile2.csv b/src/test/resources/testNameProfile2.csv
new file mode 100644
index 0000000..1692aa4
--- /dev/null
+++ b/src/test/resources/testNameProfile2.csv
@@ -0,0 +1,2 @@
+int1,bool1,floats,date1,date2
+int 12 value,wahr,1.234E1 2.345E1,15. January 1978,27. February 1974
diff --git a/src/test/resources/testNameProfile3.csv b/src/test/resources/testNameProfile3.csv
new file mode 100644
index 0000000..d4d6814
--- /dev/null
+++ b/src/test/resources/testNameProfile3.csv
@@ -0,0 +1,2 @@
+int1,bool1,floats,date1,date2,string1
+12,oui,1.2% 2.3%,15. January 1978,27. February 1974,test string
diff --git a/src/test/resources/testNameProfile4.csv b/src/test/resources/testNameProfile4.csv
new file mode 100644
index 0000000..1385ebf
--- /dev/null
+++ b/src/test/resources/testNameProfile4.csv
@@ -0,0 +1,2 @@
+int1,bool1,floats,dates,dates
+12,oui,1.2% 2.3%,01/15/1978,02/27/1974
diff --git a/src/test/resources/testNameProfileDefault.csv b/src/test/resources/testNameProfileDefault.csv
new file mode 100644
index 0000000..1385ebf
--- /dev/null
+++ b/src/test/resources/testNameProfileDefault.csv
@@ -0,0 +1,2 @@
+int1,bool1,floats,dates,dates
+12,oui,1.2% 2.3%,01/15/1978,02/27/1974
diff --git a/src/test/resources/testPositionProfile1.csv b/src/test/resources/testPositionProfile1.csv
new file mode 100644
index 0000000..06afd7c
--- /dev/null
+++ b/src/test/resources/testPositionProfile1.csv
@@ -0,0 +1 @@
+integer: 12,wahr,1.234 2.345,15. January 1978,27. February 1974
diff --git a/src/test/resources/testPositionProfile2.csv b/src/test/resources/testPositionProfile2.csv
new file mode 100644
index 0000000..31b1f63
--- /dev/null
+++ b/src/test/resources/testPositionProfile2.csv
@@ -0,0 +1 @@
+int 12 value,wahr,1.234E1 2.345E1,15. January 1978,27. February 1974
diff --git a/src/test/resources/testPositionProfile3.csv b/src/test/resources/testPositionProfile3.csv
new file mode 100644
index 0000000..79db886
--- /dev/null
+++ b/src/test/resources/testPositionProfile3.csv
@@ -0,0 +1 @@
+12,oui,1.2% 2.3%,15. January 1978,27. February 1974,test string
diff --git a/src/test/resources/testPositionProfile4.csv b/src/test/resources/testPositionProfile4.csv
new file mode 100644
index 0000000..b1aad47
--- /dev/null
+++ b/src/test/resources/testPositionProfile4.csv
@@ -0,0 +1 @@
+12,oui,1.2% 2.3%,01/15/1978,02/27/1974
diff --git a/src/test/resources/testPositionProfileDefault.csv b/src/test/resources/testPositionProfileDefault.csv
new file mode 100644
index 0000000..b1aad47
--- /dev/null
+++ b/src/test/resources/testPositionProfileDefault.csv
@@ -0,0 +1 @@
+12,oui,1.2% 2.3%,01/15/1978,02/27/1974
diff --git a/src/test/resources/testinputderivedgood.csv b/src/test/resources/testinputderivedgood.csv
index 263b55c..b52b4e1 100644
--- a/src/test/resources/testinputderivedgood.csv
+++ b/src/test/resources/testinputderivedgood.csv
@@ -1,3 +1,3 @@
-string1;bool1;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1;int in subclass
-test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;7
-+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;8
+string1;bool1;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1;currency1;int in subclass
+test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR;7
++test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;;8
diff --git a/src/test/resources/testinputfullgood.csv b/src/test/resources/testinputfullgood.csv
index 46a2d72..d310e0b 100644
--- a/src/test/resources/testinputfullgood.csv
+++ b/src/test/resources/testinputfullgood.csv
@@ -1,3 +1,3 @@
-string1;bool1;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1
-test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;tESt1
-+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;
+string1;bool1;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1;currency1
+test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;tESt1;EUR
++test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;
diff --git a/src/test/resources/testinputfullwrongcolumnnumber.csv b/src/test/resources/testinputfullwrongcolumnnumber.csv
new file mode 100644
index 0000000..44a28aa
--- /dev/null
+++ b/src/test/resources/testinputfullwrongcolumnnumber.csv
@@ -0,0 +1,5 @@
+string1;bool1;boolPrimitive;byte1;byte2;byte3;byte4;double1;double2;double3;double4;float1;float2;float3;float4;integer1;integer2;integer3;integer4;long1;long2;long3;long4;short1;short2;short3;short4;char1;char2;bigdecimal1;bigdecimal2;biginteger1;biginteger2;date1;date2;date3;date4;date5;date6;date7;date8;date9;date10;date11;date12;date13;date14;date15;date16;float5;enum1;currency1
+test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;tESt1;EUR;;
++test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02
+test string;value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;tESt1;EUR;;
++test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02
diff --git a/src/test/resources/testinputposderivedgood.csv b/src/test/resources/testinputposderivedgood.csv
index acd9031..731cc43 100644
--- a/src/test/resources/testinputposderivedgood.csv
+++ b/src/test/resources/testinputposderivedgood.csv
@@ -1,2 +1,2 @@
-test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;7
-+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;8
+test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR;7
++test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;;8
diff --git a/src/test/resources/testinputposfullgood.csv b/src/test/resources/testinputposfullgood.csv
index 97cba29..8ffee78 100644
--- a/src/test/resources/testinputposfullgood.csv
+++ b/src/test/resources/testinputposfullgood.csv
@@ -1,2 +1,2 @@
-test string;Truth value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1
-test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;
+test string;Truth value: true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR
+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.101;123202,202;123303.303;123404,404;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;;
diff --git a/src/test/resources/testinputwriteposfullgood.csv b/src/test/resources/testinputwriteposfullgood.csv
index 0ddb13a..e9a100b 100644
--- a/src/test/resources/testinputwriteposfullgood.csv
+++ b/src/test/resources/testinputwriteposfullgood.csv
@@ -1,2 +1,2 @@
-test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.1;1.000,2;2000.3;3000,4;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1
-test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.1;1.000,2;2000.3;3000,4;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2
+test string;true;false;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.1;1.000,2;2000.3;3000,4;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;1.01;TEST1;EUR
+test string;;;1;2;3;4;123,101.101;123.202,202;123303.303;123404,404;123101.1;1.000,2;2000.3;3000,4;5000;6.000;7000;8000;9000;10000;11000;12000;13000;14000;15000;16000;a;b;123101.101;123102,102;101;102;19780115T063209;19780115T163209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;19780115T063209;01/15/1978;13. Dezember 2018;19780115T063209;19780115T063209;2.02;Test2;CHF