All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleflatmapper.csv.CsvWriter Maven / Gradle / Ivy

Go to download

Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.

The newest version!
package org.simpleflatmapper.csv;

import org.simpleflatmapper.converter.ContextualConverter;
import org.simpleflatmapper.converter.ConverterService;
import org.simpleflatmapper.converter.DefaultContextFactoryBuilder;
import org.simpleflatmapper.converter.ToStringConverter;
import org.simpleflatmapper.csv.mapper.FieldMapperToAppendableFactory;
import org.simpleflatmapper.lightningcsv.CellWriter;
import org.simpleflatmapper.lightningcsv.CsvCellWriter;
import org.simpleflatmapper.map.FieldMapper;
import org.simpleflatmapper.map.MappingContext;
import org.simpleflatmapper.map.PropertyWithGetter;
import org.simpleflatmapper.map.mapper.ContextualSourceFieldMapperImpl;
import org.simpleflatmapper.map.property.FormatProperty;
import org.simpleflatmapper.map.property.FieldMapperColumnDefinition;
import org.simpleflatmapper.map.MapperConfig;
import org.simpleflatmapper.reflect.ReflectionService;
import org.simpleflatmapper.reflect.meta.ClassMeta;
import org.simpleflatmapper.reflect.meta.PropertyMeta;
import org.simpleflatmapper.util.Consumer;
import org.simpleflatmapper.util.ErrorHelper;
import org.simpleflatmapper.util.Predicate;
import org.simpleflatmapper.util.TypeHelper;
import org.simpleflatmapper.util.TypeReference;

import java.io.IOException;
import java.lang.reflect.Type;
import java.text.Format;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A CsvWriter allows the caller to write object of type T to an appendable in a specified format. See {@link CsvWriter#from(Class)} to create one.
 * 

* The DSL allows to create a CsvWriter easily. The CsvWriter will by default append the headers on the call to {@link CsvWriter.CsvWriterDSL#to(Appendable)} * Because the DSL create a mapper it is better to cache the {@link CsvWriter.CsvWriterDSL}. *
* * CsvWriter csvWriter = CsvWriter.from(MyObject.class).to(myWriter);
* csvWriter.append(obj1).append(obj2);
*
*
* You can deactivate that by calling {@link CsvWriter.CsvWriterDSL#skipHeaders()} *
* * CsvWriter csvWriter = CsvWriter.from(MyObject.class).skipHeaders().to(myWriter);
*
*
* You can also specified the property names. *
* * CsvWriter csvWriter = CsvWriter.from(MyObject.class).columns("id", "name").to(myWriter);
*
*
* Or add a property with a specified format *
* * CsvWriter csvWriter = CsvWriter.from(MyObject.class).columns("date", new SimpleDateFormat("yyyyMMdd")).to(myWriter);
*
* * @param the type of object to write */ public class CsvWriter { private final FieldMapper mapper; private final Appendable appendable; private final MappingContext mappingContext; private CsvWriter(FieldMapper mapper, Appendable appendable, MappingContext mappingContext) { this.mapper = mapper; this.appendable = appendable; this.mappingContext = mappingContext; } /** * write the specified value to the underlying appendable. * @param value the value to write * @return the current writer * @throws IOException If an I/O error occurs */ public CsvWriter append(T value) throws IOException { try { mapper.mapTo(value, appendable, mappingContext); } catch(Exception e) { ErrorHelper.rethrow(e); } return this; } /** * Create a DSL on the specified type. * @param type the type of object to write * @param the type * @return a DSL on the specified type */ public static CsvWriterDSL from(Class type) { return from((Type)type); } /** * Create a DSL on the specified type. * @param typeReference the type of object to write * @param the type * @return a DSL on the specified type */ public static CsvWriterDSL from(TypeReference typeReference) { return from(typeReference.getType()); } /** * Create a DSL on the specified type. * @param type the type of object to write * @param the type * @return a DSL on the specified type */ public static CsvWriterDSL from(Type type) { ClassMeta classMeta = ReflectionService.newInstance().getClassMeta(type); CellWriter cellWriter = CsvCellWriter.DEFAULT_WRITER; CsvWriterBuilder builder = CsvWriterBuilder .newBuilder(classMeta, cellWriter); MapperConfig mapperConfig = MapperConfig.fieldMapperConfig(); try { String[] headers = defaultHeaders(classMeta); for(String header : headers) { builder.addColumn(header); } ContextualSourceFieldMapperImpl mapper = builder.mapper(); return new DefaultCsvWriterDSL( CsvWriter.toColumnDefinitions(headers), cellWriter, mapper, classMeta, mapperConfig, false); } catch (UnsupportedOperationException e) { return new NoColumnCsvWriterDSL( cellWriter, classMeta, mapperConfig, false); } } private static String[] defaultHeaders(ClassMeta classMeta) { List columns = new ArrayList(); addDefaultHeaders(classMeta, "", columns); return columns.toArray(new String[0]); } private static

void addDefaultHeaders(final ClassMeta

classMeta, final String prefix, final List columns) { classMeta.forEachProperties(new Consumer>() { @Override public void accept(PropertyMeta propertyMeta) { if (! PropertyWithGetter.INSTANCE.test(propertyMeta)) return; String currentName = prefix + propertyMeta.getPath(); if (!canWrite(propertyMeta.getPropertyType())) { addDefaultHeaders(propertyMeta.getPropertyClassMeta(), currentName + "_", columns); } else { columns.add(toDelimiterSeparated(currentName)); } } }); } private static String toDelimiterSeparated(String str) { StringBuilder sb = new StringBuilder(str.length()); boolean lastWasUpperCase = false; for(int i = 0; i < str.length(); i ++) { char c = str.charAt(i); if (Character.isUpperCase(c)) { if (lastWasUpperCase) { sb.append(c); } else { if (i > 0) { sb.append('_'); } sb.append(Character.toLowerCase(c)); } lastWasUpperCase = true; } else { lastWasUpperCase = false; sb.append(c); } } return sb.toString(); } private static boolean canWrite(Type type) { ContextualConverter converter = ConverterService.getInstance().findConverter(type, CharSequence.class, new DefaultContextFactoryBuilder()); return (converter != null && (! (converter instanceof ToStringConverter) || allowToStringConverter(type) ) ); } private static boolean allowToStringConverter(Type type) { return TypeHelper.isPrimitive(type) || TypeHelper.isEnum(type) || TypeHelper.isInPackage(type, new Predicate() { @Override public boolean test(String s) { return s.startsWith("java."); } }) ; } @SuppressWarnings("unchecked") private static Column[] toColumnDefinitions(String[] header) { Column[] columnDefinitions = new Column[header.length]; int offset = 0; return toColumnDefinitions(header, columnDefinitions, offset); } private static Column[] toColumnDefinitions(String[] header, Column[] columnDefinitions, int offset) { FieldMapperColumnDefinition identity = FieldMapperColumnDefinition.identity(); for(int i = 0; i < header.length; i++) { columnDefinitions[i + offset] = new Column(header[i], identity); } return columnDefinitions; } /** * the csv writer DSL * @param the type of object to write */ public static class CsvWriterDSL { protected final Column[] columns; protected final ContextualSourceFieldMapperImpl mapper; protected final CellWriter cellWriter; protected final ClassMeta classMeta; protected final MapperConfig mapperConfig; protected final boolean skipHeaders; private CsvWriterDSL( Column[] columns, CellWriter cellWriter, ContextualSourceFieldMapperImpl mapper, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { this.columns = columns; this.mapper = mapper; this.cellWriter = cellWriter; this.classMeta = classMeta; this.mapperConfig = mapperConfig; this.skipHeaders = skipHeaders; } /** * Create a writer on the specified appendable for the type T * @param appendable the appendable to write to * @return a CsvWriter on the specified appendable * @throws IOException If an I/O error occurs */ public CsvWriter to(Appendable appendable) throws IOException { if (!skipHeaders) { addHeaders(appendable); } return new CsvWriter(mapper, appendable, mapper.newMappingContext()); } private void addHeaders(Appendable appendable) throws IOException { for(int i = 0; i < columns.length; i++) { if (i != 0) { cellWriter.nextCell(appendable); } cellWriter.writeValue(columns[i].name(), appendable); } cellWriter.endOfRow(appendable); } /** * Create a new DSL object identical to the current one but and append the specified columns * @param columnNames the list of property names * @return the new DSL */ @SuppressWarnings("unchecked") public CsvWriterDSL columns(String... columnNames) { Column[] newColumns = Arrays.copyOf(columns, columns.length + columnNames.length); toColumnDefinitions(columnNames, newColumns, columns.length); return newColumnMapDSL(classMeta, newColumns, mapperConfig, cellWriter, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified property added. * @param column the property name * @param property the property properties * @return the new DSL */ @SuppressWarnings("unchecked") public CsvWriterDSL column(String column, Object... property) { Column[] newColumns = Arrays.copyOf(columns, columns.length + 1); FieldMapperColumnDefinition columnDefinition = FieldMapperColumnDefinition.identity().add(property); newColumns[columns.length] = new Column(column, columnDefinition); return newColumnMapDSL(classMeta, newColumns, mapperConfig, cellWriter, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified property added. * @param column the property name * @param format the property formatter * @return the new DSL */ public CsvWriterDSL column(String column, Format format) { return column(column, new FormatProperty(format)); } /** * Create a new DSL object identical to the current one but with the specified classMeta. * @param classMeta the classMeta * @return the new DSL */ public CsvWriterDSL classMeta(ClassMeta classMeta) { return newMapDSL(classMeta, columns, mapperConfig, cellWriter, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified mapperConfig. * @param mapperConfig the mapperConfig * @return the new DSL */ public CsvWriterDSL mapperConfig(MapperConfig mapperConfig) { return newMapDSL(classMeta, columns, mapperConfig, cellWriter, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified cellWriter. * @param cellWriter the cellWriter * @return the new DSL */ public CsvWriterDSL cellWriter(CellWriter cellWriter) { return newMapDSL(classMeta, columns, mapperConfig, cellWriter, skipHeaders); } public CsvWriterDSL separator(char separator) { if (cellWriter instanceof CsvCellWriter) { return newMapDSL(classMeta, columns, mapperConfig, ((CsvCellWriter)cellWriter).separator(separator), skipHeaders); } throw new IllegalStateException("Custom cell writer set, cannot use schema to alter it"); } public CsvWriterDSL quote(char quote) { if (cellWriter instanceof CsvCellWriter) { return newMapDSL(classMeta, columns, mapperConfig, ((CsvCellWriter)cellWriter).quote(quote), skipHeaders); } throw new IllegalStateException("Custom cell writer set, cannot use schema to alter it"); } public CsvWriterDSL escape(char escape) { if (cellWriter instanceof CsvCellWriter) { return newMapDSL(classMeta, columns, mapperConfig, ((CsvCellWriter)cellWriter).escape(escape), skipHeaders); } throw new IllegalStateException("Custom cell writer set, cannot use schema to alter it"); } public CsvWriterDSL endOfLine(String endOfLine) { if (cellWriter instanceof CsvCellWriter) { return newMapDSL(classMeta, columns, mapperConfig, ((CsvCellWriter)cellWriter).endOfLine(endOfLine), skipHeaders); } throw new IllegalStateException("Custom cell writer set, cannot use schema to alter it"); } public CsvWriterDSL alwaysEscape() { if (cellWriter instanceof CsvCellWriter) { return newMapDSL(classMeta, columns, mapperConfig, ((CsvCellWriter)cellWriter).alwaysEscape(), skipHeaders); } throw new IllegalStateException("Custom cell writer set, cannot use schema to alter it"); } /** * Create a new DSL object identical to the current one except it will not append the headers to the appendable. * @return the new DSL */ public CsvWriterDSL skipHeaders() { return newMapDSL(classMeta, columns, mapperConfig, cellWriter, true); } public MapperConfig mapperConfig() { return mapperConfig; } protected CsvWriterDSL newColumnMapDSL( ClassMeta classMeta, Column[] columns, MapperConfig mapperConfig, CellWriter cellWriter, boolean skipHeaders) { CsvWriterBuilder builder = new CsvWriterBuilder(classMeta, mapperConfig, new FieldMapperToAppendableFactory(cellWriter), cellWriter); for( Column col : columns) { builder.addColumn(col.name(), col.definition()); } ContextualSourceFieldMapperImpl mapper = builder.mapper(); return new CsvWriterDSL(columns, cellWriter, mapper, classMeta, mapperConfig, skipHeaders); } protected CsvWriterDSL newMapDSL( ClassMeta classMeta, Column[] columns, MapperConfig mapperConfig, CellWriter cellWriter, boolean skipHeaders) { CsvWriterBuilder builder = new CsvWriterBuilder(classMeta, mapperConfig, new FieldMapperToAppendableFactory(cellWriter), cellWriter); for( Column col : columns) { builder.addColumn(col.name(), col.definition()); } ContextualSourceFieldMapperImpl mapper = builder.mapper(); return newCsvWriterDSL(columns, cellWriter, mapper, classMeta, mapperConfig, skipHeaders); } protected CsvWriterDSL newCsvWriterDSL(Column[] columns, CellWriter cellWriter, ContextualSourceFieldMapperImpl mapper, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { return new CsvWriterDSL(columns, cellWriter, mapper, classMeta, mapperConfig, skipHeaders); } } public static class NoColumnCsvWriterDSL extends CsvWriterDSL { @SuppressWarnings("unchecked") public NoColumnCsvWriterDSL( CellWriter cellWriter, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { super(new Column[0], cellWriter, null, classMeta, mapperConfig, skipHeaders); } @Override public CsvWriter to(Appendable appendable) throws IOException { throw new IllegalStateException("No column defined"); } @Override protected NoColumnCsvWriterDSL newCsvWriterDSL(Column[] columns, CellWriter cellWriter, ContextualSourceFieldMapperImpl mapper, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { return new NoColumnCsvWriterDSL(cellWriter, classMeta, mapperConfig, skipHeaders); } } public static class DefaultCsvWriterDSL extends CsvWriterDSL { private DefaultCsvWriterDSL( Column[] columns, CellWriter cellWriter, ContextualSourceFieldMapperImpl mapper, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { super(columns, cellWriter, mapper, classMeta, mapperConfig, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified columns instead of the default ones. * @param columnNames the list of property names * @return the new DSL */ public CsvWriterDSL columns(String... columnNames) { return newColumnMapDSL(classMeta, CsvWriter.toColumnDefinitions(columnNames), mapperConfig, cellWriter, skipHeaders); } /** * Create a new DSL object identical to the current one but with the specified property instead of the default ones. * @param column the property name * @param property the property properties * @return the new DSL */ @SuppressWarnings("unchecked") public CsvWriterDSL column(String column, Object... property) { Column[] newColumns = new Column[1]; FieldMapperColumnDefinition columnDefinition = FieldMapperColumnDefinition.identity().add(property); newColumns[0] = new Column(column, columnDefinition); return newColumnMapDSL(classMeta, newColumns, mapperConfig, cellWriter, skipHeaders); } @Override protected CsvWriterDSL newCsvWriterDSL(Column[] columns, CellWriter cellWriter, ContextualSourceFieldMapperImpl mapper, ClassMeta classMeta, MapperConfig mapperConfig, boolean skipHeaders) { return new DefaultCsvWriterDSL(columns, cellWriter, mapper, classMeta, mapperConfig, skipHeaders); } } // Tuple2> public static class Column { private final String name; private final FieldMapperColumnDefinition definition; public Column(String name, FieldMapperColumnDefinition definition) { this.name = name; this.definition = definition; } public String name() { return name; } public FieldMapperColumnDefinition definition() { return definition; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy