Thursday, December 16, 2010

Rendering CSV as ERT from BHUB with Spring

As I showed for Excel we can create a custom View to render tabular data in CSV format instead.

Note that this example uses Spring and a ControllerContext class that I use to pass information about different layers (Context-Object pattern). You can of course get the fundamental idea in case you do not use a ControllerContext in your design.

The unique dependency:
<dependency>
            <groupId>net.sf.opencsv</groupId>
            <artifactId>opencsv</artifactId>
            <version>2.0</version>
</dependency>

The Controller is very similar to the one I presented for the ExcelView post. Just a little modification so actually we can render either Excel or CSV:
package com.nestorurquiza.spring.web;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.nestorurquiza.spring.web.ExcelView;
import com.nestorurquiza.spring.web.CsvView;

@Controller
public class TabularController extends RootController {

    @RequestMapping("/board")
    public ModelAndView welcomeHandler(HttpServletRequest request,
            HttpServletResponse response) {
        //Initialize the context (mandatory)
        ControllerContext ctx = new ControllerContext(request, response);
        init(ctx);
        
        DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
        Map<String, List<Map<String, Object>>> excelWorkbookViewMap = new HashMap<String, List<Map<String, Object>>>();
        List<Map<String, Object>> excelRows = new ArrayList<Map<String, Object>>();
        try {
            Map<String, Object> excelRow = new HashMap<String, Object>(); 
            excelRow.put("Name", "Gregory");
            excelRow.put("Age", 33);
            excelRow.put("Salary", 33000.55);
            excelRow.put("DOB", df.parse("2/1/1980"));
            excelRow.put("Graduated", false);
            excelRow.put("Comments", "He is our \"report designer\"");
            excelRows.add(excelRow);
            excelRow = new HashMap<String, Object>(); 
            excelRow.put("Name", "Mark");
            excelRow.put("Age", 41);
            excelRow.put("Salary", 33000.55);
            excelRow.put("DOB", df.parse("20/12/1975"));
            excelRow.put("Graduated", true);
            excelRow.put("Comments", "He is our \"web designer\"");
            excelRows.add(excelRow);
            excelWorkbookViewMap.put("First Sheet", excelRows);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        String ert = ctx.getParameter("ert");
        String baseFilename = "board";
        if("csv".equals(ert)) {
            Map<String, List<Map<String, Object>>> csvViewMap = new HashMap<String, List<Map<String, Object>>>();
            String fileName = baseFilename + ".csv";
            csvViewMap.put(fileName, excelRows);
            return new ModelAndView(new CsvView(ctx, fileName, ','), csvViewMap);
        } else {
            String fileName = baseFilename + ".xls";
            return new ModelAndView(new ExcelView(ctx, fileName), excelWorkbookViewMap);
        }
    }
}
The CsvView:
package com.nestorurquiza.spring.web;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.AbstractView;

import au.com.bytecode.opencsv.CSVWriter;

import com.nestorurquiza.utils.Utils;
import com.nestorurquiza.web.ControllerContext;


public class CsvView extends AbstractView {
    private static final String CONTENT_TYPE = "text/csv";

    public CsvView(ControllerContext ctx, String fileName, char fieldSeparator) {
        super();
        if(Utils.isEmpty(fileName)) {
            fileName = "fileName";
        }
        this.fileName = fileName;
        this.fieldSeparator = fieldSeparator;
        this.ctx = ctx;
        setContentType(CONTENT_TYPE);
    }
    
    private String fileName;
    private char fieldSeparator;
    private ControllerContext ctx; 

    /**
     * 
     * model: Map<String, List<Map, Object>>
     * This view returns back a CSV stream
     * The model must come with an entry for fileName key
     * Each list entry (the list map) corresponds to a row
     * The headers for each row are the list map keys
     * The comma separated values are the list map values
     * 
     * @author nestor
     *
     */
    protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        CSVWriter writer = new CSVWriter(response.getWriter(), fieldSeparator);

        if (Utils.isEmpty(model)) {
            writer.writeNext("error.empty.model".split(""));
        } else {
            Set<Map.Entry<String, List<Map<String, Object>>>> set = model.entrySet();
            for (Map.Entry<String, List<Map<String, Object>>> entry : set) {
                String key = entry.getKey();
                if (fileName.equals(key)) {
                    List<Map<String, Object>> content = entry.getValue();
                    int rowCount = 0;
                    for (Map<String, Object> row : content) {
                        if (rowCount == 0) {
                            String[] tokens = new String[row.size()];
                            int i = 0;
                            for( String cellName : row.keySet() ) {
                                tokens[i] = cellName;
                                i++;
                            }
                            writer.writeNext(tokens);
                        }
                        String[] tokens = new String[row.size()];
                        int i = 0;
                        for( String cellName : row.keySet() ) {
                            Object cellValue = row.get(cellName);
                            tokens[i] = cellValue.toString();
                            i++;
                        }
                        writer.writeNext(tokens);
                        rowCount++;
                    }

                }
            }
        }
        writer.flush();
    }
}

No comments:

Followers