Parser.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.utils.csv;
  21. import java.io.ByteArrayInputStream;
  22. import java.io.File;
  23. import java.io.FileInputStream;
  24. import java.io.InputStream;
  25. import java.io.InputStreamReader;
  26. import java.io.Reader;
  27. import java.io.StringReader;
  28. import java.util.ArrayList;
  29. import java.util.Enumeration;
  30. import java.util.List;
  31. import java.util.Properties;

  32. import org.apache.commons.csv.CSVParser;
  33. import org.apache.commons.csv.CSVRecord;
  34. import org.apache.commons.io.input.BOMInputStream;
  35. import org.apache.commons.lang.ArrayUtils;
  36. import org.apache.commons.lang.CharEncoding;
  37. import org.openspcoop2.utils.Utilities;
  38. import org.openspcoop2.utils.UtilsException;
  39. import org.openspcoop2.utils.regexp.RegExpNotFoundException;
  40. import org.openspcoop2.utils.regexp.RegularExpressionEngine;

  41. /**
  42.  * Parser
  43.  *
  44.  * @author Andrea Poli (apoli@link.it)
  45.  * @author $Author$
  46.  * @version $Rev$, $Date$
  47.  */
  48. public class Parser {

  49.     private static final String PARSER_NAME_PREFIX_= "mapping.field.";
  50.     private static final String PARSER_REGEXP_PREFIX_= "mapping.regexp.";
  51.     private static final String PARSER_REGEXP_SUFFIX_NOT_FOUND= ".notFound";
  52.     private static final String PARSER_DEFAULT_PREFIX_= "mapping.default.";
  53.     private static final String PARSER_CONSTANT_PREFIX_= "mapping.constant.";
  54.     private static final String PARSER_REQUIRED_PREFIX_= "mapping.required.";
  55.    
  56.     private List<ParserMappingRecord> mapping;
  57.    
  58.     public Parser(List<ParserMappingRecord> mapping) throws UtilsException {
  59.         if(mapping==null || mapping.size()<=0){
  60.             throw new UtilsException("Map is null");
  61.         }
  62.         for (int i = 0; i < mapping.size(); i++) {
  63.             if(mapping.get(i).getName()==null){
  64.                 throw new UtilsException("RecordMapping["+i+"] without name");
  65.             }
  66.             if(mapping.get(i).getConstantValue()==null && mapping.get(i).getCsvPosition()==null && mapping.get(i).getCsvColumnName()==null ){
  67.                 throw new UtilsException("RecordMapping["+i+"] name["+mapping.get(i).getName()+"] without almost one required field: constantValue, csvPosition, csvColumnName");
  68.             }
  69.         }
  70.         this.mapping = mapping;
  71.     }
  72.     public Parser(InputStream is,boolean positionMapping) throws UtilsException {
  73.         Properties p = null;
  74.         try{
  75.             p = new Properties();
  76.             p.load(is);
  77.         }catch(Exception e){
  78.             throw new UtilsException(e.getMessage(),e);
  79.         }
  80.         this.init(p,positionMapping);
  81.     }
  82.     public Parser(Properties properties,boolean positionMapping) throws UtilsException {
  83.         this.init(properties,positionMapping);
  84.     }
  85.    
  86.     private void init(Properties properties,boolean positionMapping) throws UtilsException{
  87.        
  88.         Properties names = Utilities.readProperties(PARSER_NAME_PREFIX_, properties);
  89.         Properties regexps = Utilities.readProperties(PARSER_REGEXP_PREFIX_, properties);
  90.         Properties constants = Utilities.readProperties(PARSER_CONSTANT_PREFIX_, properties);
  91.         Properties required = Utilities.readProperties(PARSER_REQUIRED_PREFIX_, properties);
  92.         Properties defaults = Utilities.readProperties(PARSER_DEFAULT_PREFIX_, properties);
  93.        
  94.         if(names.size()<=0){
  95.             throw new UtilsException("No mapping exists");
  96.         }
  97.        
  98.         this.mapping = new ArrayList<ParserMappingRecord>();
  99.        
  100.        
  101.         Enumeration<?> enNames = names.keys();
  102.         while (enNames.hasMoreElements()) {
  103.             String key = (String) enNames.nextElement();
  104.             String value = names.getProperty(key);
  105.             if(value==null){
  106.                 throw new UtilsException("Property ["+PARSER_NAME_PREFIX_+key+"] without value");
  107.             }
  108.             value = value.trim();
  109.            
  110.             ParserMappingRecord recordMapping = null;
  111.                        
  112.             if(constants.containsKey(key)){
  113.                 if("true".equalsIgnoreCase(constants.getProperty(key).trim())){
  114.                     //  E' una costante
  115.                     recordMapping = ParserMappingRecord.newCsvConstantRecord(key, value);
  116.                 }
  117.                 else if("false".equalsIgnoreCase(required.getProperty(key).trim())){
  118.                     // verrà gestita come campo normale.
  119.                 }
  120.                 else{
  121.                     throw new UtilsException("Property ["+PARSER_CONSTANT_PREFIX_+key+"] with wrong value (expected true/false): "+required.getProperty(key).trim());
  122.                 }
  123.             }
  124.            
  125.             if(recordMapping==null){
  126.                 // non è una costante
  127.            
  128.                 String [] regExpr = null;
  129.                 ParserRegexpNotFound regexpNotFoundBehaviour = null;
  130.                 if(regexps.containsKey(key)){
  131.                     if(regexps.getProperty(key)!=null){
  132.                         List<String> r = new ArrayList<>();
  133.                         r.add(regexps.getProperty(key).trim());
  134.                         int index = 1;
  135.                         while(regexps.containsKey(key+"."+index) && regexps.getProperty(key+"."+index)!=null){
  136.                             r.add(regexps.getProperty(key+"."+index).trim());
  137.                             index++;
  138.                         }
  139.                         regExpr = r.toArray(new String[1]);
  140.                        
  141.                         if(regexps.containsKey(key+PARSER_REGEXP_SUFFIX_NOT_FOUND)){
  142.                             if(regexps.getProperty(key+PARSER_REGEXP_SUFFIX_NOT_FOUND)!=null){
  143.                                 String tmp = regexps.getProperty(key+PARSER_REGEXP_SUFFIX_NOT_FOUND).trim();
  144.                                 ParserRegexpNotFound [] p = ParserRegexpNotFound.values();
  145.                                 for (int i = 0; i < p.length; i++) {
  146.                                     if(p[i].toString().equals(tmp)){
  147.                                         regexpNotFoundBehaviour = p[i];
  148.                                         break;
  149.                                     }
  150.                                 }
  151.                                 if(regexpNotFoundBehaviour==null){
  152.                                     throw new UtilsException("Property ["+PARSER_REGEXP_PREFIX_+key+PARSER_REGEXP_SUFFIX_NOT_FOUND+"] with wrong value (expected: "+ArrayUtils.toString(p)+"): "+tmp);
  153.                                 }
  154.                             }
  155.                         }
  156.                     }
  157.                 }
  158.                
  159.                 String defaultValue = null;
  160.                 if(defaults.containsKey(key)){
  161.                     if(defaults.getProperty(key)!=null){
  162.                         defaultValue = defaults.getProperty(key).trim();
  163.                     }
  164.                 }
  165.                
  166.                 if(positionMapping){
  167.                     try{
  168.                         int intValue = Integer.parseInt(value);
  169.                         if(intValue<0){
  170.                             throw new Exception("Negative Number");
  171.                         }
  172.                         recordMapping = ParserMappingRecord.newCsvColumnPositionRecord(key, intValue, defaultValue, regexpNotFoundBehaviour, regExpr);
  173.                     }catch(Exception e){
  174.                         throw new UtilsException("Property ["+PARSER_NAME_PREFIX_+key+"] with wrong value (expected positive number): "+e.getMessage(),e);
  175.                     }
  176.                 }
  177.                 else{
  178.                     recordMapping = ParserMappingRecord.newCsvColumnNameRecord(key, value, defaultValue, regexpNotFoundBehaviour, regExpr);
  179.                 }
  180.                
  181.                 if(required.containsKey(key)){
  182.                     if("true".equalsIgnoreCase(required.getProperty(key).trim())){
  183.                         recordMapping.setRequired(true);
  184.                     }
  185.                     else if("false".equalsIgnoreCase(required.getProperty(key).trim())){
  186.                         recordMapping.setRequired(false);
  187.                     }
  188.                     else{
  189.                         throw new UtilsException("Property ["+PARSER_REQUIRED_PREFIX_+key+"] with wrong value (expected true/false): "+required.getProperty(key).trim());
  190.                     }
  191.                 }
  192.                
  193.             }
  194.            
  195.             this.mapping.add(recordMapping);
  196.         }
  197.        
  198.     }
  199.    
  200.     public ParserResult parseCsvFile(Format format, String csvContent) throws UtilsException{
  201.         Reader reader = null;
  202.         try{
  203.             reader = new StringReader(csvContent);
  204.             return this.parseCsvFile(format, reader);
  205.         }finally{
  206.             try{
  207.                 if(reader!=null){
  208.                     reader.close();
  209.                 }
  210.             }catch(Exception eClose){
  211.                 // close
  212.             }
  213.         }
  214.     }
  215.    
  216.     public ParserResult parseCsvFile(Format format, byte[] csv) throws UtilsException{
  217.         return this.parseCsvFile(format, csv, CharEncoding.UTF_8,false);
  218.     }
  219.     public ParserResult parseCsvFile(Format format, byte[] csv, String charset) throws UtilsException{
  220.         return this.parseCsvFile(format, csv, charset,false);
  221.     }
  222.     public ParserResult parseCsvFile(Format format, byte[] csv,boolean enableBomInputStream) throws UtilsException{
  223.         return this.parseCsvFile(format, csv, CharEncoding.UTF_8,enableBomInputStream);
  224.     }
  225.     public ParserResult parseCsvFile(Format format, byte[] csv, String charset,boolean enableBomInputStream) throws UtilsException{
  226.         ByteArrayInputStream bin = null;
  227.         try{
  228.             bin = new ByteArrayInputStream(csv);
  229.             return this.parseCsvFile(format, bin,charset,enableBomInputStream);
  230.         }finally{
  231.             try{
  232.                 if(bin!=null){
  233.                     bin.close();
  234.                 }
  235.             }catch(Exception eClose){
  236.                 // close
  237.             }
  238.         }
  239.     }
  240.    
  241.     public ParserResult parseCsvFile(Format format, File file) throws UtilsException{
  242.         return this.parseCsvFile(format, file, CharEncoding.UTF_8, false);
  243.     }
  244.     public ParserResult parseCsvFile(Format format, File file, String charset) throws UtilsException{
  245.         return this.parseCsvFile(format, file, charset, false);
  246.     }
  247.     public ParserResult parseCsvFile(Format format, File file, boolean enableBomInputStream) throws UtilsException{
  248.         return this.parseCsvFile(format, file, CharEncoding.UTF_8, enableBomInputStream);
  249.     }
  250.     public ParserResult parseCsvFile(Format format, File file, String charset,boolean enableBomInputStream) throws UtilsException{
  251.         FileInputStream fin = null;
  252.         try{
  253.             fin = new FileInputStream(file);
  254.             return this.parseCsvFile(format, fin,charset,enableBomInputStream);
  255.         }catch(Exception e){
  256.             throw new UtilsException(e.getMessage(),e);
  257.         }
  258.         finally{
  259.             try{
  260.                 if(fin!=null){
  261.                     fin.close();
  262.                 }
  263.             }catch(Exception eClose){
  264.                 // close
  265.             }
  266.         }
  267.     }
  268.    
  269.     public ParserResult parseCsvFile(Format format, InputStream is) throws UtilsException{
  270.         return parseCsvFile(format, is, CharEncoding.UTF_8, false);
  271.     }
  272.     public ParserResult parseCsvFile(Format format, InputStream is, String charset) throws UtilsException{
  273.         return parseCsvFile(format, is, charset, false);
  274.     }
  275.     public ParserResult parseCsvFile(Format format, InputStream is, boolean enableBomInputStream) throws UtilsException{
  276.         return parseCsvFile(format, is, CharEncoding.UTF_8, enableBomInputStream);
  277.     }
  278.     public ParserResult parseCsvFile(Format format, InputStream is, String charset,boolean enableBomInputStream) throws UtilsException{
  279.         Reader reader = null;
  280.         BOMInputStream bomInputStream = null;
  281.         try{
  282.             if(enableBomInputStream){
  283.                 bomInputStream =
  284.                         BOMInputStream.builder()
  285.                           .setInputStream(is)
  286.                           .setCharset(charset)
  287.                           .get();
  288.                 reader = new InputStreamReader(bomInputStream,charset);
  289.             }
  290.             else{
  291.                 reader = new InputStreamReader(is,charset);
  292.             }
  293.             return this.parseCsvFile(format, reader);
  294.         }catch(Exception e){
  295.             throw new UtilsException(e.getMessage(),e);
  296.         }
  297.         finally{
  298.             try{
  299.                 if(bomInputStream!=null){
  300.                     bomInputStream.close();
  301.                 }
  302.             }catch(Exception eClose){}
  303.             try{
  304.                 if(reader!=null){
  305.                     reader.close();
  306.                 }
  307.             }catch(Exception eClose){
  308.                 // close
  309.             }
  310.         }
  311.     }
  312.     public ParserResult parseCsvFile(Format format, Reader reader) throws UtilsException{
  313.        
  314.         CSVParser parser = null;
  315.         ParserResult parserResult = null;
  316.         try{
  317.             parser = new CSVParser(reader, format.getCsvFormat());
  318.            
  319.             parserResult = new ParserResult();
  320.             parserResult.setHeaderMap(parser.getHeaderMap());
  321.            
  322.             for(CSVRecord record : parser.getRecords()){
  323.                 if(format.isSkipEmptyRecord()){
  324.                     if(isRecordEmpty(record)){
  325.                         continue;
  326.                     }
  327.                 }
  328.                
  329.                 Record recordBean = new Record();
  330.                 recordBean.setComment(record.getComment());
  331.                 recordBean.setCsvLine(record.getRecordNumber());
  332.                 recordBean.setRecord(record);
  333.                 MapResult recordMap = new MapResult();
  334.                
  335.                 try{
  336.                     for (int i = 0; i < this.mapping.size(); i++) {
  337.                        
  338.                         ParserMappingRecord mappingRecord = this.mapping.get(i);
  339.                         String key = mappingRecord.getName();
  340.                         String valore = null;
  341.                        
  342.                         if(mappingRecord.getConstantValue()!=null){
  343.                             valore = mappingRecord.getConstantValue();
  344.                         }
  345.                         else{
  346.                             String tmpValue = null;
  347.                             if(mappingRecord.getCsvPosition()!=null){
  348.                                 if(mappingRecord.getCsvPosition()<record.size()){
  349.                                     tmpValue = record.get(mappingRecord.getCsvPosition());
  350.                                 }
  351.                                 else{
  352.                                     throw new Exception("Record with index ["+mappingRecord.getCsvPosition()+"] is greather or equals record size ["+record.size()+"]");
  353.                                 }
  354.                             }
  355.                             else{
  356.                                 if(record.isMapped(mappingRecord.getCsvColumnName())){
  357.                                     tmpValue = record.get(mappingRecord.getCsvColumnName());
  358.                                 }
  359.                                 else{
  360.                                     throw new Exception("Record with column name ["+mappingRecord.getCsvColumnName()+"] not exists");
  361.                                 }
  362.                             }
  363.                             if(tmpValue!=null && mappingRecord.getRegexpr()!=null){
  364.                                 String [] pattern = mappingRecord.getRegexpr();
  365.                                 String regExpValue = null;
  366.                                 for (int j = 0; j < pattern.length; j++) {
  367.                                     try{
  368.                                         regExpValue = RegularExpressionEngine.getStringMatchPattern(tmpValue, pattern[j]);
  369.                                         if(regExpValue!=null){
  370.                                             break;
  371.                                         }
  372.                                     }catch(RegExpNotFoundException notFound){}  
  373.                                 }
  374.                                 if(regExpValue==null){
  375.                                     if(ParserRegexpNotFound.ERROR.equals(mappingRecord.getRegexpNotFoundBehaviour())){
  376.                                         throw new Exception("Mapping for field ["+mappingRecord.getName()+"] failed, regular expression not match (value ["+tmpValue+"])");
  377.                                     }
  378.                                     else if(ParserRegexpNotFound.NULL.equals(mappingRecord.getRegexpNotFoundBehaviour())){
  379.                                         tmpValue = null;
  380.                                     }
  381. //                                  else if(ParserRegexpNotFound.ORIGINAL.equals(mappingRecord.getRegexpNotFoundBehaviour())){
  382. //                                      //tmpValue = tmpValue;
  383. //                                  }
  384.                                 }
  385.                                 else{
  386.                                     tmpValue = regExpValue;
  387.                                 }
  388.                             }
  389.                             if(tmpValue==null && mappingRecord.getDefaultValue()!=null){
  390.                                 tmpValue = mappingRecord.getDefaultValue();
  391.                             }
  392.                             if(tmpValue==null && mappingRecord.isRequired()){
  393.                                 throw new Exception("Mapping for field ["+mappingRecord.getName()+"] failed");
  394.                             }
  395.                             valore = tmpValue;
  396.                         }
  397.                        
  398.                         recordMap.add(key, valore);
  399.                     }
  400.                    
  401.        
  402.                 }catch(Exception e){
  403.                     throw new Exception("Record Line["+record.getRecordNumber()+"] ["+record+"]: "+e.getMessage(),e);
  404.                 }
  405.                 finally{
  406.                     try{
  407.                         if(parser!=null){
  408.                             parser.close();
  409.                             parser = null;
  410.                         }
  411.                     }catch(Exception eClose) {
  412.                         // close
  413.                     }
  414.                 }
  415.                
  416.                 recordBean.setMap(recordMap);
  417.                 parserResult.getRecords().add(recordBean);
  418.             }
  419.            
  420.             return parserResult;
  421.         }
  422.         catch(Exception e){
  423.             throw new UtilsException(e.getMessage(),e);
  424.         }finally{
  425.             try{
  426.                 if(parser!=null){
  427.                     parser.close();
  428.                 }
  429.             }catch(Exception eClose){
  430.                 // close
  431.             }
  432.         }
  433.        
  434.     }
  435.    
  436.    
  437.     public static boolean isRecordEmpty(CSVRecord record){
  438.         for (int i = 0; i < record.size(); i++) {
  439.             if(record.get(i)!=null && !record.get(i).trim().equals("") ){
  440.                 return false;
  441.             }
  442.         }
  443.         return true;
  444.     }

  445. }