package org.openflamingo.mapreduce.etl.replace.column;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.openflamingo.mapreduce.core.Delimiter;
import org.openflamingo.mapreduce.parser.CsvRowParser;
import org.openflamingo.mapreduce.util.ArrayUtils;
import org.openflamingo.mapreduce.util.CounterUtils;
import org.openflamingo.mapreduce.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* 지정한 칼럼을 삭제하는 Clean ETL Mapper.
* 이 Mapper는 지정한 칼럼을 삭제하고 다시 Delimiter를 이용하여 조립하고 Context Write를 한다.
*
* @author Edward KIM
* @author Seo Ji Hye
* @since 0.1
*/
public class ReplaceColumnMapper extends Mapper<LongWritable, Text, NullWritable, Text> {
/**
* SLF4J Logging
*/
private static Logger logger = LoggerFactory.getLogger(ReplaceColumnMapper.class);
/**
* 입력 Row를 컬럼으로 구분하기 위해서 사용하는 컬럼간 구분자
*/
private String inputDelimiter;
/**
* 출력 Row를 구성하기 위해서 사용하는 컬럼간 구분자
*/
private String outputDelimiter;
/**
* Row의 컬럼 개수
*/
private int columnSize;
/**
* 값을 변경할 컬럼의 정보
*/
private Integer[] columnsToReplace;
/**
*
*/
private String[] fromColumnsValues;
/**
*
*/
private String[] toColumnsValues;
/**
*
*/
private Replacer replacer;
/**
* CSV Row Parser
*/
private CsvRowParser parser;
@Override
protected void setup(Context context) throws IOException, InterruptedException {
Configuration configuration = context.getConfiguration();
inputDelimiter = configuration.get("inputDelimiter", Delimiter.COMMA.getDelimiter());
outputDelimiter = configuration.get("outputDelimiter", Delimiter.COMMA.getDelimiter());
columnSize = configuration.getInt("columnSize", -1);
// 모든 파라미터를 배열로 변환하여 동일한 인덱스는 동일한 Rule을 적용할 수 있도록 한다.
String[] stringColumnsToReplace = StringUtils.commaDelimitedListToStringArray(configuration.get("columnsToReplace"));
//String[] stringReplacerClassNames = StringUtils.commaDelimitedListToStringArray(configuration.get("replacerClassNames"));
fromColumnsValues = StringUtils.commaDelimitedListToStringArray(configuration.get("fromColumnsValues"));
toColumnsValues = StringUtils.commaDelimitedListToStringArray(configuration.get("toColumnsValues"));
if (columnSize == -1) {
throw new IllegalArgumentException("You must specify 'columnSize' for validating the column size.");
}
// 모든 파라미터의 개수는 동일해야한다. 동일하지 않으면 Replace를 수행할 수 없다.
if (stringColumnsToReplace.length != fromColumnsValues.length
&& stringColumnsToReplace.length != toColumnsValues.length) {
throw new IllegalArgumentException("Invalid Parameter Length");
}
columnsToReplace = ArrayUtils.toIntegerArray(stringColumnsToReplace);
// Replacer를 생성한다.
replacer = new DefaultReplacer();
/*replacers = new Replacer[columnSize];
String stringReplacerClassName = null;
for (int i = 0; i < stringReplacerClassNames.length; i++) {
try {
stringReplacerClassName = stringReplacerClassNames[i];
Class clazz = Class.forName(stringReplacerClassName);
Replacer replacer = (Replacer) clazz.newInstance();
replacers[i] = replacer;
replacers[i].setTo(toColumnsValues[i]);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot create instance '" + stringReplacerClassName + "'");
}
}*/
parser = new CsvRowParser(columnSize, inputDelimiter, outputDelimiter);
}
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
try {
parser.parse(value.toString());
} catch (IllegalArgumentException ex) {
CounterUtils.writerMapperCounter(this, "Wrong Column Size", context);
logger.warn("[Wrong Column Size] [{}] {}", columnSize, value.toString());
return;
}
// 변경할 컬럼의 위치를 찾아서 Replacer로 값을 변경한다.
for (int i = 0; i < columnsToReplace.length; i++) {
Integer index = columnsToReplace[i];
replacer.setTo(toColumnsValues[i]);
if (parser.get(index).equals(fromColumnsValues[i])) {
parser.change(replacer.replace(fromColumnsValues[i]), index);
}
}
context.write(NullWritable.get(), parser.toRowText());
}
}