001 package org.kuali.common.impex.data.service.impl; 002 003 import java.io.File; 004 import java.io.IOException; 005 import java.io.OutputStream; 006 import java.util.List; 007 008 import org.apache.commons.lang3.StringUtils; 009 import org.kuali.common.impex.data.service.DumpDataContext; 010 import org.kuali.common.impex.model.Column; 011 import org.kuali.common.impex.model.util.ModelUtils; 012 import org.kuali.common.util.CollectionUtils; 013 import org.kuali.common.util.FormatUtils; 014 import org.kuali.common.util.LocationUtils; 015 import org.kuali.common.util.Str; 016 import org.slf4j.Logger; 017 import org.slf4j.LoggerFactory; 018 019 public class DataHandler { 020 021 private static final Logger logger = LoggerFactory.getLogger(DataHandler.class); 022 023 protected static final String FS = File.separator; 024 protected static final String LF = "\n"; 025 protected static final String DOUBLE_QUOTE = "\""; 026 protected static final String COMMA = ","; 027 028 public static final String MPX_EXTENSION = ".mpx"; 029 030 public static final String CARRIAGE_RETURN = "\r"; 031 032 public static final String MPX_NULL = "${mpx.null}"; 033 public static final String MPX_CARRIAGE_RETURN = "${mpx.cr}"; 034 public static final String MPX_LINEFEED = "${mpx.lf}"; 035 public static final String MPX_QUOTE = "${mpx.quote}"; 036 037 public static final String MPX_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"; 038 039 public static File getFileForTable(DumpDataContext context, String tableName) { 040 String filename = getFilename(context.getWorkingDir(), tableName); 041 return new File(filename); 042 } 043 044 protected static String getFilename(File workingDir, String tableName) { 045 // To keep file names consistent, always capitalize the name of the table before returning a file name 046 tableName = StringUtils.upperCase(tableName); 047 return LocationUtils.getCanonicalPath(workingDir) + FS + tableName + MPX_EXTENSION; 048 } 049 050 public static void startData(DumpProgress progress) throws IOException { 051 List<Column> columns = progress.getColumns(); 052 String encoding = progress.getContext().getEncoding(); 053 String header = ModelUtils.getCsvColumnNames(columns) + LF; 054 OutputStream out = progress.getOutputStream(); 055 out.write(header.getBytes(encoding)); 056 } 057 058 public static void doData(DumpProgress progress) throws IOException { 059 String encoding = progress.getContext().getEncoding(); 060 formatMpx(progress.getCurrentData()); 061 writeRows(progress.getCurrentData(), encoding, progress.getOutputStream()); 062 } 063 064 protected static void writeRows(List<List<String>> rows, String encoding, OutputStream out) throws IOException { 065 for (List<String> row : rows) { 066 String line = getLine(row); 067 byte[] bytes = line.getBytes(encoding); 068 out.write(bytes); 069 } 070 } 071 072 /** 073 * Comma separated, all values enclosed in double quotes, always terminated by a linefeed 074 */ 075 protected static String getLine(List<String> row) { 076 StringBuilder sb = new StringBuilder(); 077 for (int column = 0; column < row.size(); column++) { 078 if (column != 0) { 079 sb.append(COMMA); 080 } 081 String columnValue = row.get(column); 082 sb.append(Str.quote(columnValue)); 083 } 084 sb.append(LF); 085 return sb.toString(); 086 } 087 088 public static void finishData(DumpProgress exportProgress) throws IOException { 089 090 // Since we only dump from memory to disk at specific intervals, there is likely to be 091 // a few trailing rows that need to get dumped 092 if (!CollectionUtils.isEmpty(exportProgress.getCurrentData())) { 093 String encoding = exportProgress.getContext().getEncoding(); 094 formatMpx(exportProgress.getCurrentData()); 095 writeRows(exportProgress.getCurrentData(), encoding, exportProgress.getOutputStream()); 096 } 097 098 // Extract our table tracker 099 TableTracker tracker = exportProgress.getTableTracker(); 100 101 // If the table had at least one row of data there will be a .mpx file for this table 102 // Log a message displaying some stats about what got dumped to disk 103 if (tracker.getTotalRowCount().getValue() > 0) { 104 long threadId = Thread.currentThread().getId(); 105 String tableName = exportProgress.getTableContext().getTable().getName(); 106 String trc = FormatUtils.getCount(tracker.getTotalRowCount().getValue()); 107 String tds = FormatUtils.getSize(tracker.getTotalDataSize().getValue()); 108 Object[] args = { threadId, tableName, trc, tds }; 109 logger.debug("[{}] - Dumped [{}] Total Rows: {} Total Size: {}", args); 110 } 111 } 112 113 protected static void formatMpx(List<List<String>> rows) { 114 for (List<String> row : rows) { 115 for (int i = 0; i < row.size(); i++) { 116 row.set(i, formatMpx(row.get(i))); 117 } 118 } 119 } 120 121 public static String formatMpx(String s) { 122 if (s == null) { 123 return MPX_NULL; 124 } 125 String converted = StringUtils.replace(s, CARRIAGE_RETURN, MPX_CARRIAGE_RETURN); 126 converted = StringUtils.replace(converted, LF, MPX_LINEFEED); 127 converted = StringUtils.replace(converted, DOUBLE_QUOTE, MPX_QUOTE); 128 return converted; 129 } 130 131 }