/* Copyright 2006-2017 The QElectroTech Team 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 "xmlelementcollection.h" #include "nameslist.h" #include "qetxml.h" #include "elementslocation.h" #include "qetproject.h" /** * @brief XmlElementCollection::XmlElementCollection * Build an empty collection. * The collection start by : * * * @param project : the project of this collection */ XmlElementCollection::XmlElementCollection(QETProject *project) : QObject(project), m_project(project) { QDomElement collection = m_dom_document.createElement("collection"); m_dom_document.appendChild(collection); QDomElement import = m_dom_document.createElement("category"); import.setAttribute("name", "import"); collection.appendChild(import); NamesList names; const QChar russian_data[24] = { 0x0418, 0x043C, 0x043F, 0x043E, 0x0440, 0x0442, 0x0438, 0x0440, 0x043E, 0x0432, 0x0430, 0x043D, 0x043D, 0x044B, 0x0435, 0x0020, 0x044D, 0x043B, 0x0435, 0x043C, 0x0435, 0x043D, 0x0442, 0x044B }; const QChar greek_data[18] = { 0x0395, 0x03b9, 0x03c3, 0x03b7, 0x03b3, 0x03bc, 0x03ad, 0x03bd, 0x03b1, 0x0020, 0x03c3, 0x03c4, 0x03bf, 0x03b9, 0x03c7, 0x03b5, 0x03af, 0x03b1 }; const QChar turkish_data[12] ={ 0x0130, 0x0074, 0x0068, 0x0061, 0x006C, 0x0020, 0x00F6, 0x011F, 0x0065, 0x006C, 0x0065, 0x0072 }; names.addName("fr", "Éléments importés"); names.addName("en", "Imported elements"); names.addName("de", "Importierte elemente"); names.addName("es", "Elementos importados"); names.addName("ru", QString(russian_data, 24)); names.addName("cs", "Zavedené prvky"); names.addName("pl", "Elementy importowane"); names.addName("pt", "elementos importados"); names.addName("it", "Elementi importati"); names.addName("el", QString(greek_data, 18)); names.addName("nl", "Elementen geïmporteerd"); names.addName("hr", "Uvezeni elementi"); names.addName("ca", "Elements importats"); names.addName("ro", "Elemente importate"); names.addName("tr", QString(turkish_data, 12)); import.appendChild(names.toXml(m_dom_document)); } /** * @brief XmlElementCollection::XmlElementCollection * Constructor with an collection. The tagName of @dom_element must be "collection" * @param dom_element -the collection in a dom_element (the dom element in cloned) * @param project : the project of this collection */ XmlElementCollection::XmlElementCollection(const QDomElement &dom_element, QETProject *project) : QObject(project), m_project(project) { if (dom_element.tagName() == "collection") m_dom_document.appendChild(m_dom_document.importNode(dom_element, true)); else qDebug() << "XmlElementCollection : tagName of dom_element is not collection"; } /** * @brief XmlElementCollection::root * The root is the first DOM-Element the xml collection, the tag name * of the dom element is : collection * @return The root QDomElement of the collection */ QDomElement XmlElementCollection::root() const { return m_dom_document.documentElement(); } /** * @brief XmlElementCollection::importCategory * @return The QDomElement import (the begining of a xml collection) or * a null QDomElement if doesn't exist. */ QDomElement XmlElementCollection::importCategory() const { return root().firstChildElement("category"); } /** * @brief XmlElementCollection::childs * @param parent_element * @return All childs element in the @parent_element tree */ QDomNodeList XmlElementCollection::childs(const QDomElement &parent_element) const { if (parent_element.ownerDocument() != m_dom_document) return QDomNodeList(); return parent_element.childNodes(); } /** * @brief XmlElementCollection::child * If parent_element have child element with an attribute name = @child_name, return it, else return a null QDomElement. * Only search for element with tag-name "category" and "element" (if child_name end with ".elmt") * @param parent_element : the parent DomElement where we search for child. * @parent_element must be a child node of this XmlElementCollection. * @param child_name : name of child to search. * @return The child QDomElement or a null QDomElement if not found */ QDomElement XmlElementCollection::child(const QDomElement &parent_element, const QString &child_name) const { if (parent_element.ownerDocument() != m_dom_document) return QDomElement(); //Get all childs element of parent_element QDomNodeList child_list = parent_element.childNodes(); QString tag_name(child_name.endsWith(".elmt")? "element" : "category"); QList found_dom_element; for (int i=0 ; i XmlElementCollection::directories(const QDomElement &parent_element) const { QList directory_list; QDomNodeList node_list = childs(parent_element); if (node_list.isEmpty()) return directory_list; for (int i=0 ; i < node_list.count() ; i++) { QDomNode node = node_list.at(i); if (node.isElement() && node.toElement().tagName() == "category") directory_list << node.toElement(); } return directory_list; } /** * @brief XmlElementCollection::directoriesNames * @param parent_element * @return a list of names for every child directories of @parent_element */ QStringList XmlElementCollection::directoriesNames(const QDomElement &parent_element) const { QList childs = directories(parent_element); QStringList names; foreach (QDomElement child, childs) { QString name = child.attribute("name"); if (!name.isEmpty()) names.append(name); } return names; } /** * @brief XmlElementCollection::elements * @param parent_element * @return A list of element stored in @parent_element */ QList XmlElementCollection::elements(const QDomElement &parent_element) const { QList element_list; QDomNodeList node_list = childs(parent_element); if (node_list.isEmpty()) return element_list; for (int i=0 ; i < node_list.count() ; i++) { QDomNode node = node_list.at(i); if (node.isElement() && node.toElement().tagName() == "element") element_list << node.toElement(); } return element_list; } /** * @brief XmlElementCollection::elementsNames * @param parent_element * @return A list of names fr every childs element of @parent_element */ QStringList XmlElementCollection::elementsNames(const QDomElement &parent_element) const { QList childs = elements(parent_element); QStringList names; foreach (QDomElement child, childs) { QString name = child.attribute("name"); if (!name.isEmpty()) names.append(name); } return names; } /** * @brief XmlElementCollection::element * @param path : path of the element in this collection * @return the QDomElement that represent the element at path @path * or a null QDomElement if not found or doesn't represent an element */ QDomElement XmlElementCollection::element(const QString &path) const { if (!path.endsWith(".elmt")) return QDomElement(); QDomElement element = child(path); if (element.tagName() == "element") return element; else return QDomElement(); } /** * @brief XmlElementCollection::directory * @param path : path of the directory in this collection * @return the QDomElement that represent the directory at path @path * or a null QDomElement if not found. */ QDomElement XmlElementCollection::directory(const QString &path) const { QDomElement directory = child(path); if (directory.tagName() == "category") return directory; else return QDomElement(); } /** * @brief XmlElementCollection::addElement * Add the element at location to this collection. * The element is copied in this collection in "import" dir with the same path, in other word * if the path is dir1/dir2/dir3/myElement.elmt, myElement is copied to this collection at the path : import/dir1/dir2/dir3/myElement.elmt * If the path doesn't exist, he was created. * If the element already exist, do nothing. * @param location, location of the element * @return the collection path of the added item or a null QString if element can't be added. */ QString XmlElementCollection::addElement(ElementsLocation &location) { //location must be an element and exist if (!(location.exist() && location.isElement())) return QString(); //Add an element from this collection to this collection have no sense if (location.isProject() && location.projectCollection() == this) return QString(); //First we check if this location exist in this collection if so, we do nothing if ( exist("import/" + location.collectionPath(false)) ) return QString(); //Get the import dir of this collection QDomElement parent_element = importCategory(); if (parent_element.isNull()) return QString(); QString integrated_path = parent_element.attribute("name"); //Split the path QStringList splitted_path = location.collectionPath(false).split("/"); if (splitted_path.isEmpty()) return QString(); if (location.isFileSystem()) { //Get the root dir of the filesystem collection QDir dir(location.fileSystemPath().remove(location.collectionPath(false))); if (!dir.exists()) return QString(); foreach(QString str, splitted_path) { QDomElement child_element = child(parent_element, str); //Child doesn't exist, we create it if (child_element.isNull()) { QDomElement created_child; //str is the path of an element, we integrate an element if (str.endsWith(".elmt")) { QFile element_file(dir.filePath(str)); if (!element_file.exists()) return QString(); created_child = QETXML::fileSystemElementToXmlCollectionElement(m_dom_document, element_file); } //str is the path of a directory, we integrate a directory. else { //Dir doesn't exist. if (!dir.cd(str)) return QString(); created_child = QETXML::fileSystemDirToXmlCollectionDir(m_dom_document, dir); } if(created_child.isNull()) return QString(); parent_element.appendChild(created_child); parent_element = created_child; } //Child exist else { if (!dir.cd(str)) return QString(); parent_element = child_element; } integrated_path.append("/"+str); } } else if (location.isProject()) { QString path; foreach(QString str, splitted_path) { if (path.isEmpty()) path = str; else path = path + "/" + str; QDomElement child_element = child(parent_element, str); //Child doesn't exist, we create it if (child_element.isNull()) { QDomElement created_child; //str is the path of an element, we integrate an element if (str.endsWith(".elmt")) { created_child = m_dom_document.createElement("element"); created_child.setAttribute("name", str); created_child.appendChild(location.xml().cloneNode(true)); } //str is the path of a directory, we integrate a directory. else { created_child = m_dom_document.createElement("category"); created_child.setAttribute("name", str); ElementsLocation sub_dir_location(path, location.project()); QDomElement names_element = sub_dir_location.nameList().toXml(m_dom_document); created_child.appendChild(names_element); } if(created_child.isNull()) return QString(); parent_element.appendChild(created_child); parent_element = created_child; } //Child exist else parent_element = child_element; integrated_path.append("/"+str); } } emit elementAdded(integrated_path); return integrated_path; } /** * @brief XmlElementCollection::addElementDefinition * Add the élément defintion @xml_definition in the directory at path @dir_path with the name @elmt_name. * @param dir_path : the path of the directory where we must add the element. * The path must be an existing directory of this collection. * @param elmt_name : The name used to store the element (the name must end with .elmt, if not, .elmt will be append to @elmt_name) * @param xml_definition : The xml definition of the element. * The tag name of @xml_definition must be "definition". * @return True if the element is added with success. */ bool XmlElementCollection::addElementDefinition(const QString &dir_path, const QString &elmt_name, const QDomElement &xml_definition) { QDomElement dom_dir = directory(dir_path); if (dom_dir.isNull()) { qDebug() << "XmlElementCollection::addElementDefinition : No directory at path : " << dir_path; return false; } if (xml_definition.tagName() != "definition") { qDebug() << "XmlElementCollection::addElementDefinition : xml_defintion tag name is not \"definition\""; return false; } QString name = elmt_name; if (!name.endsWith(".elmt")) { name += ".elmt"; } QDomElement dom_elmt = m_dom_document.createElement("element"); dom_elmt.setAttribute("name", name); dom_elmt.appendChild(xml_definition.cloneNode(true)); dom_dir.appendChild(dom_elmt); emit elementAdded(dir_path + "/" + name); return true; } /** * @brief XmlElementCollection::removeElement * Remove the element at path @path. * @param path * @return True if element is removed and emit the signal elementRemoved. * else false. */ bool XmlElementCollection::removeElement(const QString& path) { QDomElement elmt = element(path); if (!elmt.isNull()) { elmt.parentNode().removeChild(elmt); emit elementRemoved(path); return true; } return false; } /** * @brief XmlElementCollection::copy * Copy the content represented by source (an element or a directory) to destination. * Destination must be a directory of this collection. * If the destination already have an item at the same path of source, he will be replaced by source. * @param source : content to copy * @param destination : destination of the copy, must be a directory of this collection * @param rename : rename the copy with @rename else use the name of source * @param deep_copy : if true copy all childs of source (only if source is directory) * @return the ElementLocation that represent the copy, if copy failed return a null ElementLocation */ ElementsLocation XmlElementCollection::copy(ElementsLocation &source, ElementsLocation &destination, const QString& rename, bool deep_copy) { if (!(source.exist() && destination.isDirectory() && destination.isProject() && destination.projectCollection() == this)) return ElementsLocation(); if (source.isElement()) return copyElement(source, destination, rename); else return copyDirectory(source, destination, rename, deep_copy); } /** * @brief XmlElementCollection::exist * Return true if the path @path exist in this collection * @param path * @return */ bool XmlElementCollection::exist(const QString &path) const { if (child(path).isNull()) return false; else return true; } /** * @brief XmlElementCollection::createDir * Create a child directorie at path @path with the name @name. * Emit directorieAdded if success. * @param path : path of parent diectorie * @param name : name of the directori to create. * @param name_list : translation of the directorie name. * @return true if creation success, if directorie already exist return true. */ bool XmlElementCollection::createDir(const QString& path, const QString& name, const NamesList &name_list) { QString new_dir_path = path + "/" + name; if (!directory(new_dir_path).isNull()) { return true; } QDomElement parent_dir = directory(path); if (parent_dir.isNull()) { qDebug() << "XmlElementCollection::createDir : directorie at path doesn't exist"; return false; } QDomElement new_dir = m_dom_document.createElement("category"); new_dir.setAttribute("name", name); new_dir.appendChild(name_list.toXml(m_dom_document)); parent_dir.appendChild(new_dir); emit directorieAdded(new_dir_path); return true; } /** * @brief XmlElementCollection::removeDir * Remove the directory at path @path. * @param path * @return true if successfuly removed and emit directoryRemoved(QString), * else false. */ bool XmlElementCollection::removeDir(const QString& path) { QDomElement dir = directory(path); if (!dir.isNull()) { dir.parentNode().removeChild(dir); emit directoryRemoved(path); return true; } return false; } /** * @brief XmlElementCollection::elementsLocation * Return all locations stored in dom_element (element and directory). * If dom_element is null, return all location owned by this collection * dom_element must be a child of this collection. * @param dom_element : dom_element where we must to search location. * @param childs = if true return all childs location of dom_element, if false, only return the direct childs location of dom_element. * @return */ QList XmlElementCollection::elementsLocation(QDomElement dom_element, bool childs) const { QList location_list; if (dom_element.isNull()) dom_element = m_dom_document.documentElement(); if (dom_element.ownerDocument() != m_dom_document) return location_list; //get element childs QList element_list = elements(dom_element); foreach (QDomElement elmt, element_list) { ElementsLocation location = domToLocation(elmt); if (location.exist()) location_list << location; } //get directory childs QList directory_list = directories(dom_element); foreach (QDomElement dir, directory_list) { ElementsLocation location = domToLocation(dir); if (location.exist()) location_list << location; if (childs) location_list.append(elementsLocation(dir, childs)); } return location_list; } /** * @brief XmlElementCollection::domToLocation * Return the element location who represent the xml element : dom_element * dom_element must be owned by this collection * @param dom_element : the dom_element of this collection that represent an element. * The tag name of dom_element must be "element" * @return the element location, location can be null if fail. */ ElementsLocation XmlElementCollection::domToLocation(QDomElement dom_element) const { if (dom_element.ownerDocument() == m_dom_document) { QString path = dom_element.attribute("name"); while (!dom_element.parentNode().isNull() && dom_element.parentNode().isElement()) { dom_element = dom_element.parentNode().toElement(); if (dom_element.tagName() == "category") path.prepend(dom_element.attribute("name") + "/"); } return ElementsLocation(path, m_project); } else return ElementsLocation(); } /** * @brief XmlElementCollection::cleanUnusedElement * Remove elements in this collection which is not used in the owner project */ void XmlElementCollection::cleanUnusedElement() { foreach (ElementsLocation loc, m_project->unusedElements()) removeElement(loc.collectionPath(false)); } /** * @brief XmlElementCollection::cleanUnusedDirectory * Remove the empty directories of this collection */ void XmlElementCollection::cleanUnusedDirectory() { QDomNodeList lst = importCategory().elementsByTagName("category"); for(int i=0 ; ichild(source.collectionPath(false)).cloneNode(deep_copy); //We don't make a deep copy, but we must to get the local names of the copied directory if (!deep_copy) { QDomNode names = source.projectCollection()->child(source.collectionPath(false)).namedItem("names"); if (!names.isNull() && names.isElement()) other_collection_node.appendChild(names.cloneNode(true)); } if (other_collection_node.isNull()) return ElementsLocation(); QDomElement other_collection_dom_dir = other_collection_node.toElement(); other_collection_dom_dir.setAttribute("name", new_dir_name); parent_dir_dom.appendChild(other_collection_dom_dir); created_location.setPath(destination.projectCollectionPath() + "/" + new_dir_name); } emit directorieAdded(created_location.collectionPath(false)); return created_location; } /** * @brief XmlElementCollection::copyElement * Copy the element represented by source to destination (must be a directory) * If element already exist in destination he will be replaced by the new. * @param source : element to copy * @param destination : destination of the copy * @param rename : rename the copy with @rename else use the name of source * @return The ElementsLocation of the copy */ ElementsLocation XmlElementCollection::copyElement(ElementsLocation &source, ElementsLocation &destination, const QString& rename) { QString new_elmt_name = rename.isEmpty() ? source.fileName() : rename; QDomElement elmt_dom; //Copy with a file system collection source if (source.isFileSystem()) { QFile file(source.fileSystemPath()); elmt_dom = QETXML::fileSystemElementToXmlCollectionElement(m_dom_document, file, new_elmt_name); if (elmt_dom.isNull()) return ElementsLocation(); } //Copy with a xml collection source else { QDomElement other_collection = source.xml(); elmt_dom = m_dom_document.createElement("element"); elmt_dom.setAttribute("name", new_elmt_name); elmt_dom.appendChild(other_collection.cloneNode()); } //Remove the previous element with the same path QDomElement element = child(destination.collectionPath(false) + "/" + new_elmt_name); bool removed = false; if (!element.isNull()) { element.parentNode().removeChild(element); removed = true; } //Get the xml directory where the new element must be added QDomElement dir_dom = directory(destination.collectionPath(false)); if (dir_dom.isNull()) return ElementsLocation(); dir_dom.appendChild(elmt_dom); ElementsLocation copy_loc(destination.projectCollectionPath() + "/" + new_elmt_name); if (removed) { emit elementChanged(copy_loc.collectionPath(false)); } else { emit elementAdded(copy_loc.collectionPath(false)); } return copy_loc; }