/* * 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 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 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 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 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 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 mapParams, Set 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 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 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 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); } }