/*
|
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
|
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
|
*/
|
package com.megatim.generatetxt.generate;
|
|
import com.megatim.generatetxt.model.AlphaNumeriqueField;
|
import com.megatim.generatetxt.model.TruncatedElement;
|
import com.megatim.generatetxt.pojo.Constantes;
|
import com.megatim.generatetxt.pojo.FileToValidateDescription;
|
import com.megatim.generatetxt.pojo.ResultFileParsing;
|
import com.megatim.generatetxt.utilities.ParserUtils;
|
import static com.megatim.generatetxt.utilities.Utilities.getCharset;
|
import com.megatim.typefichier.validator.Validator;
|
import com.megatim.typefichier.validator.model.ConfigStreamValidator;
|
import com.megatim.validator.parser.ValidatorParser;
|
import com.megatim.validator.parser.pojo.TableDefinition;
|
import java.io.BufferedReader;
|
import java.io.BufferedWriter;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.IOException;
|
import java.io.InputStreamReader;
|
import java.math.BigDecimal;
|
import java.nio.charset.Charset;
|
import java.nio.charset.StandardCharsets;
|
import java.nio.file.Files;
|
import java.nio.file.LinkOption;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.nio.file.StandardOpenOption;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.Arrays;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Random;
|
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.logging.Level;
|
import java.util.logging.Logger;
|
import java.util.stream.Collectors;
|
import org.apache.commons.csv.CSVFormat;
|
import org.apache.commons.csv.CSVRecord;
|
import org.apache.commons.io.IOUtils;
|
|
/**
|
*
|
* @author ASUS
|
*/
|
public class ParseData {
|
|
public File validateCsvFile(FileToValidateDescription fileDescription, boolean headerPresent, Set<AlphaNumeriqueField> alphaNumeriqueToBeTruncated, int nbreThreads) throws Exception {
|
File fileToValidate = fileDescription.getFile();
|
TableDefinition tableDefinition = ValidatorParser.retrieveFieldsFromValidator(new File(fileDescription.getValidatorPath()), fileDescription.getCodeTypeFichier());
|
|
if (tableDefinition.getHeaderPresent() == null || tableDefinition.getColumnDelimiter() == null || tableDefinition.getLineDelimiter() == null) {
|
fileToValidate = parseCsvFile(fileDescription, headerPresent, alphaNumeriqueToBeTruncated);
|
}
|
|
byte[] targetArray = IOUtils.toByteArray(ParseData.class.getClassLoader().getResourceAsStream("predicatelogic-engine.xml"));
|
ConfigStreamValidator configValidator = new ConfigStreamValidator(
|
targetArray,
|
IOUtils.toByteArray(new FileInputStream(fileDescription.getValidatorPath())),
|
fileDescription.getOutputDir(),
|
fileToValidate
|
);
|
|
if (new Validator().validate(configValidator, Boolean.FALSE, Boolean.FALSE, nbreThreads)) {
|
return fileToValidate;
|
}
|
throw new Exception("Echec Validation\nFichier en entrée : " + fileDescription.getFile().getAbsolutePath() + "\nFichier en TXT sortie : " + fileToValidate.getAbsolutePath()
|
+ "\nFichier des erreurs : " + fileToValidate.getName() + Validator.ERROR_EXTENSION);
|
}
|
|
/**
|
*
|
* @param fileDescription objet contenant les informations d'extraction de
|
* données
|
* @param headerPresent : renseigne si le fichier a une ligne d'entête
|
* @param alphaNumeriqueToBeTruncated : liste des champs à tronquer
|
* @throws Exception
|
*/
|
private File parseCsvFile(FileToValidateDescription fileDescription, boolean headerPresent, Set<AlphaNumeriqueField> alphaNumeriqueToBeTruncated) throws Exception {
|
AtomicInteger numeroLigne = new AtomicInteger(0);
|
AtomicBoolean columnTruncated = new AtomicBoolean();
|
|
if (fileDescription != null) {
|
String fileName = fileDescription.getCodeTypeFichier() + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + generateTierce() + ".txt";
|
File outputFile = new File(fileDescription.getOutputDir(), fileName);
|
Map<Integer, Map> mapParams = ParserUtils.getValidateurElements(fileDescription.getValidatorPath());
|
Charset charset = getCharset(fileDescription.getFile());
|
|
CSVFormat csvFormat = CSVFormat.DEFAULT.builder()
|
.setSkipHeaderRecord(headerPresent)
|
.setIgnoreSurroundingSpaces(true)
|
.setTrim(true)
|
.setDelimiter(fileDescription.getColumnDelimiter())
|
.build();
|
|
AtomicBoolean withHeader = new AtomicBoolean(headerPresent);
|
Path pathOfTruncatedElts = createTruncatedEltsPath(fileDescription.getCodeTypeFichier(), fileDescription.getOutputDir(), fileDescription.getFile());
|
|
try ( FileInputStream fis = new FileInputStream(fileDescription.getFile()); InputStreamReader isr = new InputStreamReader(fis, charset); BufferedReader reader = new BufferedReader(isr)) {
|
try ( BufferedWriter bufferWriter = Files.newBufferedWriter(outputFile.toPath(), charset, StandardOpenOption.APPEND, StandardOpenOption.CREATE, StandardOpenOption.SYNC); BufferedWriter truncateWriter = Files.newBufferedWriter(pathOfTruncatedElts, StandardCharsets.UTF_8, StandardOpenOption.APPEND, StandardOpenOption.CREATE, StandardOpenOption.SYNC)) {
|
|
Iterable<CSVRecord> records = csvFormat.parse(reader);
|
|
for (CSVRecord record : records) {
|
|
String[] columnsTableLine = new String[record.size()];
|
|
AtomicInteger i = new AtomicInteger();
|
|
if (!withHeader.get()) {
|
|
record.forEach(column -> {
|
|
columnsTableLine[i.get()] = column != null ? column.replaceAll(" {2,}", " ").replaceAll("\r", "").replaceAll("\n", "").replaceAll("\t", "") : "";
|
|
i.set(i.get() + 1);
|
|
});
|
|
//Nettoyage d'une colonne de chaque ligne
|
ResultFileParsing resultFileParsing = buildFinalColumnsTableLine(outputFile, columnsTableLine, mapParams, alphaNumeriqueToBeTruncated, numeroLigne.incrementAndGet(), truncateWriter, fileDescription.getCodeTypeFichier());
|
String[] finalColumnsTableLine = resultFileParsing.getFinalColumnsTableLine();
|
columnTruncated.set(resultFileParsing.isColumnTruncated());
|
|
String[] encodedStrings = ParserUtils.encodeStrings(finalColumnsTableLine, charset);
|
ParserUtils.writeToFile(encodedStrings, bufferWriter);
|
|
} else {
|
|
withHeader.set(false);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
Logger.getLogger(ParseData.class.getName()).log(Level.SEVERE, e.getMessage(), e);
|
}
|
|
//Si au moins une colonne a été tronqué, alors copier le fichier dont les lignes ont été tronqué dans le répertoire d'archivage
|
if (columnTruncated.get()) {
|
Path originalFilePathInArchiveFolder = createOriginalFileInArchive(fileDescription.getCodeTypeFichier(), fileDescription.getOutputDir(), fileDescription.getFile());
|
|
//Copie du fichier dont les colonnes ont été tronqués vers le répertoire archivage
|
if (fileDescription.getFile().exists()) {
|
Files.copy(fileDescription.getFile().toPath(), originalFilePathInArchiveFolder);
|
}
|
|
} else {//Sinon supprimer le fichier des éléments tronqués, car il est vide
|
Files.deleteIfExists(pathOfTruncatedElts);
|
}
|
}
|
return outputFile;
|
}
|
return null;
|
}
|
|
/**
|
* méthode permettant de construire le chemin du fichier d'origine dans le
|
* répertoire d'archivages
|
*
|
* @param validateur
|
* @param originalFile
|
* @return
|
* @throws IOException
|
*/
|
private static Path createOriginalFileInArchive(String codeTypeFichier, String arhivageDirectory, File originalFile) throws IOException {
|
final String today = LocalDate.now().toString();
|
|
Path originalFilesDir = Paths.get(arhivageDirectory, today, codeTypeFichier, "fichiers");
|
|
if (!Files.exists(originalFilesDir, LinkOption.NOFOLLOW_LINKS)) {
|
Files.createDirectories(originalFilesDir);
|
}
|
|
Path originalFilePath = Paths.get(arhivageDirectory, today, codeTypeFichier, "fichiers", originalFile.getName());
|
final String fileNameWithoutExtension = fileNameWithoutExtension(originalFile);
|
|
//Si le fichier existe dans le répertoire d'archivages, alors ajouter un numero d'ordre dans son nom
|
if (Files.exists(originalFilePath, LinkOption.NOFOLLOW_LINKS)) {
|
List<Path> existingFiles = Files.list(originalFilesDir).filter(p
|
-> p.toString().contains(fileNameWithoutExtension)).collect(Collectors.toList());
|
|
int nbFiles = existingFiles.size();
|
|
if (nbFiles > 0) {
|
originalFilePath = Paths.get(arhivageDirectory, today, codeTypeFichier, "fichiers",
|
fileNameWithoutExtension + "(" + nbFiles + ")" + getFileExtension(originalFile));
|
}
|
}
|
|
return originalFilePath;
|
}
|
|
/**
|
* Méthode permettant de construire la donnée finale à écrire dans le
|
* fichier texte
|
*
|
* @param outputFile
|
* @param initialColumnsTableLine
|
* @param mapParams
|
* @param fieldsToBeTruncated
|
* @param numeroLigne
|
* @param truncateWriter
|
* @return
|
* @throws Exception
|
*/
|
private static ResultFileParsing buildFinalColumnsTableLine(File outputFile, String[] initialColumnsTableLine, Map<Integer, Map> mapParams, Set<AlphaNumeriqueField> fieldsToBeTruncated, int numeroLigne, BufferedWriter truncateWriter, String codeTypeFichier) throws Exception {
|
|
String[] finalColumnsTableLine = new String[mapParams.size() - 1];
|
String[] columnsTableLine = new String[mapParams.size() - 1];
|
|
//On copie les éléments à normaliser dans un nouveau tableau
|
for (int j = 0; j < finalColumnsTableLine.length; j++) {
|
|
//Arreter la copie si on atteint la fin du tableau initial
|
if (j >= initialColumnsTableLine.length) {
|
j = finalColumnsTableLine.length;
|
} else {
|
columnsTableLine[j] = initialColumnsTableLine[j];
|
}
|
}
|
|
//Type de données utilisé pour retourner le résultat de la construction des lignes de données
|
ResultFileParsing resultFileParsing = new ResultFileParsing();
|
|
int index = 0;
|
|
for (String str : columnsTableLine) {
|
|
String columnTableLine = str != null ? str.trim() : "";
|
|
if (index < mapParams.size() - 1) {
|
|
Map<String, String> map = mapParams.get(index + 1);
|
|
final int taille = Integer.parseInt(map.get(Constantes.LIBELLE_TAILLE));
|
String typeDonnee = map.get(Constantes.LIBELLE_TYPE_DONNEE);
|
|
//Traitement d'une donnée de type décimal
|
if (typeDonnee.equalsIgnoreCase(Constantes.LIBELLE_DECIMAL)) {
|
|
if (columnTableLine.length() >= taille) {
|
finalColumnsTableLine[index] = columnTableLine;
|
} else {
|
|
finalColumnsTableLine[index] = nomarlizeDecimal(columnTableLine, map);
|
}
|
|
//Traitement d'une donnée de type DATE
|
} else if (typeDonnee.equalsIgnoreCase(Constantes.LIBELLE_DATE)) {
|
|
if (columnTableLine.length() >= taille) {
|
finalColumnsTableLine[index] = columnTableLine;
|
} else {
|
|
finalColumnsTableLine[index] = columnTableLine + generateUnknown(taille - columnTableLine.length(), " ");
|
}
|
|
//Traitement d'une donnée de type NUMERIQUE
|
} else if (typeDonnee.equalsIgnoreCase(Constantes.LIBELLE_NUMERIQUE)) {
|
finalColumnsTableLine[index] = nomarlizeNumber(columnTableLine, taille);
|
|
} else if (typeDonnee.equalsIgnoreCase(Constantes.LIBELLE_ALPHANUMERIQUE)) {//Traitement d'une donnée de type ALPHANUMERIQUE
|
|
if (columnTableLine.length() < taille) {
|
|
finalColumnsTableLine[index] = columnTableLine + generateUnknown(taille - columnTableLine.length(), " ");
|
|
} else if (columnTableLine.length() == taille) {
|
|
finalColumnsTableLine[index] = columnTableLine;
|
|
} else {
|
|
if (!fieldsToBeTruncated.isEmpty()) {
|
|
//Récupérer l'index de début du champ en cours de traitement
|
int indexDebutChamp = Integer.parseInt(map.get(Constantes.LIBELLE_INDEX));
|
|
//Mettre les champs à tronquer dans une liste afin d'y rechercher le champ en cours de traitement
|
List<AlphaNumeriqueField> liste = Arrays.asList(fieldsToBeTruncated.toArray(new AlphaNumeriqueField[0]))
|
.stream().filter(a -> {
|
|
// ici on vérifie si l'index de début du champ en cours de traitement correspond à l'index d'un des champs à tronquer
|
//Car si 2 champs ont le même index, alors il s'agit d'un seul champ
|
return a.getIndex() == indexDebutChamp;
|
}).collect(Collectors.toList());
|
|
// Si on a trouvé un champ à tronquer qui a le même index que le champ courant alors, tronquer le champ
|
boolean found = (liste.size() == 1);
|
|
if (found) {
|
|
AlphaNumeriqueField alphaField = liste.get(0);
|
|
String finalData = columnTableLine.substring(0, taille);
|
|
TruncatedElement truncatedElement = new TruncatedElement(codeTypeFichier,
|
alphaField.getCodeColonne(), columnTableLine, finalData,
|
outputFile.getName(), LocalDateTime.now(), numeroLigne, taille, columnTableLine.length());
|
|
// Journalisation du champs tronqués
|
writeTruncatedElementsToFile(truncatedElement, truncateWriter);
|
|
resultFileParsing.setColumnTruncated(true);
|
|
finalColumnsTableLine[index] = finalData;
|
} else {
|
finalColumnsTableLine[index] = columnTableLine;
|
}
|
|
} else {
|
finalColumnsTableLine[index] = columnTableLine;
|
}
|
}
|
} else {
|
finalColumnsTableLine[index] = columnTableLine;
|
}
|
}
|
|
index++;
|
|
}
|
|
resultFileParsing.setFinalColumnsTableLine(finalColumnsTableLine);
|
return resultFileParsing;
|
}
|
|
/**
|
* Méthode qui normalise un nombre en ajoutant des zéros à gauche
|
*
|
* @param value
|
* @param taille
|
* @return
|
*/
|
private static String nomarlizeNumber(String value, int taille) {
|
String newValue = value
|
.trim()
|
.replaceAll("\\s+", "")
|
.replaceAll("\\u00A0", "")
|
.replaceAll("\t", "")
|
.replaceAll("\r", "")
|
.replaceAll("\n", "");
|
|
if (newValue.matches("[-+]?\\d+(\\.\\d+)?[eE][-+]?\\d+") || newValue.matches("^\\d{1,3},\\d+E[+-]?\\d+$")) {
|
newValue = new BigDecimal(newValue.replace(',', '.')).toPlainString();//Remplacer la virgule par le point
|
|
} else if (!newValue.matches("\\d+")) {
|
return generateUnknown(taille - newValue.length(), " ") + newValue;
|
}
|
return generateUnknown(taille - newValue.length(), " ") + newValue;
|
}
|
|
/**
|
* Méthode qui normalise les nombres décimaux conformat aux tailles, en
|
* ajoutant éventuellement des zéros à gauche de la partie entière et à
|
* droite de la partie décimale renseignés dans le validateur
|
*
|
* @param value : chaîne à normaliser
|
* @param map : map contenant les infos sur les parties entire et décimale
|
* @return
|
*/
|
private static String nomarlizeDecimal(String value, Map<String, String> map) {
|
String newValue = value.trim();
|
|
//taille de la partie décimale
|
int taillePartieDecimale = Integer.parseInt(map.get(Constantes.LIBELLE_TAILLE_PARTIE_DECIMALE));
|
|
//Taille totale du nombre décimal
|
int taille = Integer.parseInt(map.get(Constantes.LIBELLE_TAILLE));
|
|
//taille de la partie entière. On enlève un, car c'est la taille du séparateur
|
int taillePartieEntiere = taille - taillePartieDecimale - 1;
|
|
String separator = map.get(Constantes.LIBELLE_SEPARATEUR_DECIMAL);
|
String[] parts;
|
String escapedSeparator;
|
|
if (separator.equals(".")) {
|
escapedSeparator = "\\.";
|
} else {
|
escapedSeparator = separator;
|
}
|
|
String result = "";
|
|
if (value.isEmpty()) {
|
result = generateUnknown(taillePartieEntiere, "?") + separator + generateUnknown(taillePartieDecimale, "?");
|
} else {
|
parts = newValue.split(escapedSeparator);
|
|
//Si le nombre a une partie décimale
|
if (newValue.contains(separator)) {
|
|
//si le nombre a une partie entière et une partie décimale
|
switch (parts.length) {
|
case 2:
|
//Si la partie entière n'est pas vide
|
if (!parts[0].isEmpty()) {
|
|
result = generateUnknown(taillePartieEntiere - parts[0].length(), "0") + parts[0];
|
} else {
|
|
//Si partie entière vide, remplacer le vide par les séparateurs
|
result = generateUnknown(taillePartieEntiere, "?");
|
}
|
result += separator;
|
|
//Si la partie décimale n'est pas vide
|
if (!parts[1].isEmpty()) {
|
|
result += parts[1] + generateUnknown(taillePartieDecimale - parts[1].length(), "0");
|
} else {
|
|
//Si partie décimale vide, remplacer le vide par le point d'interrogation
|
result += generateUnknown(taillePartieDecimale, "?");
|
}
|
break;
|
case 1:
|
//Si c'est la partie entière qui est manquante
|
if (newValue.matches(escapedSeparator + ".+")) {
|
|
result += generateUnknown(taillePartieEntiere, "?") + separator + parts[0]
|
+ generateUnknown(taillePartieDecimale - parts[0].length(), "0");
|
|
//Si c'est la partie décimale qui est manquante
|
} else if (newValue.matches(".+" + escapedSeparator)) {
|
|
result += generateUnknown(taillePartieEntiere - parts[0].length(), "0") + parts[0] + separator
|
+ generateUnknown(taillePartieDecimale, "?");
|
}
|
break;
|
default:
|
result += generateUnknown(taillePartieEntiere, "?") + separator
|
+ generateUnknown(taillePartieDecimale, "?");
|
break;
|
}
|
} else {
|
|
//Si le nombre n'est pas décimale, ajouter une partie décimale en complétant avec des zéros
|
result = generateUnknown(taillePartieEntiere - newValue.length(), "0") + newValue + separator
|
+ generateUnknown(taillePartieDecimale, "0");
|
}
|
}
|
return result;
|
|
}
|
|
/**
|
* Méthode qui écrit les informations sur les éléments tronqués dans un
|
* fichier
|
*
|
* @param t
|
* @param bufferWriter
|
* @throws IOException
|
*/
|
private static void writeTruncatedElementsToFile(TruncatedElement t, final BufferedWriter bufferWriter) throws IOException {
|
|
//Ecriture dans le fichier output
|
StringBuilder safeLineBuilder = new StringBuilder(0);
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
LocalDateTime date = t.getDateAction();
|
|
safeLineBuilder.append("Fichier:").append(t.getFileName()).append("|");
|
safeLineBuilder.append("Date:").append(date.format(formatter)).append("|");
|
safeLineBuilder.append("Ligne:").append(t.getLineNumber()).append("|");
|
safeLineBuilder.append("Colonne:").append(t.getCodeColonne()).append("|");
|
safeLineBuilder.append("Taille attendue:").append(t.getTailleAttendue()).append("|");
|
safeLineBuilder.append("Donnée finale:").append(t.getFinalData()).append("|");
|
safeLineBuilder.append("Taille trouvée:").append(t.getTailleTrouvee()).append("|");
|
safeLineBuilder.append("Donnée initiale:").append(t.getInitialData());
|
|
String safeLine = safeLineBuilder.toString();
|
|
if (safeLine.length() > 0) {
|
|
bufferWriter.write(safeLine + System.lineSeparator());
|
|
}
|
}
|
|
/**
|
* Méthode qui génère un nombre précis d'un quelconque caractère
|
*
|
* @param nbOfCharacter : nombre de fois à générer le caractère
|
* @param character : caractère à générer
|
* @return
|
*/
|
private static String generateUnknown(int nbOfCharacter, String character) {
|
|
String str = "";
|
|
for (int i = 0; i < nbOfCharacter; i++) {
|
|
str += character;
|
|
}
|
|
return str;
|
|
}
|
|
/**
|
* Méthode qui retourne l'extension d'un fichier
|
*
|
* @param file
|
* @return
|
*/
|
private static String getFileExtension(File file) {
|
int index = file.getName().lastIndexOf(".");
|
|
if (index <= 0) {
|
return "";
|
|
} else {
|
return file.getName().substring(index);
|
}
|
}
|
|
/**
|
* Méthode qui construit le chemin du fichier qui va contenit les logs des
|
* éléments tronqués
|
*
|
* @param validateur
|
* @param outputFile
|
* @return
|
* @throws IOException
|
*/
|
private static Path createTruncatedEltsPath(String codeTypeFichier, String arhivageDirectory, File outputFile) throws IOException {
|
final String today = LocalDate.now().toString();
|
|
Path logDir = Paths.get(arhivageDirectory, today, codeTypeFichier, "logs");
|
|
if (!Files.exists(logDir, LinkOption.NOFOLLOW_LINKS)) {
|
Files.createDirectories(logDir);
|
}
|
|
Path pathOfTruncatedElts = Paths.get(arhivageDirectory, today, codeTypeFichier, "logs", fileNameWithoutExtension(outputFile) + "_truncated_elements.txt");
|
|
return pathOfTruncatedElts;
|
|
}
|
|
/**
|
* Méthode qui retourne le nom fichier sans extension
|
*
|
* @param file
|
* @return
|
*/
|
private static String fileNameWithoutExtension(File file) {
|
int index = file.getName().lastIndexOf(".");
|
|
if (index > 0) {
|
|
return file.getName().substring(0, index);
|
} else {
|
return file.getName();
|
}
|
}
|
|
private String generateTierce() {
|
|
// Instance of random class
|
Random rand = new Random();
|
int upperbound = 60;
|
int random = rand.nextInt(upperbound);
|
|
return String.format("%02d", random);
|
|
}
|
}
|