Skip to content

Demonstration CSV Import functionaliy #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
### Java template
*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Created by .ignore support plugin (hsz.mobi)

# Custom
.idea/
target/
*.iml
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,25 @@ own domain model. It also demonstrates a very simple add-on usage with Vaadin

If you are new to Maven and want to try this application, check out this [tiny Maven tutorial](https://vaadin.com/blog/-/blogs/the-maven-essentials-for-the-impatient-developer).

To get this running execute "mvn wildfly:run" to launch this locally on Wildfly
server or deploy to a Java EE 7+ server like Wildfly or Glassfish. With Liberty you'll need to
present a data source (other modern servers provide "development datasource" when
## Run the example

As this application is based on JEE it relies on a JEE capable application server.
The Maven project setup includes a Wildfly Maven application server for demonstration purposes.

To run the project, you can use

```
mvn wildfly:run
```
to launch this locally on Wildfly server. Afterwards, the application is available
in your local browser at

[http://localhost:8080/jpa-addressbook-1.0-SNAPSHOT](http://localhost:8080/jpa-addressbook-1.0-SNAPSHOT)


Alternatively, you can deploy to a Java EE 7+ server like Wildfly or Glassfish.
With Liberty you'll need to present a data source
(other modern servers provide "development datasource" when
no jta-datasource is present in persistence.xml).

This is a suitable basis for small to medium sized apps. For larger applications,
Expand Down
3 changes: 3 additions & 0 deletions phonebook_entries.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,number,email
Martin Senne,+49 234 823 9178,martin@vaadin.com
Marc Manager,+356 253 346 221,marc@coolcompany.com
4 changes: 4 additions & 0 deletions phonebook_incorrect.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a, b
1, 2
3, 4
5, 6
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
</properties>

<dependencies>
<!-- CSV library -->
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.3</version>
</dependency>

<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-api</artifactId>
Expand Down
189 changes: 189 additions & 0 deletions src/main/java/org/example/CSVImportView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package org.example;

import com.vaadin.cdi.CDIView;
import com.vaadin.cdi.UIScoped;
import com.vaadin.data.Container;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.ui.*;
import org.example.backend.PhoneBookEntry;
import org.example.backend.PhoneBookService;
import org.example.csv.CSVReadUtil;
import org.example.ui.upload.FileBasedUploadReceptor;
import org.vaadin.cdiviewmenu.ViewMenuItem;
import org.vaadin.viritin.label.Header;
import org.vaadin.viritin.layouts.MVerticalLayout;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.io.*;
import java.util.*;
import java.util.function.Consumer;

@UIScoped
@CDIView("csv_imports")
@ViewMenuItem(order = 2)
public class CSVImportView extends MVerticalLayout implements View {

@Inject
PhoneBookService service;

Upload upload = new Upload();
Grid grid;
Button cancelButton;
Button saveButton;
Header header;

@PostConstruct
void init() {

header = new Header("CSV Import");

/**
* External handler, after upload was successful.
*/
Consumer<FileBasedUploadReceptor.FileAndInfo> consumeReader = reader -> {
System.out.println(reader);
try {
IndexedContainer csvContainer = CSVReadUtil.buildContainerFromCSV(new FileReader(reader.getFile()));
// this is intentional, as attaching new datasource to existing grid seems cause problems in this Vaadin version
grid = new Grid();
grid.setWidth(100, Unit.PERCENTAGE);
grid.setContainerDataSource(csvContainer);
boolean columnNamesValid = checkEntries(grid.getContainerDataSource());
if ( !columnNamesValid ) {
Notification.show("Save is not possible. Schema ( column names ) of CSV not correct.", Notification.Type.WARNING_MESSAGE);
}
displayUploadedData( columnNamesValid );

} catch (IOException e) {
e.printStackTrace();
}
};

FileBasedUploadReceptor fileBasedUploadReceptor = new FileBasedUploadReceptor(consumeReader);
upload.setReceiver(fileBasedUploadReceptor);
upload.addSucceededListener(fileBasedUploadReceptor);
upload.setImmediate(true);
upload.setButtonCaption("Upload CSV.");

saveButton = new Button("Save");
saveButton.addClickListener(clickEvent -> {
Container.Indexed container = grid.getContainerDataSource();
List<PhoneBookEntry> entries = createEntries(container);
saveEntries( entries );
Notification.show( entries.size() + " entries have been imported.");
displayUpload();
});

cancelButton = new Button("Cancel");
cancelButton.addClickListener(clickEvent -> {
displayUpload();
}
);

displayUpload();
}

// ==========================================================
// ===== View related methods
// ==========================================================
public void displayUpload() {
removeAllComponents();

Label label = new Label("Please upload your CSV, that contains columns 'name', 'number' and 'email'");

addComponent(header);
addComponent(label);
addComponent(upload);
}

public void displayUploadedData( boolean activateSave ) {
removeAllComponents();

saveButton.setEnabled( activateSave );

HorizontalLayout hl = new HorizontalLayout();
hl.addComponent(cancelButton);
hl.addComponent(saveButton);

HorizontalLayout bottom = new HorizontalLayout();
bottom.setWidth(100, Unit.PERCENTAGE);
bottom.addComponent(hl);
bottom.setComponentAlignment(hl, Alignment.MIDDLE_RIGHT);

addComponent(header);
addComponent(grid);
setExpandRatio(grid, 1);
addComponent(bottom);
}

@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// nothing to do on enter
}

// ==========================================================
// ===== Entry related methods
// ==========================================================
// TODO: To be moved to separate class, but left here to keep everything "close together".

private String NAME = "name";
private String NUMBER = "number";
private String EMAIL = "email";

/**
* Check if all property ids NAME, NUMBER and EMAIL are present in given container (as read from CSV).
* @param containerDataSource
* @return true if so
*/
private boolean checkEntries(Container.Indexed containerDataSource) {
Collection<String> pids = (Collection<String>)containerDataSource.getContainerPropertyIds();

Set<String> actualSchema = new HashSet<>(pids);
Set<String> expectedSchema = new HashSet<>(Arrays.asList(NAME, NUMBER, EMAIL));

Set<String> intersect = (new HashSet<>(actualSchema));
intersect.retainAll(expectedSchema);

if (actualSchema.size() == 3) {
return true; // everything is fine
} else {
return false;
}
}

/**
* Create entries based on container content.
*
* @param container use for construction of {@link PhoneBookEntry}s.
* @return list of entries
*/
private List<PhoneBookEntry> createEntries(Container.Indexed container ) {

List<PhoneBookEntry> entries = new ArrayList<>();
int n = container.size();

// TODO: Introduce real mapping functionality
for ( Object itemId : container.getItemIds() ) {
String name = (String) container.getContainerProperty(itemId, NAME).getValue();
String number = (String) container.getContainerProperty(itemId, NUMBER).getValue();
String email = (String) container.getContainerProperty(itemId, EMAIL).getValue();
PhoneBookEntry entry = new PhoneBookEntry(name, number, email);
entries.add(entry);
}
return entries;
}

/**
* Persist given {@link PhoneBookEntry}s.
* @param entries are the entries to persist.
*/
private int saveEntries( List<PhoneBookEntry> entries ) {
for (PhoneBookEntry entry : entries) {
service.save(entry);
}
return entries.size();
}
}
2 changes: 2 additions & 0 deletions src/main/java/org/example/GroupsView.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import javax.inject.Inject;
import org.example.backend.PhoneBookGroup;
import org.example.backend.PhoneBookService;
import org.vaadin.cdiviewmenu.ViewMenuItem;
import org.vaadin.viritin.button.MButton;
import org.vaadin.viritin.fields.MTable;
import org.vaadin.viritin.fields.MTextField;
Expand All @@ -23,6 +24,7 @@

@UIScoped
@CDIView("groups")
@ViewMenuItem(order = 1)
public class GroupsView extends CssLayout implements View {

@Inject
Expand Down
Loading