package org.openflamingo.mapreduce.parser; import org.apache.hadoop.io.Text; import org.openflamingo.mapreduce.core.Constants; import org.openflamingo.mapreduce.util.ArrayUtils; import org.openflamingo.mapreduce.util.StringUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * CSV Row Record Parser. * * @author Edward KIM * @author Seo Ji Hye * @since 0.1 */ public class CsvRowParser { /** * 컬럼 정보를 가지고 있는 리스트. */ private List<String> columns = new ArrayList<String>(); /** * 파일의 1라인을 의미하는 Row. 최초 생성시 생성자의 인자로 지정하거나 파싱시 인자로 지정하면 이 변수에 값이 설졍되며 변하지 않는다. */ private String row = null; /** * 컬럼의 개수. 초기값은 -1이며 생성자 호출시 지정하게 되면 Row의 컬럼의 수가 틀린 경우 예외가 발생된다. */ private int initSize = -1; /** * Row를 컬럼 단위로 쪼개기 위한 입력 구분자. */ private String inputDelimiter; /** * 컬럼을 최종 Row로 합치기 위한 출력 구분자. */ private String outputDelimiter; /** * 최종 Row의 Hadoop {@link org.apache.hadoop.io.Text */ private Text rowText = new Text(); /** * 기본 생성자. 별도의 옵션이 주어져 있지 않으므로 기본 입출력 구분자만 설정한다. */ public CsvRowParser() { setInputDelimiter(Constants.DEFAULT_DELIMITER); setOutputDelimiter(Constants.DEFAULT_DELIMITER); } /** * 기본 생성자. 기본 입출력 구분자와 지정한 컬럼의 개수를 설정한다. * * @param initSize 컬럼의 개수 */ public CsvRowParser(int initSize) { this.initSize = initSize; setInputDelimiter(Constants.DEFAULT_DELIMITER); setOutputDelimiter(Constants.DEFAULT_DELIMITER); } /** * 기본 생성자. 지정한 Row를 기본 입출력 구분자를 이용하여 처리하고 컬럼의 개수를 설정한다. * * @param row 컬럼의 개수 */ public CsvRowParser(String row) { setInputDelimiter(Constants.DEFAULT_DELIMITER); setOutputDelimiter(Constants.DEFAULT_DELIMITER); String[] columnsArray = StringUtils.splitPreserveAllTokens(row, this.inputDelimiter); columns = ArrayUtils.stringArrayToCollection(columnsArray); this.initSize = columnsArray.length; } /** * 기본 생성자. * * @param initSize 컬럼의 개수 * @param inputDelimiter 입력 구분자 * @param outputDelimiter 출력 구분자 */ public CsvRowParser(int initSize, String inputDelimiter, String outputDelimiter) { this.initSize = initSize; setInputDelimiter(inputDelimiter); setOutputDelimiter(outputDelimiter); } /** * 문자열로 구성된 Row를 컬럼으로 구분하기 위해서 사용하는 입력 구분자를 지정한다. * * @param inputDelimiter 입력 구분자. */ public void setInputDelimiter(String inputDelimiter) { this.inputDelimiter = inputDelimiter; } /** * 출력 문자열을 구성할 때 컬럼을 구분하기 위한 출력 구분자를 지정한다. * * @param outputDelimiter 출력 구분자. */ public void setOutputDelimiter(String outputDelimiter) { this.outputDelimiter = outputDelimiter; } /** * 지정한 위치에 있는 컬럼을 삭제한다 * * @param index 삭제할 컬럼의 위치 * @return 삭제한 컬럼값 */ public String remove(int index) { return columns.remove(index); } /** * 지정한 위치의 컬럼값을 반환한다. * * @param index 컬럼의 위치치 * @return 지정한 위치의 컬럼값 */ public String get(int index) { return columns.get(index); } /** * 지정한 위치에 있는 모든 컬럼을 삭제한다 * * @param indexes 삭제할 컬럼의 위치 * @return 삭제한 컬럼값 */ public String[] remove(Integer... indexes) { String[] removed = new String[indexes.length]; Integer[] sorted = ArrayUtils.sortReverseIntegerArray(indexes); for (int i = 0; i < sorted.length; i++) { removed[i] = columns.get(sorted[i]); remove(indexes[i]); } return removed; } /** * 지정한 위치에 컬럼을 추가한다. * * @param column 컬럼값 * @param index 추가할 위치 */ public void insert(String column, int index) { columns.add(index, column); } /** * 지정한 위치에 컬럼값을 변경한다. * * @param column 컬럼값 * @param index 변경할 위치 */ public String change(String column, int index) { String changed = columns.get(index); columns.set(index, column); return changed; } /** * 컬럼의 개수를 반환한다. * * @return 컬럼의 개수 */ public int size() { return columns.size(); } /** * 출력 구분자를 이용하여 하나의 Row를 구성한다. * * @return Row */ public String toRow() { if (columns.size() == 0) { return ""; } StringBuilder builder = new StringBuilder(); Iterator iterator = columns.iterator(); while (iterator.hasNext()) { builder.append(iterator.next()); if (iterator.hasNext()) { builder.append(outputDelimiter); } } return builder.toString(); } /** * 출력 구분자를 이용하여 하나의 Row를 {@link org.apache.hadoop.io.Text}으로 구성한다. * * @return Row */ public Text toRowText() { rowText.set(toRow()); return rowText; } /** * Row를 파싱하여 컬럼을 구성한다. * * @param row Row */ public void parse(String row) { String[] strings = StringUtils.splitPreserveAllTokens(row, this.inputDelimiter); if (initSize == -1) { // Not initialized this.initSize = strings.length; } if (initSize != strings.length) { throw new IllegalArgumentException("Wrong column size. Init Size [" + initSize + "] Column Size [" + strings.length + "]"); } clear(); columns = ArrayUtils.stringArrayToCollection(strings); this.row = row; } /** * 모든 컬럼 정보를 삭제한다. */ public void clear() { columns.clear(); initSize = -1; } }