From 3a30a1709c865a030fabda23de48e3501b0c4198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Boull=C3=A9?= Date: Wed, 9 Oct 2024 14:59:43 +0200 Subject: [PATCH] Initalize new class CommandFile and refactor UIObject Creation d'une nouvelle classe CommandFile, pour encapsuler la gestion du parametrage par des fichiers de commande - gestion de fichiers de commande en entree et en sortie - gestion du parametrage par search/replace - gestion du fichier de parametrage json La classe UIObject delegue desormais ses services de gestion des fichiers de commande a cette nouvelle classe Refactoring de UIObject, qui desormais redirige ses services vers un objet interne statique commandFile - supression des methodes de gestion des parametres de search/replace - supression des parametres de gestion des ficgier de commande en entree et sorties - renommage des fonctions de parametrage des options de la ligne de commande, qui ne font plus que du parametrage - OpenInputCommandFile -> InputCommand - OpenOutputCommandFile -> OutputCommand - renommage des services nettoyage lie a la gestioopn de la ligne de commande - CloseCommandFiles -> CleanCommandLineManagement - ExitHandlerCloseCommandFiles -> ExitHandlerCleanCommandLineManagement - UIObject::ParseMainParameters: impacts principaux - renommage - CheckOptions -> CheckCommandLineOptions --- .../KWLearningProblem/KWLearningProject.cpp | 4 +- src/Learning/MODL/MODL.cpp | 3 +- .../MODL_Coclustering/MODL_Coclustering.cpp | 1 + src/Norm/base/CommandFile.cpp | 427 ++++++++++++++++++ src/Norm/base/CommandFile.h | 195 ++++++++ src/Norm/base/UIObject.cpp | 399 ++-------------- src/Norm/base/UIUnit.cpp | 8 +- src/Norm/base/UserInterface.h | 55 +-- 8 files changed, 690 insertions(+), 402 deletions(-) create mode 100644 src/Norm/base/CommandFile.cpp create mode 100644 src/Norm/base/CommandFile.h diff --git a/src/Learning/KWLearningProblem/KWLearningProject.cpp b/src/Learning/KWLearningProblem/KWLearningProject.cpp index 2490a8c0..aa8c1f9b 100644 --- a/src/Learning/KWLearningProblem/KWLearningProject.cpp +++ b/src/Learning/KWLearningProblem/KWLearningProject.cpp @@ -218,8 +218,8 @@ void KWLearningProject::StartMaster(int argc, char** argv) delete learningProblemView; delete learningProblem; - // Fermeture des fichiers input output et erreurs - UIObject::CloseCommandFiles(); + // Fermeture des fichiers de commandes input, output et erreurs + UIObject::CleanCommandLineManagement(); // Dechargement de la DLL jvm, potentiellement chargee soit pour l'IHM, soit pour HDFS // Et cela n'est pas un probleme d'appeler cette methode si la DLL jvm diff --git a/src/Learning/MODL/MODL.cpp b/src/Learning/MODL/MODL.cpp index f0923173..9d2dcb6f 100644 --- a/src/Learning/MODL/MODL.cpp +++ b/src/Learning/MODL/MODL.cpp @@ -15,7 +15,7 @@ void SetWindowsDebugDir(const ALString& sDatasetFamily, const ALString& sDataset // A parametrer pour chaque utilisateur // Devra etre fait plus proprement quand tout l'equipe sera sur git, par exemple via une variable // d'environnement et quelques commentaires clairs - sUserRootPath = "C:/Applications/boullema/LearningTest.V10.5.3-b.0/TestKhiops/"; + sUserRootPath = "C:/Applications/boullema/LearningTest.V10.5.5-b.0/TestKhiops/"; // Pour permettre de continuer a utiliser LearningTest, on ne fait rien s'il y a deja un fichier test.prm // dans le repertoire courante @@ -24,6 +24,7 @@ void SetWindowsDebugDir(const ALString& sDatasetFamily, const ALString& sDataset // Changement de repertoire, uniquement pour Windows nRet = _chdir(sUserRootPath + sDatasetFamily + "/" + sDataset); + assert(nRet == 0); #endif } diff --git a/src/Learning/MODL_Coclustering/MODL_Coclustering.cpp b/src/Learning/MODL_Coclustering/MODL_Coclustering.cpp index 199cd4da..5575afd1 100644 --- a/src/Learning/MODL_Coclustering/MODL_Coclustering.cpp +++ b/src/Learning/MODL_Coclustering/MODL_Coclustering.cpp @@ -24,6 +24,7 @@ void SetWindowsDebugDir(const ALString& sDatasetFamily, const ALString& sDataset // Changement de repertoire, uniquement pour Windows nRet = _chdir(sUserRootPath + sDatasetFamily + "/" + sDataset); + assert(nRet == 0); #endif } diff --git a/src/Norm/base/CommandFile.cpp b/src/Norm/base/CommandFile.cpp new file mode 100644 index 00000000..870926b1 --- /dev/null +++ b/src/Norm/base/CommandFile.cpp @@ -0,0 +1,427 @@ +// Copyright (c) 2024 Orange. All rights reserved. +// This software is distributed under the BSD 3-Clause-clear License, the text of which is available +// at https://spdx.org/licenses/BSD-3-Clause-Clear.html or see the "LICENSE" file for more details. + +#include "CommandFile.h" + +CommandFile::CommandFile() +{ + fInputCommands = NULL; + fOutputCommands = NULL; + bPrintOutputInConsole = false; +} + +CommandFile::~CommandFile() +{ + require(not IsCommandFileOpened()); +} + +void CommandFile::Reset() +{ + require(not IsCommandFileOpened()); + + SetInputCommandFileName(""); + SetOutputCommandFileName(""); + DeleteAllInputSearchReplaceValues(); + SetInputParameterFileName(""); +} + +void CommandFile::SetInputCommandFileName(const ALString& sFileName) +{ + require(not IsCommandFileOpened()); + + sInputCommandFileName = sFileName; +} + +const ALString& CommandFile::GetInputCommandFileName() const +{ + return sInputCommandFileName; +} + +void CommandFile::SetOutputCommandFileName(const ALString& sFileName) +{ + require(not IsCommandFileOpened()); + + sOutputCommandFileName = sFileName; + + // Si les fichier de sortie est /dev/stdout ou /dev/stderr, c'est un eredirection vers la console + bPrintOutputInConsole = false; +#ifdef __linux_or_apple__ + if (sOutputCommandFileName == "/dev/stdout" or sLocalOutputCommandsFileName == "/dev/stderr") + bPrintOutputInConsole = true; +#endif +} + +const ALString& CommandFile::GetOutputCommandFileName() const +{ + return sOutputCommandFileName; +} + +boolean CommandFile::GetPrintOutputInConsole() const +{ + return bPrintOutputInConsole; +} + +void CommandFile::AddInputSearchReplaceValues(const ALString& sSearchValue, const ALString& sReplaceValue) +{ + require(sSearchValue != ""); + + require(not IsCommandFileOpened()); + + // Ajout des valeurs + svInputCommandSearchValues.Add(sSearchValue); + svInputCommandReplaceValues.Add(sReplaceValue); +} + +int CommandFile::GetInputSearchReplaceValueNumber() +{ + assert(svInputCommandSearchValues.GetSize() == svInputCommandReplaceValues.GetSize()); + return svInputCommandSearchValues.GetSize(); +} + +const ALString& CommandFile::GetInputSearchValueAt(int nIndex) +{ + require(0 <= nIndex and nIndex < GetInputSearchReplaceValueNumber()); + return svInputCommandSearchValues.GetAt(nIndex); +} + +const ALString& CommandFile::GetInputReplaceValueAt(int nIndex) +{ + require(0 <= nIndex and nIndex < GetInputSearchReplaceValueNumber()); + return svInputCommandReplaceValues.GetAt(nIndex); +} + +void CommandFile::DeleteAllInputSearchReplaceValues() +{ + svInputCommandSearchValues.SetSize(0); + svInputCommandReplaceValues.SetSize(0); +} + +void CommandFile::SetInputParameterFileName(const ALString& sFileName) +{ + require(not IsCommandFileOpened()); + + sInputParameterFileName = sFileName; +} + +const ALString& CommandFile::GetInputParameterFileName() const +{ + return sInputParameterFileName; +} + +boolean CommandFile::Check() const +{ + return true; +} + +boolean CommandFile::OpenInputCommandFile() +{ + boolean bOk; + + require(Check()); + require(GetInputCommandFileName() != ""); + require(not IsInputCommandFileOpened()); + + // Copie depuis HDFS si necessaire + bOk = PLRemoteFileService::BuildInputWorkingFile(sInputCommandFileName, sLocalInputCommandFileName); + + // Fermeture du fichier si celui-ci est deja ouvert + // Ce cas peut arriver si on appelle plusieurs fois ParseParameters (notamment via MODL_dll) + if (fInputCommands != NULL) + { + fclose(fInputCommands); + fInputCommands = NULL; + } + + // Ouverture du fichier en lecture + if (bOk) + fInputCommands = p_fopen(sLocalInputCommandFileName, "r"); + if (fInputCommands == NULL) + { + Global::AddError("Input command file", sInputCommandFileName, "Unable to open file"); + bOk = false; + } + return bOk; +} + +boolean CommandFile::IsInputCommandFileOpened() const +{ + return fInputCommands != NULL; +} + +boolean CommandFile::OpenOutputCommandFile() +{ + boolean bOk; + + require(Check()); + require(GetOutputCommandFileName() != ""); + require(not IsOutputCommandFileOpened()); + + // Creation si necessaire des repertoires intermediaires + PLRemoteFileService::MakeDirectories(FileService::GetPathName(sOutputCommandFileName)); + assert(FileService::GetApplicationTmpDir().IsEmpty()); + + // PPreparation pour HDFS si necessaire + bOk = PLRemoteFileService::BuildOutputWorkingFile(sOutputCommandFileName, sLocalOutputCommandFileName); + + // Fermeture du fichier si celui-ci est deja ouvert + // Ce cas peut arriver si on appelle plusieurs fois ParseParameters (notamment via MODL_dll) + if (fOutputCommands != NULL) + { + fclose(fOutputCommands); + fOutputCommands = NULL; + } + + // Ouverture du fichier en ecriture + if (bOk) + fOutputCommands = p_fopen(sLocalOutputCommandFileName, "w"); + if (fOutputCommands == NULL) + { + Global::AddError("Output command file", sOutputCommandFileName, "Unable to open file"); + bOk = false; + } + return bOk; +} + +boolean CommandFile::IsOutputCommandFileOpened() const +{ + return fOutputCommands != NULL; +} + +boolean CommandFile::IsCommandFileOpened() const +{ + return IsInputCommandFileOpened() or IsOutputCommandFileOpened(); +} + +void CommandFile::CloseCommandFiles() +{ + // Fermeture du fichier d'entree + if (fInputCommands != NULL) + { + fclose(fInputCommands); + fInputCommands = NULL; + + // Si le fichier est sur HDFS, on supprime la copie locale + PLRemoteFileService::CleanInputWorkingFile(sInputCommandFileName, sLocalInputCommandFileName); + } + + // Fermeture du fichier de sortie, uniquement en mode batch (sinon, on enregistre toujours le scenario) + if (fOutputCommands != NULL) + { + fclose(fOutputCommands); + fOutputCommands = NULL; + + // Copie vers HDFS si necessaire + PLRemoteFileService::CleanOutputWorkingFile(sOutputCommandFileName, sLocalOutputCommandFileName); + } +} + +boolean CommandFile::ReadInputCommand(StringVector* svIdentifierPath, ALString& sValue) +{ + const char cDEL = (char)127; + int i; + char sCharBuffer[1 + BUFFER_LENGTH]; + ALString sStringBuffer; + int nLength; + ALString sIdentifierPath; + int nPosition; + ALString sToken; + ALString sIdentifier; + + require(svIdentifierPath != NULL); + require(svIdentifierPath->GetSize() == 0); + + // On arrete si pas de fichier ou si fin de fichier + if (fInputCommands == NULL or feof(fInputCommands)) + return false; + + // Boucle de lecture pour ignorer les lignes vides + // ou ne comportant que des commentaires + while (sStringBuffer == "" and not feof(fInputCommands)) + { + // Lecture + StandardGetInputString(sCharBuffer, fInputCommands); + + // Si erreur ou caractere fin de fichier, on ne renvoie rien + if (ferror(fInputCommands) or sCharBuffer[0] == '\0') + { + // Nettoyage + fclose(fInputCommands); + fInputCommands = NULL; + return false; + } + + // Suppression du premier '\n' + for (i = 0; i < BUFFER_LENGTH; i++) + { + if (sCharBuffer[i] == '\n') + sCharBuffer[i] = '\0'; + } + + // Recherche /remplacement dans la partie valeur de la commande + sStringBuffer = ProcessSearchReplaceCommand(sCharBuffer); + + // Suppression des commentaires en partant de la fin + // Ainsi on ne supprime que le dernier commentaire, ce qui permet d'avoir + // des paires (IdentifierPath, valeur) avec valeur contenant des " //" + // si on a un commentaire est en fin de ligne + nLength = sStringBuffer.GetLength(); + for (i = nLength - 1; i >= 2; i--) + { + if (sStringBuffer.GetAt(i) == '/' and sStringBuffer.GetAt(i - 1) == '/' and + iswspace(sStringBuffer.GetAt(i - 2))) + { + sStringBuffer = sStringBuffer.Left(i - 1); + break; + } + } + + // Suppression des blancs au debut et a la fin + sStringBuffer.TrimRight(); + sStringBuffer.TrimLeft(); + + // Suppression si necessaire du dernier caractere s'il s'agit de cDEL + // (methode FromJstring qui utilise ce cartactere special en fin de chaine pour + // les conversions entre Java et C++) + if (sStringBuffer.GetLength() > 0 and sStringBuffer.GetAt(sStringBuffer.GetLength() - 1) == cDEL) + sStringBuffer.GetBufferSetLength(sStringBuffer.GetLength() - 1); + + // On supprime la ligne si elle commence par un commentaire + // Cela permet de commenter y compris les lignes ayant une valeur contenant des "//" + if (sStringBuffer.GetLength() >= 2 and sStringBuffer.GetAt(0) == '/' and sStringBuffer.GetAt(1) == '/') + sStringBuffer = ""; + } + + // Recherche de l'IdentifierPath et de la valeur + nPosition = sStringBuffer.Find(' '); + if (nPosition == -1) + { + sIdentifierPath = sStringBuffer; + sValue = ""; + } + else + { + sIdentifierPath = sStringBuffer.Left(nPosition); + sValue = sStringBuffer.Right(sStringBuffer.GetLength() - nPosition - 1); + sValue.TrimRight(); + sValue.TrimLeft(); + } + + // Parsing de l'IdentifierPath: recherche des '.' + while (sIdentifierPath != "") + { + // Recherche d'un '.', sinon de la fin de la ligne + nPosition = sIdentifierPath.Find('.'); + if (nPosition == -1) + nPosition = sIdentifierPath.GetLength(); + assert(nPosition >= 0); + + // Recherche d'un Identifier du PathIdentifier + sToken = sIdentifierPath.Left(nPosition); + sToken.TrimRight(); + sToken.TrimLeft(); + if (sToken != "") + { + sIdentifier = sToken; + svIdentifierPath->Add(sIdentifier); + } + else + { + // Erreur dans la syntaxe de la commande + svIdentifierPath->SetSize(0); + break; + } + + // Passage a la suite du buffer + if (nPosition < sIdentifierPath.GetLength()) + sIdentifierPath = sIdentifierPath.Right(sIdentifierPath.GetLength() - nPosition - 1); + else + sIdentifierPath = ""; + sIdentifierPath.TrimRight(); + sIdentifierPath.TrimLeft(); + } + + // On renvoie le resultat + if (svIdentifierPath->GetSize() != 0) + return true; + else + return false; +} + +void CommandFile::WriteOutputCommand(const ALString& sIdentifierPath, const ALString& sValue, const ALString& sLabel) +{ + if (fOutputCommands != NULL) + { + ALString sCommand; + int nCommandLength; + + // Fabrication de la commande + sCommand = sIdentifierPath; + if (sValue != "") + sCommand += " " + sValue; + + // Calcul de la position du libelle + nCommandLength = 10 * ((9 + sCommand.GetLength()) / 10); + if (nCommandLength < 30) + nCommandLength = 30; + + // Impression + if (bPrintOutputInConsole) + // Si redirection vers la console, on ajoute un prefixe + fprintf(fOutputCommands, "Khiops.command\t"); + if (sCommand == "" and sLabel == "") + fprintf(fOutputCommands, "\n"); + else if (sCommand == "") + fprintf(fOutputCommands, "// %s\n", (const char*)sLabel); + else + fprintf(fOutputCommands, "%-*.*s // %s\n", nCommandLength, nCommandLength, + (const char*)sCommand, (const char*)sLabel); + fflush(fOutputCommands); + } +} + +const ALString CommandFile::ProcessSearchReplaceCommand(const ALString& sInputCommand) +{ + ALString sInputString; + ALString sBeginString; + ALString sEndString; + ALString sOutputCommand; + ALString sSearchValue; + ALString sReplaceValue; + int nSearchPosition; + int i; + + // Parcours de toutes les paires search/replace a appliquer + sOutputCommand = sInputCommand; + for (i = 0; i < GetInputSearchReplaceValueNumber(); i++) + { + sSearchValue = GetInputSearchValueAt(i); + sReplaceValue = GetInputReplaceValueAt(i); + + // Remplacement iteratif des pattern trouves a partir de la chaine pretraitee precedente + if (sSearchValue.GetLength() > 0) + { + sBeginString = ""; + sEndString = sOutputCommand; + nSearchPosition = 0; + sOutputCommand = ""; + while (nSearchPosition >= 0 and sEndString.GetLength() > 0) + { + nSearchPosition = sEndString.Find(sSearchValue); + + // Si non trouve, on garde la fin de la chaine en cours de traitement + if (nSearchPosition == -1) + sOutputCommand += sEndString; + // Sinon, en prend le debut puis la valeur de remplacement + else + { + sBeginString = sEndString.Left(nSearchPosition); + sEndString = sEndString.Right(sEndString.GetLength() - + sSearchValue.GetLength() - nSearchPosition); + sOutputCommand += sBeginString + sReplaceValue; + } + } + } + } + return sOutputCommand; +} diff --git a/src/Norm/base/CommandFile.h b/src/Norm/base/CommandFile.h new file mode 100644 index 00000000..86a7c4a2 --- /dev/null +++ b/src/Norm/base/CommandFile.h @@ -0,0 +1,195 @@ +// Copyright (c) 2024 Orange. All rights reserved. +// This software is distributed under the BSD 3-Clause-clear License, the text of which is available +// at https://spdx.org/licenses/BSD-3-Clause-Clear.html or see the "LICENSE" file for more details. + +#pragma once + +class CommandFile; + +#include "Object.h" +#include "ALString.h" +#include "Vector.h" +#include "PLRemoteFileService.h" +#include "FileService.h" + +/////////////////////////////////////////////////////////////////////////////////////// +// Classe CommandFile +// Classe qui permet de gerer les fichiers de commandes en entree et en sortie +// parametres par la ligne de commande +class CommandFile : public Object +{ +public: + // Constructeur + CommandFile(); + ~CommandFile(); + + // Reinitialisation du parametrage + void Reset(); + + /////////////////////////////////////////////////////////////////////////////////////// + // Parametrage des fichiers de commandes en entree et en sortie + + // Fichier de commande en entree + void SetInputCommandFileName(const ALString& sFileName); + const ALString& GetInputCommandFileName() const; + + // Fichier de commande en sortie + void SetOutputCommandFileName(const ALString& sFileName); + const ALString& GetOutputCommandFileName() const; + + // Indique qu'il faut afficher les sorties dans la console + // Determine par le parametrage du fichier de commande en sortie + boolean GetPrintOutputInConsole() const; + + /////////////////////////////////////////////////////////////////////////////////////// + // Personnalisation du fichier de commande en entree par des paires (Search value, Replace value). + // Chaque paire est appliquee sur les operandes de chaque ligne de commande en entree avant son execution + + // Ajout d'une paire (SearchValue, ReplaceValue) pour la personnalisation des commandes en entree + void AddInputSearchReplaceValues(const ALString& sSearchValue, const ALString& sReplaceValue); + + // Nombre de paires de personnalisation + int GetInputSearchReplaceValueNumber(); + + // Acces aux valeurs d'une paire de personnalisation + const ALString& GetInputSearchValueAt(int nIndex); + const ALString& GetInputReplaceValueAt(int nIndex); + + // Nettoyage de toutes les paires + void DeleteAllInputSearchReplaceValues(); + + /////////////////////////////////////////////////////////////////////////////////////// + // Personnalisation par un fichier de parametres au format json + // Cette option de personnalisation par un fichier json est exclusive de la + // personnalisation par des paires search/replace + // + // # Structure de controle dans les scenarios + // On ajoute quelques structures de controle dans les scenario pour permettre + // un pilotage complet des operations de search/replace. + // Les structures de controle sont materialisees par des instructions en UPPER CASE sur des lignes dediees. + // + // ## Boucle + // Une structure de boucle permet d'entourer un bloc de lignes de scenario entre deux instructions + // LOOP + // END LOOP + // Toutes les lignes d'un bloc de boucle sont repetees autant de fois que necessaire. + // ## Test + // Une structure de test permet d'entourer un bloc de lignes de scenario entre deux instructions + // IF + // END IF + // Toutes les lignes d'un bloc de test sont prise en compte conditionnellement au test. + // + // # Parametrage par une structure de donnees contenu dans le fichier json + // Le fichier json contient une serie de paires cle/valeur: + // - valeur de type string ou number + // - cle dans le scenario a remplacer par la valeur + // - valeur de type array + // - la cle du tableau permet d'identifier (loop key) un bloc de lignes dans le scenario, + // pour une structure de boucle (LOOP) + // - la valeur est associee dans le json a un array contenant des object json, tous de la meme structure, + // avec la meme liste de paires cle/valeur de type string ou number + // - on duplique les lignes de la boucle autant de fois qu'il y d'objets dans le tableau, + // en effectuant les search/replace selon les cles/valeur de l'objet courant + // - valeur de type boolean + // - la cle du boolean permet d'identifier (if key) un bloc de lignes dans le scenario, + // pour une structure de test (IF) + // - on prend en compte les lignes du test selon la valeur true ou false associee a la cle + // + // # Contraintes sur la structure du json + // Seule une petite partie de l'expressivite du format json est geree + // - pas de valeur nul + // - pas de recursion dans la structure: la seul usage autorise et celui d'un array contenant des object + // Contraintes sur les cles + // - les cles utilisees dans le json doivent etre distinctes + // - pour l'object a la racine d'objet json principal + // - pour chaque tableau, localement au tableau, et entre les cle du tableau et celle de l'objet englobant + // - les cles ont une syntaxe de variables de langage: uniquement des caracteres alpha-numeriques + // - format camelCase obligatoire, coherent avec les recommandations: https://jsonapi.org/recommendations/ + // - aucune cle ne doit etre une sous-partie d'une autre cle + // - evite les ambiguites selon l'ordre de remplacement dans les search/replace + // Liens entre cles dans le json et dans le scenario + // - chaque cle dans le scenario est utilisable dans le json si elle est entouree de '__' (double tiret du 8) + // - exemple, une cle name dans le json est utilisable avec la valeur a remplacer name dans le scenario + // - chaque cle dans le json doit etre utilisee dans le scenario, et reciproquement + // - exception: si une cle de tableau peut etre absent du json, cela equivaut a un tableau vide + // - chaque cle dans un array du json ne peut etre utilisee que dans la boucle correspondante du scenario + // - dans le cas d'une cle dont la valeur est une string, la ligne du scenario utilisant cette cle devra etre + // terminee par " // commentaire" afin d'autoriser les valeurs contenant la sous-chaine '//' + // + // # Choix d'encodage + // On choisit un encodage UTF-8 systematique pour le json en parametre de Khiops, selon la norme Json. + // Dans le cas de parametres dont les valeurs peuvent etre soit des strings UTF-8, soit des chaines de bytes, + // on etend le format json de la facon suivantes. + // Pour un parametre concerne (ex: dataPath): + // - le fichier scenario reste inchange, avec utilisation de __dataPath__ + // - le fichier json en parametre peut comporter les deux variantes de la valeurs + // - chaine de caracteres UTF-8: nom de la variable et valeur sans encodage + // - exemple: "dataPath = "UTF-8 string value"_ + // - chaine de bytes: nom de la variable prefixe par _byte_, la premiere lettre du nom de la variable + // en majuscule, et valeur avec encodage Base64 + // - cf. https://fr.wikipedia.org/wiki/Base64 + // - exemple: "byteDataPath" = "dmFsdWUK" + // Au moment de l'ecriture du scenario en sortie, on recherche la cle correspondante dans le json ou + // sa variante avec prefixe "byte" pour decoder ou non la valeur dans le search/replace. + // Contraintes specifique sur la structure du json: + // - chaque cle associe a une valeur chaine de caracteres, et presente dans le scenario, doit exister + // sous une seule des deux variantes, avec ou sans prefixe "byte". + + // Fichier de parametre json en entree + void SetInputParameterFileName(const ALString& sFileName); + const ALString& GetInputParameterFileName() const; + + /////////////////////////////////////////////////////////////////////////////////////// + // Exploitation du parametrage + + // Verification de la validite des parametres, avec emission de messages d'erreurs + boolean Check() const override; + + // Ouverture du fichier de commande en entree, avec son son eventuele fichier de parametrage json + boolean OpenInputCommandFile(); + boolean IsInputCommandFileOpened() const; + + // Ouverture du fichier de commande en sortie + boolean OpenOutputCommandFile(); + boolean IsOutputCommandFileOpened() const; + + // Indique si on est en train de traiter les commande, en entree ou en sortie + boolean IsCommandFileOpened() const; + + // Ouverture des fichiers de commandes + void CloseCommandFiles(); + + // Lecture d'une commande + // Renvoie false si pas de commande, sinon un vecteur de chaines de caracteres representant + // le parsing de IdentifierPath et une valeur optionnelle + boolean ReadInputCommand(StringVector* svIdentifierPath, ALString& sValue); + + // Ecriture d'une commande + void WriteOutputCommand(const ALString& sIdentifierPath, const ALString& sValue, const ALString& sLabel); + + /////////////////////////////////////////////////////////////////////////////////////// + ///// Implementation +protected: + // Application des recherche/remplacement de valeurs successivement sur une commande + const ALString ProcessSearchReplaceCommand(const ALString& sInputCommand); + + // Nom des fichiers + ALString sInputCommandFileName; + ALString sOutputCommandFileName; + ALString sInputParameterFileName; + + // Variante locale des noms de fichier de commande, dans le cas de fichiers HDFS + ALString sLocalInputCommandFileName; + ALString sLocalOutputCommandFileName; + + // Fichiers de gestion des commandes + FILE* fInputCommands; + FILE* fOutputCommands; + + // Redirection de la sortie outputCommand vers la console + boolean bPrintOutputInConsole; + + // Gestion des chaines des patterns a remplacer par des valeurs dans les fichiers d'input de scenario + StringVector svInputCommandSearchValues; + StringVector svInputCommandReplaceValues; +}; diff --git a/src/Norm/base/UIObject.cpp b/src/Norm/base/UIObject.cpp index 3f6c95b7..12bbe21e 100644 --- a/src/Norm/base/UIObject.cpp +++ b/src/Norm/base/UIObject.cpp @@ -8,15 +8,11 @@ ALString UIObject::sIconImageJarPath; void* UIObject::jvmHandle = NULL; boolean UIObject::bIsJVMLoaded = false; -ALString UIObject::sLocalInputCommandsFileName; -ALString UIObject::sLocalOutputCommandsFileName; -ALString UIObject::sOutputCommandFileName; -ALString UIObject::sInputCommandFileName; ALString UIObject::sLocalErrorLogFileName; ALString UIObject::sErrorLogFileName; ALString UIObject::sTaskProgressionLogFileName; CommandLine UIObject::commandLineOptions; -boolean UIObject::bPrintOutputInConsole = false; +CommandFile UIObject::commandFile; const ALString UIObject::GetClassLabel() const { @@ -300,12 +296,10 @@ ALString UIObject::GetHtmlText(const ALString sText) const return sHtmlText; } -#define STRING_MAX_LENGTH 1000 - const ALString& UIObject::TextualReadInput() { int i; - char sCharBuffer[1 + STRING_MAX_LENGTH]; + char sCharBuffer[1 + BUFFER_LENGTH]; static ALString sResult; // Lecture depuis le shell @@ -1674,7 +1668,7 @@ CommandLine* UIObject::GetCommandLineOptions() ////////////////////////////////////////////////// // Gestion des fichiers d'entree et de sortie -boolean UIObject::CheckOptions(const ObjectArray& oaOptions) +boolean UIObject::CheckCommandLineOptions(const ObjectArray& oaOptions) { boolean bOk = true; int nCurrentUIMode; @@ -1798,6 +1792,7 @@ boolean UIObject::CheckOptions(const ObjectArray& oaOptions) void UIObject::ParseMainParameters(int argc, char** argv) { static boolean bIsUserExitHandlerSet = false; + boolean bOk = true; CommandLineOption* oInputScenario; CommandLineOption* oError; CommandLineOption* oOutputScenario; @@ -1807,7 +1802,6 @@ void UIObject::ParseMainParameters(int argc, char** argv) ALString sMessage; ALString sToolName; ALString sGlobalHelp; - boolean bOk; // Aide globale sToolName = commandLineOptions.GetCommandName(); @@ -1847,7 +1841,7 @@ void UIObject::ParseMainParameters(int argc, char** argv) oInputScenario->AddDescriptionLine("replay commands stored in the file"); oInputScenario->SetParameterRequired(true); oInputScenario->SetParameterDescription(CommandLineOption::sParameterFile); - oInputScenario->SetMethod(OpenInputCommandFile); + oInputScenario->SetMethod(InputCommand); commandLineOptions.AddOption(oInputScenario); oOutputScenario = new CommandLineOption; @@ -1855,7 +1849,7 @@ void UIObject::ParseMainParameters(int argc, char** argv) oOutputScenario->AddDescriptionLine("record commands in the file"); oOutputScenario->SetParameterRequired(true); oOutputScenario->SetParameterDescription(CommandLineOption::sParameterFile); - oOutputScenario->SetMethod(OpenOutputCommandFile); + oOutputScenario->SetMethod(OutputCommand); commandLineOptions.AddOption(oOutputScenario); oReplace = new CommandLineOption; @@ -1876,16 +1870,15 @@ void UIObject::ParseMainParameters(int argc, char** argv) oTask->SetMethod(TaskProgressionCommand); commandLineOptions.AddOption(oTask); commandLineOptions.SetGlobalHelp(sGlobalHelp); - commandLineOptions.SetGlobalCheckMethod(CheckOptions); + commandLineOptions.SetGlobalCheckMethod(CheckCommandLineOptions); // Nettoyage initial - CloseCommandFiles(); - DeleteAllInputSearchReplaceValues(); + CleanCommandLineManagement(); - // Fermeture des fichiers en cas d'erreur fatale + // Nettoyage et notament fermeture des fichiers en cas d'erreur fatale if (not bIsUserExitHandlerSet) { - AddUserExitHandler(ExitHandlerCloseCommandFiles); + AddUserExitHandler(ExitHandlerCleanCommandLineManagement); bIsUserExitHandlerSet = true; } @@ -1894,15 +1887,36 @@ void UIObject::ParseMainParameters(int argc, char** argv) bVerboseCommandReplay = false; bBatchMode = false; + // Analyse des option de la ligne de commande commandLineOptions.ParseMainParameters(argc, argv); // Initialisation des managers de message InitializeMessageManagers(); + // Ouverture du fichier de commande en entree + if (bOk and commandFile.GetInputCommandFileName() != "") + bOk = commandFile.OpenInputCommandFile(); + + // Ouverture du fichier de commande en sortie + if (bOk and commandFile.GetOutputCommandFileName() != "") + { + bOk = commandFile.OpenOutputCommandFile(); + + // En mode Graphic on renvoie systematiquement true : on permet de lancer l'outil meme si on ne peut + // pas ecrire dans le fichier de log (les options -e et -o sont passees par defaut a l'outil dans les scripts de + // lancement bash ou cmd) En mode Textual on est plus strict (utilisation via python, java ou sur cluster) + if (not IsBatchMode()) + bOk = true; + } + + // Si il y a eu un pbm lors de l'excution d'une methode, on sort en erreur + if (not bOk) + GlobalExit(); + // Ecriture d'un en-tete dans le fichier des commandes WriteOutputCommand("", "", CurrentTimestamp()); WriteOutputCommand("", "", commandLineOptions.GetCommandName()); - if (not bPrintOutputInConsole) + if (commandFile.GetPrintOutputInConsole()) { sMessage = "Output command file\n"; sMessage += "//\n"; @@ -1960,72 +1974,16 @@ boolean UIObject::GetTextualInteractiveModeAllowed() return bTextualInteractiveModeAllowed; } -boolean UIObject::OpenInputCommandFile(const ALString& sFileName) +boolean UIObject::InputCommand(const ALString& sFileName) { - boolean bOk; - sInputCommandFileName = sFileName; - - // Copie depuis HDFS si necessaire - bOk = PLRemoteFileService::BuildInputWorkingFile(sInputCommandFileName, sLocalInputCommandsFileName); - - // Fermeture du fichier si celui-ci est deja ouvert - // Ce cas peut arriver si on appelle plusieurs fois ParseParameters (notamment via MODL_dll) - if (fInputCommands != NULL) - { - fclose(fInputCommands); - fInputCommands = NULL; - } - - // Ouverture du fichier en lecture - if (bOk) - fInputCommands = p_fopen(sLocalInputCommandsFileName, "r"); - if (fInputCommands == NULL) - { - Global::AddError("Input command file", sInputCommandFileName, "Unable to open file"); - bOk = false; - } - return bOk; + commandFile.SetInputCommandFileName(sFileName); + return true; } -boolean UIObject::OpenOutputCommandFile(const ALString& sFileName) +boolean UIObject::OutputCommand(const ALString& sFileName) { - boolean bOk = true; - - sOutputCommandFileName = sFileName; - - // Creation si necessaire des repertoires intermediaires - PLRemoteFileService::MakeDirectories(FileService::GetPathName(sOutputCommandFileName)); - - assert(FileService::GetApplicationTmpDir().IsEmpty()); - bOk = PLRemoteFileService::BuildOutputWorkingFile(sOutputCommandFileName, sLocalOutputCommandsFileName); - - // Fermeture du fichier si celui-ci est deja ouvert - // Ce cas peut arriver si on appelle plusieurs fois ParseParameters (notamment via MODL_dll) - if (fOutputCommands != NULL) - { - fclose(fOutputCommands); - fOutputCommands = NULL; - } - - // Ouverture du fichier en ecriture - if (bOk) - fOutputCommands = p_fopen(sLocalOutputCommandsFileName, "w"); - if (fOutputCommands == NULL) - { - Global::AddError("Output command file", sOutputCommandFileName, "Unable to open file"); - bOk = false; - } - -// Si les fichier de sortie est /dev/stdout ou /dev/stderr, c'est un eredirection vers la console -#ifdef __linux_or_apple__ - if (sLocalOutputCommandsFileName == "/dev/stdout" or sLocalOutputCommandsFileName == "/dev/stderr") - bPrintOutputInConsole = true; -#endif - - // En mode Graphic on renvoie systematiquement true : on permet de lancer l'outil meme si on ne peut - // pas ecrire dans le fichier output (les options -e et -o sont passees par defaut a l'outil dans les scripts de - // lancement bash ou cmd) En mode Textual on est plus strict (utilisation via python, java ou sur cluster) - return not IsBatchMode() or bOk; + commandFile.SetOutputCommandFileName(sFileName); + return true; } boolean UIObject::ReplaceCommand(const ALString& sSearchReplacePattern) @@ -2042,7 +2000,7 @@ boolean UIObject::ReplaceCommand(const ALString& sSearchReplacePattern) { sSearchValue = sSearchReplacePattern.Left(nEqualPosition); sReplaceValue = sSearchReplacePattern.Right(sSearchReplacePattern.GetLength() - nEqualPosition - 1); - AddInputSearchReplaceValues(sSearchValue, sReplaceValue); + commandFile.AddInputSearchReplaceValues(sSearchValue, sReplaceValue); } else { @@ -2086,28 +2044,15 @@ boolean UIObject::TaskProgressionCommand(const ALString& sTaskFile) return true; } -void UIObject::CloseCommandFiles() +void UIObject::CleanCommandLineManagement() { - // Fermeture du fichier d'entree - if (fInputCommands != NULL) - { - fclose(fInputCommands); - fInputCommands = NULL; + // Fermeture des fichiers de commande en entree et sortie + commandFile.CloseCommandFiles(); - // Si le fichier est sur HDFS, on supprime la copie locale - PLRemoteFileService::CleanInputWorkingFile(sInputCommandFileName, sLocalInputCommandsFileName); - } - - // Fermeture du fichier de sortie, uniquement en mode batch (sinon, on enregistre toujours le scenario) - if (fOutputCommands != NULL) - { - fclose(fOutputCommands); - fOutputCommands = NULL; - - // Copie vers HDFS si necessaire - PLRemoteFileService::CleanOutputWorkingFile(sOutputCommandFileName, sLocalOutputCommandsFileName); - } + // Reinitialisation du parametrage des fichiers de commande + commandFile.Reset(); + // Fermeture du fichier d'erreur if (Global::GetErrorLogFileName() != "") { // Fermeture du fichier de log (la reinitialisation entraine la fermeture du fichier) @@ -2118,173 +2063,19 @@ void UIObject::CloseCommandFiles() } } -void UIObject::ExitHandlerCloseCommandFiles(int nExitCode) +void UIObject::ExitHandlerCleanCommandLineManagement(int nExitCode) { - CloseCommandFiles(); + CleanCommandLineManagement(); } boolean UIObject::ReadInputCommand(StringVector* svIdentifierPath, ALString& sValue) { - const char cDEL = (char)127; - int i; - char sCharBuffer[1 + STRING_MAX_LENGTH]; - ALString sStringBuffer; - int nLength; - ALString sIdentifierPath; - int nPosition; - ALString sToken; - ALString sIdentifier; - - require(svIdentifierPath != NULL); - require(svIdentifierPath->GetSize() == 0); - - // On arrete si pas de fichier ou si fin de fichier - if (fInputCommands == NULL or feof(fInputCommands)) - return false; - - // Boucle de lecture pour ignorer les lignes vides - // ou ne comportant que des commentaires - while (sStringBuffer == "" and not feof(fInputCommands)) - { - // Lecture - StandardGetInputString(sCharBuffer, fInputCommands); - - // Si erreur ou caractere fin de fichier, on ne renvoie rien - if (ferror(fInputCommands) or sCharBuffer[0] == '\0') - { - // Nettoyage - fclose(fInputCommands); - fInputCommands = NULL; - return false; - } - - // Suppression du premier '\n' - for (i = 0; i < STRING_MAX_LENGTH; i++) - { - if (sCharBuffer[i] == '\n') - sCharBuffer[i] = '\0'; - } - - // Recherche /remplacement dans la partie valeur de la commande - sStringBuffer = ProcessSearchReplaceCommand(sCharBuffer); - - // Suppression des commentaires en partant de la fin - // Ainsi on ne supprime que le dernier commentaire, ce qui permet d'avoir - // des paires (IdentifierPath, valeur) avec valeur contenant des " //" - // si on a un commentaire est en fin de ligne - nLength = sStringBuffer.GetLength(); - for (i = nLength - 1; i >= 2; i--) - { - if (sStringBuffer.GetAt(i) == '/' and sStringBuffer.GetAt(i - 1) == '/' and - iswspace(sStringBuffer.GetAt(i - 2))) - { - sStringBuffer = sStringBuffer.Left(i - 1); - break; - } - } - - // Suppression des blancs au debut et a la fin - sStringBuffer.TrimRight(); - sStringBuffer.TrimLeft(); - - // Suppression si necessaire du dernier caractere s'il s'agit de cDEL - // (methode FromJstring qui utilise ce cartactere special en fin de chaine pour - // les conversions entre Java et C++) - if (sStringBuffer.GetLength() > 0 and sStringBuffer.GetAt(sStringBuffer.GetLength() - 1) == cDEL) - sStringBuffer.GetBufferSetLength(sStringBuffer.GetLength() - 1); - - // On supprime la ligne si elle commence par un commentaire - // Cela permet de commenter y compris les lignes ayant une valeur contenant des "//" - if (sStringBuffer.GetLength() >= 2 and sStringBuffer.GetAt(0) == '/' and sStringBuffer.GetAt(1) == '/') - sStringBuffer = ""; - } - - // Recherche de l'IdentifierPath et de la valeur - nPosition = sStringBuffer.Find(' '); - if (nPosition == -1) - { - sIdentifierPath = sStringBuffer; - sValue = ""; - } - else - { - sIdentifierPath = sStringBuffer.Left(nPosition); - sValue = sStringBuffer.Right(sStringBuffer.GetLength() - nPosition - 1); - sValue.TrimRight(); - sValue.TrimLeft(); - } - - // Parsing de l'IdentifierPath: recherche des '.' - while (sIdentifierPath != "") - { - // Recherche d'un '.', sinon de la fin de la ligne - nPosition = sIdentifierPath.Find('.'); - if (nPosition == -1) - nPosition = sIdentifierPath.GetLength(); - assert(nPosition >= 0); - - // Recherche d'un Identifier du PathIdentifier - sToken = sIdentifierPath.Left(nPosition); - sToken.TrimRight(); - sToken.TrimLeft(); - if (sToken != "") - { - sIdentifier = sToken; - svIdentifierPath->Add(sIdentifier); - } - else - { - // Erreur dans la syntaxe de la commande - svIdentifierPath->SetSize(0); - break; - } - - // Passage a la suite du buffer - if (nPosition < sIdentifierPath.GetLength()) - sIdentifierPath = sIdentifierPath.Right(sIdentifierPath.GetLength() - nPosition - 1); - else - sIdentifierPath = ""; - sIdentifierPath.TrimRight(); - sIdentifierPath.TrimLeft(); - } - - // On renvoie le resultat - if (svIdentifierPath->GetSize() != 0) - return true; - else - return false; + return commandFile.ReadInputCommand(svIdentifierPath, sValue); } void UIObject::WriteOutputCommand(const ALString& sIdentifierPath, const ALString& sValue, const ALString& sLabel) { - if (fOutputCommands != NULL) - { - ALString sCommand; - int nCommandLength; - - // Fabrication de la commande - sCommand = sIdentifierPath; - if (sValue != "") - sCommand += " " + sValue; - - // Calcul de la position du libelle - nCommandLength = 10 * ((9 + sCommand.GetLength()) / 10); - if (nCommandLength < 30) - nCommandLength = 30; - - // Impression - if (bPrintOutputInConsole) - // Si redirection vers la console, on ajoute un prefixe - fprintf(fOutputCommands, "Khiops.command\t"); - if (sCommand == "" and sLabel == "") - fprintf(fOutputCommands, "\n"); - else if (sCommand == "") - fprintf(fOutputCommands, "// %s\n", (const char*)sLabel); - else - fprintf(fOutputCommands, "%-*.*s // %s\n", nCommandLength, nCommandLength, - (const char*)sCommand, (const char*)sLabel); - fflush(fOutputCommands); - } + commandFile.WriteOutputCommand(sIdentifierPath, sValue, sLabel); } void UIObject::WriteOutputFieldCommand(const ALString& sValue) const @@ -2331,94 +2122,6 @@ void UIObject::InitializeMessageManagers() } } -void UIObject::AddInputSearchReplaceValues(const ALString& sSearchValue, const ALString& sReplaceValue) -{ - static boolean bAtexitDeleteAllInputSearchReplaceValues = false; - - require(sSearchValue != ""); - - // Enregistrement de la supression des donnees de search/replace - if (not bAtexitDeleteAllInputSearchReplaceValues) - { - atexit(DeleteAllInputSearchReplaceValues); - bAtexitDeleteAllInputSearchReplaceValues = true; - } - - // Ajout des valeurs - svInputCommandSearchValues.Add(sSearchValue); - svInputCommandReplaceValues.Add(sReplaceValue); -} - -const ALString UIObject::ProcessSearchReplaceCommand(const ALString& sInputCommand) -{ - ALString sInputString; - ALString sBeginString; - ALString sEndString; - ALString sOutputCommand; - ALString sSearchValue; - ALString sReplaceValue; - int nSearchPosition; - int i; - - // Parcours de toutes les paires search/replace a appliquer - sOutputCommand = sInputCommand; - for (i = 0; i < GetInputSearchReplaceValueNumber(); i++) - { - sSearchValue = GetInputSearchValueAt(i); - sReplaceValue = GetInputReplaceValueAt(i); - - // Remplacement iteratif des pattern trouves a partir de la chaine pretraitee precedente - if (sSearchValue.GetLength() > 0) - { - sBeginString = ""; - sEndString = sOutputCommand; - nSearchPosition = 0; - sOutputCommand = ""; - while (nSearchPosition >= 0 and sEndString.GetLength() > 0) - { - nSearchPosition = sEndString.Find(sSearchValue); - - // Si non trouve, on garde la fin de la chaine en cours de traitement - if (nSearchPosition == -1) - sOutputCommand += sEndString; - // Sinon, en prend le debut puis la valeur de remplacement - else - { - sBeginString = sEndString.Left(nSearchPosition); - sEndString = sEndString.Right(sEndString.GetLength() - - sSearchValue.GetLength() - nSearchPosition); - sOutputCommand += sBeginString + sReplaceValue; - } - } - } - } - return sOutputCommand; -} - -int UIObject::GetInputSearchReplaceValueNumber() -{ - assert(svInputCommandSearchValues.GetSize() == svInputCommandReplaceValues.GetSize()); - return svInputCommandSearchValues.GetSize(); -} - -const ALString& UIObject::GetInputSearchValueAt(int nIndex) -{ - require(0 <= nIndex and nIndex < GetInputSearchReplaceValueNumber()); - return svInputCommandSearchValues.GetAt(nIndex); -} - -const ALString& UIObject::GetInputReplaceValueAt(int nIndex) -{ - require(0 <= nIndex and nIndex < GetInputSearchReplaceValueNumber()); - return svInputCommandReplaceValues.GetAt(nIndex); -} - -void UIObject::DeleteAllInputSearchReplaceValues() -{ - svInputCommandSearchValues.SetSize(0); - svInputCommandReplaceValues.SetSize(0); -} - void UIObjectDisplayErrorFunction(const Error* e) { ALString sDisplayMessage; @@ -2435,8 +2138,4 @@ boolean UIObject::bVerboseCommandReplay = false; boolean UIObject::bBatchMode = false; boolean UIObject::bFastExitMode = true; boolean UIObject::bTextualInteractiveModeAllowed = false; -FILE* UIObject::fInputCommands = NULL; -FILE* UIObject::fOutputCommands = NULL; -StringVector UIObject::svInputCommandSearchValues; -StringVector UIObject::svInputCommandReplaceValues; ObjectDictionary UIObject::odListIndexCommands; diff --git a/src/Norm/base/UIUnit.cpp b/src/Norm/base/UIUnit.cpp index 4e3767ca..076a89c0 100644 --- a/src/Norm/base/UIUnit.cpp +++ b/src/Norm/base/UIUnit.cpp @@ -151,8 +151,7 @@ void UIUnit::Open() "Replay failure\t: " + sCommand + " " + sValue + " at index " + IntToString(uiUnit->nCurrentItemIndex)); - CloseCommandFiles(); - DeleteAllInputSearchReplaceValues(); + CleanCommandLineManagement(); if (bBatchMode) Global::AddFatalError("Command file", "", "Batch mode failure"); break; @@ -222,8 +221,7 @@ void UIUnit::Open() // Arret sinon Global::AddError("Command file", "", "Replay failure\t: " + sCommand + " " + sValue); - CloseCommandFiles(); - DeleteAllInputSearchReplaceValues(); + CleanCommandLineManagement(); if (bBatchMode) Global::AddFatalError("Command file", "", "Batch mode failure"); break; @@ -274,7 +272,7 @@ void UIUnit::Open() else // Sinon, en mode textuel, on n'autorise pas d'interactions sans scenario { - if (fInputCommands != NULL) + if (commandFile.IsInputCommandFileOpened()) AddFatalError("Unexpected end of file in the input commands file"); else AddFatalError("Missing input commands file"); diff --git a/src/Norm/base/UserInterface.h b/src/Norm/base/UserInterface.h index 03a73945..35d986a7 100644 --- a/src/Norm/base/UserInterface.h +++ b/src/Norm/base/UserInterface.h @@ -29,6 +29,7 @@ class UIAction; #include "PLRemoteFileService.h" #include "FileService.h" #include "CommandLine.h" +#include "CommandFile.h" // Directive de compilation dediee au developpement des composants UI // Seule la librairie Base a besoin effectivement de pour etre compilee @@ -382,19 +383,19 @@ class UIObject : public Object // .List.Index // List index comment // .List.Key // List key comment - // Ouverture des fichiers d'entree-sortie des commandes - static boolean OpenInputCommandFile(const ALString& sFileName); - static boolean OpenOutputCommandFile(const ALString& sFileName); + // Parametrage des options de la ligne de commande + static boolean InputCommand(const ALString& sFileName); + static boolean OutputCommand(const ALString& sFileName); static boolean ReplaceCommand(const ALString& sSearchReplacePattern); static boolean BatchCommand(const ALString& sParameter); static boolean ErrorCommand(const ALString& sErrorLog); static boolean TaskProgressionCommand(const ALString& sTaskFile); - // Fermeture des fichiers d'entree-sortie des commandes - static void CloseCommandFiles(); + // Nettoyage de la gestion de la ligne de commande, fermeture des fichiers ouverts... + static void CleanCommandLineManagement(); - // Fonction de sortie utilisateur pour fermer les fichiers d'entre-sortie de commande - static void ExitHandlerCloseCommandFiles(int nExitCode); + // Fonction de sortie utilisateur pour fermer les fichiers d'entre-sortie de commande et d'erreur + static void ExitHandlerCleanCommandLineManagement(int nExitCode); // Lecture d'une commande // renvoie false si pas de commande, sinon un vecteur de chaines de caracteres representant le @@ -410,30 +411,8 @@ class UIObject : public Object // Conditionne par le mode batch static void InitializeMessageManagers(); - ///////////////////////////////////////////////////////////// - // Personnalisation des fichiers de commandes en entree par des - // paires (Search value, Replace value). - // Chaque paire est appliquee sur les operandes de chaque ligne de commande en - // entree avant son execution - - // Ajout d'une paire (SearchValue, ReplaceValue) pour la personnalisation des commandes en entree - static void AddInputSearchReplaceValues(const ALString& sSearchValue, const ALString& sReplaceValue); - - // Application des recherche/remplacement de valeurs successivement sur une commande - static const ALString ProcessSearchReplaceCommand(const ALString& sInputCommand); - - // Nombre de paires de personnalisation - static int GetInputSearchReplaceValueNumber(); - - // Acces aux valeurs d'une paire de personnalisation - static const ALString& GetInputSearchValueAt(int nIndex); - static const ALString& GetInputReplaceValueAt(int nIndex); - - // Nettoyage de toutes les paires - static void DeleteAllInputSearchReplaceValues(); - // Verifie que les noms de fichiers passes en parametres sont coherents - static boolean CheckOptions(const ObjectArray& oaOptions); + static boolean CheckCommandLineOptions(const ObjectArray& oaOptions); // Attributs d'instance ALString sLabel; @@ -453,25 +432,13 @@ class UIObject : public Object static boolean bFastExitMode; static boolean bTextualInteractiveModeAllowed; static ALString sIconImageJarPath; - static ALString sLocalInputCommandsFileName; - static ALString sLocalOutputCommandsFileName; - static ALString sInputCommandFileName; - static ALString sOutputCommandFileName; static ALString sLocalErrorLogFileName; static ALString sErrorLogFileName; static ALString sTaskProgressionLogFileName; static CommandLine commandLineOptions; - // Fichiers de gestion des scenarii - static FILE* fInputCommands; - static FILE* fOutputCommands; - - // Redirection de la sortie outputCommand vers la console - static boolean bPrintOutputInConsole; - - // Gestion des chaines des patterns a remplacer par des valeurs dans les fichiers d'input de scenario - static StringVector svInputCommandSearchValues; - static StringVector svInputCommandReplaceValues; + // Gestion des fichiers de commande + static CommandFile commandFile; // Dictionnaire de bufferisation des commandes de selection d'index dans les listes // Permet de ne memoriser que la derniere selection avant une action, et evite