001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.layout.collections;
017
018import org.kuali.rice.krad.uif.container.CollectionGroup;
019import org.kuali.rice.krad.uif.element.Label;
020import org.kuali.rice.krad.uif.field.Field;
021import org.kuali.rice.krad.uif.layout.TableLayoutManager;
022import org.kuali.rice.krad.util.KRADUtils;
023
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029/**
030 * @author Kuali Rice Team (rice.collab@kuali.org)
031 */
032public class TableExporter {
033
034    /**
035     * Generates formatted table data based on the posted view results and format type.
036     *
037     * @param collectionGroup collection group instance that should be exported
038     * @param model top level object containing the data
039     * @param formatType format which the table should be generated in
040     * @return generated table data
041     */
042    public static String buildExportTableData(CollectionGroup collectionGroup, Object model, String formatType) {
043        // load table format elements used for generated particular style
044        Map<String, String> exportTableFormatOptions = getExportTableFormatOptions(formatType);
045        String startTable = exportTableFormatOptions.get("startTable");
046        String endTable = exportTableFormatOptions.get("endTable");
047
048        StringBuilder tableRows = new StringBuilder("");
049
050        TableLayoutManager layoutManager = (TableLayoutManager) collectionGroup.getLayoutManager();
051
052        List<Label> headerLabels = layoutManager.getHeaderLabels();
053        List<Field> rowFields = layoutManager.getAllRowFields();
054        int numberOfColumns = layoutManager.getNumberOfColumns();
055
056        List<Integer> ignoredColumns = findIgnoredColumns(layoutManager, collectionGroup);
057
058        // append table header data as first row
059        if (!headerLabels.isEmpty()) {
060            List<String> labels = new ArrayList<String>();
061
062            for (Label label : headerLabels) {
063                labels.add(label.getLabelText());
064            }
065
066            tableRows.append(buildExportTableRow(labels, exportTableFormatOptions, ignoredColumns));
067        }
068
069        // load all subsequent rows to the table
070        if (!rowFields.isEmpty()) {
071            List<String> columnData = new ArrayList<String>();
072
073            for (Field field : rowFields) {
074                columnData.add(KRADUtils.getSimpleFieldValue(model, field));
075
076                if (columnData.size() >= numberOfColumns) {
077                    tableRows.append(buildExportTableRow(columnData, exportTableFormatOptions, ignoredColumns));
078                    columnData.clear();
079                }
080            }
081        }
082
083        return startTable + tableRows.toString() + endTable;
084    }
085
086    /**
087     * Helper function to determine whether if column should be displayed. Used to help extract
088     * columns used in screen format such as action or select that is not needed for export.
089     *
090     * @param layoutManager The layout manager.
091     * @param collectionGroup The collection group.
092     * @return Index numbers for all columns that should be ignored.
093     */
094    protected static List<Integer> findIgnoredColumns(TableLayoutManager layoutManager,
095            CollectionGroup collectionGroup) {
096        List<Integer> ignoreColumns = new ArrayList<Integer>();
097
098        int actionColumnIndex = layoutManager.getActionColumnIndex();
099        int numberOfColumns = layoutManager.getNumberOfColumns();
100        boolean renderActions = collectionGroup.isRenderLineActions() && !Boolean.TRUE.equals(collectionGroup.getReadOnly());
101        boolean renderSelectField = collectionGroup.isIncludeLineSelectionField();
102        boolean renderSequenceField = layoutManager.isRenderSequenceField();
103
104        if (renderActions || renderSelectField || renderSequenceField) {
105            int shiftColumn = 0;
106
107            if (renderSelectField) {
108                ignoreColumns.add(shiftColumn);
109                shiftColumn++;
110            }
111            if (renderSequenceField) {
112                ignoreColumns.add(shiftColumn);
113                shiftColumn++;
114            }
115            if (renderActions) {
116                if (actionColumnIndex == 1) {
117                    ignoreColumns.add(shiftColumn);
118                } else if (actionColumnIndex == -1) {
119                    ignoreColumns.add(numberOfColumns - 1);
120                } else if (actionColumnIndex > 1) {
121                    ignoreColumns.add(actionColumnIndex);
122                }
123            }
124        }
125
126        return ignoreColumns;
127    }
128
129    /**
130     * Helper method used to build formatted table row data for export.
131     *
132     * @param columnData Formatted column data.
133     * @param tableFormatOptions Format options: startRow and endRow are added to the row,
134     * startColumn and endColumn are added to each column.
135     * @param ignoredColumns Index numbers of columns to ignore.
136     * @return Formatted table data for one row.
137     */
138    protected static String buildExportTableRow(List<String> columnData, Map<String, String> tableFormatOptions,
139            List<Integer> ignoredColumns) {
140        String startRow = tableFormatOptions.get("startRow");
141        String endRow = tableFormatOptions.get("endRow");
142        String startColumn = tableFormatOptions.get("startColumn");
143        String endColumn = tableFormatOptions.get("endColumn");
144        boolean appendLastColumn = Boolean.valueOf(tableFormatOptions.get("appendLastColumn"));
145        int columnIndex = 0;
146
147        StringBuilder builder = new StringBuilder();
148        for (String columnItem : columnData) {
149            boolean displayColumn = !ignoredColumns.contains(columnIndex);
150            if (displayColumn) {
151                builder.append(startColumn + columnItem + endColumn);
152            }
153            if (columnIndex >= columnData.size() - 1 && !appendLastColumn) {
154                builder.delete(builder.length() - endColumn.length(), builder.length());
155            }
156            columnIndex++;
157        }
158
159        return startRow + builder.toString() + endRow;
160    }
161
162    /**
163     * Identify table formatting elements based on formatType. Defaults to txt format if not found
164     *
165     * @param formatType The format type: csv, xls, or xml.
166     * @return The format options for to use with the indicated format type.
167     */
168    protected static Map<String, String> getExportTableFormatOptions(String formatType) {
169        HashMap<String, String> map = new HashMap<String, String>();
170
171        map.put("contentType", "text/plain");
172        map.put("formatType", "txt");
173        map.put("startTable", "");
174        map.put("endTable", "");
175        map.put("startRow", "");
176        map.put("endRow", "\n");
177        map.put("startColumn", "");
178        map.put("endColumn", ", ");
179        map.put("appendLastColumn", "false");
180
181        if ("csv".equals(formatType)) {
182            map.put("contentType", "text/csv");
183            map.put("formatType", "csv");
184            map.put("startTable", "");
185            map.put("endTable", "");
186            map.put("startRow", "");
187            map.put("endRow", "\n");
188            map.put("startColumn", "");
189            map.put("endColumn", ", ");
190            map.put("appendLastColumn", "false");
191
192        } else if ("xls".equals(formatType)) {
193            map.put("contentType", "application/vnd.ms-excel");
194            map.put("formatType", "xls");
195            map.put("startTable", "");
196            map.put("endTable", "");
197            map.put("startRow", "");
198            map.put("endRow", "\n");
199            map.put("startColumn", "\"");
200            map.put("endColumn", "\"\t");
201            map.put("appendLastColumn", "true");
202
203        } else if ("xml".equals(formatType)) {
204            map.put("contentType", "application/xml");
205            map.put("formatType", "xml");
206            map.put("startTable", "<table>\n");
207            map.put("endTable", "</table>\n");
208            map.put("startRow", "  <row>\n");
209            map.put("endRow", "  </row>\n");
210            map.put("startColumn", "    <column>");
211            map.put("endColumn", "</column>\n");
212            map.put("appendLastColumn", "true");
213
214        }
215
216        return map;
217    }
218}