annotated tag v3.0-alpha-1 created (now 54c178a)
This is an automated email from the git hooks/post-receive script. New change to annotated tag v3.0-alpha-1 in repository nuiton-csv. See https://gitlab.nuiton.org/nuiton/nuiton-csv.git at 54c178a (tag) tagging 5b50b36e688bf5ce14bc6aa2370e6ca94e35c6d4 (commit) tagged by Maven Release Manager on Tue Jul 23 15:44:58 2013 +0000 - Log ----------------------------------------------------------------- [maven-release-plugin] copy for tag nuiton-csv-3.0-alpha-1 ----------------------------------------------------------------------- This annotated tag includes the following new commits: new 5b50b36 [maven-release-plugin] copy for tag nuiton-csv-3.0-alpha-1 The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 5b50b36e688bf5ce14bc6aa2370e6ca94e35c6d4 Author: Maven Release Manager <???> Date: Tue Jul 23 15:44:58 2013 +0000 [maven-release-plugin] copy for tag nuiton-csv-3.0-alpha-1 -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
This is an automated email from the git hooks/post-receive script. New commit to annotated tag v3.0-alpha-1 in repository nuiton-csv. See https://gitlab.nuiton.org/nuiton/nuiton-csv.git commit 5b50b36e688bf5ce14bc6aa2370e6ca94e35c6d4 Author: Maven Release Manager <???> Date: Tue Jul 23 15:44:58 2013 +0000 [maven-release-plugin] copy for tag nuiton-csv-3.0-alpha-1 --- LICENSE.txt | 166 +++++++ README.txt | 0 pom.xml | 263 ++++++++++ .../org/nuiton/csv/AbstractImportErrorInfo.java | 67 +++ src/main/java/org/nuiton/csv/Column.java | 186 +++++++ src/main/java/org/nuiton/csv/Common.java | 538 +++++++++++++++++++++ src/main/java/org/nuiton/csv/CsvModel.java | 76 +++ src/main/java/org/nuiton/csv/Export.java | 297 ++++++++++++ src/main/java/org/nuiton/csv/ExportModel.java | 39 ++ src/main/java/org/nuiton/csv/ExportableColumn.java | 41 ++ src/main/java/org/nuiton/csv/Import.java | 409 ++++++++++++++++ src/main/java/org/nuiton/csv/Import2.java | 449 +++++++++++++++++ src/main/java/org/nuiton/csv/ImportConf.java | 87 ++++ .../java/org/nuiton/csv/ImportExportModel.java | 34 ++ src/main/java/org/nuiton/csv/ImportModel.java | 44 ++ .../java/org/nuiton/csv/ImportParseErrorInfo.java | 48 ++ .../java/org/nuiton/csv/ImportReadErrorInfo.java | 40 ++ src/main/java/org/nuiton/csv/ImportRow.java | 104 ++++ .../org/nuiton/csv/ImportRuntimeException.java | 46 ++ .../java/org/nuiton/csv/ImportSetErrorInfo.java | 56 +++ src/main/java/org/nuiton/csv/ImportToMap.java | 151 ++++++ src/main/java/org/nuiton/csv/ImportableColumn.java | 45 ++ .../org/nuiton/csv/ImportableExportableColumn.java | 31 ++ src/main/java/org/nuiton/csv/ModelBuilder.java | 130 +++++ src/main/java/org/nuiton/csv/MyModelBuilder.java | 166 +++++++ src/main/java/org/nuiton/csv/ValidationResult.java | 109 +++++ src/main/java/org/nuiton/csv/ValueFormatter.java | 37 ++ src/main/java/org/nuiton/csv/ValueGetter.java | 37 ++ .../java/org/nuiton/csv/ValueGetterSetter.java | 34 ++ src/main/java/org/nuiton/csv/ValueParser.java | 35 ++ .../java/org/nuiton/csv/ValueParserFormatter.java | 30 ++ src/main/java/org/nuiton/csv/ValueSetter.java | 34 ++ .../org/nuiton/csv/ext/AbstractExportModel.java | 84 ++++ .../nuiton/csv/ext/AbstractImportExportModel.java | 177 +++++++ .../org/nuiton/csv/ext/AbstractImportModel.java | 119 +++++ src/main/java/org/nuiton/csv/ext/CsvReaders.java | 57 +++ .../java/org/nuiton/csv/ext/RepeatableExport.java | 101 ++++ src/main/java/org/nuiton/csv/ext/package-info.java | 30 ++ src/main/java/org/nuiton/csv/package-info.java | 31 ++ .../resources/i18n/nuiton-csv_en_GB.properties | 8 + .../resources/i18n/nuiton-csv_es_ES.properties | 8 + .../resources/i18n/nuiton-csv_fr_FR.properties | 8 + src/site/apt/index.apt | 212 ++++++++ src/site/apt/versions.apt | 34 ++ src/site/site_fr.xml | 109 +++++ src/test/java/org/nuiton/csv/ExportTest.java | 90 ++++ src/test/java/org/nuiton/csv/Import2Test.java | 172 +++++++ src/test/java/org/nuiton/csv/ImportTest.java | 394 +++++++++++++++ .../java/org/nuiton/csv/MyModelBuilderTest.java | 71 +++ src/test/java/org/nuiton/csv/RowBean.java | 112 +++++ src/test/java/org/nuiton/csv/RowBeanEnum.java | 35 ++ .../java/org/nuiton/csv/RowBeanExportModel.java | 41 ++ src/test/resources/log4j.properties | 33 ++ 53 files changed, 5755 insertions(+) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3f7b8b1 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bbe98e2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>mavenpom4redmineAndCentral</artifactId> + <version>3.4.13</version> + </parent> + + <artifactId>nuiton-csv</artifactId> + <version>3.0-alpha-1</version> + + <name>Nuiton CSV</name> + <description>Simple CSV API</description> + <url>http://maven-site.nuiton.org/nuiton-csv</url> + <inceptionYear>2013</inceptionYear> + + <developers> + + <developer> + <name>Brendan Le Ny</name> + <id>bleny</id> + <email>bleny@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://www.codelutin.com/</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>developer</role> + </roles> + </developer> + + <developer> + <name>Tony Chemit</name> + <id>tchemit</id> + <email>chemit at codelutin dot com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://www.codelutin.com/</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>developer</role> + </roles> + </developer> + + </developers> + + <scm> + <connection> + scm:svn:http://svn.nuiton.org/svn/nuiton-csv/tags/nuiton-csv-3.0-alpha-1 + </connection> + <developerConnection> + scm:svn:http://svn.nuiton.org/svn/nuiton-csv/tags/nuiton-csv-3.0-alpha-1 + </developerConnection> + <url>http://nuiton.org/projects/nuiton-csv/repository/show/tags/nuiton-csv-3.0-alpha-1</url> + </scm> + <distributionManagement> + <site> + <id>${platform}</id> + <url>${our.site.repository}/${projectId}</url> + </site> + </distributionManagement> + + <properties> + + <projectId>nuiton-csv</projectId> + + <nuitonI18nVersion>2.5.2</nuitonI18nVersion> + + <!-- i18n configuration --> + <i18n.bundles>fr_FR,en_GB,es_ES</i18n.bundles> + + <!-- Documentation is in apt format --> + <siteSourcesType>apt</siteSourcesType> + + <!-- extra files to include in release --> + <redmine.releaseFiles>${redmine.libReleaseFiles}</redmine.releaseFiles> + + </properties> + + <dependencies> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + <version>2.6.12</version> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + <version>${nuitonI18nVersion}</version> + </dependency> + + <dependency> + <groupId>net.sourceforge.javacsv</groupId> + <artifactId>javacsv</artifactId> + <version>2.0</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>provided</scope> + </dependency> + + </dependencies> + + <build> + <pluginManagement> + <plugins> + + <!-- plugin i18n --> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <version>${nuitonI18nVersion}</version> + </plugin> + + </plugins> + </pluginManagement> + <plugins> + + <!-- plugin i18n --> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <configuration> + <silent>true</silent> + <bundles>fr_FR,en_GB,es_ES</bundles> + </configuration> + <executions> + <execution> + <goals> + <goal>parserJava</goal> + <goal>gen</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> + + <profiles> + + <profile> + <id>reporting</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <reporting> + <plugins> + + <plugin> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>${projectInfoReportsPluginVersion}</version> + <reportSets> + <reportSet> + <reports> + <report>project-team</report> + <report>mailing-list</report> + <report>cim</report> + <report>issue-tracking</report> + <report>license</report> + <report>scm</report> + <report>dependency-info</report> + <report>dependencies</report> + <report>dependency-convergence</report> + <report>plugin-management</report> + <report>plugins</report> + <report>dependency-management</report> + <report>summary</report> + </reports> + </reportSet> + </reportSets> + </plugin> + + </plugins> + </reporting> + + </profile> + + <!-- create assemblies at release time --> + <profile> + <id>assembly-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <defaultGoal>package</defaultGoal> + <plugins> + + <!-- launch in a release the assembly automaticly --> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>create-assemblies</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + <configuration> + <attach>false</attach> + <descriptorRefs> + <descriptorRef>deps</descriptorRef> + <descriptorRef>full</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + + </plugins> + + </build> + </profile> + + </profiles> +</project> diff --git a/src/main/java/org/nuiton/csv/AbstractImportErrorInfo.java b/src/main/java/org/nuiton/csv/AbstractImportErrorInfo.java new file mode 100644 index 0000000..ecc85d7 --- /dev/null +++ b/src/main/java/org/nuiton/csv/AbstractImportErrorInfo.java @@ -0,0 +1,67 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * Abstract import esxception which contains the {@link ImportRow} object. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public abstract class AbstractImportErrorInfo<E> { + + protected final ImportableColumn<E, Object> field; + + protected final long lineNumber; + + protected final E bean; + + protected final Throwable cause; + + protected AbstractImportErrorInfo(ImportRow<E> row, + ImportableColumn<E, Object> field, + Throwable cause) { + this.cause = cause; + this.field = field; + lineNumber = row.getLineNumber(); + bean = row.getBean(); + } + + public long getLineNumber() { + return lineNumber; + } + + public E getBean() { + return bean; + } + + public ImportableColumn<E, Object> getField() { + return field; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/src/main/java/org/nuiton/csv/Column.java b/src/main/java/org/nuiton/csv/Column.java new file mode 100644 index 0000000..732a080 --- /dev/null +++ b/src/main/java/org/nuiton/csv/Column.java @@ -0,0 +1,186 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.text.ParseException; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class Column<E, T> implements ImportableExportableColumn<E, T> { + + public static <E, T> Column<E, T> newExportableColumn(String headerName, + ValueGetter<E, T> valueGetter, + ValueFormatter<T> valueFormatter, + boolean ignored) { + return new Column<E, T>(headerName, + valueGetter, + valueFormatter, + null, + null, + ignored, + !ignored + ); + } + + public static <E, T> Column<E, T> newImportableColumn(String headerName, + ValueParser<T> valueParser, + ValueSetter<E, T> valueSetter, + boolean ignored, + boolean mandatory) { + return new Column<E, T>(headerName, + null, + null, + valueParser, + valueSetter, + ignored, + mandatory + ); + } + + public static <E, T> Column<E, T> newImportableExportableColumn(String headerName, + ValueGetter<E, T> valueGetter, + ValueFormatter<T> valueFormatter, + ValueParser<T> valueParser, + ValueSetter<E, T> valueSetter, + boolean ignored) { + return new Column<E, T>(headerName, + valueGetter, + valueFormatter, + valueParser, + valueSetter, + ignored, + !ignored + ); + } + + public static <E, T> Column<E, T> newImportableExportableColumn(String headerName, + ValueGetterSetter<E, T> valueGetterSetter, + ValueParserFormatter<T> valueParserFormatter, + boolean ignored) { + return newImportableExportableColumn(headerName, + valueGetterSetter, + valueParserFormatter, + valueParserFormatter, + valueGetterSetter, + ignored + ); + } + + protected String headerName; + + protected boolean mandatory = true; + + protected boolean ignored; + + protected ValueParser<T> valueParser; + + protected ValueFormatter<T> valueFormatter; + + protected ValueGetter<E, T> valueGetter; + + protected ValueSetter<E, T> valueSetter; + + protected Column(String headerName, + ValueGetter<E, T> valueGetter, + ValueFormatter<T> valueFormatter, + ValueParser<T> valueParser, + ValueSetter<E, T> valueSetter, + boolean ignored, + boolean mandatory) { + this.headerName = headerName; + this.valueGetter = valueGetter; + this.valueFormatter = valueFormatter; + this.valueSetter = valueSetter; + this.valueParser = valueParser; + this.ignored = ignored; + this.mandatory = mandatory; + } + + @Override + public String getHeaderName() { + return headerName; + } + + @Override + public boolean isMandatory() { + return mandatory; + } + + @Override + public boolean isIgnored() { + return ignored; + } + + @Override + public String formatValue(T value) { + if (valueFormatter == null) { + throw new UnsupportedOperationException("no formatter provided for " + this); + } else { + return valueFormatter.format(value); + } + } + + @Override + public T parseValue(String value) throws ParseException { + if (valueParser == null) { + throw new UnsupportedOperationException("no parser provided for " + this); + } else { + return valueParser.parse(value); + } + } + + @Override + public T getValue(E object) throws Exception { + if (valueGetter == null) { + throw new UnsupportedOperationException("no getter provided for " + this); + } else { + return valueGetter.get(object); + } + } + + @Override + public void setValue(E object, T value) throws Exception { + if (!isIgnored()) { + if (valueSetter == null) { + throw new UnsupportedOperationException("no setter provided for " + this); + } else { + valueSetter.set(object, value); + } + } + } + + @Override + public String toString() { + return "{" + + "headerName='" + headerName + '\'' + + ", mandatory=" + mandatory + + ", ignored=" + ignored + + '}'; + } +} diff --git a/src/main/java/org/nuiton/csv/Common.java b/src/main/java/org/nuiton/csv/Common.java new file mode 100644 index 0000000..26f8bdf --- /dev/null +++ b/src/main/java/org/nuiton/csv/Common.java @@ -0,0 +1,538 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.lang3.StringUtils; +import org.nuiton.util.StringUtil; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class Common { + + public static final ValueParserFormatter<String> STRING = + new StringValueParser(); + + public static final ValueFormatter<?> TO_STRING_FORMATTER = + new ToStringValueFormatter(); + + public static final ValueParserFormatter<Boolean> BOOLEAN = + new BooleanParserFormatter(null, true); + + public static final ValueParserFormatter<Boolean> PRIMITIVE_BOOLEAN = + new BooleanParserFormatter(false, false); + + public static ValueParserFormatter<Character> CHAR = + new CharacterParserFormatter(null, true); + + public static ValueParserFormatter<Integer> INTEGER = + new IntegerParserFormatter(null, true); + + public static ValueParserFormatter<Integer> PRIMITIVE_INTEGER = + new IntegerParserFormatter(0, false); + + public static ValueParserFormatter<Long> LONG = + new LongParserFormatter(null, true); + + public static ValueParserFormatter<Long> PRIMITIVE_LONG = + new LongParserFormatter(0l, false); + + public static ValueParserFormatter<Float> FLOAT = + new FloatParserFormatter(null, true); + + public static ValueParserFormatter<Float> PRIMITIVE_FLOAT = + new FloatParserFormatter(0f, false); + + public static ValueParserFormatter<Double> DOUBLE = + new DoubleParserFormatter(null, true); + + public static ValueParserFormatter<Double> DOUBLE_PRIMITIVE = + new DoubleParserFormatter(0d, false); + + public static final ValueParserFormatter<Date> DAY = + new DateValue("dd/MM/yyyy"); + + public static final ValueParserFormatter<Date> DAY_TIME = + new DateValue("dd/MM/yyyy HH:mm"); + + public static final ValueParserFormatter<Date> DAY_TIME_SECOND = + new DateValue("dd/MM/yyyy HH:mm:ss"); + + /** A week in a given year, ie "1/2011" until "52/2011" */ + public static final ValueParserFormatter<Date> WEEK = + new DateValue("w/yyyy"); + + public static <E extends Map<String, Object>, T> MapProperty<E, T> newMapProperty(String propertyName) { + return new MapProperty<E, T>(propertyName); + } + + public static <E, T> BeanProperty<E, T> newBeanProperty(String propertyName) { + return new BeanProperty<E, T>(propertyName); + } + + public static <E extends Enum<E>> ValueParserFormatter<E> newEnumByNameParserFormatter(Class<E> enumType) { + return new EnumByNameParserFormatter<E>(enumType); + } + + public static <E extends Enum<E>> ValueParserFormatter<E> newEnumByOrdinalParserFormatter(Class<E> enumType) { + return new EnumByOrdinalParserFormatter<E>(enumType); + } + + public static class StringValueParser implements ValueParserFormatter<String> { + + @Override + public String parse(String value) { + return value; + } + + @Override + public String format(String value) { + return value == null ? "" : value; + } + } + + public static class ToStringValueFormatter implements ValueFormatter<Object> { + + @Override + public String format(Object value) { + return value == null ? "" : value.toString(); + } + } + + public static class BeanProperty<E, T> implements ValueGetterSetter<E, T> { + + protected String propertyName; + + BeanProperty(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public void set(E bean, T value) throws Exception { + BeanUtilsBean.getInstance().getPropertyUtils().setProperty(bean, propertyName, value); + } + + @Override + public T get(E object) throws Exception { + T value = (T) PropertyUtils.getProperty(object, propertyName); + return value; + } + } + + public static class MapProperty<E extends Map<String, Object>, T> implements ValueGetterSetter<E, T> { + + protected String propertyName; + + MapProperty(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public void set(E bean, T value) throws Exception { + bean.put(propertyName, value); + } + + @Override + public T get(E object) throws Exception { + T value = (T) object.get(propertyName); + return value; + } + } + + public static class DateValue implements ValueParserFormatter<Date> { + + protected DateFormat dateFormat; + + public DateValue(String dateFormatPattern) { + dateFormat = new SimpleDateFormat(dateFormatPattern); + } + + @Override + public Date parse(String value) throws ParseException { + Date date = null; + if (StringUtils.isNotBlank(value)) { + date = dateFormat.parse(value); + } + return date; + } + + @Override + public String format(Date date) { + String value = ""; + if (date != null) { + value = dateFormat.format(date); + } + return value; + } + } + + public static class EnumByNameParserFormatter<E extends Enum<E>> implements ValueParserFormatter<E> { + + + private final Class<E> enumType; + + public EnumByNameParserFormatter(Class<E> enumType) { + this.enumType = enumType; + } + + @Override + public E parse(String value) throws ParseException { + E result; + if (StringUtils.isBlank(value)) { + result = null; + } else { + result = Enum.valueOf(enumType, value); + } + return result; + } + + @Override + public String format(E date) { + String value = ""; + if (date != null) { + value = date.name(); + } + return value; + } + } + + public static class EnumByOrdinalParserFormatter<E extends Enum<E>> implements ValueParserFormatter<E> { + + private final E[] universe; + + private final int maxValue; + + public EnumByOrdinalParserFormatter(Class<E> enumType) { + universe = enumType.getEnumConstants(); + maxValue = universe.length - 1; + } + + @Override + public E parse(String value) throws ParseException { + E result; + if (StringUtils.isBlank(value)) { + result = null; + } else { + Integer ordinal = null; + try { + ordinal = Integer.valueOf(value); + } catch (NumberFormatException e) { + throw new ParseException( + "Could not parse ordinal value [" + value + "]", 0); + } + if (ordinal > maxValue) { + throw new ParseException( + "Ordinal value [" + ordinal + + "] not inbound (possible value from [0.." + + maxValue + "] for enum " + Arrays.toString(universe), + 0); + } + result = universe[ordinal]; + } + return result; + } + + @Override + public String format(E date) { + String value = ""; + if (date != null) { + value = date.name(); + } + return value; + } + } + + public static class ToStringParserFormatter<E> implements ValueParserFormatter<E> { + + protected Map<E, String> toStrings = new HashMap<E, String>(); + + protected Map<String, E> fromString = new HashMap<String, E>(); + + public ToStringParserFormatter(List<E> values) { + this(values, null); + } + + public ToStringParserFormatter(E[] values) { + computeToStrings(values, null); + } + + public ToStringParserFormatter(List<E> values, + StringUtil.ToString<E> toString) { + computeToStrings(values, toString); + } + + /** fill toStrings and fromString */ + protected void computeToStrings(E[] values, + StringUtil.ToString<E> toString) { + List<E> valuesAsList = new LinkedList<E>(); + Collections.addAll(valuesAsList, values); + computeToStrings(valuesAsList, toString); + } + + /** fill toStrings and fromString */ + protected void computeToStrings(List<E> values, + StringUtil.ToString<E> toString) { + for (E value : values) { + String valueToString; + if (toString == null) { + valueToString = value.toString(); + } else { + valueToString = toString.toString(value); + } + toStrings.put(value, valueToString); + fromString.put(valueToString, value); + } + } + + @Override + public String format(E value) { + String valueAsString = toStrings.get(value); + if (valueAsString == null) { + throw new IllegalArgumentException(); + } + return valueAsString; + } + + @Override + public E parse(String valueAsString) throws ParseException { + E value = fromString.get(valueAsString); + if (value == null) { + throw new IllegalArgumentException("Unable to parse value '" + valueAsString + + "'. Possible values are " + fromString.keySet().toString()); + } + return value; + } + } + + public static class ValueSaver<E, T> implements ValueGetterSetter<E, T> { + + protected T value; + + @Override + public T get(E object) throws Exception { + return value; + } + + @Override + public void set(E object, T value) throws Exception { + this.value = value; + } + } + + public static abstract class NullableParserFormatter<O> implements ValueParserFormatter<O> { + + protected O defaultValue; + + protected boolean nullAllowed; + + protected abstract O parseNoneEmptyValue(String value); + + protected NullableParserFormatter(O defaultValue, + boolean nullAllowed) { + this.defaultValue = defaultValue; + this.nullAllowed = nullAllowed; + } + + @Override + public O parse(String value) throws ParseException { + O result; + if (StringUtils.isBlank(value)) { + result = defaultValue; + } else { + result = parseNoneEmptyValue(value); + } + + if (result == null && !nullAllowed) { + throw new IllegalArgumentException(); + } + return result; + } + } + + public static class BooleanParserFormatter extends NullableParserFormatter<Boolean> { + + public BooleanParserFormatter(Boolean defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Boolean bool) { + String value; + if (bool == null) { + if (nullAllowed) { + value = "?"; + } else { + throw new IllegalArgumentException(); + } + } else if (bool) { + value = "Y"; + } else { + value = "N"; + } + return value; + } + + @Override + protected Boolean parseNoneEmptyValue(String value) { + Boolean result; + if ("?".equals(value)) { + result = null; + } else if ("Y".equals(value)) { + result = true; + } else if ("N".equals(value)) { + result = false; + } else { + result = Boolean.parseBoolean(value); + } + return result; + } + } + + public static class CharacterParserFormatter extends NullableParserFormatter<Character> { + + public CharacterParserFormatter(Character defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Character value) { + String str = ""; + if (value != null) { + str = String.valueOf(value); + } + return str; + } + + @Override + protected Character parseNoneEmptyValue(String value) { + return value.charAt(0); + } + } + + public static class IntegerParserFormatter extends NullableParserFormatter<Integer> { + + public IntegerParserFormatter(Integer defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Integer value) { + String str = ""; + if (value != null) { + str = String.valueOf(value); + } + return str; + } + + @Override + protected Integer parseNoneEmptyValue(String value) { + return Integer.valueOf(value); + } + } + + public static class LongParserFormatter extends NullableParserFormatter<Long> { + + public LongParserFormatter(Long defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Long value) { + String str = ""; + if (value != null) { + str = String.valueOf(value); + } + return str; + } + + @Override + protected Long parseNoneEmptyValue(String value) { + return Long.valueOf(value); + } + + } + + public static class FloatParserFormatter extends NullableParserFormatter<Float> { + + public FloatParserFormatter(Float defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Float value) { + String str = ""; + if (value != null) { + str = String.valueOf(value); + } + return str; + } + + @Override + protected Float parseNoneEmptyValue(String value) { + return Float.valueOf(value); + } + + } + + public static class DoubleParserFormatter extends NullableParserFormatter<Double> { + + public DoubleParserFormatter(Double defaultValue, boolean nullAllowed) { + super(defaultValue, nullAllowed); + } + + @Override + public String format(Double value) { + String str = ""; + if (value != null) { + str = String.valueOf(value); + } + return str; + } + + @Override + protected Double parseNoneEmptyValue(String value) { + return Double.valueOf(value); + } + + } + + +} diff --git a/src/main/java/org/nuiton/csv/CsvModel.java b/src/main/java/org/nuiton/csv/CsvModel.java new file mode 100644 index 0000000..c5a8857 --- /dev/null +++ b/src/main/java/org/nuiton/csv/CsvModel.java @@ -0,0 +1,76 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.util.List; + +/** + * @param <E> + */ +public class CsvModel<E> implements ImportExportModel<E> { + + public interface Factory<E> { + E newEmpty(); + } + + protected char separator; + + protected Iterable<ImportableColumn<E, Object>> columnsForImport; + + protected Iterable<ExportableColumn<E, Object>> columnsForExport; + + protected Factory<E> factory; + + public CsvModel(char separator, Iterable<ImportableColumn<E, Object>> columnsForImport, Iterable<ExportableColumn<E, Object>> columnsForExport, Factory<E> factory) { + this.separator = separator; + this.columnsForImport = columnsForImport; + this.columnsForExport = columnsForExport; + this.factory = factory; + } + + @Override + public char getSeparator() { + return separator; + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + // + } + + @Override + public E newEmptyInstance() { + return factory.newEmpty(); + } + + @Override + public Iterable<ImportableColumn<E, Object>> getColumnsForImport() { + return columnsForImport; + } + + @Override + public Iterable<ExportableColumn<E, Object>> getColumnsForExport() { + return columnsForExport; + } +} diff --git a/src/main/java/org/nuiton/csv/Export.java b/src/main/java/org/nuiton/csv/Export.java new file mode 100644 index 0000000..12be132 --- /dev/null +++ b/src/main/java/org/nuiton/csv/Export.java @@ -0,0 +1,297 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.StringUtil; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * Object to realize a export from a {@link ExportModel} and some datas. + * <h2>Usefull static methods</h2> + * There is some usefull methods here to do operation in one line : + * <h3>To export to a file</h3> + * <pre> + * Export.exportToFile(model, data, file); + * </pre> + * <h3>To export to a writer</h3> + * <pre> + * Export.exportToWriter(model, data, writer); + * </pre> + * <h3>To export as a string</h3> + * <pre> + * String exportcontent = Export.exportToString(model, data); + * </pre> + * <h3>To obtain a new instance of an exporter</h3> + * <pre> + * Export<E> exporter = Export.newExport(model, data); + * </pre> + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class Export<E> { + + /** Logger. */ + private static final Log log = LogFactory.getLog(Export.class); + + /** Export model. */ + protected final ExportModel<E> model; + + /** Data to export. */ + protected Iterable<E> data; + + /** + * Cell separator. + * + * @see ExportModel#getSeparator() + */ + protected final String separator; + + public static <E> Export<E> newExport(ExportModel<E> model, + Iterable<E> data) { + return new Export<E>(model, data); + } + + public static <E> void exportToWriter(ExportModel<E> model, + Iterable<E> data, + Writer writer) throws Exception { + Export<E> exporter = newExport(model, data); + exporter.write(writer); + } + + public static <E> void exportToWriter(ExportModel<E> model, + Iterable<E> data, + Writer writer, + boolean writeHeader) throws Exception { + Export<E> exporter = newExport(model, data); + exporter.write(writer, writeHeader); + } + + public static <E> void exportToFile(ExportModel<E> model, + Iterable<E> data, + File file) throws Exception { + Export<E> exporter = newExport(model, data); + exporter.write(file); + } + + public static <E> void exportToFile(ExportModel<E> model, + Iterable<E> data, + File file, + Charset charset) throws Exception { + Export<E> exporter = newExport(model, data); + exporter.write(file, charset); + } + + public static <E> void exportToFile(ExportModel<E> model, + Iterable<E> data, + File file, + Charset charset, + boolean writeHeader) throws Exception { + Export<E> exporter = newExport(model, data); + exporter.write(file, charset, writeHeader); + } + + public static <E> String exportToString(ExportModel<E> model, + Iterable<E> data) throws Exception { + Export<E> exporter = newExport(model, data); + return exporter.toString(Charset.defaultCharset()); + } + + public static <E> String exportToString(ExportModel<E> model, + Iterable<E> data, + Charset charset) throws Exception { + Export<E> exporter = newExport(model, data); + return exporter.toString(charset); + } + + public static <E> String exportToString(ExportModel<E> model, + Iterable<E> data, + Charset charset, + boolean writeHeader) throws Exception { + Export<E> exporter = newExport(model, data); + return exporter.toString(charset, writeHeader); + } + + protected Export(ExportModel<E> model, Iterable<E> data) { + this.model = model; + this.data = data; + separator = String.valueOf(model.getSeparator()); + } + + public void write(Writer writer) throws Exception { + write(writer, true); + } + + public void write(Writer writer, boolean writeHeader) throws Exception { + + // write csv header + if (writeHeader) { + writeHeader(writer); + } + + // obtain export columns + Iterable<ExportableColumn<E, Object>> columns = + model.getColumnsForExport(); + + for (E row : data) { + + // write the row for this data + writeRow(writer, columns, row); + } + } + + protected void writeHeader(Writer writer) throws IOException { + + List<String> headerNames = new LinkedList<String>(); + for (ExportableColumn<E, ?> column : model.getColumnsForExport()) { + headerNames.add(column.getHeaderName()); + } + String headersLine = StringUtil.join(headerNames, separator, true); + writer.write(headersLine); + writer.write('\n'); + if (log.isDebugEnabled()) { + log.debug("headers for export are '" + headersLine + "'"); + if (data instanceof Collection) { + log.debug("will export " + ((Collection<E>) data).size() + " lines"); + } + } + } + + protected void writeRow(Writer writer, + Iterable<ExportableColumn<E, Object>> columns, + E row) throws Exception { + for (ExportableColumn<E, Object> column : columns) { + Object cell = column.getValue(row); + String formattedCell = column.formatValue(cell); + if (formattedCell == null) { + throw new NullPointerException( + "column for header " + column.getHeaderName() + + " returned a null value." + column.toString()); + } + formattedCell = StringUtil.escapeCsvValue(formattedCell, separator); + writer.write(formattedCell); + writer.write(separator); + } + writer.write('\n'); + } + + public void write(OutputStream outputStream, Charset charset) throws Exception { + write(outputStream, charset, true); + } + + public void write(OutputStream outputStream, Charset charset, boolean writeHeader) throws Exception { + Writer writer = new OutputStreamWriter(outputStream, charset); + try { + write(writer, writeHeader); + } finally { + writer.close(); + } + } + + public void write(OutputStream outputStream) throws Exception { + write(outputStream, Charset.defaultCharset()); + } + + public void write(OutputStream outputStream, boolean writeHeader) throws Exception { + write(outputStream, Charset.defaultCharset(), writeHeader); + } + + public void write(File file, Charset charset) throws Exception { + write(file, charset, true); + } + + public void write(File file, Charset charset, boolean writeHeader) throws Exception { + FileOutputStream fileOutputStream = new FileOutputStream(file); + try { + write(fileOutputStream, charset, writeHeader); + } finally { + fileOutputStream.close(); + } + } + + public void write(File file) throws Exception { + write(file, true); + } + + public void write(File file, boolean writeHeader) throws Exception { + write(file, Charset.defaultCharset(), writeHeader); + } + + public String toString(Charset charset) throws Exception { + return toString(charset, true); + } + + public String toString(Charset charset, boolean writeHeader) throws Exception { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + write(outputStream, charset, writeHeader); + byte[] bytes = outputStream.toByteArray(); + String result = new String(bytes, charset); + return result; + } + + /** @deprecated since 2.4.3, use {@link #write(java.io.File)} instead. */ + @Deprecated + public void exportToFile(File file) throws Exception { + write(file); + } + + /** @deprecated since 2.4.3, use {@link #write(java.io.Writer)} instead. */ + @Deprecated + public void startExport(Writer writer) throws Exception { + write(writer); + } + + /** @deprecated since 2.4.3, use {@link #toString(java.nio.charset.Charset)} instead. */ + @Deprecated + public String startExportAsString() throws Exception { + return toString(Charset.defaultCharset()); + } + + /** + * @deprecated since 2.4.3. It's not the role of the API to give an InputStream + * you can use {@link #toString(java.nio.charset.Charset)} and + * {@link org.apache.commons.io.IOUtils#toInputStream(String)} + */ + @Deprecated + public InputStream startExport() throws Exception { + String content = toString(Charset.defaultCharset()); + return IOUtils.toInputStream(content); + } +} diff --git a/src/main/java/org/nuiton/csv/ExportModel.java b/src/main/java/org/nuiton/csv/ExportModel.java new file mode 100644 index 0000000..ca9236b --- /dev/null +++ b/src/main/java/org/nuiton/csv/ExportModel.java @@ -0,0 +1,39 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ExportModel<E> { + + char getSeparator(); + + Iterable<ExportableColumn<E, Object>> getColumnsForExport(); + +} diff --git a/src/main/java/org/nuiton/csv/ExportableColumn.java b/src/main/java/org/nuiton/csv/ExportableColumn.java new file mode 100644 index 0000000..a484ff2 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ExportableColumn.java @@ -0,0 +1,41 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ExportableColumn<E, T> { + + String getHeaderName(); + + T getValue(E object) throws Exception; + + String formatValue(T value); + +} diff --git a/src/main/java/org/nuiton/csv/Import.java b/src/main/java/org/nuiton/csv/Import.java new file mode 100644 index 0000000..e3aa152 --- /dev/null +++ b/src/main/java/org/nuiton/csv/Import.java @@ -0,0 +1,409 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import com.csvreader.CsvReader; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.StringUtil; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import static org.nuiton.i18n.I18n._; + +/** + * Import engine for a given import model. + * <p/> + * It acts as an {@link Iterable}, you can use directly inside a foreach. + * <p/> + * The method {@link #prepareAndValidate()} will be invoked before all and + * only once. It mainly obtain header from the csv input, pass it to the model + * and then validate the model. + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @author fdesbois <desbois@codelutin.com> + * @since 2.4 + */ +public class Import<E> implements Iterable<E>, Closeable { + + /** Logger. */ + private static final Log log = LogFactory.getLog(Import.class); + + /** Csv import model. */ + protected ImportModel<E> model; + + /** Csv reader (this is the input). */ + protected CsvReader reader; + + /** + * A flag to know if model was already validated. + * <p/> + * Save once done to prevent multiple headers read leading to consider + * first lines as headers. + */ + protected boolean validate; + + public static <E> Import<E> newImport(ImportModel<E> model, + InputStream inputStream) { + return new Import<E>(model, inputStream); + } + + public static <E> Import<E> newImport(ImportModel<E> model, + Reader reader) { + return new Import<E>(model, reader); + } + + public static <E> Import<E> newImport(ImportModel<E> model, + InputStream inputStream, + boolean safetySwitch) { + return new Import<E>(model, inputStream, safetySwitch); + } + + public static <E> Import<E> newImport(ImportModel<E> model, + Reader reader, + boolean safetySwitch) { + return new Import<E>(model, reader, safetySwitch); + } + + /** + * Define iterator over import. First of all, the input stream will be + * validated based on defined model. Iteration will be done on all csv + * rows except first headers line. + * + * @return the Iterator used for csv iteration + * @see #prepareAndValidate() + */ + @Override + public Iterator<E> iterator() { + + prepareAndValidate(); + + return new Iterator<E>() { + + // read first line since first line is header + boolean hasNext = readRow(); + + // get once for all columns to import + List<ImportableColumn<E, Object>> columns = getNonIgnoredHeaders(); + + // to stock the current line number + int lineNumber; + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public E next() + throws NoSuchElementException, ImportRuntimeException { + + if (!hasNext) { + throw new NoSuchElementException(); + } + + lineNumber += 1; + + E element = model.newEmptyInstance(); + + for (ImportableColumn<E, Object> field : columns) { + + // read value from csv cell + String value = readValue(field, lineNumber); + + // contravariance ftw + Object parsedValue = parseValue(field, lineNumber, value); + + // set value to element + setValue(field, lineNumber, element, parsedValue); + } + + // check if there is a next row to read + hasNext = readRow(); + + return element; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public void close() { + reader.close(); + } + + /** + * Read the first mandatory headers line and validate it with the input + * model. This will check if headers are unique, known by the model and + * if mandatory headers exist in the file. During this phase, the model + * will retrieve headers value with {@link + * ImportModel#pushCsvHeaderNames(List)} call. + * + * @since 2.4.1 + */ + public void prepareAndValidate() { + + if (validate) { + + // was already validated + return; + } + + // mark as validated + validate = true; + + // obtains headers + String[] headers = getHeaders(); + + if (log.isTraceEnabled()) { + log.trace("headers of the CSV file are : " + + Arrays.toString(headers)); + } + + // hook to do some stuff from the model + model.pushCsvHeaderNames(Arrays.asList(headers)); + + // check model columns name are unique + checkUniqueModelColumnNames(); + + // check that given headers from csv file are all known + checkHeaderNamesAreAllKnown(headers); + + // check all mandatories column are on csv header + checkAllMandatoryHeadersArePresent(headers); + } + + protected <T> String readValue(ImportableColumn<E, T> field, + int lineNumber) { + try { + String value = reader.get(field.getHeaderName()); + return value; + } catch (Exception e) { + reader.close(); + throw new ImportRuntimeException( + _("csv.import.error.unableToReadField", + field.getHeaderName(), lineNumber), e); + } + } + + protected <T> T parseValue(ImportableColumn<E, T> field, + int lineNumber, + String value) { + try { + T parsedValue = field.parseValue(value); + return parsedValue; + } catch (Exception e) { + String message = _("csv.import.error.unableToParseValue", + value, field.getHeaderName(), lineNumber) + + "\n" + e.getMessage(); + throw new ImportRuntimeException(message, e); + } + } + + protected <T> void setValue(ImportableColumn<E, T> field, + int lineNumber, + E element, + T parsedValue) { + try { + field.setValue(element, parsedValue); + } catch (Exception e) { + String message = _("csv.import.error.unableToSetValue", + parsedValue, + element.toString(), + lineNumber, field.getHeaderName()); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new ImportRuntimeException(message, e); + } + } + + protected void checkHeaderNamesAreAllKnown(String[] headers) { + List<String> csvHeaders = new ArrayList<String>(); + Collections.addAll(csvHeaders, headers); + + for (ImportableColumn<E, ?> field : model.getColumnsForImport()) { + csvHeaders.remove(field.getHeaderName()); + } + if (!csvHeaders.isEmpty()) { + List<String> validHeaderNames = new LinkedList<String>(); + for (ImportableColumn<E, ?> importableColumn : + model.getColumnsForImport()) { + validHeaderNames.add(importableColumn.getHeaderName()); + } + String validationMessage = + _("csv.import.error.unrecognizedHeaders", + StringUtil.join(csvHeaders, ", ", true), + StringUtil.join(validHeaderNames, ", ", true)); + throw new ImportRuntimeException(validationMessage); + } + } + + protected void checkUniqueModelColumnNames() { + Set<String> headerNames = new HashSet<String>(); + Set<String> doubleHeaderNames = new HashSet<String>(); + for (ImportableColumn<E, ?> importableColumn : + model.getColumnsForImport()) { + String headerName = importableColumn.getHeaderName(); + boolean alreadyUsed = !headerNames.add(headerName); + if (alreadyUsed) { + doubleHeaderNames.add(headerName); + } + } + if (!doubleHeaderNames.isEmpty()) { + String message = _("csv.import.error.duplicatedHeaders", + StringUtil.join(doubleHeaderNames, ", ", true)); + + throw new ImportRuntimeException( + message); + } + } + + protected void checkAllMandatoryHeadersArePresent(String[] headers) { + + List<String> csvHeaders = new ArrayList<String>(); + Collections.addAll(csvHeaders, headers); + + List<String> mandatoryHeadersNames = new ArrayList<String>(); + for (ImportableColumn<E, ?> field : getAllMandatoryHeaders()) { + mandatoryHeadersNames.add(field.getHeaderName()); + } + mandatoryHeadersNames.removeAll(csvHeaders); + + if (!mandatoryHeadersNames.isEmpty()) { + String validationMessage = + _("csv.import.error.missingMandatoryHeaders", + StringUtil.join(mandatoryHeadersNames, ", ", true)); + throw new ImportRuntimeException(validationMessage); + } + } + + protected String[] getHeaders() throws ImportRuntimeException { + try { + boolean canReadHeaders = reader.readHeaders(); + if (!canReadHeaders) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders")); + } + } catch (IOException e) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders"), e); + } + + try { + String[] result = reader.getHeaders(); + return result; + } catch (IOException e) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders"), e); + } + } + + protected List<ImportableColumn<E, Object>> getNonIgnoredHeaders() { + List<ImportableColumn<E, Object>> nonIgnoredHeaders = + new ArrayList<ImportableColumn<E, Object>>(); + for (ImportableColumn<E, Object> field : model.getColumnsForImport()) { + if (!field.isIgnored()) { + nonIgnoredHeaders.add(field); + } + } + return nonIgnoredHeaders; + } + + protected List<ImportableColumn<E, ?>> getAllMandatoryHeaders() { + List<ImportableColumn<E, ?>> allMandatoryHeaders = + new ArrayList<ImportableColumn<E, ?>>(); + for (ImportableColumn<E, ?> field : model.getColumnsForImport()) { + if (field.isMandatory()) { + allMandatoryHeaders.add(field); + } + } + return allMandatoryHeaders; + } + + protected Import(ImportModel<E> model, InputStream inputStream) { + this(model, inputStream, true); + } + + protected Import(ImportModel<E> model, Reader reader) { + this(model, reader, true); + } + + protected Import(ImportModel<E> model, InputStream inputStream, boolean safetySwitch) { + if (inputStream == null) { + throw new NullPointerException("inputStream is null"); + } + this.model = model; + reader = new CsvReader(inputStream, model.getSeparator(), Charset.forName("UTF-8")); + reader.setTrimWhitespace(true); + this.reader.setSafetySwitch(safetySwitch); + } + + protected Import(ImportModel<E> model, Reader reader, boolean safetySwitch) { + if (reader == null) { + throw new NullPointerException("reader is null"); + } + this.model = model; + this.reader = new CsvReader(reader, model.getSeparator()); + this.reader.setTrimWhitespace(true); + this.reader.setSafetySwitch(safetySwitch); + } + + /** + * Read the next row from the reader and return {@code true} if line + * was successfully read. + * + * @return {@code true} if line was successfully read, says in fact there is + * something after this line. + * @throws ImportRuntimeException if could not read line + */ + protected boolean readRow() throws ImportRuntimeException { + try { + boolean hasNext = reader.readRecord(); + return hasNext; + } catch (IOException e) { + reader.close(); + throw new ImportRuntimeException(_("csv.import.error.unableToReadLine", 1), e); + } + } + +} diff --git a/src/main/java/org/nuiton/csv/Import2.java b/src/main/java/org/nuiton/csv/Import2.java new file mode 100644 index 0000000..14eb380 --- /dev/null +++ b/src/main/java/org/nuiton/csv/Import2.java @@ -0,0 +1,449 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.csvreader.CsvReader; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.StringUtil; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import static org.nuiton.i18n.I18n._; + +/** + * Improve the first {@link Import} class with the notion of {@link ImportRow}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class Import2<E> implements Iterable<ImportRow<E>>, Closeable { + + /** Logger. */ + private static final Log log = LogFactory.getLog(Import2.class); + + /** Csv import configuration. */ + protected final ImportConf conf; + + /** Csv import model. */ + protected ImportModel<E> model; + + /** Csv reader (this is the input). */ + protected CsvReader reader; + + /** + * A flag to know if model was already validated. + * <p/> + * Save once done to prevent multiple headers read leading to consider + * first lines as headers. + */ + protected boolean validate; + + public static <E> Import2<E> newImport(ImportModel<E> model, + InputStream inputStream) { + return newImport(new ImportConf(), model, inputStream); + } + + public static <E> Import2<E> newImport(ImportModel<E> model, + Reader reader) { + return newImport(new ImportConf(), model, reader); + } + + public static <E> Import2<E> newImport(ImportConf conf, + ImportModel<E> model, + InputStream inputStream) { + return new Import2<E>(conf, model, inputStream); + } + + public static <E> Import2<E> newImport(ImportConf conf, + ImportModel<E> model, + Reader reader) { + return new Import2<E>(conf, model, reader); + } + + /** + * Define iterator over import. First of all, the input stream will be + * validated based on defined model. Iteration will be done on all csv + * rows except first headers line. + * + * @return the Iterator used for csv iteration + * @see #prepareAndValidate() + */ + @Override + public Iterator<ImportRow<E>> iterator() { + + prepareAndValidate(); + + ImportRowIterator itr = new ImportRowIterator(getNonIgnoredHeaders()); + return itr; + } + + @Override + public void close() { + reader.close(); + } + + /** + * Read the first mandatory headers line and validate it with the input + * model. This will check if headers are unique, known by the model and + * if mandatory headers exist in the file. During this phase, the model + * will retrieve headers value with {@link + * ImportModel#pushCsvHeaderNames(List)} call. + * + * @since 2.4.1 + */ + public void prepareAndValidate() { + + if (validate) { + + // was already validated + return; + } + + // mark as validated + validate = true; + + // obtains headers + String[] headers = getHeaders(); + + if (log.isTraceEnabled()) { + log.trace("headers of the CSV file are : " + + Arrays.toString(headers)); + } + + // hook to do some stuff from the model + model.pushCsvHeaderNames(Arrays.asList(headers)); + + // check model columns name are unique + checkUniqueModelColumnNames(); + + if (!conf.isIgnoreUnknownHeader()) { + + // check that given headers from csv file are all known + checkHeaderNamesAreAllKnown(headers); + } + + // check all mandatories column are on csv header + checkAllMandatoryHeadersArePresent(headers); + } + + protected <T> String readValue(ImportableColumn<E, T> field) throws Exception { + String value = reader.get(field.getHeaderName()); + return value; + } + + protected <T> T parseValue(ImportableColumn<E, T> field, + long lineNumber, + String value) { + try { + T parsedValue = field.parseValue(value); + return parsedValue; + } catch (Exception e) { + String message = _("csv.import.error.unableToParseValue", + value, field.getHeaderName(), lineNumber) + + "\n" + e.getMessage(); + throw new ImportRuntimeException(message, e); + } + } + + protected <T> void setValue(ImportableColumn<E, T> field, + long lineNumber, + E element, + T parsedValue) { + try { + field.setValue(element, parsedValue); + } catch (Exception e) { + String message = _("csv.import.error.unableToSetValue", + parsedValue, + element.toString(), + lineNumber, field.getHeaderName()); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new ImportRuntimeException(message, e); + } + } + + protected void checkHeaderNamesAreAllKnown(String[] headers) { + List<String> csvHeaders = new ArrayList<String>(); + Collections.addAll(csvHeaders, headers); + + for (ImportableColumn<E, ?> field : model.getColumnsForImport()) { + csvHeaders.remove(field.getHeaderName()); + } + if (!csvHeaders.isEmpty()) { + List<String> validHeaderNames = new LinkedList<String>(); + for (ImportableColumn<E, ?> importableColumn : + model.getColumnsForImport()) { + validHeaderNames.add(importableColumn.getHeaderName()); + } + String validationMessage = + _("csv.import.error.unrecognizedHeaders", + StringUtil.join(csvHeaders, ", ", true), + StringUtil.join(validHeaderNames, ", ", true)); + throw new ImportRuntimeException(validationMessage); + } + } + + protected void checkUniqueModelColumnNames() { + Set<String> headerNames = new HashSet<String>(); + Set<String> doubleHeaderNames = new HashSet<String>(); + for (ImportableColumn<E, ?> importableColumn : + model.getColumnsForImport()) { + String headerName = importableColumn.getHeaderName(); + boolean alreadyUsed = !headerNames.add(headerName); + if (alreadyUsed) { + doubleHeaderNames.add(headerName); + } + } + if (!doubleHeaderNames.isEmpty()) { + String message = _("csv.import.error.duplicatedHeaders", + StringUtil.join(doubleHeaderNames, ", ", true)); + + throw new ImportRuntimeException( + message); + } + } + + protected void checkAllMandatoryHeadersArePresent(String[] headers) { + + List<String> csvHeaders = new ArrayList<String>(); + Collections.addAll(csvHeaders, headers); + + List<String> mandatoryHeadersNames = new ArrayList<String>(); + for (ImportableColumn<E, ?> field : getAllMandatoryHeaders()) { + mandatoryHeadersNames.add(field.getHeaderName()); + } + mandatoryHeadersNames.removeAll(csvHeaders); + + if (!mandatoryHeadersNames.isEmpty()) { + String validationMessage = + _("csv.import.error.missingMandatoryHeaders", + StringUtil.join(mandatoryHeadersNames, ", ", true)); + throw new ImportRuntimeException(validationMessage); + } + } + + protected String[] getHeaders() throws ImportRuntimeException { + try { + boolean canReadHeaders = reader.readHeaders(); + if (!canReadHeaders) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders")); + } + } catch (IOException e) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders"), e); + } + + try { + String[] result = reader.getHeaders(); + return result; + } catch (IOException e) { + throw new ImportRuntimeException( + _("csv.import.error.unableToReadHeaders"), e); + } + } + + protected List<ImportableColumn<E, Object>> getNonIgnoredHeaders() { + List<ImportableColumn<E, Object>> nonIgnoredHeaders = + new ArrayList<ImportableColumn<E, Object>>(); + for (ImportableColumn<E, Object> field : model.getColumnsForImport()) { + if (!field.isIgnored()) { + nonIgnoredHeaders.add(field); + } + } + return nonIgnoredHeaders; + } + + protected List<ImportableColumn<E, ?>> getAllMandatoryHeaders() { + List<ImportableColumn<E, ?>> allMandatoryHeaders = + new ArrayList<ImportableColumn<E, ?>>(); + for (ImportableColumn<E, ?> field : model.getColumnsForImport()) { + if (field.isMandatory()) { + allMandatoryHeaders.add(field); + } + } + return allMandatoryHeaders; + } + + protected Import2(ImportConf conf, ImportModel<E> model, InputStream inputStream) { + if (inputStream == null) { + throw new NullPointerException("inputStream is null"); + } + this.conf = conf; + this.model = model; + reader = new CsvReader(inputStream, model.getSeparator(), Charset.forName("UTF-8")); + reader.setTrimWhitespace(true); + this.reader.setSafetySwitch(conf.isSafetySwitch()); + } + + protected Import2(ImportConf conf, ImportModel<E> model, Reader reader) { + if (reader == null) { + throw new NullPointerException("reader is null"); + } + this.conf = conf; + this.model = model; + this.reader = new CsvReader(reader, model.getSeparator()); + this.reader.setTrimWhitespace(true); + this.reader.setSafetySwitch(conf.isSafetySwitch()); + } + + /** + * Read the next row from the reader and return {@code true} if line + * was successfully read. + * + * @return {@code true} if line was successfully read, says in fact there is + * something after this line. + * @throws ImportRuntimeException if could not read line + */ + protected boolean readRow() throws ImportRuntimeException { + try { + boolean hasNext = reader.readRecord(); + return hasNext; + } catch (IOException e) { + reader.close(); + throw new ImportRuntimeException(_("csv.import.error.unableToReadLine", 1), e); + } + } + + private class ImportRowIterator implements Iterator<ImportRow<E>> { + + protected final ImportRow<E> row; + + protected final List<ImportableColumn<E, Object>> columns; + + private ImportRowIterator(List<ImportableColumn<E, Object>> columns) { + + // get once for all columns to import + this.columns = columns; + row = new ImportRow<E>(); + + // read first line since first line is header + boolean hasNext = readRow(); + row.setNext(hasNext); + } + + @Override + public boolean hasNext() { + return row.hasNext(); + } + + @Override + public ImportRow<E> next() + throws NoSuchElementException, ImportRuntimeException { + + if (!hasNext()) { + throw new NoSuchElementException(); + } + + E element = model.newEmptyInstance(); + + row.prepareNextRow(element); + + long lineNumber = row.getLineNumber(); + + boolean strictMode = conf.isStrictMode(); + + for (ImportableColumn<E, Object> field : columns) { + + // read value from csv cell + String value; + try { + value = readValue(field); + } catch (Exception e) { + + + if (strictMode) { + // throw an error + throw new ImportRuntimeException( + _("csv.import.error.unableToReadField", + field.getHeaderName(), lineNumber), e); + } else { + row.addError(new ImportReadErrorInfo<E>( + row, field, e)); + } + continue; + } + + // contravariance ftw + Object parsedValue; + try { + parsedValue = parseValue(field, lineNumber, value); + } catch (Exception e) { + if (strictMode) { + String message = _("csv.import.error.unableToParseValue", + value, field.getHeaderName(), lineNumber) + + "\n" + e.getMessage(); + throw new ImportRuntimeException(message, e); + } else { + row.addError(new ImportParseErrorInfo<E>( + row, field, value, e)); + } + continue; + } + + try { + // set value to element + setValue(field, lineNumber, element, parsedValue); + } catch (Exception e) { + if (strictMode) { + String message = _("csv.import.error.unableToSetValue", + parsedValue, + element.toString(), + lineNumber, field.getHeaderName()); + throw new ImportRuntimeException(message, e); + } else { + row.addError(new ImportSetErrorInfo<E>( + row, field, value, parsedValue, e)); + } + } + } + + // check if there is a next row to read + row.setNext(readRow()); + + return row; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/java/org/nuiton/csv/ImportConf.java b/src/main/java/org/nuiton/csv/ImportConf.java new file mode 100644 index 0000000..203442c --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportConf.java @@ -0,0 +1,87 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.csvreader.CsvReader; + +/** + * To configure an import using the {@link Import2}. + * <p/> + * If you do not give this object to the {@link Import2}, then it will + * instanciate a new one using all default values. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class ImportConf { + + /** + * Flag to turn or not the safetySwitch (see {@link CsvReader#getSafetySwitch()}). + * <p/> + * By default, not used. + */ + protected boolean safetySwitch = false; + + /** + * Flag to use a strict mode (says import will failed at the first error), + * if setted to {@link false}, then errors for each rows will be stored in + * the current row and import will continue to the end. + * <p/> + * By default, used (strict mode). + */ + protected boolean strictMode = true; + + /** + * Flag to ignore header found in a import file and not declared in the + * import model. + * <p/> + * By default, not used (strict mode). + */ + protected boolean ignoreUnknownHeader = false; + + public boolean isSafetySwitch() { + return safetySwitch; + } + + public void setSafetySwitch(boolean safetySwitch) { + this.safetySwitch = safetySwitch; + } + + public boolean isStrictMode() { + return strictMode; + } + + public void setStrictMode(boolean strictMode) { + this.strictMode = strictMode; + } + + public boolean isIgnoreUnknownHeader() { + return ignoreUnknownHeader; + } + + public void setIgnoreUnknownHeader(boolean ignoreUnknownHeader) { + this.ignoreUnknownHeader = ignoreUnknownHeader; + } +} diff --git a/src/main/java/org/nuiton/csv/ImportExportModel.java b/src/main/java/org/nuiton/csv/ImportExportModel.java new file mode 100644 index 0000000..ed33324 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportExportModel.java @@ -0,0 +1,34 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ImportExportModel<E> extends ImportModel<E>, ExportModel<E> { +} diff --git a/src/main/java/org/nuiton/csv/ImportModel.java b/src/main/java/org/nuiton/csv/ImportModel.java new file mode 100644 index 0000000..fe0048b --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportModel.java @@ -0,0 +1,44 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.util.List; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ImportModel<E> { + + char getSeparator(); + + void pushCsvHeaderNames(List<String> headerNames); + + E newEmptyInstance(); + + Iterable<ImportableColumn<E, Object>> getColumnsForImport(); +} diff --git a/src/main/java/org/nuiton/csv/ImportParseErrorInfo.java b/src/main/java/org/nuiton/csv/ImportParseErrorInfo.java new file mode 100644 index 0000000..bbc0076 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportParseErrorInfo.java @@ -0,0 +1,48 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * Exception to be thrown when a parse error occurs at import time. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class ImportParseErrorInfo<E> extends AbstractImportErrorInfo<E> { + + protected final String value; + + public ImportParseErrorInfo(ImportRow<E> row, + ImportableColumn<E, Object> field, + String value, + Throwable cause) { + super(row, field, cause); + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/org/nuiton/csv/ImportReadErrorInfo.java b/src/main/java/org/nuiton/csv/ImportReadErrorInfo.java new file mode 100644 index 0000000..e53befe --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportReadErrorInfo.java @@ -0,0 +1,40 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * Exception to be thrown when a read error occurs at import time. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class ImportReadErrorInfo<E> extends AbstractImportErrorInfo<E> { + + public ImportReadErrorInfo(ImportRow<E> row, + ImportableColumn<E, Object> field, + Throwable cause) { + super(row, field, cause); + } +} diff --git a/src/main/java/org/nuiton/csv/ImportRow.java b/src/main/java/org/nuiton/csv/ImportRow.java new file mode 100644 index 0000000..d4f0d0d --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportRow.java @@ -0,0 +1,104 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.Sets; +import org.apache.commons.collections.CollectionUtils; + +import java.util.Set; + +/** + * Object to box a row to import. + * <p/> + * It contains all the context for the current row to import. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class ImportRow<E> { + + /** Current line number. */ + protected long lineNumber; + + /** Current bean loaded by the import tool for the current row. */ + protected E bean; + + /** Errors found while loading the row into the bean. */ + protected Set<AbstractImportErrorInfo<E>> errors; + + protected boolean next; + + public ImportRow(ImportRow<E> row) { + this.lineNumber = row.getLineNumber(); + this.bean = row.getBean(); + this.errors = Sets.newHashSet(row.getErrors()); + this.setNext(row.hasNext()); + } + + public ImportRow() { + } + + public long getLineNumber() { + return lineNumber; + } + + public E getBean() { + return bean; + } + + public Set<AbstractImportErrorInfo<E>> getErrors() { + return errors; + } + + public boolean isValid() { + return CollectionUtils.isEmpty(errors); + } + + public boolean hasNext() { + return next; + } + + public void setNext(boolean next) { + this.next = next; + } + + public void setLineNumber(long lineNumber) { + this.lineNumber = lineNumber; + } + + public void addError(AbstractImportErrorInfo<E> error) { + if (errors == null) { + errors = Sets.newHashSet(); + } + errors.add(error); + } + + public void prepareNextRow(E bean) { + this.bean = bean; + lineNumber++; + errors = null; + } + +} diff --git a/src/main/java/org/nuiton/csv/ImportRuntimeException.java b/src/main/java/org/nuiton/csv/ImportRuntimeException.java new file mode 100644 index 0000000..b22e9ff --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportRuntimeException.java @@ -0,0 +1,46 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class ImportRuntimeException extends RuntimeException{ + private static final long serialVersionUID = 1L; + + public ImportRuntimeException(String message) { + super(message); + } + + public ImportRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public ImportRuntimeException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/nuiton/csv/ImportSetErrorInfo.java b/src/main/java/org/nuiton/csv/ImportSetErrorInfo.java new file mode 100644 index 0000000..16f7cfe --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportSetErrorInfo.java @@ -0,0 +1,56 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * Exception to be thrown when a set error occurs at import time. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class ImportSetErrorInfo<E> extends AbstractImportErrorInfo<E> { + + protected final String value; + + protected final Object parsedValue; + + public ImportSetErrorInfo(ImportRow<E> row, + ImportableColumn<E, Object> field, + String value, + Object parsedValue, + Throwable cause) { + super(row, field, cause); + this.value = value; + this.parsedValue = parsedValue; + } + + public String getValue() { + return value; + } + + public Object getParsedValue() { + return parsedValue; + } +} diff --git a/src/main/java/org/nuiton/csv/ImportToMap.java b/src/main/java/org/nuiton/csv/ImportToMap.java new file mode 100644 index 0000000..db2a44d --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportToMap.java @@ -0,0 +1,151 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.io.InputStream; +import java.io.Reader; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * A extended {@link Import} to read csv lines into a single map. + * <p/> + * <strong>Warning:</strong> The map used to push values for a csv line is the + * same for all lines, it means you have to copy to your own object. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class ImportToMap extends Import<Map<String, Object>> { + + public static ImportToMap newImportToMap(ImportModel<Map<String, Object>> model, + InputStream inputStream) { + return new ImportToMap(model, inputStream); + } + + public static ImportToMap newImportToMap(ImportModel<Map<String, Object>> model, + Reader reader) { + return new ImportToMap(model, reader); + } + + public static ImportToMap newImportToMap(ImportModel<Map<String, Object>> model, + InputStream inputStream, boolean safetySwitch) { + return new ImportToMap(model, inputStream, safetySwitch); + } + + public static ImportToMap newImportToMap(ImportModel<Map<String, Object>> model, + Reader reader, boolean safetySwitch) { + return new ImportToMap(model, reader, safetySwitch); + } + + @Override + public Iterator<Map<String, Object>> iterator() { + + // obtain headers from csv input and validate the model + prepareAndValidate(); + + return new Iterator<Map<String, Object>>() { + + // read first line since first line is header + boolean hasNext = readRow(); + + // get once for all columns to import + List<ImportableColumn<Map<String, Object>, Object>> columns = + getNonIgnoredHeaders(); + + // to stock the current line number + int lineNumber; + + // the map where to object of a row + final Map<String, Object> element = new HashMap<String, Object>(); + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public Map<String, Object> next() + throws NoSuchElementException, ImportRuntimeException { + + if (!hasNext) { + throw new NoSuchElementException(); + } + + lineNumber += 1; + + // clean all values from the element + element.clear(); + + for (ImportableColumn<Map<String, Object>, Object> field : + columns) { + + // read value from csv cell + String value = readValue(field, lineNumber); + + // contravariance ftw + Object parsedValue = parseValue(field, lineNumber, value); + + // set value to element + setValue(field, lineNumber, element, parsedValue); + } + + // check if there is a next row to read + hasNext = readRow(); + + return element; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + protected ImportToMap(ImportModel<Map<String, Object>> mapImportModel, + InputStream inputStream) { + this(mapImportModel, inputStream, true); + } + + protected ImportToMap(ImportModel<Map<String, Object>> mapImportModel, + Reader reader) { + this(mapImportModel, reader, true); + } + + protected ImportToMap(ImportModel<Map<String, Object>> mapImportModel, + InputStream inputStream, boolean safetySwitch) { + super(mapImportModel, inputStream); + this.reader.setSafetySwitch(safetySwitch); + } + + protected ImportToMap(ImportModel<Map<String, Object>> mapImportModel, + Reader reader, boolean safetySwitch) { + super(mapImportModel, reader); + this.reader.setSafetySwitch(safetySwitch); + } +} diff --git a/src/main/java/org/nuiton/csv/ImportableColumn.java b/src/main/java/org/nuiton/csv/ImportableColumn.java new file mode 100644 index 0000000..2f977f7 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportableColumn.java @@ -0,0 +1,45 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.text.ParseException; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ImportableColumn<E, T> { + String getHeaderName(); + + boolean isMandatory(); + + boolean isIgnored(); + + T parseValue(String value) throws ParseException; + + void setValue(E object, T value) throws Exception; +} diff --git a/src/main/java/org/nuiton/csv/ImportableExportableColumn.java b/src/main/java/org/nuiton/csv/ImportableExportableColumn.java new file mode 100644 index 0000000..63c1ee4 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ImportableExportableColumn.java @@ -0,0 +1,31 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * @param <E> + * @param <T> + */ +public interface ImportableExportableColumn<E, T> extends ImportableColumn<E, T>, ExportableColumn<E, T> { +} diff --git a/src/main/java/org/nuiton/csv/ModelBuilder.java b/src/main/java/org/nuiton/csv/ModelBuilder.java new file mode 100644 index 0000000..79a2391 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ModelBuilder.java @@ -0,0 +1,130 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class ModelBuilder<E> { + + protected Collection<ImportableColumn<E, ?>> columnsForImport = + new LinkedList<ImportableColumn<E, ?>>(); + + protected Collection<ExportableColumn<E, ?>> columnsForExport = + new LinkedList<ExportableColumn<E, ?>>(); + + public <T> ImportableColumn<E, T> newIgnoredColumn(String headerName) { + Column<E, T> newColumn = Column.newImportableColumn(headerName, null, null, true, false); + columnsForImport.add(newColumn); + return newColumn; + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, String propertyName) { + return newMandatoryColumn(headerName, propertyName, Common.STRING); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return newMandatoryColumn(headerName, valueParser, new Common.BeanProperty<E, T>(propertyName)); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, ValueSetter<E, String> valueSetter) { + return newMandatoryColumn(headerName, Common.STRING, valueSetter); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> valueSetter) { + Column<E, T> newColumn = Column.newImportableColumn(headerName, valueParser, valueSetter, false, true); + columnsForImport.add(newColumn); + return newColumn; + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, String propertyName) { + return newOptionalColumn(headerName, propertyName, Common.STRING); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return newOptionalColumn(headerName, valueParser, new Common.BeanProperty<E, T>(propertyName)); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, ValueSetter<E, String> valueSetter) { + return newOptionalColumn(headerName, Common.STRING, valueSetter); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> valueSetter) { + Column<E, T> newColumn = Column.newImportableColumn(headerName, valueParser, valueSetter, false, false); + columnsForImport.add(newColumn); + return newColumn; + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, String propertyName) { + return newColumnForExport(headerName, propertyName, Common.STRING); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, ValueGetter<E, String> valueGetter) { + return newColumnForExport(headerName, valueGetter, Common.STRING); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, String propertyName, ValueFormatter<T> valueFormatter) { + return newColumnForExport(headerName, new Common.BeanProperty<E, T>(propertyName), valueFormatter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, ValueGetter<E, T> valueGetter, ValueFormatter<T> valueFormatter) { + ExportableColumn<E, T> newColumn = Column.newExportableColumn(headerName, valueGetter, valueFormatter, false); + columnsForExport.add(newColumn); + return newColumn; + } + + public ImportableExportableColumn<E, String> newColumnForImportExport(String headerName, String propertyName) { + return newColumnForImportExport(headerName, propertyName, Common.STRING); + } + + public ImportableExportableColumn<E, String> newColumnForImportExport(String headerName, ValueGetterSetter<E, String> valueGetterSetter) { + return newColumnForImportExport(headerName, valueGetterSetter, Common.STRING); + } + + public <T> ImportableExportableColumn<E, T> newColumnForImportExport(String headerName, String propertyName, ValueParserFormatter<T> valueParserFormatter) { + return newColumnForImportExport(headerName, new Common.BeanProperty<E, T>(propertyName), valueParserFormatter); + } + + public <T> ImportableExportableColumn<E, T> newColumnForImportExport(String headerName, ValueGetterSetter<E, T> valueGetterSetter, ValueParserFormatter<T> valueParserFormatter) { + ImportableExportableColumn<E, T> newColumn = Column.newImportableExportableColumn(headerName, valueGetterSetter, valueParserFormatter, false); + columnsForImport.add(newColumn); + columnsForExport.add(newColumn); + return newColumn; + } + + public Collection<ImportableColumn<E, ?>> getColumnsForImport() { + return columnsForImport; + } + + public Collection<ExportableColumn<E, ?>> getColumnsForExport() { + return columnsForExport; + } +} diff --git a/src/main/java/org/nuiton/csv/MyModelBuilder.java b/src/main/java/org/nuiton/csv/MyModelBuilder.java new file mode 100644 index 0000000..3f154d3 --- /dev/null +++ b/src/main/java/org/nuiton/csv/MyModelBuilder.java @@ -0,0 +1,166 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.util.LinkedList; +import java.util.List; + +/** + * @param <E> + */ +public class MyModelBuilder<E> { + + protected char separator; + + protected List<ImportableColumn<E, Object>> columnsForImport = new LinkedList<ImportableColumn<E, Object>>(); + + protected List<ExportableColumn<E, Object>> columnsForExport = new LinkedList<ExportableColumn<E, Object>>(); + + public BuildOrAddColumnStep newEmptyModel(char separator) { + this.separator = separator; + return new BuildOrAddColumnStep(); + } + + public class BuildOrAddColumnStep<E> { + + public ChooseImportStrategyForColumnStep<E> addColumn(String columnName) { + return new ChooseImportStrategyForColumnStep(columnName); + } + + public CsvModel<E> buildModelForExport() { + return new CsvModel<E>(separator, (Iterable) columnsForImport, (Iterable) columnsForExport, null); + } + + public CsvModel<E> buildModelForImport(CsvModel.Factory<E> emptyEFactory) { + return new CsvModel<E>(separator, (Iterable) columnsForImport, (Iterable) columnsForExport, (CsvModel.Factory) emptyEFactory); + } + } + + public class ChooseImportStrategyForColumnStep<E> { + + protected String columnName; + + public ChooseImportStrategyForColumnStep(String columnName) { + this.columnName = columnName; + } + + public ChooseExportStrategyForColumnStep<E> ignoreAtImport() { + Column<E, Object> importableColumn = Column.newImportableColumn(columnName, null, null, true, false); + return new ChooseExportStrategyForColumnStep(columnName, importableColumn); + } + + public ChooseExportStrategyForColumnStep<E> mandatoryAtImport(String propertyName) { + return mandatoryAtImport(propertyName, Common.STRING); + } + + public <T> ChooseExportStrategyForColumnStep<E> mandatoryAtImport(String propertyName, ValueParser<T> valueParser) { + return mandatoryAtImport(valueParser, new Common.BeanProperty<E, T>(propertyName)); + } + + public ChooseExportStrategyForColumnStep<E> mandatoryAtImport(ValueSetter<E, String> valueSetter) { + return mandatoryAtImport(Common.STRING, valueSetter); + } + + public <T> ChooseExportStrategyForColumnStep<E> mandatoryAtImport(ValueParser<T> valueParser, ValueSetter<E, T> valueSetter) { + Column<E, T> importableColumn = Column.newImportableColumn(columnName, valueParser, valueSetter, false, true); + return new ChooseExportStrategyForColumnStep(columnName, importableColumn); + } + + public ChooseExportStrategyForColumnStep<E> optionalAtImport(String propertyName) { + return optionalAtImport(propertyName, Common.STRING); + } + + public <T> ChooseExportStrategyForColumnStep<E> optionalAtImport(String propertyName, ValueParser<T> valueParser) { + return optionalAtImport(valueParser, new Common.BeanProperty<E, T>(propertyName)); + } + + public ChooseExportStrategyForColumnStep<E> optionalAtImport(ValueSetter<E, String> valueSetter) { + return optionalAtImport(Common.STRING, valueSetter); + } + + public <T> ChooseExportStrategyForColumnStep<E> optionalAtImport(ValueParser<T> valueParser, ValueSetter<E, T> valueSetter) { + Column<E, T> importableColumn = Column.newImportableColumn(columnName, valueParser, valueSetter, false, true); + return new ChooseExportStrategyForColumnStep(columnName, importableColumn); + } + + } + + public class ChooseExportStrategyForColumnStep<E> { + + protected String columnName; + + protected Column<E, Object> importableColumn; + + public ChooseExportStrategyForColumnStep(String columnName, Column<E, Object> importableColumn) { + this.columnName = columnName; + this.importableColumn = importableColumn; + } + + public AddColumnStep<E> ignoredAtExport() { + Column<E, Object> exportableColumn = Column.newExportableColumn(columnName, null, null, true); + return new AddColumnStep(importableColumn, exportableColumn); + } + + public AddColumnStep<E> writeAtExport(String propertyName) { + return writeAtExport(propertyName, Common.STRING); + } + + public AddColumnStep<E> writeAtExport(ValueGetter<E, String> valueGetter) { + return writeAtExport(valueGetter, Common.STRING); + } + + public <T> AddColumnStep<E> writeAtExport(String propertyName, ValueFormatter<T> valueFormatter) { + return writeAtExport( new Common.BeanProperty<E, T>(propertyName), valueFormatter); + } + + public <T> AddColumnStep<E> writeAtExport(ValueGetter<E, T> valueGetter, ValueFormatter<T> valueFormatter) { + ExportableColumn<E, T> exportableColumn = Column.newExportableColumn(columnName, valueGetter, valueFormatter, false); + return new AddColumnStep<E>((ImportableColumn) importableColumn, (ExportableColumn) exportableColumn); + } + } + + public class AddColumnStep<E> { + + protected ImportableColumn<E, Object> importableColumn; + + protected ExportableColumn<E, Object> exportableColumn; + + public AddColumnStep(ImportableColumn<E, Object> importableColumn, ExportableColumn<E, Object> exportableColumn) { + this.importableColumn = importableColumn; + this.exportableColumn = exportableColumn; + } + + public BuildOrAddColumnStep<E> add() { + return addIf(true); + } + + public BuildOrAddColumnStep<E> addIf(boolean condition) { + if (condition) { + columnsForImport.add((ImportableColumn) importableColumn); + columnsForExport.add((ExportableColumn) exportableColumn); + } + return new BuildOrAddColumnStep<E>(); + } + } +} diff --git a/src/main/java/org/nuiton/csv/ValidationResult.java b/src/main/java/org/nuiton/csv/ValidationResult.java new file mode 100644 index 0000000..9b7be0c --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValidationResult.java @@ -0,0 +1,109 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.Serializable; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class ValidationResult implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String PROPERTY_SUCCESS = "success"; + + public static final String PROPERTY_MESSAGE = "message"; + + protected boolean success; + + protected String message; + + protected final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public boolean getSuccess() { + return success; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + boolean oldValue = getSuccess(); + this.success = success; + firePropertyChange(PROPERTY_SUCCESS, oldValue, success); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + String oldValue = getMessage(); + this.message = message; + firePropertyChange(PROPERTY_MESSAGE, oldValue, message); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + + @Override + public String toString() { + String toString = ToStringBuilder.reflectionToString(this); + return toString; + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + + protected void firePropertyChange(String propertyName, Object newValue) { + firePropertyChange(propertyName, null, newValue); + } + + +} //ValidationResult diff --git a/src/main/java/org/nuiton/csv/ValueFormatter.java b/src/main/java/org/nuiton/csv/ValueFormatter.java new file mode 100644 index 0000000..5474994 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueFormatter.java @@ -0,0 +1,37 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ValueFormatter<T> { + + String format(T value); + +} diff --git a/src/main/java/org/nuiton/csv/ValueGetter.java b/src/main/java/org/nuiton/csv/ValueGetter.java new file mode 100644 index 0000000..dd4b11f --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueGetter.java @@ -0,0 +1,37 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ValueGetter<E, T> { + + T get(E object) throws Exception; + +} diff --git a/src/main/java/org/nuiton/csv/ValueGetterSetter.java b/src/main/java/org/nuiton/csv/ValueGetterSetter.java new file mode 100644 index 0000000..d5626f8 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueGetterSetter.java @@ -0,0 +1,34 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * TODO + * + * @author bleny <leny@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public interface ValueGetterSetter<E, T> extends ValueGetter<E, T>, ValueSetter<E, T> { +} diff --git a/src/main/java/org/nuiton/csv/ValueParser.java b/src/main/java/org/nuiton/csv/ValueParser.java new file mode 100644 index 0000000..08b5af4 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueParser.java @@ -0,0 +1,35 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.text.ParseException; + +/** + * @param <T> + */ +public interface ValueParser<T> { + + T parse(String value) throws ParseException; + +} diff --git a/src/main/java/org/nuiton/csv/ValueParserFormatter.java b/src/main/java/org/nuiton/csv/ValueParserFormatter.java new file mode 100644 index 0000000..9f22fe3 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueParserFormatter.java @@ -0,0 +1,30 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * @param <T> + */ +public interface ValueParserFormatter<T> extends ValueParser<T>, ValueFormatter<T> { +} diff --git a/src/main/java/org/nuiton/csv/ValueSetter.java b/src/main/java/org/nuiton/csv/ValueSetter.java new file mode 100644 index 0000000..bca4a7a --- /dev/null +++ b/src/main/java/org/nuiton/csv/ValueSetter.java @@ -0,0 +1,34 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin, Tony Chemit, Brendan Le Ny + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +/** + * @param <E> + * @param <T> + */ +public interface ValueSetter<E, T> { + + void set(E object, T value) throws Exception; + +} diff --git a/src/main/java/org/nuiton/csv/ext/AbstractExportModel.java b/src/main/java/org/nuiton/csv/ext/AbstractExportModel.java new file mode 100644 index 0000000..de4ea8b --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/AbstractExportModel.java @@ -0,0 +1,84 @@ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.csv.ExportModel; +import org.nuiton.csv.ExportableColumn; +import org.nuiton.csv.ModelBuilder; +import org.nuiton.csv.ValueFormatter; +import org.nuiton.csv.ValueGetter; + +/** + * Abstract implementation of a {@link ExportModel} to avoid all the + * boilerplate code when creating a new model. + * + * @param <E> type of object to export + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +public abstract class AbstractExportModel<E> implements ExportModel<E> { + + private final char separator; + + protected final ModelBuilder<E> modelBuilder; + + public AbstractExportModel(char separator) { + this.separator = separator; + modelBuilder = new ModelBuilder<E>(); + } + + @Override + public final char getSeparator() { + return separator; + } + + @Override + public final Iterable<ExportableColumn<E, Object>> getColumnsForExport() { + return (Iterable) modelBuilder.getColumnsForExport(); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName) { + return modelBuilder.newColumnForExport(headerName, headerName); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, String propertyName) { + return modelBuilder.newColumnForExport(headerName, propertyName); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, ValueGetter<E, String> eStringValueGetter) { + return modelBuilder.newColumnForExport(headerName, eStringValueGetter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, headerName, valueFormatter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, String propertyName, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, propertyName, valueFormatter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, ValueGetter<E, T> etValueGetter, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, etValueGetter, valueFormatter); + } +} diff --git a/src/main/java/org/nuiton/csv/ext/AbstractImportExportModel.java b/src/main/java/org/nuiton/csv/ext/AbstractImportExportModel.java new file mode 100644 index 0000000..6ed94ef --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/AbstractImportExportModel.java @@ -0,0 +1,177 @@ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.csv.ExportableColumn; +import org.nuiton.csv.ImportExportModel; +import org.nuiton.csv.ImportableColumn; +import org.nuiton.csv.ImportableExportableColumn; +import org.nuiton.csv.ModelBuilder; +import org.nuiton.csv.ValueFormatter; +import org.nuiton.csv.ValueGetter; +import org.nuiton.csv.ValueGetterSetter; +import org.nuiton.csv.ValueParser; +import org.nuiton.csv.ValueParserFormatter; +import org.nuiton.csv.ValueSetter; + +import java.util.List; + +/** + * Abstract implementation of a {@link ImportExportModel} to avoid all the + * boilerplate code when creating a new model. + * + * @param <E> type of object to import/export + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +public abstract class AbstractImportExportModel<E> implements ImportExportModel<E> { + + private final char separator; + + protected final ModelBuilder<E> modelBuilder; + + public AbstractImportExportModel(char separator) { + this.separator = separator; + modelBuilder = new ModelBuilder<E>(); + } + + @Override + public final char getSeparator() { + return separator; + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + } + + @Override + public final Iterable<ExportableColumn<E, Object>> getColumnsForExport() { + return (Iterable) modelBuilder.getColumnsForExport(); + } + + @Override + public final Iterable<ImportableColumn<E, Object>> getColumnsForImport() { + return (Iterable) modelBuilder.getColumnsForImport(); + } + + public <T> ImportableColumn<E, T> newIgnoredColumn(String headerName) { + return modelBuilder.newIgnoredColumn(headerName); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName) { + return modelBuilder.newMandatoryColumn(headerName, headerName); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, String propertyName) { + return modelBuilder.newMandatoryColumn(headerName, propertyName); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, ValueParser<T> valueParser) { + return modelBuilder.newMandatoryColumn(headerName, headerName, valueParser); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return modelBuilder.newMandatoryColumn(headerName, propertyName, valueParser); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, ValueSetter<E, String> eStringValueSetter) { + return modelBuilder.newMandatoryColumn(headerName, eStringValueSetter); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> etValueSetter) { + return modelBuilder.newMandatoryColumn(headerName, valueParser, etValueSetter); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName) { + return modelBuilder.newOptionalColumn(headerName, headerName); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, String propertyName) { + return modelBuilder.newOptionalColumn(headerName, propertyName); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, ValueParser<T> valueParser) { + return modelBuilder.newOptionalColumn(headerName, headerName, valueParser); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return modelBuilder.newOptionalColumn(headerName, propertyName, valueParser); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, ValueSetter<E, String> eStringValueSetter) { + return modelBuilder.newOptionalColumn(headerName, eStringValueSetter); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> etValueSetter) { + return modelBuilder.newOptionalColumn(headerName, valueParser, etValueSetter); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName) { + return modelBuilder.newColumnForExport(headerName, headerName); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, String propertyName) { + return modelBuilder.newColumnForExport(headerName, propertyName); + } + + public ExportableColumn<E, String> newColumnForExport(String headerName, ValueGetter<E, String> eStringValueGetter) { + return modelBuilder.newColumnForExport(headerName, eStringValueGetter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, headerName, valueFormatter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, String propertyName, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, propertyName, valueFormatter); + } + + public <T> ExportableColumn<E, T> newColumnForExport(String headerName, ValueGetter<E, T> etValueGetter, ValueFormatter<T> valueFormatter) { + return modelBuilder.newColumnForExport(headerName, etValueGetter, valueFormatter); + } + + public ImportableExportableColumn<E, String> newColumnForImportExport(String headerName) { + return modelBuilder.newColumnForImportExport(headerName, headerName); + } + + public ImportableExportableColumn<E, String> newColumnForImportExport(String headerName, String propertyName) { + return modelBuilder.newColumnForImportExport(headerName, propertyName); + } + + public ImportableExportableColumn<E, String> newColumnForImportExport(String headerName, ValueGetterSetter<E, String> eStringValueGetterSetter) { + return modelBuilder.newColumnForImportExport(headerName, eStringValueGetterSetter); + } + + public <T> ImportableExportableColumn<E, T> newColumnForImportExport(String headerName, ValueParserFormatter<T> valueParserFormatter) { + return modelBuilder.newColumnForImportExport(headerName, headerName, valueParserFormatter); + } + + public <T> ImportableExportableColumn<E, T> newColumnForImportExport(String headerName, String propertyName, ValueParserFormatter<T> valueParserFormatter) { + return modelBuilder.newColumnForImportExport(headerName, propertyName, valueParserFormatter); + } + + public <T> ImportableExportableColumn<E, T> newColumnForImportExport(String headerName, ValueGetterSetter<E, T> etValueGetterSetter, ValueParserFormatter<T> valueParserFormatter) { + return modelBuilder.newColumnForImportExport(headerName, etValueGetterSetter, valueParserFormatter); + } +} diff --git a/src/main/java/org/nuiton/csv/ext/AbstractImportModel.java b/src/main/java/org/nuiton/csv/ext/AbstractImportModel.java new file mode 100644 index 0000000..dce0954 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/AbstractImportModel.java @@ -0,0 +1,119 @@ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.csv.ImportModel; +import org.nuiton.csv.ImportableColumn; +import org.nuiton.csv.ModelBuilder; +import org.nuiton.csv.ValueParser; +import org.nuiton.csv.ValueSetter; + +import java.util.List; + +/** + * Abstract implementation of a {@link ImportModel} to avoid all the + * boilerplate code when creating a new model. + * + * @param <E> type of object to import + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +public abstract class AbstractImportModel<E> implements ImportModel<E> { + + private final char separator; + + protected final ModelBuilder<E> modelBuilder; + + public AbstractImportModel(char separator) { + this.separator = separator; + modelBuilder = new ModelBuilder<E>(); + } + + @Override + public final char getSeparator() { + return separator; + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + } + + @Override + public final Iterable<ImportableColumn<E, Object>> getColumnsForImport() { + return (Iterable) modelBuilder.getColumnsForImport(); + } + + public <T> ImportableColumn<E, T> newIgnoredColumn(String headerName) { + return modelBuilder.newIgnoredColumn(headerName); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName) { + return modelBuilder.newMandatoryColumn(headerName, headerName); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, String propertyName) { + return modelBuilder.newMandatoryColumn(headerName, propertyName); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, ValueParser<T> valueParser) { + return modelBuilder.newMandatoryColumn(headerName, headerName, valueParser); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return modelBuilder.newMandatoryColumn(headerName, propertyName, valueParser); + } + + public ImportableColumn<E, String> newMandatoryColumn(String headerName, ValueSetter<E, String> eStringValueSetter) { + return modelBuilder.newMandatoryColumn(headerName, eStringValueSetter); + } + + public <T> ImportableColumn<E, T> newMandatoryColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> etValueSetter) { + return modelBuilder.newMandatoryColumn(headerName, valueParser, etValueSetter); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName) { + return modelBuilder.newOptionalColumn(headerName, headerName); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, String propertyName) { + return modelBuilder.newOptionalColumn(headerName, propertyName); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, ValueParser<T> valueParser) { + return modelBuilder.newOptionalColumn(headerName, headerName, valueParser); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, String propertyName, ValueParser<T> valueParser) { + return modelBuilder.newOptionalColumn(headerName, propertyName, valueParser); + } + + public ImportableColumn<E, String> newOptionalColumn(String headerName, ValueSetter<E, String> eStringValueSetter) { + return modelBuilder.newOptionalColumn(headerName, eStringValueSetter); + } + + public <T> ImportableColumn<E, T> newOptionalColumn(String headerName, ValueParser<T> valueParser, ValueSetter<E, T> etValueSetter) { + return modelBuilder.newOptionalColumn(headerName, valueParser, etValueSetter); + } + +} diff --git a/src/main/java/org/nuiton/csv/ext/CsvReaders.java b/src/main/java/org/nuiton/csv/ext/CsvReaders.java new file mode 100644 index 0000000..2af55c6 --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/CsvReaders.java @@ -0,0 +1,57 @@ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import org.nuiton.csv.ImportRuntimeException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; + +/** + * Useful method around csv readers. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +public class CsvReaders { + + public static String[] getHeader(File file, char charSeprator) { + + try { + BufferedReader reader = Files.newReader(file, Charsets.UTF_8); + try { + String header = reader.readLine(); + String[] result = header.split(charSeprator + ""); + return result; + } finally { + reader.close(); + } + } catch (IOException e) { + throw new ImportRuntimeException("Could not obtain header of file " + file, e); + } + } +} diff --git a/src/main/java/org/nuiton/csv/ext/RepeatableExport.java b/src/main/java/org/nuiton/csv/ext/RepeatableExport.java new file mode 100644 index 0000000..bc0720a --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/RepeatableExport.java @@ -0,0 +1,101 @@ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.csv.Export; +import org.nuiton.csv.ExportModel; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * Extends the {@link Export} classes to be able to generate only once + * the header. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +public class RepeatableExport<E> extends Export<E> { + + public static <E> RepeatableExport<E> newExport(ExportModel<E> model, + Iterable<E> data, + boolean writeOnceHeader) { + return new RepeatableExport<E>(model, data, writeOnceHeader); + } + + public static <E> void exportToWriter(ExportModel<E> model, + Iterable<E> data, + Writer writer, + boolean writeOnceHeader) throws Exception { + Export<E> exporter = newExport(model, data, writeOnceHeader); + exporter.write(writer); + } + + public static <E> void exportToFile(ExportModel<E> model, + Iterable<E> data, + File file, + Charset charset, + boolean writeOnceHeader) throws Exception { + Export<E> exporter = newExport(model, data, writeOnceHeader); + exporter.write(file, charset); + } + + public static <E> String exportToString(ExportModel<E> model, + Iterable<E> data, + Charset charset, + boolean writeOnceHeader) throws Exception { + Export<E> exporter = newExport(model, data, writeOnceHeader); + return exporter.toString(charset); + } + + protected final boolean writeOnceHeader; + + protected boolean headerWritten; + + public boolean isHeaderWritten() { + return headerWritten; + } + + protected RepeatableExport(ExportModel<E> model, + Iterable<E> data, + boolean writeOnceHeader) { + super(model, data); + this.writeOnceHeader = writeOnceHeader; + } + + @Override + protected void writeHeader(Writer writer) throws IOException { + if (!writeOnceHeader || !headerWritten) { + + // no header generated, let's do it! + + super.writeHeader(writer); + + // mark it as written + headerWritten = true; + } + } +} diff --git a/src/main/java/org/nuiton/csv/ext/package-info.java b/src/main/java/org/nuiton/csv/ext/package-info.java new file mode 100644 index 0000000..b6c3d8b --- /dev/null +++ b/src/main/java/org/nuiton/csv/ext/package-info.java @@ -0,0 +1,30 @@ +/** + * Offers some extended api. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6 + */ +package org.nuiton.csv.ext; +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ diff --git a/src/main/java/org/nuiton/csv/package-info.java b/src/main/java/org/nuiton/csv/package-info.java new file mode 100644 index 0000000..5e15332 --- /dev/null +++ b/src/main/java/org/nuiton/csv/package-info.java @@ -0,0 +1,31 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * This package contains a framework to import and export data from a + * csv file using a model which permits us to validate what to do. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +package org.nuiton.csv; diff --git a/src/main/resources/i18n/nuiton-csv_en_GB.properties b/src/main/resources/i18n/nuiton-csv_en_GB.properties new file mode 100644 index 0000000..a91f6a2 --- /dev/null +++ b/src/main/resources/i18n/nuiton-csv_en_GB.properties @@ -0,0 +1,8 @@ +csv.import.error.duplicatedHeaders=Fields %s are duplicated. +csv.import.error.missingMandatoryHeaders=The mandatory fields %s are missing +csv.import.error.unableToParseValue=Unable to parse value '%s' (column '%s', line %s) +csv.import.error.unableToReadField=Unable to read value of column '%s' at line %s +csv.import.error.unableToReadHeaders=Unable to read headers +csv.import.error.unableToReadLine=Unable to read line %s +csv.import.error.unableToSetValue=Unable to set value '%s' (object '%s', line %s, column '%s') +csv.import.error.unrecognizedHeaders=Fields %s are not recognized. Accepted fields are %s. diff --git a/src/main/resources/i18n/nuiton-csv_es_ES.properties b/src/main/resources/i18n/nuiton-csv_es_ES.properties new file mode 100644 index 0000000..de7d536 --- /dev/null +++ b/src/main/resources/i18n/nuiton-csv_es_ES.properties @@ -0,0 +1,8 @@ +csv.import.error.duplicatedHeaders=Fields %s are duplicated. +csv.import.error.missingMandatoryHeaders=The mandatory fields %s are missing +csv.import.error.unableToParseValue=Unable to parse value '%s' (column '%s', line %s) +csv.import.error.unableToReadField=Unable to read value of column '%s' at line %s +csv.import.error.unableToReadHeaders=Unable to read headers +csv.import.error.unableToReadLine=Unable to read line %s +csv.import.error.unableToSetValue=Unable to set value '%s' (object'%s', line %s, column '%s') +csv.import.error.unrecognizedHeaders=Fields %s are not recognized. Accepted fields are %s. diff --git a/src/main/resources/i18n/nuiton-csv_fr_FR.properties b/src/main/resources/i18n/nuiton-csv_fr_FR.properties new file mode 100644 index 0000000..3a54560 --- /dev/null +++ b/src/main/resources/i18n/nuiton-csv_fr_FR.properties @@ -0,0 +1,8 @@ +csv.import.error.duplicatedHeaders=Les champs %s sont dupliqués. +csv.import.error.missingMandatoryHeaders=Les champs obligatoires %s sont manquants +csv.import.error.unableToParseValue=Erreur lors de l'interprétation de la valeur '%s' (colonne '%s', ligne %s) +csv.import.error.unableToReadField=Impossible de lire la colonne '%s' à la ligne %s +csv.import.error.unableToReadHeaders=Impossible de lire les en-têtes de colonnes +csv.import.error.unableToReadLine=Impossible de lire la ligne %s +csv.import.error.unableToSetValue=Impossible d'enregistrer la valeur '%s' (objet '%s', ligne %s, column '%s') +csv.import.error.unrecognizedHeaders=Les champs %s ne sont pas reconnus. Les champs possibles sont %s. diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt new file mode 100644 index 0000000..13bde6f --- /dev/null +++ b/src/site/apt/index.apt @@ -0,0 +1,212 @@ +~~~ +~~ #%L +~~ Nuiton CSV +~~ $Id$ +~~ $HeadURL$ +~~ %% +~~ Copyright (C) 2011 CodeLutin +~~ %% +~~ This program is free software: you can redistribute it and/or modify +~~ it under the terms of the GNU Lesser General Public License as +~~ published by the Free Software Foundation, either version 3 of the +~~ License, or (at your option) any later version. +~~ +~~ This program is distributed in the hope that it will be useful, +~~ but WITHOUT ANY WARRANTY; without even the implied warranty of +~~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +~~ GNU General Lesser Public License for more details. +~~ +~~ You should have received a copy of the GNU General Lesser Public +~~ License along with this program. If not, see +~~ <http://www.gnu.org/licenses/lgpl-3.0.html>. +~~ #L% +~~~ + ---- + Nuiton csv + ---- + ---- + 2011-12-14 + ---- + +Présentation + + <<nuiton-csv>> définit une api simple d'import-export au format CSV. Cette API + permet de construire un modèle d'import (et/ou d'export) où les différentes + préocupations sont bien séparées. + +Note + + <<Nuiton-csv>> quitte le projet <nuiton-utils> pour devenir un projet autonome. + + Voici quelques liens sur le nouveau projet: + + * {{{http://svn.nuiton.org/svn/nuiton-csv}svn}} + + * {{{http://nuiton.org/projects/nuiton-csv}forge}} + + * {{{http://maven-site.nuiton.org/nuiton-csv}site}} + + [] + + A noter que le GAV de l'artefact ne change pas (<org.nuiton:nuiton-csv>). + + La dernière version stable dans nuiton-utils est la 2.7; vous pouvez dès à + présent utiliser la version 3.0-alpha-1 de nuiton-csv. + + Pour plus de détails sur les changements importants entre chaque version, + vous pouvez consulter les {{{./versions.html}Notes de versions}}. + +API + +* Import + + Pour un import, et pour chaque ligne à importer, il y a trois choses à faire : + + * lire la donnée depuis la source d'entrée + + * convertir la donnée en objet + + * persister la donnée convertie dans un objet + + La classe <<org.nuiton.csv.Import>> permet simplement d'effectuer des + imports, une fois le modèle crée. + +* Export + + Pour un export et pour chaque objet à persister dans le fichier csv, il y a + aussi trois choses : + + * lire la donnée depuis l'objet à persister + + * convertir la donnée au format texte + + * persister la donnée convertie dans le flux de sortie + + La classe <<org.nuiton.csv.Export>> permet simplement d'effectuer un + export, une fois le modèle d'export crée et les données à persister récupérées. + +API + +* org.nuiton.csv.ValueFormatter + + A faire. + +* org.nuiton.csv.ValueParser + + A faire. + +* org.nuiton.csv.ValueParserFormatter + + A faire. + +* org.nuiton.csv.ImportModel + + A faire. + +* org.nuiton.csv.ExportModel + + A faire. + +* org.nuiton.csv.ImportExportModel + + A faire. + +* org.nuiton.csv.ModelBuilder + + A faire. + +* org.nuiton.csv.Import + + A faire. + +* org.nuiton.csv.Export + + A faire. + +Exemple + +* Construire un modèle d'import/export + +-------------------------------------------------------------------------------- +A faire... +-------------------------------------------------------------------------------- + +* Importer des données + +-------------------------------------------------------------------------------- + +// création du modèle d'import csv +ImportModel<E> csvModel = null; + +// creation d'un importer à partir d'un modèle et d'un reader sur fichier csv +Import<E> importer = Import.newImport(csvModel, reader); + +try { + + // parcours des objets crées à partir de chaque ligne du fichier csv + for (E entity : importer) { + + // A vous :) + + } +} finally { + + // fermeture de l'importer (ne ferme pas le flux d'entrée) + importer.close(); +} +-------------------------------------------------------------------------------- + +* Controler les headers pour construire un modèle dynamque + +Il est possible, dans un ImportModel de ne définir le modèle qu'après lecture des +entetes du fichier csv. Cela permet par exemple: + - de ne pas rendre certains colonne absente obligatoire + - de gerer une casse différente de header + - d'avoir plusieurs nom différent pour le même header + +-------------------------------------------------------------------------------- + +MyImportModel extends AbstractImportModel<Entity> { + + public MyImportModel() { + super(';'); + } + + /** + * Cette methode sera appeler après lecture des headers et avant la validation des entetes + * par rapport au modèle. + */ + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + super.pushCsvHeaderNames(headerNames); + + newMandatoryColumn("Column1"); + newMandatoryColumn("Column2"); + newMandatoryColumn("Column3"); + if (header.contains("Type") { + newMandatoryColumn("Type"); + } else { + // nom alternatif de Type = Ref + newMandatoryColumn("Ref", "Type"); + } + } +} + +-------------------------------------------------------------------------------- + +* Exporter des données + +-------------------------------------------------------------------------------- + +// création du modèle d'export csv +ExportModel<E> csvModel = null; + +// les données à exporter +Iterable<E> datas = null; + +// création d'un exporter +Export<E> exporter = Export.newExport(csvModel, datas); + +// lancement de l'export vers le fichier +exporter.exportToFile(new File("output.csv")); +-------------------------------------------------------------------------------- diff --git a/src/site/apt/versions.apt b/src/site/apt/versions.apt new file mode 100644 index 0000000..be66602 --- /dev/null +++ b/src/site/apt/versions.apt @@ -0,0 +1,34 @@ +~~~ +~~ #%L +~~ Nuiton Config +~~ $Id$ +~~ $HeadURL: http://svn.nuiton.org/svn/nuiton-config/trunk/src/site/apt/index.apt $ +~~ %% +~~ Copyright (C) 2013 CodeLutin +~~ %% +~~ This program is free software: you can redistribute it and/or modify +~~ it under the terms of the GNU Lesser General Public License as +~~ published by the Free Software Foundation, either version 3 of the +~~ License, or (at your option) any later version. +~~ +~~ This program is distributed in the hope that it will be useful, +~~ but WITHOUT ANY WARRANTY; without even the implied warranty of +~~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +~~ GNU General Lesser Public License for more details. +~~ +~~ You should have received a copy of the GNU General Lesser Public +~~ License along with this program. If not, see +~~ <http://www.gnu.org/licenses/lgpl-3.0.html>. +~~ #L% +~~~ + ---- + Nuiton csv + ---- + ---- + 2013-07-23 + ---- + +Utilisation de la version 3.0 + + * Pour passer sur cette version, il faut changer les packages <org.nuiton.util.csv> + en <org.nuiton.csv>. \ No newline at end of file diff --git a/src/site/site_fr.xml b/src/site/site_fr.xml new file mode 100644 index 0000000..21badaf --- /dev/null +++ b/src/site/site_fr.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + Nuiton CSV + $Id$ + $HeadURL$ + %% + Copyright (C) 2013 CodeLutin + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + + +<project name="${project.name}"> + + <skin> + <groupId>org.apache.maven.skins</groupId> + <artifactId>maven-fluido-skin</artifactId> + <version>1.3.0</version> + </skin> + + <custom> + <fluidoSkin> + <topBarEnabled>false</topBarEnabled> + <googleSearch/> + <sideBarEnabled>true</sideBarEnabled> + <searchEnabled>true</searchEnabled> + <sourceLineNumbersEnabled>true</sourceLineNumbersEnabled> + </fluidoSkin> + </custom> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <bannerRight> + <src>http://www.codelutin.com/images/lutinorange-codelutin.png</src> + <href>http://www.codelutin.com</href> + </bannerRight> + + <publishDate position="right" /> + <version position="right" /> + + <poweredBy> + + <logo href="http://maven.apache.org" name="Maven" + img="http://maven-site.chorem.org/public/images/logos/maven-feather.png"/> + + </poweredBy> + + <body> + + <head> + <script type="text/javascript" + src="http://maven-site.chorem.org/public/js/mavenpom-site.js"> + </script> + + <link rel="stylesheet" type="text/css" + href="http://maven-site.chorem.org/public/css/mavenpom-site.css"/> + </head> + + <links> + <item name="Nuiton.org" href="http://nuiton.org"/> + <item name="Code Lutin" href="http://www.codelutin.com"/> + <item name="Libre entreprise" href="http://www.libre-entreprise.org"/> + </links> + + <breadcrumbs> + <item name="${project.name}" + href="${project.url}/index.html"/> + </breadcrumbs> + + <menu name="Utilisateur"> + <item name="Accueil" href="index.html"/> + <item name="Note de versions" href="versions.html"/> + </menu> + + <menu ref="reports"/> + + <footer> + + <div id='projectMetas' + projectversion='${project.version}' + platform='${project.platform}' + projectid='${project.projectId}' + scm='${project.scm.developerConnection}' + scmwebeditorenabled='${project.scmwebeditorEnabled}' + scmwebeditorurl='${project.scmwebeditorUrl}' + siteSourcesType='${project.siteSourcesType}' + piwikEnabled='${project.piwikEnabled}' + piwikId='${project.piwikId}' locale='fr'> + </div> + </footer> + </body> +</project> diff --git a/src/test/java/org/nuiton/csv/ExportTest.java b/src/test/java/org/nuiton/csv/ExportTest.java new file mode 100644 index 0000000..2936574 --- /dev/null +++ b/src/test/java/org/nuiton/csv/ExportTest.java @@ -0,0 +1,90 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.nuiton.util.DateUtil; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ExportTest { + + private static final Log log = LogFactory.getLog(ExportTest.class); + + protected Set<RowBean> oneSizedSet = new HashSet<RowBean>(); + + protected Set<RowBean> twoSizedSet = new HashSet<RowBean>(); + + protected Set<RowBean> fiveSizedSet = new HashSet<RowBean>(); + + protected List<Set<RowBean>> sets; + + protected ExportModel<RowBean> model = new RowBeanExportModel(); + + @Before + public void setUp() { + + oneSizedSet.add(new RowBean(DateUtil.createDate(1, 12, 2011), "Batman", 1, RowBeanEnum.ONE)); + + twoSizedSet.addAll(oneSizedSet); + twoSizedSet.add(new RowBean(DateUtil.createDate(2, 12, 2011), "", 7, RowBeanEnum.TWO)); + + fiveSizedSet.addAll(twoSizedSet); + fiveSizedSet.add(new RowBean(DateUtil.createDate(7, 12, 2011), "", 9, RowBeanEnum.ZERO)); + fiveSizedSet.add(new RowBean(DateUtil.createDate(18, 12, 2011), "", 18, RowBeanEnum.ONE)); + fiveSizedSet.add(new RowBean(DateUtil.createDate(23, 12, 2011), "", 4, RowBeanEnum.TWO)); + + sets = Arrays.asList(new HashSet<RowBean>(), oneSizedSet, twoSizedSet, fiveSizedSet); + } + + @Test + public void testExportToString() throws Exception { + + for (Set<RowBean> set : sets) { + + String csv = Export.exportToString(model, set, Charset.forName("UTF-8")); + + if (log.isDebugEnabled()) { + log.debug("exported csv:\n" + csv); + } + + // 1 header line + one line per RowBean instance + int expectedLineCount = 1 + set.size(); + // number of '\n' in csv + int actualLineCount = csv.split("\n").length; + Assert.assertEquals("exported CSV must have all lines", + expectedLineCount, actualLineCount); + } + + } + +} diff --git a/src/test/java/org/nuiton/csv/Import2Test.java b/src/test/java/org/nuiton/csv/Import2Test.java new file mode 100644 index 0000000..c71cbc4 --- /dev/null +++ b/src/test/java/org/nuiton/csv/Import2Test.java @@ -0,0 +1,172 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.nuiton.util.DateUtil; +import org.nuiton.csv.ext.AbstractImportModel; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Tests the {@link Import2}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.3 + */ +public class Import2Test { + + protected ImportModel<RowBean> importModel; + + @Before + public void setUp() { + AbstractImportModel<RowBean> importModel = new AbstractImportModel<RowBean>(';') { + @Override + public RowBean newEmptyInstance() { + return new RowBean(); + } + }; + importModel.newMandatoryColumn("NUMBER", "number", Common.INTEGER); + importModel.newMandatoryColumn("TITLE", "title", Common.STRING); + importModel.newMandatoryColumn("DATE", "date", new Common.DateValue("yyyy-MM-dd")); + importModel.newMandatoryColumn("ROWBEANENUM", "rowBeanEnum", Common.newEnumByNameParserFormatter(RowBeanEnum.class)); + + this.importModel = importModel; + } + + @Test + public void testSimpleImportWithNoData() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM"; + + List<RowBean> rows; + rows = importContent(importModel, content); + Assert.assertEquals(0, rows.size()); + + content = "DATE;NUMBER;TITLE;ROWBEANENUM\n"; + rows = importContent(importModel, content); + Assert.assertEquals(0, rows.size()); + } + + @Test + public void testSimpleImportWithOneLine() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n2011-12-05;18;\"1ère ligne\";ZERO;"; + + List<RowBean> rows = importContent(importModel, content); + Assert.assertEquals(1, rows.size()); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne", RowBeanEnum.ZERO); + } + + @Test + public void testSimpleImport() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n" + + "2011-12-05;18;\"1ère ligne\";ZERO\n" + + "2011-12-06;19;\"2ème ligne\";ONE\n" + + "2011-12-07;21;\"3ème ligne\";TWO"; + List<RowBean> rows = importContent(importModel, content); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne", RowBeanEnum.ZERO); + assertRowEquals(rows.get(1), DateUtil.createDate(6, 12, 2011), 19, "2ème ligne", RowBeanEnum.ONE); + assertRowEquals(rows.get(2), DateUtil.createDate(7, 12, 2011), 21, "3ème ligne", RowBeanEnum.TWO); + } + + @Test + public void testSimpleImportWithExtraUnknownHEaders() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM;BLABLA\n" + + "2011-12-05;18;\"1ère ligne\";ZERO;BLABLA\n" + + "2011-12-06;19;\"2ème ligne\";ONE;BLABLA\n" + + "2011-12-07;21;\"3ème ligne\";TWO;BLABLA"; + ImportConf conf = new ImportConf(); + conf.setIgnoreUnknownHeader(true); + List<RowBean> rows = importContent(conf, importModel, content); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne", RowBeanEnum.ZERO); + assertRowEquals(rows.get(1), DateUtil.createDate(6, 12, 2011), 19, "2ème ligne", RowBeanEnum.ONE); + assertRowEquals(rows.get(2), DateUtil.createDate(7, 12, 2011), 21, "3ème ligne", RowBeanEnum.TWO); + } + + @Test + public void testSimpleImportWithNotStrictMode() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n" + + "2011-12-05;18;\"1ère ligne\";BOUH!\n" + + "NOT_A_DATE;19;\"2ème ligne\";ONE\n" + + "2011-12-07;AHAH;\"3ème ligne\";TWO"; + ImportConf conf = new ImportConf(); + conf.setStrictMode(false); + List<RowBean> rows = importContent(conf, importModel, content); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne", null); + assertRowEquals(rows.get(1), null, 19, "2ème ligne", RowBeanEnum.ONE); + assertRowEquals(rows.get(2), DateUtil.createDate(7, 12, 2011), null, "3ème ligne", RowBeanEnum.TWO); + } + + protected List<RowBean> importContent(ImportConf conf, ImportModel<RowBean> model, + String content) throws IOException { + Reader reader = new StringReader(content); + try { + Import2<RowBean> rowImport = Import2.newImport(conf, model, reader); + try { + List<RowBean> result = new ArrayList<RowBean>(); + for (ImportRow<RowBean> row : rowImport) { + result.add(row.getBean()); + } + return result; + } finally { + rowImport.close(); + } + } finally { + reader.close(); + } + } + + protected List<RowBean> importContent(ImportModel<RowBean> model, + String content) throws IOException { + Reader reader = new StringReader(content); + try { + Import2<RowBean> rowImport = Import2.newImport(model, reader); + try { + List<RowBean> result = new ArrayList<RowBean>(); + for (ImportRow<RowBean> row : rowImport) { + result.add(row.getBean()); + } + return result; + } finally { + rowImport.close(); + } + } finally { + reader.close(); + } + } + + private void assertRowEquals(RowBean row, Date date, Integer number, String title, RowBeanEnum rowBeanEnum) { + Assert.assertEquals(date, row.getDate()); + Assert.assertEquals(number, row.getNumber()); + Assert.assertEquals(title, row.getTitle()); + Assert.assertEquals(rowBeanEnum, row.getRowBeanEnum()); + } +} diff --git a/src/test/java/org/nuiton/csv/ImportTest.java b/src/test/java/org/nuiton/csv/ImportTest.java new file mode 100644 index 0000000..4cae9c1 --- /dev/null +++ b/src/test/java/org/nuiton/csv/ImportTest.java @@ -0,0 +1,394 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Test; +import org.nuiton.util.DateUtil; +import org.nuiton.csv.Common.BeanProperty; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * Created on 28/11/11 + * + * @author fdesbois <desbois@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @since 2.4 + */ +public class ImportTest { + + + @Test + public void testSimpleImportWithNoData() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM"; + + List<RowBean> rows; + rows = importContent(new SimpleImportModel(), content); + Assert.assertEquals(0, rows.size()); + + content = "DATE;NUMBER;TITLE;ROWBEANENUM\n"; + rows = importContent(new SimpleImportModel(), content); + Assert.assertEquals(0, rows.size()); + } + + @Test + public void testSimpleImportWithOneLine() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n2011-12-05;18;\"1ère ligne\";ZERO;"; + + List<RowBean> rows = importContent(new SimpleImportModel(), content); + Assert.assertEquals(1, rows.size()); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne",RowBeanEnum.ZERO); + } + + /** + * Test with {@link SimpleImportModel} that directly implements necessary {@link ImportableColumn}. + * + * @throws Exception for errors + */ + @Test + public void testSimpleImport() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n" + + "2011-12-05;18;\"1ère ligne\";ZERO\n" + + "2011-12-06;19;\"2ème ligne\";ONE\n" + + "2011-12-07;21;\"3ème ligne\";TWO"; + List<RowBean> rows = importContent(new SimpleImportModel(), content); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne",RowBeanEnum.ZERO); + assertRowEquals(rows.get(1), DateUtil.createDate(6, 12, 2011), 19, "2ème ligne",RowBeanEnum.ONE); + assertRowEquals(rows.get(2), DateUtil.createDate(7, 12, 2011), 21, "3ème ligne",RowBeanEnum.TWO); + } + + /** + * Test with {@link ColumnImportModel} that uses {@link Column} and {@link Common} tools. + * + * @throws Exception for errors + */ + @Test + public void testColumnImport() throws Exception { + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n" + + "2011-12-05;18;\"1ère ligne\";2\n" + + "2011-12-06;19;\"2ème ligne\";1\n" + + "2011-12-07;21;\"3ème ligne\";0"; + List<RowBean> rows = importContent(new ColumnImportModel(), content); + assertRowEquals(rows.get(0), DateUtil.createDate(5, 12, 2011), 18, "1ère ligne",RowBeanEnum.TWO); + assertRowEquals(rows.get(1), DateUtil.createDate(6, 12, 2011), 19, "2ème ligne",RowBeanEnum.ONE); + assertRowEquals(rows.get(2), DateUtil.createDate(7, 12, 2011), 21, "3ème ligne",RowBeanEnum.ZERO); + } + + protected List<RowBean> importContent(ImportModel<RowBean> model, + String content) throws IOException { + Reader reader = new StringReader(content); + try { + Import<RowBean> rowImport = Import.newImport(model, reader); + try { + List<RowBean> result = new ArrayList<RowBean>(); + for (RowBean row : rowImport) { + result.add(row); + } + return result; + } finally { + rowImport.close(); + } + } finally { + reader.close(); + } + } + + private void assertRowEquals(RowBean row, Date date, Integer number, String title,RowBeanEnum rowBeanEnum) { + Assert.assertEquals(date, row.getDate()); + Assert.assertEquals(number, row.getNumber()); + Assert.assertEquals(title, row.getTitle()); + Assert.assertEquals(rowBeanEnum, row.getRowBeanEnum()); + } + + private static class ColumnImportModel implements ImportModel<RowBean> { + + @Override + public char getSeparator() { + return ';'; + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + } + + @Override + public RowBean newEmptyInstance() { + return new RowBean(); + } + + @SuppressWarnings({"unchecked"}) + @Override + public Collection<ImportableColumn<RowBean, Object>> getColumnsForImport() { + List<ImportableColumn<RowBean, Object>> result = new ArrayList<ImportableColumn<RowBean, Object>>(); + // Column types are not checked but safe because Object is necessary, and element type is always Row + result.add(getTitleColumn()); + result.add(getNumberColumn()); + result.add(getDateColumn()); + result.add(getEnumColumn()); + return result; + } + + private Column getTitleColumn() { + return Column.newImportableColumn( + "TITLE", + Common.STRING, + withBeanSetter("title", String.class), + false, + true + ); + } + + private Column getDateColumn() { + return Column.newImportableColumn( + "DATE", + withDateParser(), + withBeanSetter("date", Date.class), + false, + true + ); + } + + private Column getNumberColumn() { + return Column.newImportableColumn( + "NUMBER", + Common.INTEGER, + withBeanSetter("number", Integer.class), + false, + true + ); + } + + private Column getEnumColumn() { + return Column.newImportableColumn( + "ROWBEANENUM", + Common.newEnumByOrdinalParserFormatter(RowBeanEnum.class), + withBeanSetter("rowBeanEnum", RowBeanEnum.class), + false, + true + ); + } + @SuppressWarnings({"UnusedParameters"}) + private <T> ValueSetter<RowBean, T> withBeanSetter(String propertyName, Class<T> propertyClass) { + // propertyClass is not used but useful to check type + return new BeanProperty<RowBean, T>(propertyName); + } + + private ValueParser<Date> withDateParser() { + return new ValueParser<Date>() { + + @Override + public Date parse(String value) + throws ParseException { + return DateUtils.parseDate(value, "yyyy-MM-dd"); + } + }; + } + } + + private static class SimpleImportModel implements ImportModel<RowBean> { + + private Log log = LogFactory.getLog(SimpleImportModel.class); + + @Override + public char getSeparator() { + return ';'; + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + if (log.isDebugEnabled()) { + log.debug("Headers are : " + headerNames); + } + } + + @Override + public RowBean newEmptyInstance() { + return new RowBean(); + } + + @Override + public Collection<ImportableColumn<RowBean, Object>> getColumnsForImport() { + List<ImportableColumn<RowBean, Object>> result = new ArrayList<ImportableColumn<RowBean, Object>>(); + result.add(getTitleImportable()); + result.add(getNumberImportable()); + result.add(getDateImportable()); + result.add(getEnumImportable()); + return result; + } + + private ImportableColumn<RowBean, Object> getTitleImportable() { + return new AbstractImportableColumn<RowBean, String>() { + + @Override + public String getHeaderName() { + return "TITLE"; + } + + @Override + public boolean isMandatory() { + return true; + } + + @Override + public boolean isIgnored() { + return false; + } + + @Override + public String parseValue(String value) { + return value; + } + + @Override + public void update(RowBean object, + String value) + throws Exception { + object.setTitle(value); + } + }; + } + + private ImportableColumn<RowBean, Object> getDateImportable() { + return new AbstractImportableColumn<RowBean, Date>() { + + @Override + public String getHeaderName() { + return "DATE"; + } + + @Override + public boolean isMandatory() { + return true; + } + + @Override + public boolean isIgnored() { + return false; + } + + @Override + public Date parseValue(String value) + throws ParseException { + return DateUtils.parseDate(value, "yyyy-MM-dd"); + } + + @Override + public void update(RowBean object, + Date value) + throws Exception { + object.setDate(value); + } + }; + } + + private ImportableColumn<RowBean, Object> getNumberImportable() { + return new AbstractImportableColumn<RowBean, Integer>() { + + @Override + public String getHeaderName() { + return "NUMBER"; + } + + @Override + public boolean isMandatory() { + return true; + } + + @Override + public boolean isIgnored() { + return false; + } + + @Override + public Integer parseValue(String value) { + return Integer.parseInt(value); + } + + @Override + public void update(RowBean object, + Integer value) + throws Exception { + object.setNumber(value); + } + }; + } + + private ImportableColumn<RowBean, Object> getEnumImportable() { + return new AbstractImportableColumn<RowBean, RowBeanEnum>() { + + @Override + public String getHeaderName() { + return "ROWBEANENUM"; + } + + @Override + public boolean isMandatory() { + return true; + } + + @Override + public boolean isIgnored() { + return false; + } + + @Override + public RowBeanEnum parseValue(String value) { + return RowBeanEnum.valueOf(value); + } + + @Override + public void update(RowBean object, + RowBeanEnum value) + throws Exception { + object.setRowBeanEnum(value); + } + }; + } + } + + private static abstract class AbstractImportableColumn<E, F> implements ImportableColumn<E, Object> { + + @Override + public void setValue(E object, + Object value) + throws Exception { + update(object, (F) value); + } + + protected abstract void update(E object, F value) + throws Exception; + } + +} // $Id$ diff --git a/src/test/java/org/nuiton/csv/MyModelBuilderTest.java b/src/test/java/org/nuiton/csv/MyModelBuilderTest.java new file mode 100644 index 0000000..1af869c --- /dev/null +++ b/src/test/java/org/nuiton/csv/MyModelBuilderTest.java @@ -0,0 +1,71 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import org.apache.commons.io.IOUtils; +import org.junit.Test; + +public class MyModelBuilderTest { + + @Test + public void testNewEmptyModel() throws Exception { + + String content = "DATE;NUMBER;TITLE;ROWBEANENUM\n" + + "25/12/2011;18;\"1ère ligne\";ZERO\n" + + "26/12/2011;19;\"2ème ligne\";ONE\n" + + "27/12/2011;21;\"3ème ligne\";TWO"; + + CsvModel.Factory<RowBean> emptyRowBeanFactory = new CsvModel.Factory<RowBean>() { + @Override + public RowBean newEmpty() { + return new RowBean(); + } + }; + + CsvModel<RowBean> model = new MyModelBuilder<RowBean>().newEmptyModel(';') + .addColumn("DATE") + .mandatoryAtImport("date", Common.DAY) + .writeAtExport("date", Common.DAY) + .add() + .addColumn("TITLE") + .mandatoryAtImport("title") + .ignoredAtExport() + .add() + .addColumn("NUMBER") + .mandatoryAtImport("number", Common.INTEGER) + .ignoredAtExport() + .add() + .addColumn("ROWBEANENUM") + .mandatoryAtImport("rowBeanEnum", Common.newEnumByNameParserFormatter(RowBeanEnum.class)) + .ignoredAtExport() + .add() + .buildModelForImport(emptyRowBeanFactory); + + Import<RowBean> rowBeans = Import.newImport(model, IOUtils.toInputStream(content, "UTF-8")); + for (RowBean rowBean : rowBeans) { + System.out.println(rowBean); + } + } + +} diff --git a/src/test/java/org/nuiton/csv/RowBean.java b/src/test/java/org/nuiton/csv/RowBean.java new file mode 100644 index 0000000..e73c5a6 --- /dev/null +++ b/src/test/java/org/nuiton/csv/RowBean.java @@ -0,0 +1,112 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +import java.util.Date; + +public class RowBean { + + private Date date; + + private String title; + + private Integer number; + + private RowBeanEnum rowBeanEnum; + + public RowBean() { + } + + public RowBean(Date date, String title, Integer number, RowBeanEnum rowBeanEnum) { + this.date = date; + this.title = title; + this.number = number; + this.rowBeanEnum = rowBeanEnum; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Integer getNumber() { + return number; + } + + public void setNumber(Integer number) { + this.number = number; + } + + public RowBeanEnum getRowBeanEnum() { + return rowBeanEnum; + } + + public void setRowBeanEnum(RowBeanEnum rowBeanEnum) { + this.rowBeanEnum = rowBeanEnum; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RowBean)) return false; + + RowBean row = (RowBean) o; + + if (!date.equals(row.date)) return false; + if (!number.equals(row.number)) return false; + if (!rowBeanEnum.equals(row.rowBeanEnum)) return false; + return title.equals(row.title); + + } + + @Override + public int hashCode() { + int result = date.hashCode(); + result = 31 * result + title.hashCode(); + result = 31 * result + number.hashCode(); + result = 31 * result + rowBeanEnum.hashCode(); + return result; + } + + @Override + public String toString() { + return "RowBean{" + + "date=" + date + + ", title='" + title + '\'' + + ", number=" + number + + ", rowBeanEnum=" + rowBeanEnum + + '}'; + } +} diff --git a/src/test/java/org/nuiton/csv/RowBeanEnum.java b/src/test/java/org/nuiton/csv/RowBeanEnum.java new file mode 100644 index 0000000..646b277 --- /dev/null +++ b/src/test/java/org/nuiton/csv/RowBeanEnum.java @@ -0,0 +1,35 @@ +package org.nuiton.csv; + +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since TODO + */ +public enum RowBeanEnum { + ZERO,ONE,TWO +} diff --git a/src/test/java/org/nuiton/csv/RowBeanExportModel.java b/src/test/java/org/nuiton/csv/RowBeanExportModel.java new file mode 100644 index 0000000..8080b9b --- /dev/null +++ b/src/test/java/org/nuiton/csv/RowBeanExportModel.java @@ -0,0 +1,41 @@ +/* + * #%L + * Nuiton CSV + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2011 - 2012 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.csv; + +class RowBeanExportModel implements ExportModel<RowBean> { + + @Override + public char getSeparator() { + return ';'; + } + + @Override + public Iterable<ExportableColumn<RowBean, Object>> getColumnsForExport() { + ModelBuilder<RowBean> modelBuilder = new ModelBuilder<RowBean>(); + modelBuilder.newColumnForExport("DATE", "date", Common.DAY); + modelBuilder.newColumnForExport("TITLE", "title"); + modelBuilder.newColumnForExport("NUMBER", "number", Common.INTEGER); + return (Iterable) modelBuilder.getColumnsForExport(); + } +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties new file mode 100644 index 0000000..ae431b8 --- /dev/null +++ b/src/test/resources/log4j.properties @@ -0,0 +1,33 @@ +### +# #%L +# Nuiton CSV +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Global logging configuration +log4j.rootLogger=ERROR, stdout + +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n + +# package level +log4j.logger.org.nuiton.util=INFO -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
participants (1)
-
nuiton.org scm