/* Copyright 2006-2010 Xavier Guerrin This file is part of QElectroTech. QElectroTech is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. QElectroTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QElectroTech. If not, see . */ #include "fileelementscategory.h" #include "fileelementscollection.h" #include "fileelementdefinition.h" /** Constructeur @param path Chemin du dossier de la categorie @param parent Categorie parente @param coll Collection parente */ FileElementsCategory::FileElementsCategory(const QString &path, FileElementsCategory *parent, FileElementsCollection *coll) : ElementsCategory(parent, coll), file_parent_collection_(coll), file_parent_category_(parent), cat_dir(path) { reload(); } /** Destructeur */ FileElementsCategory::~FileElementsCategory() { deleteContent(); } /** @return le nom de la categorie utilisable dans un chemin (virtuel ou reel) */ QString FileElementsCategory::pathName() const { return(cat_dir.dirName()); } /** @return le chemin virtuel de la categorie, sans le protocole */ QString FileElementsCategory::virtualPath() { // il n'est pas possible d'avoir un chemin virtuel sans appartenir a une collection if (!hasParentCollection()) return(QString()); // recupere le chemin absolu de la racine de la collection QString root_abs_path(parentCollection() -> filePath()); if (!filePath().startsWith(root_abs_path)) return(QString()); QString virtual_path(filePath()); virtual_path.remove(root_abs_path); virtual_path.remove(QRegExp("^/")); return(virtual_path); } /** @return true si la categorie possede un chemin sur un systeme de fichiers */ bool FileElementsCategory::hasFilePath() { return(!filePath().isEmpty()); } /** @return le chemin sur le systeme de fichiers de la categorie */ QString FileElementsCategory::filePath() { return(cat_dir.path()); } /** Definit le chemin du dossier representant la categorie @param p nouveau chemin du dossier representant la categorie */ void FileElementsCategory::setFilePath(const QString &p) { cat_dir.setPath(p); } /** @return la liste des sous-categories de la categorie */ QList FileElementsCategory::categories() { QList result; QList keys(categories_.keys()); qSort(keys.begin(), keys.end()); foreach(QString key, keys) result << categories_[key]; return(result); } /** @return la categorie correspondant au chemin virtuel cat_path, ou 0 en cas d'echec @param cat_path Chemin virtuel de la categorie voulue */ ElementsCategory *FileElementsCategory::category(const QString &cat_path) { // recupere les differentes parties du chemin QString cat_path_(cat_path); QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive); if (!path_parts.count()) { return(this); } else { // a-t-on la premiere sous-categorie ? if (!categories_.contains(path_parts.first())) { return(0); } // on a la premiere sous-categorie ElementsCategory *first_sub_cat = categories_.value(path_parts.first()); if (path_parts.count() == 1) return(first_sub_cat); // on demande a la premiere sous-categorie de fournir la categorie finale path_parts.removeFirst(); return(first_sub_cat -> category(path_parts.join("/"))); } } /** Cree une categorie. La categorie parente doit exister. @param path chemin d'une categorie a creer sous la forme d'une adresse virtuelle comme common://cat1/cat2/cat3 @return la nouvelle categorie demandee, ou 0 en cas d'echec */ ElementsCategory *FileElementsCategory::createCategory(const QString &path) { // on ne doit pas etre en lecture seule if (!isWritable()) return(0); // recupere les differentes parties du chemin QString cat_path_(path); QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive); if (!path_parts.count()) { // le chemin est vide, on renvoie 0 return(0); } else if (path_parts.count() == 1) { // il n'y a plus qu'une categorie dans le chemin : il faut la creer ici // on verifie si la categorie n'existe pas deja if (categories_.contains(path_parts[0])) { return(categories_.value(path_parts[0])); } // on cree un objet FileElementsCategory *new_category = new FileElementsCategory( cat_dir.absolutePath() + "/" + path_parts[0], this, file_parent_collection_ ); // on l'integre dans la liste des sous-categories connues categories_.insert(path_parts[0], new_category); // on le renvoie return(new_category); } else { // il y a plusieurs categories dans le chemin : // on delegue le travail a la premiere sous-categorie // a-t-on la premiere sous-categorie ? if (!categories_.contains(path_parts.first())) { return(0); } // on a la premiere sous-categorie ElementsCategory *first_sub_cat = categories_.value(path_parts.first()); // on demande a la premiere sous-categorie de fournir la categorie finale path_parts.removeFirst(); return(first_sub_cat -> category(path_parts.join("/"))); } } /** @return la liste des elements de la categorie */ QList FileElementsCategory::elements() { QList result; QList keys(elements_.keys()); qSort(keys.begin(), keys.end()); foreach(QString key, keys) result << elements_[key]; return(result); } /** @return l'element correspondant au chemin virtuel elmt_path, ou 0 en cas d'echec @param elmt_path Chemin virtuel de l'element voulu */ ElementDefinition *FileElementsCategory::element(const QString &elmt_path) { // recupere les differentes parties du chemin QString elmt_path_(elmt_path); QStringList path_parts = elmt_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive); if (!path_parts.count()) { // chemin vide return(0); } else if (path_parts.count() == 1) { // seulement le nom de fichier QString element_filename = path_parts.takeLast(); if (!elements_.contains(element_filename)) { return(0); } else { return(elements_.value(element_filename)); } } else { // separe le nom de fichier du chemin de la categorie et recupere celle-ci QString element_filename = path_parts.takeLast(); ElementsCategory *elmt_cat = category(path_parts.join("/")); if (!elmt_cat) { return(0); } else { return(elmt_cat -> element(element_filename)); } } } /** Cree un element. La categorie parente doit exister. @param path chemin d'un element a creer sous la forme d'une adresse virtuelle comme common://cat1/cat2/cat3/dog.elmt @return le nouvel element demande, ou 0 en cas d'echec */ ElementDefinition *FileElementsCategory::createElement(const QString &path) { // on ne doit pas etre en lecture seule if (!isWritable()) return(0); // recupere les differentes parties du chemin QString cat_path_(path); QStringList path_parts = cat_path_.split(QChar('/'), QString::SkipEmptyParts, Qt::CaseInsensitive); if (!path_parts.count()) { // le chemin est vide, on renvoie 0 return(0); } else if (path_parts.count() == 1) { // il n'y a plus que l'element dans le chemin : il faut le creer ici // on verifie si l'element n'existe pas deja if (elements_.contains(path_parts[0])) { return(elements_.value(path_parts[0])); } // on cree un objet FileElementDefinition *new_element = new FileElementDefinition( cat_dir.absolutePath() + "/" + path_parts[0], this, file_parent_collection_ ); // on l'integre dans la liste des elements connus elements_.insert(path_parts[0], new_element); // on le renvoie return(new_element); } else { // il y a plusieurs categories dans le chemin : // on delegue le travail a la premiere sous-categorie // a-t-on la premiere sous-categorie ? if (!categories_.contains(path_parts.first())) { return(0); } // on a la premiere sous-categorie ElementsCategory *first_sub_cat = categories_.value(path_parts.first()); // on demande a la premiere sous-categorie de fournir la categorie finale path_parts.removeFirst(); return(first_sub_cat -> createElement(path_parts.join("/"))); } } /** @return true si le dossier representant la categorie existe */ bool FileElementsCategory::exists() { return(cat_dir.exists()); } /** @return true si la categorie est accessible en lecture Cett methode retourne true a partir du moment ou le dossier representant cette categorie est accessible en lecture. Il se peut que les elements ou le fichier qet_directory a l'interieur ne soient pas accessibles en ecriture. */ bool FileElementsCategory::isReadable() { return(QFileInfo(cat_dir.absolutePath()).isReadable()); } /** @return true s'il est possible d'ecrire le fichier qet_directory dans la categorie */ bool FileElementsCategory::isWritable() { // informations sur le dossier de la categorie QFileInfo category(cat_dir.absolutePath()); QFileInfo qet_directory(cat_dir.absolutePath() + "/qet_directory"); /* soit qet_directory n'existe pas et le dossier est accessible en ecriture, soit qet_directory existe et est accessible en ecriture */ return( category.isWritable() && // le dossier lui-meme est accessible en ecriture ( !qet_directory.exists() || (qet_directory.exists() && qet_directory.isWritable()) ) // le fichier qet_directory est accessible en ecriture ); } /** Recharge le contenu et les noms de la categorie */ void FileElementsCategory::reload() { // supprime l'ancien contenu deleteContent(); category_names.clearNames(); // la categorie doit exister if (!cat_dir.exists()) return; // charge les noms de la categorie loadNames(); // charge les sous-categories QStringList dirs = cat_dir.entryList(QStringList(), QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::Name); foreach(QString dir, dirs) { categories_.insert(dir, new FileElementsCategory(cat_dir.absoluteFilePath(dir), this, file_parent_collection_)); } // charge les elements QStringList elmts = cat_dir.entryList(QStringList("*.elmt"), QDir::Files, QDir::Name); foreach(QString elmt, elmts) { elements_.insert(elmt, new FileElementDefinition(cat_dir.absoluteFilePath(elmt), this, file_parent_collection_)); } } /** Supprime le contenu de la categorie puis la categorie elle-meme @return true si l'operation s'est bien passee, false sinon */ bool FileElementsCategory::remove() { // suppression du contenu de la categorie if (!removeContent()) return(false); // une categorie racine ne se supprime pas elle-meme if (isRootCategory()) return(true); // suppression du fichier de description de la categorie if (cat_dir.exists("qet_directory")) { if (!cat_dir.remove("qet_directory")) return(false); } // suppression de la categorie elle-meme return(cat_dir.rmdir(cat_dir.absolutePath())); } /** Supprime le contenu de la categorie sans supprimer la categorie elle-meme. @return true si l'operation s'est bien passee, false sinon */ bool FileElementsCategory::removeContent() { // suppression des sous-categories foreach(QString cat_name, categories_.keys()) { ElementsCategory *cat = categories_.value(cat_name); if (cat -> remove()) { categories_.take(cat_name); delete cat; } else { return(false); } } // suppression des elements foreach(QString elmt_name, elements_.keys()) { ElementDefinition *elmt = elements_.value(elmt_name); if (elmt -> remove()) { elements_.take(elmt_name); delete elmt; } else { return(false); } } return(true); } /** Cree la categorie @return true si la creation a reussi, false sinon */ bool FileElementsCategory::write() { // cree le dossier de la categorie if (!cat_dir.mkpath(cat_dir.path())) return(false); // prepare la structure XML QDomDocument document; QDomElement root = document.createElement("qet-directory"); document.appendChild(root); root.appendChild(category_names.toXml(document)); // repere le chemin du fichier de configuration de la categorie QFile directory_conf(cat_dir.absolutePath() + "/qet_directory"); // ouvre le fichier if (!directory_conf.open(QIODevice::Text | QIODevice::WriteOnly)) return(false); // ecrit le fichier QTextStream out(&directory_conf); out.setCodec("UTF-8"); out << document.toString(4); // ferme le fichier directory_conf.close(); return(true); } /** Supprime un repertoire recursivement. @return true si la suppression a reussie, false sinon */ bool FileElementsCategory::rmdir(const QString &path) const { QDir directory(path); // supprime les dossiers, recursivement foreach (QString file, directory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { if (!rmdir(directory.absolutePath() + "/" + file)) return(false); } // supprime les fichiers foreach (QString file, directory.entryList(QDir::Files | QDir::NoDotAndDotDot)) { if (!directory.remove(file)) return(false); } // supprime le dossier lui-meme return(directory.rmdir(path)); } /** Charge la liste des noms possibles pour la categorie */ void FileElementsCategory::loadNames() { // repere le chemin du fichier de configuration de la categorie QFile directory_conf(cat_dir.absolutePath() + "/qet_directory"); // verifie l'existence du fichier if (!directory_conf.exists()) return; // ouvre le fichier if (!directory_conf.open(QIODevice::ReadOnly | QIODevice::Text)) return; // lit le contenu du fichier dans un QDomDocument XML QDomDocument document; if (!document.setContent(&directory_conf)) return; // verifie la racine QDomElement root = document.documentElement(); if (root.tagName() != "qet-directory") return; category_names.fromXml(root); // ferme le fichier directory_conf.close(); } /** Supprime le contenu de la categorie en memoire */ void FileElementsCategory::deleteContent() { // suppression des elements foreach(QString elmt_name, elements_.keys()) { FileElementDefinition *elmt = elements_.take(elmt_name); delete elmt; } // suppression des categories foreach(QString cat_name, categories_.keys()) { FileElementsCategory *cat = categories_.take(cat_name); delete cat; } }