IDEMPIERE-2875 GridTabCSVImporter code refactoring

This commit is contained in:
Diego Ruiz 2015-10-02 15:12:52 -05:00
parent 16791faf20
commit 27d4865ff1
1 changed files with 619 additions and 474 deletions

View File

@ -85,47 +85,238 @@ public class GridTabCSVImporter implements IGridTabImporter
{
private static final String ERROR_HEADER = "_ERROR_";
private static final String LOG_HEADER = "_LOG_";
private boolean m_isError = false;
private String m_import_mode = null;
private static final String IMPORT_MODE_MERGE = "M";
private static final String IMPORT_MODE_UPDATE = "U";
private static final String IMPORT_MODE_INSERT = "I";
private boolean m_isError = false;
private String m_import_mode = null;
private List<String> header;
private List<CellProcessor> readProcArray;
private List<GridField> locationFields;
private boolean isThereKey;
private boolean isThereDocAction;
private Map<GridTab,Integer> tabMapIndexes;
private List<Map<String, Object>> data;
private ICsvMapReader mapReader;
private TreeMap<GridTab,Integer> sortedtTabMapIndexes;
private List<String> rawData;
private boolean isMasterok = true;
private boolean isDetailok = true;
private boolean error = false;
private List<String> rowsTmpResult;
private PO masterRecord;
//Files management
private File errFile;
private File logFile;
private PrintWriter errFileW;
private PrintWriter logFileW;
private CsvPreference csvpref = CsvPreference.STANDARD_PREFERENCE;
private String delimiter = String.valueOf((char) csvpref.getDelimiterChar());
private String quoteChar = String.valueOf((char) csvpref.getQuoteChar());
//Trx
private Trx trx;
private String trxName;
/** Logger */
private static CLogger log = CLogger.getCLogger(GridTabCSVImporter.class);
public File fileImport(GridTab gridTab, List<GridTab> childs, InputStream filestream, Charset charset , String importMode) {
return fileImport(gridTab, childs, filestream, charset, importMode, null);
}
}//fileImport
@Override
public File fileImport(GridTab gridTab, List<GridTab> childs, InputStream filestream, Charset charset, String importMode, IProcessUI processUI) {
ICsvMapReader mapReader = null;
File errFile = null;
File logFile = null;
PrintWriter errFileW = null;
PrintWriter logFileW = null;
CsvPreference csvpref = CsvPreference.STANDARD_PREFERENCE;
String delimiter = String.valueOf((char) csvpref.getDelimiterChar());
String quoteChar = String.valueOf((char) csvpref.getQuoteChar());
m_import_mode = importMode;
PO masterRecord = null;
if(!gridTab.isInsertRecord() && isInsertMode())
throwAdempiereException("Insert record disabled for Tab");
try {
String errFileName = FileUtil.getTempMailName("Import_" + gridTab.getTableName(), "_err.csv");
initValues();
m_import_mode = importMode;
errFile = new File(errFileName);
errFileW = new PrintWriter(errFile, charset.name());
mapReader = new CsvMapReader(new InputStreamReader(filestream, charset), csvpref);
List<String> header = Arrays.asList(mapReader.getHeader(true));
List<CellProcessor> readProcArray = new ArrayList<CellProcessor>();
Map<GridTab,Integer> tabMapIndexes = new HashMap<GridTab,Integer>();
int indxDetail=0;
List<GridField> locationFields = null;
boolean isThereKey = false;
boolean isThereDocAction = false;
header = Arrays.asList(mapReader.getHeader(true));
//Mapping header
int indxDetail = mapCSVHeader(gridTab);
if(isUpdateOrMergeMode() && !isThereKey)
throwAdempiereException(gridTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound"));
tabMapIndexes.put(gridTab,indxDetail-1);
isThereKey = false;
locationFields = null;
//Mapping details
mapCSVDetail(indxDetail, childs);
sortedtTabMapIndexes = null;
if (childs.size()>0 && !tabMapIndexes.isEmpty()){
ValueComparator bvc = new ValueComparator(tabMapIndexes);
sortedtTabMapIndexes = new TreeMap<GridTab,Integer>(bvc);
sortedtTabMapIndexes.putAll(tabMapIndexes);
}else{
Map<GridTab,Integer> localMapIndexes = new HashMap<GridTab,Integer>();
localMapIndexes.put(gridTab, header.size()-1);
ValueComparator bvc = new ValueComparator(localMapIndexes);
sortedtTabMapIndexes = new TreeMap<GridTab,Integer>(bvc);
sortedtTabMapIndexes.putAll(localMapIndexes);
}
m_isError = false;
// write the header
String rawHeader = mapReader.getUntokenizedRow();
errFileW.write(rawHeader + delimiter + ERROR_HEADER + "\n");
data = new ArrayList<Map<String, Object>>();
rawData = new ArrayList<String>();
// pre-process to check for errors
preProcess(processUI, gridTab, indxDetail);
if ( !m_isError ) {
String logFileName = FileUtil.getTempMailName("Import_" + gridTab.getTableName(), "_log.csv");
logFile = new File(logFileName);
logFileW = new PrintWriter(logFile, charset.name());
// write the header
logFileW.write(rawHeader + delimiter + LOG_HEADER + "\n");
// no errors found - process header and then details
isMasterok = true;
isDetailok = true;
error = false;
trx = null;
trxName = null;
rowsTmpResult = new ArrayList<String>();
long lastOutput = new Date().getTime();
for (int idx = 0; idx < data.size(); idx++) {
if( processUI != null && new Date().getTime()-lastOutput > 1000 /* one second */){
processUI.statusUpdate(refreshImportStatus(idx + 1, data.size() + 1));
lastOutput = new Date().getTime();
}
String rawLine = rawData.get(idx);
StringBuilder rowResult = new StringBuilder();
boolean isDetail = false;
if (rawLine.charAt(0)==','){
isDetail=true;
//check out if master row comes empty
Map<String, Object> rowMap = data.get(idx);
for(int i=0; i < indxDetail-1; i++){
if(rowMap.get(header.get(i))!=null){
isDetail=false;
break;
}
}
}
if (!isMasterok && isDetail){
rawLine = rawLine + delimiter + quoteChar + Msg.getMsg(Env.getCtx(),"NotProcessed") + quoteChar + "\n";
rowsTmpResult.add(rawLine);
continue;
}else if(isMasterok && isDetail && !isDetailok){
rawLine = rawLine + delimiter + quoteChar + "Record not proccesed due to detail record failure" + quoteChar + "\n";
rowsTmpResult.add(rawLine);
continue;
}
if( !isDetail ){
processMaster(gridTab);
}
String detailResult = processDetails(importMode, gridTab, indxDetail, isDetail, idx, rowResult);
rowResult.append(detailResult);
// write
rawLine = rawLine + delimiter + quoteChar + rowResult.toString().replaceAll(delimiter, "") + quoteChar + "\n";
rowsTmpResult.add(rawLine);
}
if( trx != null ){
completeDetailTrx(gridTab,childs);
}
}
} catch (IOException e) {
throw new AdempiereException(e);
} catch (Exception ex) {
throw new AdempiereException(ex);
} finally {
try {
if (mapReader != null)
mapReader.close();
if (errFileW != null) {
errFileW.flush();
errFileW.close();
}
if (logFileW != null) {
logFileW.flush();
logFileW.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (logFile != null)
return logFile;
else
return errFile;
}//fileImport
private void initValues(){
mapReader = null;
errFile = null;
logFile = null;
errFileW = null;
logFileW = null;
masterRecord = null;
readProcArray = new ArrayList<CellProcessor>();
tabMapIndexes = new HashMap<GridTab,Integer>();
locationFields = null;
isThereKey = false;
isThereDocAction = false;
}
/**
* Rollsback the trx and update the text in the file
*/
private void rollbackTrx(){
trx.rollback();
for( String row : rowsTmpResult ){
row = row.replaceAll("Updated","RolledBack");
row = row.replaceAll("Inserted","RolledBack");
logFileW.write(row);
}
}
/**
* Commit the trx and writes in the file
*/
private void commitTrx(){
trx.commit();
for( String row : rowsTmpResult )
logFileW.write(row);
}
/**
* Map the header and returns the index where the detail starts
* @param gridTab
* @return
*/
private int mapCSVHeader(GridTab gridTab){
int indxDetail = 0;
for(int idx = 0; idx < header.size(); idx++) {
String headName = header.get(idx);
@ -170,15 +361,18 @@ public class GridTabCSVImporter implements IGridTabImporter
}
}
if(isUpdateOrMergeMode() && !isThereKey)
throwAdempiereException(gridTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound"));
return indxDetail;
}//mapFileHeader
/**
* Map details fields in the csv file
* @param indxDetail
*/
private void mapCSVDetail(int indxDetail, List<GridTab> childs){
tabMapIndexes.put(gridTab,indxDetail-1);
String childTableName = null;
isThereKey = false;
locationFields = null;
GridTab currentDetailTab = null;
//Mapping details
for(int idx = indxDetail; idx < header.size(); idx++) {
String detailName = header.get(idx);
if(detailName!=null && detailName.indexOf(">") > 0){
@ -232,7 +426,6 @@ public class GridTabCSVImporter implements IGridTabImporter
throwAdempiereException(Msg.getMsg(Env.getCtx(),"WrongDetailName",new Object[] {" col("+idx+") ",detailName}));
}
if(currentDetailTab!=null){
if(isUpdateOrMergeMode() && !isThereKey)
throwAdempiereException(currentDetailTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound"));
@ -240,43 +433,39 @@ public class GridTabCSVImporter implements IGridTabImporter
tabMapIndexes.put(currentDetailTab,header.size()-1);
}
TreeMap<GridTab,Integer> sortedtTabMapIndexes= null;
if (childs.size()>0 && !tabMapIndexes.isEmpty()){
ValueComparator bvc = new ValueComparator(tabMapIndexes);
sortedtTabMapIndexes = new TreeMap<GridTab,Integer>(bvc);
sortedtTabMapIndexes.putAll(tabMapIndexes);
}else{
Map<GridTab,Integer> localMapIndexes = new HashMap<GridTab,Integer>();
localMapIndexes.put(gridTab, header.size()-1);
ValueComparator bvc = new ValueComparator(localMapIndexes);
sortedtTabMapIndexes = new TreeMap<GridTab,Integer>(bvc);
sortedtTabMapIndexes.putAll(localMapIndexes);
}
}//mapFileDetail
/**
* Pre process te file lookign for errors
* @param processUI
* @param gridTab
* @param indxDetail
*/
private void preProcess(IProcessUI processUI, GridTab gridTab, int indxDetail){
CellProcessor[] processors = readProcArray.toArray(new CellProcessor[readProcArray.size()]);
m_isError = false;
// write the header
String rawHeader = mapReader.getUntokenizedRow();
errFileW.write(rawHeader + delimiter + ERROR_HEADER + "\n");
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
List<String> rawData = new ArrayList<String>();
// pre-process to check for errors
long lastOutput = new Date().getTime();
while (true) {
if( processUI != null && new Date().getTime()-lastOutput > 1000 /* one second */){
processUI.statusUpdate(refreshImportStatus(data.size(), 0));
lastOutput = new Date().getTime();
}
Map<String, Object> map = null;
boolean isLineError = false;
StringBuilder errMsg = new StringBuilder();
try {
map = mapReader.read( (String []) header.toArray(), processors);
} catch (SuperCsvCellProcessorException e) {
int idx = e.getCsvContext().getColumnNumber() - 1;
errMsg.append(header.get(idx)).append(": ").append(e.getMessage());
isLineError = true;
} catch (IOException e) {
throw new AdempiereException(e);
}
String rawLine = mapReader.getUntokenizedRow();
if (! isLineError) {
if(map == null)
@ -313,71 +502,23 @@ public class GridTabCSVImporter implements IGridTabImporter
rawLine = rawLine + delimiter + quoteChar + errMsg.toString().replaceAll(quoteChar, "") + quoteChar + "\n";
errFileW.write(rawLine);
}
}//preProcess
if (!m_isError) {
String logFileName = FileUtil.getTempMailName("Import_" + gridTab.getTableName(), "_log.csv");
logFile = new File(logFileName);
logFileW = new PrintWriter(logFile, charset.name());
// write the header
logFileW.write(rawHeader + delimiter + LOG_HEADER + "\n");
// no errors found - process header and then details
boolean isMasterok = true;
boolean isDetailok = true;
boolean error=false;
Trx trx = null;
String trxName= null;
List<String> rowsTmpResult = new ArrayList<String>();
lastOutput = new Date().getTime();
/**
* Process the line until a detail field is found
* @param gridTab
*/
private void processMaster(GridTab gridTab){
for (int idx = 0; idx < data.size(); idx++) {
if( processUI != null && new Date().getTime()-lastOutput > 1000 /* one second */){
processUI.statusUpdate(refreshImportStatus(idx + 1, data.size() + 1));
lastOutput = new Date().getTime();
}
String rawLine = rawData.get(idx);
String logMsg = null;
StringBuilder rowResult = new StringBuilder();
GridTab currentGridTab=null;
boolean isDetail=false;
int currentColumn=0;
if (rawLine.charAt(0)==','){
isDetail=true;
//check out if master row comes empty
Map<String, Object> rowMap = data.get(idx);
for(int i=0; i < indxDetail-1; i++){
if(rowMap.get(header.get(i))!=null){
isDetail=false;
break;
}
}
}
if (!isMasterok && isDetail){
rawLine = rawLine + delimiter + quoteChar + Msg.getMsg(Env.getCtx(),"NotProcessed") + quoteChar + "\n";
rowsTmpResult.add(rawLine);
continue;
}else if(isMasterok && isDetail && !isDetailok){
rawLine = rawLine + delimiter + quoteChar + "Record not proccesed due to detail record failure" + quoteChar + "\n";
rowsTmpResult.add(rawLine);
continue;
}
try {
if(!isDetail){
if( trx != null ){
if(error){
trx.rollback();
for(String row:rowsTmpResult){
row =row.replaceAll("Updated","RolledBack");
row =row.replaceAll("Inserted","RolledBack");
logFileW.write(row);
}
error =false;
if( isError() ) {
rollbackTrx();
setError(false);
}else {
if( isThereDocAction ){
boolean isError = false;
int AD_Process_ID = MColumn.get(Env.getCtx(), gridTab.getField("DocAction").getAD_Column_ID()).getAD_Process_ID();
@ -393,36 +534,48 @@ public class GridTabCSVImporter implements IGridTabImporter
}
if( isError ){
trx.rollback();
for(String row:rowsTmpResult){
row = row.replaceAll("Updated","RolledBack");
row = row.replaceAll("Inserted","RolledBack");
logFileW.write(row);
rollbackTrx();
}else{
commitTrx();
}
}else{
trx.commit();
for(String row:rowsTmpResult)
logFileW.write(row);
}
}else{
trx.commit();
for(String row:rowsTmpResult)
logFileW.write(row);
commitTrx();
}
}
trx.close();
trx=null;
}
trxName = "Import_" + gridTab.getTableName() + "_" + UUID.randomUUID();
trxName = getTrxName(gridTab.getTableName());
gridTab.getTableModel().setImportingMode(true,trxName);
trx = Trx.get(trxName,true);
masterRecord = null;
rowsTmpResult.clear();
isMasterok = true;
isDetailok = true;
}
}//processMaster
/**
* Process the details
* @param importMode
* @param gridTab
* @param indxDetail
* @param isDetail
* @param idx
* @param rowResult
* @return
*/
private String processDetails(String importMode, GridTab gridTab, int indxDetail, boolean isDetail, int idx, StringBuilder rowResult){
String logMsg = null;
GridTab currentGridTab = null;
int currentColumn = 0;
try {
for( Map.Entry<GridTab, Integer> tabIndex : sortedtTabMapIndexes.entrySet() ) {
currentGridTab = tabIndex.getKey();
if( isDetail && gridTab.equals(currentGridTab) ){
@ -440,6 +593,7 @@ public class GridTabCSVImporter implements IGridTabImporter
logMsg = areValidKeysAndColumns(currentGridTab,data.get(idx),header,currentColumn,j,masterRecord,trx);
if (logMsg == null){
if ( isInsertMode() ){
if( !currentGridTab.getTableModel().isOpen() )
currentGridTab.getTableModel().open(0);
@ -462,13 +616,15 @@ public class GridTabCSVImporter implements IGridTabImporter
logMsg = "";
continue;
}else
error =true;
setError(true);
}
}else {
error =true;
setError(true);
currentColumn = j + 1;
}
if (! error) {
if ( !isError() ) {
if ( currentGridTab.dataSave(false) ){
PO po = currentGridTab.getTableModel().getPO(currentGridTab.getCurrentRow());
//Keep master record for details validation
@ -497,6 +653,7 @@ public class GridTabCSVImporter implements IGridTabImporter
logMsg = Msg.getMsg(Env.getCtx(), "Error") + " " + Msg.getMsg(Env.getCtx(), "SaveError") + " (" + info + ")";
currentGridTab.dataIgnore();
//Problem in the master record
if( currentGridTab.equals(gridTab) && masterRecord == null ){
isMasterok = false;
rowResult.append( "<" + currentGridTab.getTableName() + ">: " );
@ -505,6 +662,7 @@ public class GridTabCSVImporter implements IGridTabImporter
break;
}
//Problem in the detail record
if( !currentGridTab.equals(gridTab) && masterRecord != null ){
isDetailok = false;
rowResult.append( "<" + currentGridTab.getTableName() + ">: " );
@ -513,10 +671,12 @@ public class GridTabCSVImporter implements IGridTabImporter
break;
}
}
rowResult.append( "<" + currentGridTab.getTableName() + ">: " );
rowResult.append(logMsg);
rowResult.append(" / ");
} else {
} else { //if error true
currentGridTab.dataIgnore();
rowResult.append( "<" + currentGridTab.getTableName() + ">: " );
@ -529,6 +689,7 @@ public class GridTabCSVImporter implements IGridTabImporter
break;
}
//Detail failed
if( !currentGridTab.equals(gridTab) && masterRecord != null ){
isDetailok = false;
break;
@ -537,12 +698,14 @@ public class GridTabCSVImporter implements IGridTabImporter
m_import_mode = importMode;
}
} catch (Exception e) {
rowResult.append( "<" + currentGridTab.getTableName() + ">: " );
rowResult.append(Msg.getMsg(Env.getCtx(), "Error") + " " + e);
rowResult.append(" / ");
currentGridTab.dataIgnore();
error = true;
setError(true);
//Master Failed, thus details cannot be imported
if( currentGridTab.equals(gridTab) && masterRecord == null )
isMasterok = false;
@ -553,20 +716,23 @@ public class GridTabCSVImporter implements IGridTabImporter
} finally {
m_import_mode = importMode;
}
// write
rawLine = rawLine + delimiter + quoteChar + rowResult.toString().replaceAll(delimiter, "") + quoteChar + "\n";
rowsTmpResult.add(rawLine);
}
if(trx!=null){
if(error){
trx.rollback();
for(String row:rowsTmpResult){
row =row.replaceAll("Updated","RolledBack");
row =row.replaceAll("Inserted","RolledBack");
logFileW.write(row);
}
return rowResult.toString();
}//processDetails
/**
* Finishes the detail import process
* When the detail failed - rollback the trx
* if the detail succeed commit and close the trx
* @param gridTab
* @param childs
*/
private void completeDetailTrx(GridTab gridTab, List<GridTab> childs){
if( isError() ){
rollbackTrx();
}else {
if(isThereDocAction){
boolean isError = false;
@ -584,21 +750,12 @@ public class GridTabCSVImporter implements IGridTabImporter
}
if( isError ){
trx.rollback();
for(String row:rowsTmpResult){
row = row.replaceAll("Updated","RolledBack");
row = row.replaceAll("Inserted","RolledBack");
logFileW.write(row);
rollbackTrx();
}else{
commitTrx();
}
}else {
trx.commit();
for(String row:rowsTmpResult)
logFileW.write(row);
}
}else {
trx.commit();
for(String row:rowsTmpResult)
logFileW.write(row);
commitTrx();
}
}
@ -611,34 +768,14 @@ public class GridTabCSVImporter implements IGridTabImporter
detail.getTableModel().setImportingMode(false,null);
}
}
trx.close();
trx = null;
}
}
} catch (IOException e) {
throw new AdempiereException(e);
} catch (Exception ex) {
throw new AdempiereException(ex);
} finally {
try {
if (mapReader != null)
mapReader.close();
if (errFileW != null) {
errFileW.flush();
errFileW.close();
}
if (logFileW != null) {
logFileW.flush();
logFileW.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (logFile != null)
return logFile;
else
return errFile;
}//completeDetailTrx
private String getTrxName(String gritTabName){
return "Import_" + gritTabName + "_" + UUID.randomUUID();
}
private void throwAdempiereException(String msg){
@ -1410,6 +1547,14 @@ public class GridTabCSVImporter implements IGridTabImporter
return localFile;
}
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
static class ValueComparator implements Comparator<GridTab> {
Map<GridTab,Integer> base;
public ValueComparator(Map<GridTab,Integer> base) {