#include "expat.h" #include "inc/tinyxml2.h" #include "emc_parser.cpp" #include "treemodel.h" #include "mainwindow.h" #include "utility.h" #include "treeitem.h" #include "emc_parse_utility.h" #include "itemtabledelegate.h" #include <QtWidgets> #include "treemodelcompleter.h" #include <QFile> #include "unordered_map" #include "vector" #include <QtCore> #include <QFileDialog> #include <QDebug> #include <QStandardItemModel> #include <QHash> #define BUFFER_SIZE 100000 using namespace tinyxml2; using namespace std; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupUi(this); source_completer = new TreeModelCompleter(this); cible_completer = new TreeModelCompleter(this); //bind Model to the View model_files_input = new QStandardItemModel(this); list_input->setModel(model_files_input); sourceView->setModel(NULL); targetView->setModel(NULL); //view->setModel(model_target); // for (int column = 0; column < model->columnCount(); ++column) // view->resizeColumnToContents(column); //set the Model for the tableView and set the Item Delegate ItemTableDelegate* table_delegate = new ItemTableDelegate(source_completer, cible_completer, this); //need to bring a model to the table and fill it with data table_model = new QStandardItemModel(1,2,this); table_model->setHorizontalHeaderItem(0, new QStandardItem("Cible")); table_model->setHorizontalHeaderItem(1, new QStandardItem("Source")); tableView->setModel(table_model); tableView->setItemDelegate(table_delegate); targetView->setItemDelegateForColumn(1, table_delegate); //targetView->setSelectionMode(QAbstractItemView::ExtendedSelection); //targetView->setDragEnabled(true); targetView->setAcceptDrops(true); targetView->setDropIndicatorShown(true); sourceView->setSelectionMode(QAbstractItemView::ExtendedSelection); sourceView->setDragEnabled(true); //sourceView->setAcceptDrops(true); //sourceView->setDropIndicatorShown(true); QObject::connect(source_completer, SIGNAL(highlighted(QModelIndex)), this, SLOT(highlight(QModelIndex))); QObject::connect(cible_completer, SIGNAL(highlighted(QModelIndex)), this, SLOT(highlight(QModelIndex))); connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(sourceView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(updateActions())); connect(targetView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(updateActions())); connect(actionsMenu, SIGNAL(aboutToShow()), this, SLOT(updateActions())); connect(insertRowAction, SIGNAL(triggered()), this, SLOT(insertRow())); connect(removeRowAction, SIGNAL(triggered()), this, SLOT(removeRow())); connect(insertChildAction, SIGNAL(triggered()), this, SLOT(insertChild())); updateActions(); } void MainWindow::insertChild() { //detect in which TreeWidget QWidget * fw = qApp->focusWidget(); QTreeView* current_view = (QTreeView*) fw; QModelIndex index = current_view->selectionModel()->currentIndex(); QAbstractItemModel *model = current_view->model(); if (model->columnCount(index) == 0) { if (!model->insertColumn(0, index)) return; } if (!model->insertRow(0, index)) return; for (int column = 0; column < model->columnCount(index); ++column) { QModelIndex child = model->index(0, column, index); model->setData(child, QVariant("[No data]"), Qt::EditRole); if (!model->headerData(column, Qt::Horizontal).isValid()) model->setHeaderData(column, Qt::Horizontal, QVariant("[No header]"), Qt::EditRole); } current_view->selectionModel()->setCurrentIndex(model->index(0, 0, index), QItemSelectionModel::ClearAndSelect); updateActions(); } bool MainWindow::insertColumn(const QModelIndex &parent) { //detect in which TreeWidget QWidget * fw = qApp->focusWidget(); QTreeView* current_view = (QTreeView*) fw; QAbstractItemModel *model = current_view->model(); int column = current_view->selectionModel()->currentIndex().column(); // Insert a column in the parent item. bool changed = model->insertColumn(column + 1, parent); if (changed) model->setHeaderData(column + 1, Qt::Horizontal, QVariant("[No header]"), Qt::EditRole); updateActions(); return changed; } void MainWindow::insertRow() { //detect in which TreeWidget QWidget * fw = qApp->focusWidget(); QTreeView* current_view = (QTreeView*) fw; QModelIndex index = current_view->selectionModel()->currentIndex(); QAbstractItemModel *model = current_view->model(); if (!model->insertRow(index.row()+1, index.parent())) return; updateActions(); for (int column = 0; column < model->columnCount(index.parent()); ++column) { QModelIndex child = model->index(index.row()+1, column, index.parent()); model->setData(child, QVariant("[No data]"), Qt::EditRole); } } bool MainWindow::removeColumn(const QModelIndex &parent) { QAbstractItemModel *model = sourceView->model(); int column = sourceView->selectionModel()->currentIndex().column(); // Insert columns in each child of the parent item. bool changed = model->removeColumn(column, parent); if (!parent.isValid() && changed) updateActions(); return changed; } void MainWindow::removeRow() { //detect in which TreeWidget QWidget * fw = qApp->focusWidget(); QTreeView* current_view = (QTreeView*) fw; QModelIndex index = current_view->selectionModel()->currentIndex(); QAbstractItemModel *model = current_view->model(); if (model->removeRow(index.row(), index.parent())) updateActions(); } void MainWindow::updateActions() { bool hasSelection = !(sourceView->selectionModel()->selection().isEmpty() || targetView->selectionModel()->selection().isEmpty()); removeRowAction->setEnabled(hasSelection); bool hasCurrent = (sourceView->selectionModel()->currentIndex().isValid() || targetView->selectionModel()->currentIndex().isValid()); insertRowAction->setEnabled(hasCurrent); //show which row you're selecting. /**if (hasCurrent) { view->closePersistentEditor(view->selectionModel()->currentIndex()); int row = view->selectionModel()->currentIndex().row(); int column = view->selectionModel()->currentIndex().column(); if (view->selectionModel()->currentIndex().parent().isValid()) statusBar()->showMessage(tr("Position: (%1,%2)").arg(row).arg(column)); else statusBar()->showMessage(tr("Position: (%1,%2) in top level").arg(row).arg(column)); }**/ } void MainWindow::on_btn_parcourir_source_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Open a file"), "/", tr("File (*.xml *.pdf *.xls)")); char buffer[BUFFER_SIZE]; if (fileName.isEmpty()) return; ModelXML* model_a = new ModelXML(NULL, "american"); parse_xml(buffer, BUFFER_SIZE, fileName.toStdString().c_str(), model_a, mapping); QStringList headers; headers << tr("Tag"); model_source = new DragDropModel(headers, model_a->root); sourceView->setModel(model_source); source_completer->setModel(model_source); source_completer->setCaseSensitivity(Qt::CaseInsensitive); source_completer->setSeparator(QLatin1String(".")); source_completer->setCompletionMode(QCompleter::PopupCompletion); } void MainWindow::on_btn_parcourir_target_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Open a file"), "/", tr("File (*.xml *.pdf *.xls)")); char buffer[BUFFER_SIZE]; if (fileName.isEmpty()) return; ModelXML* model_a = new ModelXML(NULL, "Vanilla"); parse_xml(buffer, BUFFER_SIZE, fileName.toStdString().c_str(), model_a, mapping); QStringList headers; headers << tr("Tag") << tr("Mapping"); model_cible = new DragDropModel(headers, model_a->root); targetView->setModel(model_cible); cible_completer->setModel(model_cible); cible_completer->setCaseSensitivity(Qt::CaseInsensitive); cible_completer->setSeparator(QLatin1String(".")); cible_completer->setCompletionMode(QCompleter::PopupCompletion); } void MainWindow::highlight(const QModelIndex &index) { // detects who sent the signal TreeModelCompleter* completer = qobject_cast<TreeModelCompleter*> (this->sender()); QTreeView* view; if (index.column() == 0) view = this->targetView; else view = this->sourceView; QAbstractItemModel *completionModel = completer->completionModel(); QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(completionModel); if (!proxy) return; QModelIndex sourceIndex = proxy->mapToSource(index); view->selectionModel()->select(sourceIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); view->scrollTo(index); } void MainWindow::on_btn_add_mapping_clicked() { QModelIndex source_index = sourceView->selectionModel()->currentIndex(); QModelIndex target_index = targetView->selectionModel()->currentIndex(); QString source = source_index.data().toString() , target = target_index.data().toString(); if (source_index.isValid() && target_index.isValid()) { source = Utility::findPath(source, source_index); target = Utility::findPath(target, target_index); QStandardItemModel* model_table = qobject_cast<QStandardItemModel*>(tableView->model()); QList<QStandardItem*> row; row.append(new QStandardItem(target)); row.append(new QStandardItem(source)); model_table->appendRow(row); } } void MainWindow::on_btn_save_mapping_clicked() { // the use choose to save mapping from drag and drop or from table // can't mix both QMessageBox msgBox; msgBox.setText(tr("Save Mapping")); msgBox.setInformativeText(tr("Would you like to save them using:")); QPushButton* tree_view = msgBox.addButton(tr("Tree View"), QMessageBox::ActionRole); QPushButton* table_view = msgBox.addButton(tr("Table View"), QMessageBox::ActionRole); msgBox.exec(); QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("Text files (*.txt)")); if (fileName.isEmpty()) return; QFile file(fileName); if(!file.open(QIODevice::WriteOnly)) return; QDataStream out(&file); // check which button was clicked if (msgBox.clickedButton() == tree_view) { TreeModel* target_model = qobject_cast<TreeModel *> (targetView->model()); TreeItem* root = target_model->getRoot(); // fake root represent the header if (root != NULL && root->childCount() > 0) root = root->child(0); else return; // we are at the top element // we nedd to trarverse the tree using a stack // each time we visit an element we check if it has a mapping // if yes we save it QStack<TreeItem*> s; s.push(root); while(!s.empty()) { TreeItem* current = s.top(); s.pop(); if (!current->data(1).toString().isEmpty()) { QString element = current->data(0).toString(); element = Utility::findPath(element, current->parent()); // to check: serialize to string is necessary ?? for deserialize ?? out << element << current->data(1).toString(); } for (int i = 0; i < current->childCount(); ++i) { s.push(current->child(i)); } } } else if (msgBox.clickedButton() == table_view) { /** save using the table **/ QStandardItemModel* model_table = qobject_cast<QStandardItemModel *> (tableView->model()); int num_row = model_table->rowCount(); // fixs saving if user did not finish editing tableView->setDisabled(true); tableView->setDisabled(false); for (int i = 0; i < num_row; ++i) { QStandardItem* item1 = model_table->item(i, 0); QStandardItem* item2 = model_table->item(i, 1); // A validate transformation -> both items !empty if (!item1->text().isEmpty() && !item2->text().isEmpty()) out << item1->text() << item2->text(); } /** end of save **/ } file.flush(); file.close(); // // add the target model into the mapping // TreeModel* target_model = qobject_cast<TreeModel *> (treeView->model()); // if (target_model == NULL) // return; // TreeItem* fake_root = target_model->getRoot(); // TreeItem root = fake_root->child(0); // QStack<TreeItem*> s; // int space = 0; // s.push(root); // while (!s.empty()) { // TreeItem* top = s.top(); s.pop(); // // common treatment // out << top->data(0); // int num_child = top->childCount(); // for (int i = 0; i < num_child; ++i) { // s.push(top->child(i)); // } // } } void MainWindow::on_btn_upload_mapping_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Open a file"), "/", tr("File (*.xml *.pdf *.txt)")); if (fileName.isEmpty()) return; QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) return; QDataStream in(&file); QStandardItemModel* model_table = qobject_cast<QStandardItemModel *> (tableView->model()); //int nb_row = model_table->rowCount(); int row = 0; while (!in.atEnd()) { //model_table->appendRow(new QStandardItem(1)); QString col1, col2; in >> col1 >> col2; model_table->setItem(row, new QStandardItem(col1)); model_table->setItem(row, 1, new QStandardItem(col2)); ++row; } tableView->setDisabled(false); file.close(); } void MainWindow::on_btn_upload_mapping_2_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Load a mapping"), "/", tr("File (*.xml *.txt)")); lineEdit_mapping->setText(fileName); } void print_to_xml_Helper (TreeItem* root, XMLElement* parent, XMLDocument& xmlDoc, unordered_map<string, unordered_map<int, string>> &values_target, unordered_map<string, int> &occurences, QHash<QString, QString>& mapping, QString root_path) { QString root_data = root->data(0).toString(); // find if available the correspand item in input MODEL QString mapped = mapping[root_path]; // check how many copies of this tag we have in input FILE int occurences_input = 1; // in case there is no associated mapping, we suppose that there is only one element // if this element has attributes || childs, we print it. Otherwise NO ! // you could force that by changing "bool forcePrint = true"; if (!mapped.isEmpty()) occurences_input = occurences[mapped.toStdString()]; for (int occurence = 0; occurence < occurences_input; ++occurence) { XMLElement * pElement = xmlDoc.NewElement(root_data.toStdString().c_str()); if (strcmp(pElement->Value(), "variable") == 0) cout << ""; int count = root->childCount(); // help determine if node is leaf bool valueAsAttribute = false; // look for attributes if any // attribute are located at the last child of each TreeItem for (int position = count - 1; position >= 0; --position) { TreeItem* current_child = root->child(position); QString current_child_data = current_child->data(0).toString(); QString child_path = root_path + "." + current_child_data; if (current_child_data.compare("Attributes") == 0) { // attributes of current pElement QString attributes_path = child_path; // start at 0 and do personalized treatment for attribute value for (int i = 0; i < current_child->childCount(); ++i) { TreeItem* attribute = current_child->child(i); // check for each attribute if there is an associated mapping // try to use a function QString checkMapping(QString) QString path = attributes_path; path = path + "." + attribute->data(0).toString(); QString mapped = mapping[path]; string value; if (!mapped.isEmpty()) { // there is a mapping but is there a value in this input file ? unordered_map<int, string> test_map = values_target[mapped.toStdString()]; value = test_map[occurence]; } string att_value; //if (values.is() > 0 && occurence < values.size()) // att_value = values.at(occurence); //else att_value = value; // not needed //add attribute string att_name = (attribute->data(0)).toString().toStdString(); if (att_name.compare("value") != 0 || (att_name.compare("value") == 0 && valueAsAttribute)) pElement->SetAttribute(att_name.c_str(), att_value.c_str()); else pElement->SetText(att_value.c_str()); } } else { // children of current element // call the Helper function here print_to_xml_Helper(current_child, pElement, xmlDoc, values_target, occurences, mapping, child_path); } } // check if this element is leaf and if it has a value /** removed after the bug QString mapped = mapping[] if (count == 1 && hasAttributes && !mapped.isEmpty()) { // this item is a leaf // its mapped and may has a value vector<string> values = values_target[mapped.toStdString()]; if (values.size() > 0) pElement->SetText(values.at(occurence).c_str()); } **/ // bind it to the parent element parent->InsertEndChild(pElement); } } /* * Print to xml cible based on a source model filled with data * */ void print_to_xml (XMLDocument& xmlDoc, TreeModel* m_target, QHash<QString, QString>& mapping, unordered_map<string, unordered_map<int, string>>& values_target, unordered_map<string, int> &occurence) { // we are doing a tree traversal of our structure using a stack stack<TreeItem*> s; // root of target model TreeItem* root = m_target->getRoot(); if (root->childCount() > 0) root = root->child(0); // replace fake root with the real one else return; // init stack with the root element s.push(root); // create an XML Doc QString root_data = root->data(0).toString(); // insert root node XMLNode * pRoot = xmlDoc.NewElement(root_data.toStdString().c_str()); xmlDoc.InsertEndChild(pRoot); QString path = root_data; // path used in mapping // print using a helper function for (int i = root->childCount() - 1; i > 0; --i) { // -1 because the last one is "Attributes" QString child_tag = root->child(i)->data(0).toString(); QString child_path = path + "." + child_tag; print_to_xml_Helper(root->child(i), pRoot->ToElement(), xmlDoc, values_target, occurence, mapping, child_path); } } void saveFile(XMLDocument& xmlDoc, MainWindow* mainWindow) { /* prepare and save the file */ string filename = QFileDialog::getSaveFileName(mainWindow, "Save File", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "XML (*.xml)").toStdString(); XMLError e = xmlDoc.SaveFile(filename.c_str()); } void MainWindow::on_btn_transform_clicked() { btn_transform->setDisabled(true); // the Magic happens here !! QString fileName = lineEdit_mapping->text(); // load mapping QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) return; QDataStream in(&file); while (!in.atEnd()) { QString key, value; in >> key >> value; // key and value as key in HashMap to allow reverse mapping mapping[key] = value; mapping[value] = key; } // load target cible TreeModel* target_model = qobject_cast<TreeModel *> (treeViewTarget->model()); XMLDocument xmlDoc; // load xml input char buffer[BUFFER_SIZE]; int num_row = model_files_input->rowCount(); for (int i = 0; i < num_row; ++i) { ModelXML* model_a = new ModelXML(NULL, "Vanilla"); QString input = model_files_input->item(i)->text(); parse_xml(buffer, BUFFER_SIZE, input.toStdString().c_str(), model_a, mapping); //transform print_to_xml(xmlDoc, target_model,mapping, model_a->elements, model_a->occurence); } // save saveFile(xmlDoc, this); btn_transform->setDisabled(false); } void MainWindow::on_btn_remove_mapping_clicked() { int row = tableView->currentIndex().row(); qDebug() << row; table_model->removeRow(row); } void MainWindow::on_btn_add_input_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Load your input"), "/", tr("File (*.xml *.xls)")); //lineEdit_input->setText(fileName); model_files_input->appendRow(new QStandardItem(fileName)); } void MainWindow::on_btn_remove_input_clicked() { int row = list_input->currentIndex().row(); model_files_input->removeRow(row); } void MainWindow::on_btn_upload_model_target_clicked() { QString fileName; fileName = QFileDialog::getOpenFileName(this, tr("Load your mapping"), "/", tr("File (*.txt)")); // char buffer[BUFFER_SIZE]; // ModelXML* model_a = new ModelXML(NULL, "american"); // parse_xml(buffer, BUFFER_SIZE, fileName.toStdString().c_str(), model_a, mapping); // // use target model if available // if (model_cible != NULL) // model_target = qobject_cast<TreeModel*> (model_source); // else if (fileName.isEmpty()) return; QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) return; QDataStream in(&file); // todo : check if file data is valid QStack<pair<TreeItem*, int>*> s; QVector<QVariant> data; int num; QVariant var; in >> var >> num; data << "Tag"; TreeItem* root = new TreeItem(data); root->insertChildren(0, 1, var); s.push(new pair<TreeItem*, int>(root->child(0), num)); // now we have created our root and know how many childs it has deserializeModel(in, s); model_target = new TreeModel(root); treeViewTarget->setModel(model_target); } void MainWindow::deserializeModel(QDataStream &in, QStack<pair<TreeItem*, int>*>& s) { if (s.empty()) return; TreeItem* current = s.top()->first; int num_childs = s.top()->second; s.pop(); QVariant var; int num; for (int i = 0; i < num_childs; ++i) { // until - 1 because the last element is Attributes in >> var >> num; current->insertChildren(i, 1, var); s.push(new pair<TreeItem*, int>(current->child(i), num)); // before we continue deserializeModel(in, s); } } void MainWindow::on_pushButton_clicked() { TreeModel* source_model = qobject_cast<TreeModel*> (sourceView->model()); serializeModel(source_model, this); } void MainWindow::serializeModel(TreeModel* model, MainWindow* mainWindow) { // serialize model into a file if (model == NULL) return; // fake root is the header, child(0) represent the tree root TreeItem* root = model->getRoot(); if (root == NULL) return; root = root->child(0); // we suppose it's not an empty tree, checked that at beggining QString fileName = QFileDialog::getSaveFileName(mainWindow, "Save Mapping", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "Text files (*.txt)"); if (fileName.isEmpty()) return; QFile file(fileName); if(!file.open(QIODevice::WriteOnly)) return; QDataStream out(&file); // we use a stack to traverse the structure and serialize it's content QStack<TreeItem*> s; // initialise it with root element s.push(root); while (!s.empty()) { // process each element TreeItem* current = s.top(); s.pop(); // we need data but also how many childs it has to deserialize it later out << current->data(0) << current->childCount(); // add childrens to stack for (int i = 0; i < current->childCount(); ++i) { s.push(current->child(i)); } } // push to the file file.flush(); file.close(); } void MainWindow::on_pushButton_2_clicked() { TreeModel* target_model = qobject_cast<TreeModel*> (targetView->model()); serializeModel(target_model, this); }