[90] | 1 | #ifndef __XML_PARSER__ |
---|
| 2 | #define __XML_PARSER__ |
---|
| 3 | |
---|
| 4 | #include "xmlio_std.hpp" |
---|
| 5 | |
---|
| 6 | /// HISTORIQUE /// |
---|
| 7 | // <RV> 20/04/10 - 23/04/10 : Nouvelle implémentation parsing, création de la classe principale XmlIOServer::XML::XMLParser. |
---|
| 8 | // <RV> 23/04/10 : Création de la classe abstraite XmlIOServer::XML::XMLElement + ajout de quelques commentaires. |
---|
| 9 | // <RV> 26/04/10 : Ajout de la gestion simplifiée des héritages descendants + gestion des flux de sortie. |
---|
| 10 | // <RV> 29/04/10 : Ajout de la gestion simplifiée des attributs par défaut. |
---|
| 11 | // <RV> 30/04/10 : Création et Vérification de la classe de test XML XmlIOServer::XML::XMLClassTest. |
---|
| 12 | // <RV> 03/05/10 : Nouvelle implémentation classe de test + ajout de tous les commentaires. |
---|
| 13 | // <RV> 04/05/10 : Suppression documentation excessive + correction de 2 problÚmes de parsing sur situations particuliÚres non gérées. |
---|
| 14 | |
---|
| 15 | /** |
---|
| 16 | * \file XMLParser.hpp |
---|
| 17 | * \brief Ce fichier contient le code source permettant de parser un fichier de configuration xml pour le serveur d'E/S. |
---|
| 18 | * \author Ozdoba Hervé (contact: herve.ozdoba@glsce.ipsl.fr) |
---|
| 19 | * \author Meurdesoif Yann (contact Yann.Meurdesoif@cea.fr) |
---|
| 20 | * \version 0.1.0 |
---|
| 21 | * \date Avril - Mai 2010 |
---|
| 22 | */ |
---|
| 23 | |
---|
| 24 | // Utilisation de la STL |
---|
| 25 | using std::string; |
---|
| 26 | |
---|
| 27 | using std::pair; |
---|
| 28 | using std::vector; |
---|
| 29 | using std::deque; |
---|
| 30 | |
---|
| 31 | using std::ifstream; |
---|
| 32 | using std::ostream; |
---|
| 33 | |
---|
| 34 | // Utilisation de la biliothÚque POCO |
---|
| 35 | using Poco::XML::DOMParser; |
---|
| 36 | using Poco::XML::InputSource; |
---|
| 37 | |
---|
| 38 | using Poco::XML::Document; |
---|
| 39 | using Poco::XML::Node; |
---|
| 40 | using Poco::XML::Element; |
---|
| 41 | |
---|
| 42 | using Poco::XML::NamedNodeMap; |
---|
| 43 | |
---|
| 44 | //using Poco::XML::AutoPtr; |
---|
| 45 | |
---|
| 46 | using Poco::Exception; |
---|
| 47 | using Poco::HashMap; |
---|
| 48 | |
---|
| 49 | /** |
---|
| 50 | * \namespace XmlIOServer |
---|
| 51 | * \brief Cet espace de nommage regroupe l'intégralité des classes du serveur des E/S. |
---|
| 52 | * \see XmlIOServer::XML |
---|
| 53 | * |
---|
| 54 | * Espace de nommage général qui regroupe toutes les classes dont dépend le serveur de gestion des entrées/sorties |
---|
| 55 | * parallÚles avec configuration par fichier de données xml. |
---|
| 56 | */ |
---|
| 57 | namespace XmlIOServer |
---|
| 58 | { |
---|
| 59 | /** |
---|
| 60 | * \namespace XmlIOServer::XML |
---|
| 61 | * \brief Cet espace de nommage contient les classes permettant de parser le fichier de configuration XML. |
---|
| 62 | */ |
---|
| 63 | namespace XML |
---|
| 64 | { |
---|
| 65 | /** |
---|
| 66 | * \typedef pair<string, string> TAttribute |
---|
| 67 | * \brief Définition d'un type pair associant un nom d'attribut à sa valeur. |
---|
| 68 | * \see TListAttributes |
---|
| 69 | * |
---|
| 70 | * Ce type permet de récupérer une pair contenant : |
---|
| 71 | * \li Le nom de l'attribut; |
---|
| 72 | * \li La valeur associée à l'attribut sous forme de chaîne de caractÚres. |
---|
| 73 | */ |
---|
| 74 | typedef pair<string, string> TAttribute; |
---|
| 75 | |
---|
| 76 | /** |
---|
| 77 | * \typedef HashMap<string, string> THashAttributes |
---|
| 78 | * \brief Définition d'une HashMap de TAttribute. |
---|
| 79 | * \see TAttribute |
---|
| 80 | */ |
---|
| 81 | typedef HashMap<string, string> THashAttributes; |
---|
| 82 | |
---|
| 83 | /** |
---|
| 84 | * \enum TNodeType |
---|
| 85 | * \brief Définition d'un type de noeud xml. |
---|
| 86 | */ |
---|
| 87 | typedef enum |
---|
| 88 | { |
---|
| 89 | UNKNOWN = 0, CONTEXT = 1, |
---|
| 90 | FIELD_GROUP = 2, FILE_GROUP = 3, AXIS_GROUP = 4, GRID_GROUP = 5, |
---|
| 91 | FIELD = 6, FILE = 7, AXIS = 8, GRID = 9 |
---|
| 92 | |
---|
| 93 | } TNodeType; |
---|
| 94 | |
---|
| 95 | /** |
---|
| 96 | * \struct XmlIOServer::XML::_element |
---|
| 97 | * \brief Association d'un type de noeud avec un HashMap des attributs qui lui sont associés. |
---|
| 98 | */ |
---|
| 99 | |
---|
| 100 | /** |
---|
| 101 | * \typedef struct XmlIOServer::XML::_element TElement |
---|
| 102 | * \brief Redéfinition du type _element pour simplification d'écriture. |
---|
| 103 | * \see _element |
---|
| 104 | */ |
---|
| 105 | typedef struct _element |
---|
| 106 | { |
---|
| 107 | /** |
---|
| 108 | * \fn _element(TNodeType t, THashAttributes& l) |
---|
| 109 | * \brief Simple constructeur du structure. |
---|
| 110 | * \param t Un type d'élément. |
---|
| 111 | * \param l Une HashMap d'attributs associé à l'élément. |
---|
| 112 | */ |
---|
| 113 | _element(TNodeType t, THashAttributes& l) : type(t),list(l) |
---|
| 114 | { /* Ne rien faire de plus */}; |
---|
| 115 | |
---|
| 116 | /** |
---|
| 117 | * \var TNodeType type |
---|
| 118 | * \brief Type d'élément. |
---|
| 119 | */ |
---|
| 120 | TNodeType type; |
---|
| 121 | |
---|
| 122 | /** |
---|
| 123 | * \var THashAttributes list |
---|
| 124 | * \brief HashMap d'attributs. |
---|
| 125 | */ |
---|
| 126 | THashAttributes list; //< HashMap d'attributs. |
---|
| 127 | |
---|
| 128 | } TElement; |
---|
| 129 | |
---|
| 130 | |
---|
| 131 | /** |
---|
| 132 | * \typedef deque<TElement> TListElements |
---|
| 133 | * \brief Liste de TElement utilisée comme pile pour la gestion (optionnelle) |
---|
| 134 | * de l'héritage descendant dans la classe de parsing xml. |
---|
| 135 | * \see XmlIOServer::XML::XMLParser::path |
---|
| 136 | */ |
---|
| 137 | typedef deque<TElement> TListElements; |
---|
| 138 | |
---|
| 139 | /** |
---|
| 140 | * \typedef Document* PDocument |
---|
| 141 | * \brief Redéfinition du pointeur sur document xml pour simplification d'écriture. |
---|
| 142 | */ |
---|
| 143 | typedef Document* PDocument; |
---|
| 144 | |
---|
| 145 | /** |
---|
| 146 | * \typedef Node* PNode |
---|
| 147 | * \brief Redéfinition du pointeur sur noeud xml pour simplification d'écriture. |
---|
| 148 | * \see XmlIOServer::XML::cNode |
---|
| 149 | */ |
---|
| 150 | typedef Node* PNode; |
---|
| 151 | |
---|
| 152 | /** |
---|
| 153 | * \class XMLElement |
---|
| 154 | * \brief Classe abstraite pour parsing xml. |
---|
| 155 | * \see XmlIOServer::XML::XMLParser |
---|
| 156 | * |
---|
| 157 | * Les classes susceptibles d'être instanciées par parsing Xml doivent hériter des fonctions de celle-ci pour être |
---|
| 158 | * traoter par la classe XMLParser. |
---|
| 159 | */ |
---|
| 160 | class XMLElement |
---|
| 161 | { |
---|
| 162 | public : |
---|
| 163 | |
---|
| 164 | // TODO ? Enlever le param. name serait une bonne chose ? |
---|
| 165 | /** |
---|
| 166 | * \fn XMLElement* addChild(TNodeType type, string name, THashAttributes& attr) |
---|
| 167 | * \brief Fonction appelée par XMLParser signifiant l'ajout d'un fils à l'élément actuel. |
---|
| 168 | * \param type le type du noeud à ajouter. |
---|
| 169 | * \param name le nom du noeud à ajouter (lié au paramêtre <i>type</i>) |
---|
| 170 | * \param attr une HashMap des attributs associés à l'élément à ajouter. |
---|
| 171 | * \return Un pointeur sur l'élément fils nouvellement créé. |
---|
| 172 | */ |
---|
| 173 | virtual XMLElement* addChild(TNodeType type, TListElements& path, THashAttributes& attr) = 0; |
---|
| 174 | |
---|
| 175 | |
---|
| 176 | /** |
---|
| 177 | * \fn XMLElement* getPReference(void) |
---|
| 178 | * \brief Renvoie la référence au noeud parent e l'élément actuel. |
---|
| 179 | * \return la référence au noeud parent. |
---|
| 180 | * |
---|
| 181 | */ |
---|
| 182 | virtual XMLElement* getPReference(void) = 0; |
---|
| 183 | |
---|
| 184 | }; // class XMLElement |
---|
| 185 | |
---|
| 186 | /** |
---|
| 187 | * \class XMLClassTest |
---|
| 188 | * \brief Une classe de test pour le parsing xml. |
---|
| 189 | * |
---|
| 190 | * Une simple classe de test qui écrit les arguments reçus lors du parsing vers la console. |
---|
| 191 | */ |
---|
| 192 | class XMLClassTest : public XMLElement |
---|
| 193 | { |
---|
| 194 | public: |
---|
| 195 | |
---|
| 196 | /** |
---|
| 197 | * \fn XMLClassTest() |
---|
| 198 | * \brief Simple constructeur d'initialisation d'un élément racine. |
---|
| 199 | */ |
---|
| 200 | XMLClassTest(XMLElement* ref = NULL) |
---|
| 201 | { |
---|
| 202 | this->pref = ref; |
---|
| 203 | //std::cout << "[simulation]" << std::endl; |
---|
| 204 | } |
---|
| 205 | |
---|
| 206 | /** |
---|
| 207 | * \fn XMLClassTest(XMLClassTest* ref, TNodeType type, string& name, THashAttributes& attr) |
---|
| 208 | * \brief Constructeur pour tout autre élément que celui de la racine. |
---|
| 209 | * \param ref Référence à l'élement parent (théorique, celui qui appelle le constructeur via la méthode addChild). |
---|
| 210 | * \param type Le type du noeud à instancier. |
---|
| 211 | * \param name Le nom du noeud à instancier. |
---|
| 212 | * \param attr Une HashMap des attributs liés au noeud à instancier. |
---|
| 213 | */ |
---|
| 214 | XMLClassTest(XMLClassTest* ref, TNodeType type, THashAttributes& attr) |
---|
| 215 | { |
---|
| 216 | this->pref = ref; |
---|
| 217 | //for(int i = 0; i<INC; i++) std::cout << " "; |
---|
| 218 | /*std::cout << "[" << type; |
---|
| 219 | |
---|
| 220 | for (THashAttributes::Iterator it = attr.begin(); it != attr.end(); it++) |
---|
| 221 | std::cout << " "<< (*it).first<< "=\"" << (*it).second << "\""; |
---|
| 222 | |
---|
| 223 | std::cout << "]" << std::endl;*/ |
---|
| 224 | } |
---|
| 225 | |
---|
| 226 | friend ostream& operator<< (ostream& out, const XMLClassTest& c) |
---|
| 227 | { out << "[Non implémentée]" << std::endl; return out; } |
---|
| 228 | |
---|
| 229 | virtual XMLClassTest* addChild(TNodeType type, TListElements& path, THashAttributes& attr) |
---|
| 230 | { INC++; return (new XMLClassTest(this, type, attr)); } |
---|
| 231 | |
---|
| 232 | virtual XMLElement* getPReference(void) { INC--; return (this->pref); } |
---|
| 233 | |
---|
| 234 | /** |
---|
| 235 | * \fn ~XMLClassTest() |
---|
| 236 | * \brief Simple destructeur. |
---|
| 237 | */ |
---|
| 238 | ~XMLClassTest() { /* Ne rien faire de plus */ } |
---|
| 239 | |
---|
| 240 | protected: |
---|
| 241 | |
---|
| 242 | private: |
---|
| 243 | |
---|
| 244 | /** |
---|
| 245 | * \var XMLClassTest* pref |
---|
| 246 | * \brief Référence au parent de l'élÚment actuel (NULL si on se trouve à la racine). |
---|
| 247 | */ |
---|
| 248 | XMLElement* pref; // TODO Vérifier la gestion de la mémoire. |
---|
| 249 | |
---|
| 250 | /** |
---|
| 251 | * \var int INC |
---|
| 252 | * \brief Entier permettant de connaître le niveau d'incrémentation pour l'écriture sur la sortie standard. |
---|
| 253 | */ |
---|
| 254 | static int INC; |
---|
| 255 | |
---|
| 256 | }; // class XMLClassTest |
---|
| 257 | |
---|
| 258 | int XMLClassTest::INC = 0; |
---|
| 259 | |
---|
| 260 | /** |
---|
| 261 | * \class XMLParser |
---|
| 262 | * \brief Classe qui initie le parsing d'un fichier de configuration Xml. |
---|
| 263 | * |
---|
| 264 | * Cette classe permet de parser un document XML afin de configurer le serveur des E/S.\n |
---|
| 265 | * Cette méthode de récupération des paramÚtres est statique puisque le traitement est effectué une fois au |
---|
| 266 | * démarrage du serveur.\n Elle est complémentaire d'une seconde méthode, dynamique cette fois, consistant à paramétrer |
---|
| 267 | * l'application par remoting client-serveur à travers MPI. |
---|
| 268 | */ |
---|
| 269 | class XMLParser |
---|
| 270 | { |
---|
| 271 | public : |
---|
| 272 | |
---|
| 273 | XMLParser(const string& filename, const string& rootName = string("simulation")) |
---|
| 274 | { |
---|
| 275 | XMLParser(); |
---|
| 276 | ifstream istr( filename.data() , ifstream::in ); |
---|
| 277 | if ((istr.good())) |
---|
| 278 | { // S'il est possible de lire le flux en entrée ... |
---|
| 279 | InputSource src(istr); |
---|
| 280 | DOMParser parser; |
---|
| 281 | |
---|
| 282 | // On parse la source XML et on vérifie que le premier noeud (racine) est du type "Element" |
---|
| 283 | // ... et à pour valeur la chaîne rootName. |
---|
| 284 | PDocument pDoc = parser.parse(&src); |
---|
| 285 | if (!(pDoc->documentElement()->nodeName().compare(rootName))) |
---|
| 286 | { |
---|
| 287 | this->cNode = pDoc->documentElement(); |
---|
| 288 | this->goToChildElement(); |
---|
| 289 | } |
---|
| 290 | else |
---|
| 291 | std::cout << "L'élément racine doit avoir pour valeur <"<< rootName <<"> (\"" |
---|
| 292 | << (pDoc->documentElement()->nodeName()) <<"\" lue)"<< std::endl; |
---|
| 293 | |
---|
| 294 | } |
---|
| 295 | else |
---|
| 296 | std::cout << "Impossible de lire le flux du fichier en entrée !" << std::endl; |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | |
---|
| 300 | void parse(XMLElement* const root, ostream& log = std::cout) |
---|
| 301 | { |
---|
| 302 | XMLElement* current = root; |
---|
| 303 | THashAttributes attr; |
---|
| 304 | bool start = true; |
---|
| 305 | |
---|
| 306 | do{ |
---|
| 307 | if(!start)path.pop_back(); |
---|
| 308 | while((start) ? start : this->goToNextElement()) // PARCOURS SUR ELEMENT SUIVANT |
---|
| 309 | { |
---|
| 310 | // APPEL ADD PARENT |
---|
| 311 | if(!start)path.pop_back(); |
---|
| 312 | |
---|
| 313 | this->getAttributes(attr); |
---|
| 314 | path.push_back(TElement(this->getElementType(), attr)); |
---|
| 315 | current = current->addChild(this->getElementType(),this->getTreePath(), attr); |
---|
| 316 | attr.clear(); |
---|
| 317 | |
---|
| 318 | while (this->goToChildElement()) // PARCOURS SUR ELEMENT ENFANT |
---|
| 319 | { |
---|
| 320 | // APPEL ADD LOCAL |
---|
| 321 | this->getAttributes(attr); // ou getGlobalAttributes pour gestion de l'héritage descendant. |
---|
| 322 | path.push_back(TElement(this->getElementType(), attr)); |
---|
| 323 | current = current->addChild(this->getElementType(),this->getTreePath(), attr); |
---|
| 324 | attr.clear(); |
---|
| 325 | } |
---|
| 326 | |
---|
| 327 | current = current->getPReference(); |
---|
| 328 | |
---|
| 329 | if (start) start = false; |
---|
| 330 | } |
---|
| 331 | |
---|
| 332 | if(current == root ) break; |
---|
| 333 | else current = current->getPReference(); |
---|
| 334 | |
---|
| 335 | } while(this->goToParentElement()); // PARCOURS SUR ELEMENT PARENT |
---|
| 336 | |
---|
| 337 | return; |
---|
| 338 | } |
---|
| 339 | |
---|
| 340 | ~XMLParser() |
---|
| 341 | { /* Ne rien faire pour le moment*/ } |
---|
| 342 | |
---|
| 343 | static void ClassTest(const char* path) |
---|
| 344 | { |
---|
| 345 | try |
---|
| 346 | { |
---|
| 347 | // Instanciation du parseur. |
---|
| 348 | XMLParser parser(path); |
---|
| 349 | // Instanciation de l'objet racine. |
---|
| 350 | XMLClassTest root ; |
---|
| 351 | |
---|
| 352 | // Début du parsing XML ... |
---|
| 353 | parser.parse(&root); |
---|
| 354 | } |
---|
| 355 | catch (Exception& exc) |
---|
| 356 | { |
---|
| 357 | // TODO : Remplacer sorties vers cerr et cout par exception |
---|
| 358 | std::cerr << (exc.displayText()) << std::endl; |
---|
| 359 | } |
---|
| 360 | |
---|
| 361 | return; |
---|
| 362 | } |
---|
| 363 | |
---|
| 364 | TListElements& getTreePath() { return (this->path); } |
---|
| 365 | void setTreePath(const TListElements& p) { this->path = p; } |
---|
| 366 | |
---|
| 367 | protected : |
---|
| 368 | |
---|
| 369 | TNodeType getElementType(void) const |
---|
| 370 | { |
---|
| 371 | if(this->isElementName("context"))return (CONTEXT); |
---|
| 372 | |
---|
| 373 | // les groupes, quels qu'ils soient. |
---|
| 374 | if(this->isElementName("field_definition") or this->isElementName("field_group"))return (FIELD_GROUP); |
---|
| 375 | if(this->isElementName("file_definition") or this->isElementName("file_group")) return (FILE_GROUP); |
---|
| 376 | if(this->isElementName("axis_definition") or this->isElementName("axis_group")) return (AXIS_GROUP); |
---|
| 377 | if(this->isElementName("grid_definition") or this->isElementName("grid_group")) return (GRID_GROUP); |
---|
| 378 | |
---|
| 379 | // les éléments finaux (hormis "file"). |
---|
| 380 | if(this->isElementName("field"))return (FIELD); |
---|
| 381 | if(this->isElementName("file")) return (FILE); |
---|
| 382 | if(this->isElementName("axis")) return (AXIS); |
---|
| 383 | if(this->isElementName("grid")) return (GRID); |
---|
| 384 | |
---|
| 385 | return (UNKNOWN); |
---|
| 386 | } |
---|
| 387 | |
---|
| 388 | //////////////////////// PARSING ////////////////////////////// |
---|
| 389 | |
---|
| 390 | // TODO voir pour récupérer une référence. |
---|
| 391 | string getElementName(void) const {return (this->cNode->nodeName());} |
---|
| 392 | |
---|
| 393 | bool isElementName(const char* val) const {return !(this->getElementName().compare(string(val)));} |
---|
| 394 | |
---|
| 395 | bool goToNextElement(Node* nextElement = 0) |
---|
| 396 | { |
---|
| 397 | nextElement = (!nextElement)? this->cNode->nextSibling() : nextElement; |
---|
| 398 | |
---|
| 399 | // On parcourt la liste des "siblings" jusqu'à trouver un élément quelconque. |
---|
| 400 | for(; ; nextElement = nextElement->nextSibling()) |
---|
| 401 | if (nextElement == NULL) break; |
---|
| 402 | else if (nextElement->nodeType() == 1) |
---|
| 403 | {// Si l'un des noeuds est un élément... |
---|
| 404 | this->setCurrentNode(nextElement); |
---|
| 405 | return (true); |
---|
| 406 | } |
---|
| 407 | return (false); |
---|
| 408 | } |
---|
| 409 | |
---|
| 410 | bool goToChildElement(void) |
---|
| 411 | { |
---|
| 412 | // On parcourt la liste des enfants jusqu'à trouver un élément quelconque. |
---|
| 413 | if (this->cNode->firstChild()) |
---|
| 414 | if (this->goToNextElement(this->cNode->firstChild())) |
---|
| 415 | return (true); |
---|
| 416 | |
---|
| 417 | return (false); |
---|
| 418 | } |
---|
| 419 | |
---|
| 420 | bool goToParentElement(void) |
---|
| 421 | { |
---|
| 422 | // Pas de retour au parent si on est à la racine. |
---|
| 423 | if (!(this->getElementName().compare(string("simulation")))) return (false); |
---|
| 424 | this->setCurrentNode(this->cNode->parentNode()); |
---|
| 425 | return (true); |
---|
| 426 | } |
---|
| 427 | |
---|
| 428 | bool getAttributes(THashAttributes& attributes) |
---|
| 429 | { |
---|
| 430 | if(!this->cNode->hasAttributes()) return (false); |
---|
| 431 | NamedNodeMap* map = this->cNode->attributes(); |
---|
| 432 | |
---|
| 433 | for(unsigned int i = 0; i< map->length(); i++) |
---|
| 434 | attributes[map->item(i)->nodeName()] = map->item(i)->nodeValue(); |
---|
| 435 | |
---|
| 436 | return (true); |
---|
| 437 | } |
---|
| 438 | |
---|
| 439 | private : |
---|
| 440 | |
---|
| 441 | XMLParser() : path() //pDoc(), cNode() << Ancien |
---|
| 442 | { /* Ne rien faire de plus */ } |
---|
| 443 | |
---|
| 444 | bool setCurrentNode(PNode node){ if (!node) return false; this->cNode = node; return (true);} |
---|
| 445 | PNode getCurrentNode(void){return (this->cNode);} |
---|
| 446 | |
---|
| 447 | /** |
---|
| 448 | * \var AElement cNode |
---|
| 449 | * \brief Pointeur sur l'objet représentant l'élément courant. |
---|
| 450 | */ |
---|
| 451 | PNode cNode; |
---|
| 452 | |
---|
| 453 | TListElements path; |
---|
| 454 | |
---|
| 455 | };// class XMLParser |
---|
| 456 | |
---|
| 457 | };// namespace XML |
---|
| 458 | |
---|
| 459 | };// namespace XmlIOServer |
---|
| 460 | |
---|
| 461 | #endif //__XML_PARSER__ |
---|
| 462 | |
---|