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>.