Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 80 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,41 +66,101 @@ Export/Writing
Here is an export example:
```java
Windmill
.export(Arrays.asList(bean1, bean2, bean3))
.withHeaderMapping(
new ExportHeaderMapping<Bean>()
.add("Name", Bean::getName)
.add("User login", bean -> bean.getUser().getLogin())
)
.asExcel()
.writeTo(new FileOutputStream("Export.xlsx"));
.<Bean>exporter()
.withHeaders()
.column("Name", Bean::getName)
.column("User login", bean -> bean.getUser().getLogin())
.asExcel()
.writeRows(Arrays.asList(bean1, bean2, bean3))
.writeInto(new FileOutputStream("Export.xlsx"));
```
And an import example:
```java
Windmill
.importer()
.source(FileSource.of(new FileInputStream("Export.xlsx")))
.withHeaders()
.stream()
.map(row -> new Bean(row.cell("Name").asString(), new User(row.cell(1).asString())))
.collect(Collectors.toList());
```

Options can be passed to the exporter, for example with CSV files,
it is possible to specify multiple parameters like the separator character or the escape character:
```java
Windmill
.export(Arrays.asList(bean1, bean2, bean3))
.withNoHeaderMapping(Bean::getName, bean -> bean.getUser().getLogin())
.asCsv(ExportCsvConfig.builder().separator(';').escapeChar('"').build());
.toByteArray();
.<Bean>exporter()
.withoutHeaders()
.column(Bean::getName)
.column(bean -> bean.getUser().getLogin())
.asCsv(ExportCsvConfig.builder()
.separator(';')
.escapeChar('"')
.build())
.writeRows(Arrays.asList(bean1, bean2, bean3))
.toByteArray();
```
```java
Windmill
.importer()
.source(FileSource.of(bytes))
.parser(Parsers.csv(CsvParserConfig.builder()
.separator(';')
.escapeChar('"')
.quoteChar('\'')
.build()))
.withoutHeaders()
.stream()
.map(row -> new Bean(row.cell(0).asString(), new User(row.cell(1).asString())))
.collect(Collectors.toList());
```

It is also possible to export multiple tabs in one Excel workbook:
```java
Workbook xlsxFile = new XSSFWorkbook();

Windmill
.export(Arrays.asList(bean1, bean2, bean3))
.withNoHeaderMapping(Bean::getName, bean -> bean.getUser().getLogin())
.asExcel(ExportExcelConfig.fromWorkbook(xlsxFile).build("First tab"))
.write();
.<Bean>exporter()
.withoutHeaders()
.column(Bean::getName)
.column(bean -> bean.getUser().getLogin())
.asExcel(ExportExcelConfig.fromWorkbook(xlsxFile)
.build("First tab"))
.writeRows(Arrays.asList(bean1, bean2, bean3));

Windmill
.export(Arrays.asList(film1, film2))
.withNoHeaderMapping(Film::getTitle, Film::getReleaseDate)
.asExcel(ExportExcelConfig.fromWorkbook(xlsxFile).build("Second tab with films"))
.write();
.<Film>exporter()
.withoutHeaders()
.column(Film::getTitle)
.column(Film::getReleaseDate)
.asExcel(ExportExcelConfig.fromWorkbook(xlsxFile)
.build("Second tab with films"))
.writeRow(film1)
.writeRow(film2);

xlsxFile.write(new FileOutputStream("Export.xlsx"));
```
```java
Windmill
.importer()
.source(FileSource.of(new FileInputStream("Export.xlsx")))
.parser(Parsers.xlsx("First tab"))
.withoutHeaders()
.stream()
.map(row -> new Bean(row.cell(0).asString(), new User(row.cell(1).asString())))
.collect(Collectors.toList());

Windmill
.importer()
.source(FileSource.of(new FileInputStream("Export.xlsx")))
.parser(Parsers.xlsx("Second tab with films"))
.withoutHeaders()
.stream()
.map(row -> {
String title = row.cell(0).asString();
// TODO: create convenient method for custom types
Date releaseDate = DateUtil.getJavaDate(row.cell(1).asDouble().value());
return new Film(title, releaseDate);
})
.collect(Collectors.toList());
```
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@
<version>3.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
145 changes: 145 additions & 0 deletions src/main/java/com/coreoz/windmill/Exporter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.coreoz.windmill;

import com.coreoz.windmill.exports.exporters.csv.CsvExporter;
import com.coreoz.windmill.exports.exporters.csv.ExportCsvConfig;
import com.coreoz.windmill.exports.exporters.excel.ExcelExporter;
import com.coreoz.windmill.exports.exporters.excel.ExportExcelConfig;
import com.coreoz.windmill.exports.mapping.ExportHeaderMapping;
import com.coreoz.windmill.exports.mapping.ExportMapping;
import com.coreoz.windmill.exports.mapping.NoHeaderDecorator;
import org.apache.commons.collections4.map.LinkedMap;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

public interface Exporter<T> extends Consumer<T> {

Exporter<T> writeRow(T row);
Exporter<T> writeRows(Iterable<T> rows);

default void accept(T row) {
writeRow(row);
}

/**
* Write the exported state into an existing {@link OutputStream}.
*
* This {@link OutputStream} will not be closed automatically:
* it should be closed manually after this method is called.
*
* @throws IOException if anything can't be written.
*/
Exporter<T> writeInto(OutputStream outputStream);

/**
* @throws IOException if anything can't be written.
*/
byte[] toByteArray();

interface InitialState<T> {
NamedValueMapperStage<T> withHeaders();
ValueMapperStage<T> withoutHeaders();
PresentationState<T> withExportMapping(ExportMapping<T> mapping);
}

interface ValueMapperStage<T> extends PresentationState<T> {
ValueMapperStage<T> column(Function<T, ?> applier);
PresentationState<T> columns(Collection<Function<T, ?>> appliers);
}

interface NamedValueMapperStage<T> extends PresentationState<T> {
NamedValueMapperStage<T> column(String name, Function<T, ?> applier);
PresentationState<T> columns(Map<String, Function<T, ?>> appliers);
}

interface PresentationState<T> {
CsvExporter<T> asCsv();
CsvExporter<T> asCsv(ExportCsvConfig config);
ExcelExporter<T> asExcel();
ExcelExporter<T> asExcel(ExportExcelConfig config);
}

class Builder<T> implements InitialState<T>, ValueMapperStage<T>, NamedValueMapperStage<T>, PresentationState<T> {

private final LinkedMap<String, Function<T, ?>> toValues;

private ExportMapping<T> headerMapping;

public Builder(LinkedMap<String, Function<T, ?>> toValues) {
this.toValues = toValues;
}

@Override
public NamedValueMapperStage<T> withHeaders() {
this.headerMapping = new ExportHeaderMapping<>(toValues);
return this;
}

@Override
public ValueMapperStage<T> withoutHeaders() {
this.headerMapping = new NoHeaderDecorator<>(new ExportHeaderMapping<>(toValues));
return this;
}

@Override
public ValueMapperStage<T> column(Function<T, ?> applier) {
toValues.put(applier.toString(), applier);
return this;
}

@Override
public PresentationState<T> columns(Collection<Function<T, ?>> appliers) {
for (Function<T, ?> applier : appliers) {
column(applier);
}

return this;
}

@Override
public NamedValueMapperStage<T> column(String name, Function<T, ?> applier) {
toValues.put(name, applier);
return this;
}

@Override
public PresentationState<T> columns(Map<String, Function<T, ?>> appliers) {
toValues.putAll(appliers);
return this;
}

@Override
public PresentationState<T> withExportMapping(ExportMapping<T> mapping) {
this.headerMapping = mapping;
return this;
}

@Override
public CsvExporter<T> asCsv() {
return asCsv(ExportCsvConfig.builder().build());
}

@Override
public CsvExporter<T> asCsv(ExportCsvConfig config) {
return new CsvExporter<>(headerMapping, config);
}

@Override
public ExcelExporter<T> asExcel() {
return asExcel(ExportExcelConfig.newXlsxFile().build());
}

@Override
public ExcelExporter<T> asExcel(ExportExcelConfig config) {
return new ExcelExporter<>(headerMapping, config);
}
}

static <T> InitialState<T> builder() {
return new Builder<>(new LinkedMap<>());
}
}
60 changes: 60 additions & 0 deletions src/main/java/com/coreoz/windmill/Importer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.coreoz.windmill;

import com.coreoz.windmill.files.FileSource;
import com.coreoz.windmill.files.FileTypeGuesser;
import com.coreoz.windmill.imports.FileParser;
import com.coreoz.windmill.imports.Parsers;
import com.coreoz.windmill.imports.Row;

import java.util.stream.Stream;

public interface Importer {

Stream<Row> stream();

interface InitialState {
ParserState source(FileSource fileSource);
}

interface ParserState extends HeaderState {
HeaderState parser(FileParser fileParser);
}

interface HeaderState {
Importer withHeaders();
Importer withoutHeaders();
}

class Builder implements InitialState, ParserState, HeaderState {

private FileParser parser;
private FileSource fileSource;

@Override
public ParserState source(FileSource fileSource) {
this.fileSource = fileSource;
this.parser = Parsers.forType(FileTypeGuesser.guess(fileSource));
return this;
}

@Override
public HeaderState parser(FileParser fileParser) {
this.parser = fileParser;
return this;
}

@Override
public Importer withHeaders() {
return () -> parser.parse(fileSource).skip(1);
}

@Override
public Importer withoutHeaders() {
return () -> parser.parse(fileSource);
}
}

static InitialState builder() {
return new Builder();
}
}
Loading