/*************************************************************************** * This file is part of the Lime Report project * * Copyright (C) 2015 by Alexander Arin * * arin_a@bk.ru * * * ** GNU General Public License Usage ** * * * This library 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 3 of the License, or * * (at your option) any later version. * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ** GNU Lesser General Public License ** * * * This library is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public * * License along with this library. * * If not, see . * * * * This library 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. * ****************************************************************************/ #include "lrdatadesignintf.h" #include #include #include #include #include #include "lrdatasourcemanager.h" namespace LimeReport{ ModelHolder::ModelHolder(QAbstractItemModel *model, bool owned /*false*/) { ModelToDataSource* mh = new ModelToDataSource(model,owned); m_dataSource = mh; m_owned=owned; connect(mh, SIGNAL(modelStateChanged()), this, SIGNAL(modelStateChanged())); } ModelHolder::~ModelHolder(){ delete m_dataSource; } IDataSource * ModelHolder::dataSource(IDataSource::DatasourceMode mode) { Q_UNUSED(mode); return m_dataSource; } QueryHolder::QueryHolder(QString queryText, QString connectionName, DataSourceManager *dataManager) : m_query(0), m_queryText(queryText), m_connectionName(connectionName), m_mode(IDataSource::RENDER_MODE), m_dataManager(dataManager), m_prepared(true) { extractParams(); } QueryHolder::~QueryHolder() { if (m_query) delete m_query; } bool QueryHolder::runQuery(IDataSource::DatasourceMode mode) { m_mode = mode; QSqlDatabase db = QSqlDatabase::database(m_connectionName); if (!db.isValid()) { setLastError(QObject::tr("Invalid connection! %1").arg(m_connectionName)); return false; } if (!m_prepared){ extractParams(); if (!m_prepared) return false; } if (!m_query){ m_query = new QSqlQuery(db); m_query->prepare(m_preparedSQL); } fillParams(m_query); m_query->exec(); QSqlQueryModel *model = new QSqlQueryModel; model->setQuery(*m_query); while (model->canFetchMore()) model->fetchMore(); if (model->lastError().isValid()){ if (m_dataSource) m_dataSource.clear(); setLastError(model->lastError().text()); delete model; return false; } else setLastError(""); setDatasource(IDataSource::Ptr(new ModelToDataSource(model,true))); return true; } QString QueryHolder::connectionName() { return m_connectionName; } void QueryHolder::setConnectionName(QString connectionName) { m_connectionName=connectionName; } void QueryHolder::invalidate(IDataSource::DatasourceMode mode, bool dbWillBeClosed){ QSqlDatabase db = QSqlDatabase::database(m_connectionName); if (!db.isValid() || dbWillBeClosed){ setLastError(QObject::tr("Invalid connection! %1").arg(m_connectionName)); delete m_query; m_dataSource.clear(); } else { runQuery(mode); } } void QueryHolder::update() { runQuery(m_mode); } void QueryHolder::setDatasource(IDataSource::Ptr value){ m_dataSource.clear(); m_dataSource=value; } void QueryHolder::fillParams(QSqlQuery *query) { foreach(QString param,m_aliasesToParam.keys()){ QVariant value; if (param.contains(".")){ value = dataManager()->fieldData(m_aliasesToParam.value(param)); param=param.right(param.length()-param.indexOf('.')-1); } else { value = dataManager()->variable(m_aliasesToParam.value(param)); } if (value.isValid() || m_mode == IDataSource::DESIGN_MODE) query->bindValue(':'+param,value); } } void QueryHolder::extractParams() { m_preparedSQL = replaceVariables(m_queryText); m_prepared = true; } QString QueryHolder::replaceVariables(QString query) { QRegExp rx(Const::VARIABLE_RX); int curentAliasIndex = 0; if (query.contains(rx)){ int pos = -1; while ((pos=rx.indexIn(query))!=-1){ QString variable=rx.cap(0); variable.remove("$V{"); variable.remove("}"); if (m_aliasesToParam.contains(variable)){ curentAliasIndex++; m_aliasesToParam.insert(variable+"_alias"+QString::number(curentAliasIndex),variable); variable += "_alias"+QString::number(curentAliasIndex); } else { m_aliasesToParam.insert(variable,variable); } query.replace(pos,rx.cap(0).length(),":"+variable); } } return query; } QString QueryHolder::queryText() { return m_queryText; } void QueryHolder::setQueryText(QString queryText) { m_queryText=queryText; m_prepared = false; if (m_query) { delete m_query; m_query = 0; } } IDataSource* QueryHolder::dataSource(IDataSource::DatasourceMode mode) { if ((m_mode != mode && m_mode == IDataSource::DESIGN_MODE) || m_dataSource==0) { m_mode = mode; runQuery(mode); } if (m_dataSource) return m_dataSource.data(); else return 0; } // QueryHolder // ModelToDataSource ModelToDataSource::ModelToDataSource(QAbstractItemModel* model, bool owned) : QObject(), m_model(model), m_owned(owned), m_curRow(-1), m_lastError("") { Q_ASSERT(model); if (model){ while (model->canFetchMore(QModelIndex())) model->fetchMore(QModelIndex()); connect(model, SIGNAL(destroyed()), this, SLOT(slotModelDestroed())); connect(model, SIGNAL(modelReset()), this, SIGNAL(modelStateChanged())); } } ModelToDataSource::~ModelToDataSource() { if ((m_owned) && m_model!=0) delete m_model; } bool ModelToDataSource::next() { if (isInvalid()) return false; if (m_curRow<(m_model->rowCount())) { if (bof()) m_curRow++; m_curRow++; return true; } else return false; } bool ModelToDataSource::hasNext() { if (isInvalid()) return false; return m_curRowrowCount()-1; } bool ModelToDataSource::prior() { if (isInvalid()) return false; if (m_curRow>-1){ if (eof()) m_curRow--; m_curRow--; return true; } else return false; } void ModelToDataSource::first() { m_curRow=0; } void ModelToDataSource::last() { if (isInvalid()) m_curRow=0; else m_curRow=m_model->rowCount()-1; } bool ModelToDataSource::eof() { if (isInvalid()) return true; return (m_curRow==m_model->rowCount())||(m_model->rowCount()==0); } bool ModelToDataSource::bof() { if (isInvalid()) return true; return (m_curRow==-1)||(m_model->rowCount()==0); } QVariant ModelToDataSource::data(const QString &columnName) { if (isInvalid()) return QVariant(); return m_model->data(m_model->index(currentRow(),columnIndexByName(columnName))); } int ModelToDataSource::columnCount() { if (isInvalid()) return 0; return m_model->columnCount(); } QString ModelToDataSource::columnNameByIndex(int columnIndex) { if (isInvalid()) return ""; QString result = m_model->headerData(columnIndex,Qt::Horizontal, Qt::UserRole).isValid()? m_model->headerData(columnIndex,Qt::Horizontal, Qt::UserRole).toString(): m_model->headerData(columnIndex,Qt::Horizontal).toString(); return result; } int ModelToDataSource::columnIndexByName(QString name) { if (isInvalid()) return 0; for(int i=0;icolumnCount();i++){ QString columnName = m_model->headerData(i,Qt::Horizontal, Qt::UserRole).isValid()? m_model->headerData(i,Qt::Horizontal, Qt::UserRole).toString(): m_model->headerData(i,Qt::Horizontal).toString(); if (columnName.compare(name,Qt::CaseInsensitive)==0) return i; } return -1; } QString ModelToDataSource::lastError() { return m_lastError; } QAbstractItemModel * ModelToDataSource::model() { return m_model; } int ModelToDataSource::currentRow() { if (eof()) return m_curRow-1; if (bof()) return m_curRow+1; return m_curRow; } bool ModelToDataSource::isInvalid() const { return m_model==0; } void ModelToDataSource::slotModelDestroed() { m_model = 0; m_lastError = tr("model is destroyed"); emit modelStateChanged(); } ConnectionDesc::ConnectionDesc(QSqlDatabase db, QObject *parent) : QObject(parent), m_connectionName(db.connectionName()), m_connectionHost(db.hostName()), m_connectionDriver(db.driverName()), m_databaseName(db.databaseName()), m_user(db.userName()), m_password(db.password()), m_autoconnect(false), m_internal(false), m_keepDBCredentials(true) {} ConnectionDesc::ConnectionDesc(QObject *parent) :QObject(parent),m_connectionName(""),m_connectionHost(""),m_connectionDriver(""), m_databaseName(""), m_user(""), m_password(""), m_autoconnect(false), m_internal(false), m_keepDBCredentials(true) {} ConnectionDesc::Ptr ConnectionDesc::create(QSqlDatabase db, QObject *parent) { return Ptr(new ConnectionDesc(db,parent)); } void ConnectionDesc::setName(const QString &value) { if (m_connectionName!=value) emit nameChanged(m_connectionName,value); m_connectionName=value; } bool ConnectionDesc::isEqual(const QSqlDatabase &db) { return (db.databaseName() == m_databaseName) && (db.driverName() == m_connectionDriver) && (db.hostName() == m_connectionHost) && (db.connectionName() == m_connectionName) && (db.userName() == m_user) && (db.password() == m_password); } QString ConnectionDesc::connectionNameForUser(const QString &connectionName) { return connectionName.compare(QSqlDatabase::defaultConnection) == 0 ? tr("defaultConnection") : connectionName; } QString ConnectionDesc::connectionNameForReport(const QString &connectionName) { return connectionName.compare(tr("defaultConnection")) == 0 ? QSqlDatabase::defaultConnection : connectionName; } bool ConnectionDesc::keepDBCredentials() const { return m_keepDBCredentials; } void ConnectionDesc::setKeepDBCredentials(bool keepDBCredentals) { m_keepDBCredentials = keepDBCredentals; } QueryDesc::QueryDesc(QString queryName, QString queryText, QString connection) :m_queryName(queryName), m_queryText(queryText), m_connectionName(connection) {} SubQueryHolder::SubQueryHolder(QString queryText, QString connectionName, QString masterDatasource, DataSourceManager* dataManager) : QueryHolder(queryText, connectionName, dataManager), m_masterDatasource(masterDatasource)/*, m_invalid(false)*/ { extractParams(); } void SubQueryHolder::setMasterDatasource(const QString &value) { if (dataManager()->dataSource(value)){ m_masterDatasource = value; } } void SubQueryHolder::extractParams() { if (!dataManager()->containsDatasource(m_masterDatasource)){ setLastError(QObject::tr("Master datasource \"%1\" not found!").arg(m_masterDatasource)); setPrepared(false); } else { m_preparedSQL = replaceFields(replaceVariables(queryText())); setPrepared(true); } } QString SubQueryHolder::extractField(QString source) { if (source.contains('.')) { return source.right(source.length()-(source.indexOf('.')+1)); } return source; } QString SubQueryHolder::replaceFields(QString query) { QRegExp rx(Const::FIELD_RX); int curentAliasIndex=0; if (query.contains(rx)){ int pos; while ((pos=rx.indexIn(query))!=-1){ QString field=rx.cap(0); field.remove("$D{"); field.remove("}"); if (!m_aliasesToParam.contains(field)){ if (field.contains(".")) m_aliasesToParam.insert(field,field); else m_aliasesToParam.insert(field,m_masterDatasource+"."+field); } else { curentAliasIndex++; if (field.contains(".")) m_aliasesToParam.insert(field+"_alias"+QString::number(curentAliasIndex),field); else m_aliasesToParam.insert(field+"_alias"+QString::number(curentAliasIndex),m_masterDatasource+"."+field); field+="_alias"+QString::number(curentAliasIndex); } query.replace(pos,rx.cap(0).length(),":"+extractField(field)); } } return query; } SubQueryDesc::SubQueryDesc(QString queryName, QString queryText, QString connection, QString masterDatasourceName) :QueryDesc(queryName,queryText,connection), m_masterDatasourceName(masterDatasourceName) { } QObject *ProxyDesc::createElement(const QString &collectionName, const QString &) { if (collectionName=="fields"){ FieldMapDesc* fieldMapDesc = new FieldMapDesc; m_maps.append(fieldMapDesc); return fieldMapDesc; } return 0; } int ProxyDesc::elementsCount(const QString &collectionName) { Q_UNUSED(collectionName) return m_maps.count(); } QObject *ProxyDesc::elementAt(const QString &collectionName, int index) { Q_UNUSED(collectionName) return m_maps.at(index); } ProxyHolder::ProxyHolder(ProxyDesc* desc, DataSourceManager* dataManager) :m_model(0), m_desc(desc), m_lastError(""), m_mode(IDataSource::RENDER_MODE), m_invalid(false), m_dataManger(dataManager) {} QString ProxyHolder::masterDatasource() { if (m_desc) return m_desc->master(); return QString(); } void ProxyHolder::filterModel() { if (!m_datasource){ if (dataManager()){ IDataSource* master = dataManager()->dataSource(m_desc->master()); IDataSource* child = dataManager()->dataSource(m_desc->child()); if (master&&child){ m_model = new MasterDetailProxyModel(dataManager()); connect(child->model(),SIGNAL(destroyed()), this, SLOT(slotChildModelDestoroyed())); m_model->setSourceModel(child->model()); m_model->setMaster(m_desc->master()); m_model->setChildName(m_desc->child()); m_model->setFieldsMap(m_desc->fieldsMap()); try{ m_model->rowCount(); m_datasource = IDataSource::Ptr(new ModelToDataSource(m_model,true)); } catch (ReportError& exception) { m_lastError = exception.what(); } m_invalid = false; m_lastError.clear(); } else { m_lastError.clear(); if(!master) m_lastError+=QObject::tr("Master datasouce \"%1\" not found!").arg(m_desc->master()); if(!child) m_lastError+=((m_lastError.isEmpty())?QObject::tr("Child"):QObject::tr(" and child "))+ QObject::tr("datasouce \"%1\" not found!").arg(m_desc->child()); } } } else { if (!isInvalid()){ m_model->invalidate(); m_datasource->first(); } } } IDataSource *ProxyHolder::dataSource(IDataSource::DatasourceMode mode) { if ((m_mode != mode && m_mode == IDataSource::DESIGN_MODE) || m_datasource==0) { m_mode = mode; m_datasource.clear(); filterModel(); } return m_datasource.data(); } void ProxyHolder::invalidate(IDataSource::DatasourceMode mode, bool dbWillBeClosed) { Q_UNUSED(mode) Q_UNUSED(dbWillBeClosed); if (m_model && m_model->isInvalid()){ m_invalid = true; m_lastError = tr("Datasource has been invalidated"); } else { filterModel(); } } void ProxyHolder::slotChildModelDestoroyed(){ m_datasource.clear(); m_model = 0; } void ProxyDesc::addFieldsCorrelation(const FieldsCorrelation& fieldsCorrelation) { m_maps.append(new FieldMapDesc(fieldsCorrelation)); } void MasterDetailProxyModel::setMaster(QString name){ m_masterName=name; } bool MasterDetailProxyModel::isInvalid() const { if (m_masterName.isEmpty() || m_childName.isEmpty()) return true; IDataSource* masterData = dataManager()->dataSource(m_masterName); IDataSource* childData = dataManager()->dataSource(m_childName); if (!masterData || !childData) return true; return masterData->isInvalid() || childData->isInvalid(); } bool MasterDetailProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) foreach (FieldMapDesc* fieldCorrelation, *m_maps) { QVariant master = masterData(fieldCorrelation->master()); QVariant detail = sourceData(fieldCorrelation->detail(),source_row); if (master==detail) return true; } return false; } int MasterDetailProxyModel::fieldIndexByName(QString fieldName) const { for(int i=0;icolumnCount();++i){ QString sourceFieldName = sourceModel()->headerData(i,Qt::Horizontal,Qt::UserRole).isValid()? sourceModel()->headerData(i,Qt::Horizontal,Qt::UserRole).toString(): sourceModel()->headerData(i,Qt::Horizontal).toString(); if (sourceFieldName.compare(fieldName,Qt::CaseInsensitive)==0){ return i; } } return -1; } QVariant MasterDetailProxyModel::sourceData(QString fieldName, int row) const { int fieldIndex = fieldIndexByName(fieldName); if (fieldIndex!=-1){ return sourceModel()->index(row,fieldIndex).data(); } else { throw ReportError( tr("Field: \"%1\" not found in \"%2\" child datasource").arg(fieldName).arg(m_childName) ); } } QVariant MasterDetailProxyModel::masterData(QString fieldName) const { IDataSource* master = dataManager()->dataSource(m_masterName); int columnIndex = master->columnIndexByName(fieldName); if (columnIndex!=-1){ return master->data(fieldName); } else { throw ReportError( tr("Field: \"%1\" not found in \"%2\" master datasource").arg(fieldName).arg(m_masterName) ); } } bool CallbackDatasource::next(){ if (!m_eof){ bool nextRowExists = checkNextRecord(m_currentRow); if (m_currentRow>-1){ if (!m_getDataFromCache && nextRowExists){ for (int i = 0; i < m_columnCount; ++i ){ m_valuesCache[columnNameByIndex(i)] = data(columnNameByIndex(i)); } } } if (!nextRowExists){ m_eof = true; return false; } m_currentRow++; bool result = true; if (!m_getDataFromCache) emit changePos(CallbackInfo::Next,result); m_getDataFromCache = false; if (m_rowCount != -1){ if (m_rowCount > 0 && m_currentRow < m_rowCount){ m_eof = false; } else { m_eof = true; } return !m_eof; } else { m_eof = !result; return result; } } else return false; } bool CallbackDatasource::prior(){ if (m_currentRow !=-1) { if (!m_getDataFromCache && !m_valuesCache.isEmpty()){ m_getDataFromCache = true; m_currentRow--; m_eof = false; return true; } else { return false; } } else { return false; } } void CallbackDatasource::first(){ m_currentRow = 0; m_eof=checkIfEmpty(); bool result=false; QVariant rowCount; CallbackInfo info; info.dataType = CallbackInfo::RowCount; emit getCallbackData(info,rowCount); if (rowCount.isValid()) m_rowCount = rowCount.toInt(); emit changePos(CallbackInfo::First,result); if (m_rowCount>0) m_eof = false; else m_eof = !result; } QVariant CallbackDatasource::data(const QString& columnName) { QVariant result; if (!bof()) { if (!m_getDataFromCache){ CallbackInfo info; info.dataType = CallbackInfo::ColumnData; info.columnName = columnName; info.index = m_currentRow; emit getCallbackData(info,result); } else { result = m_valuesCache[columnName]; } } return result; } int CallbackDatasource::columnCount(){ CallbackInfo info; if (m_columnCount == -1){ QVariant columnCount; info.dataType = CallbackInfo::ColumnCount; emit getCallbackData(info,columnCount); if (columnCount.isValid()){ m_columnCount = columnCount.toInt(); } if (m_columnCount != -1){ for(int i=0;i0) m_columnCount = m_headers.size(); return m_columnCount; } QString CallbackDatasource::columnNameByIndex(int columnIndex) { if (columnIndex < m_headers.size()) return m_headers[columnIndex]; else return QString(); } int CallbackDatasource::columnIndexByName(QString name) { for (int i=0;i 0) { return (m_currentRow < (m_rowCount-1)); } else { QVariant result = false; CallbackInfo info; info.dataType = CallbackInfo::HasNext; info.index = recordNum; emit getCallbackData(info,result); return result.toBool(); } } bool CallbackDatasource::checkIfEmpty(){ if (m_rowCount == 0) { return true; } else { QVariant isEmpty = true; QVariant recordCount = 0; CallbackInfo info; info.dataType = CallbackInfo::RowCount; emit getCallbackData(info, recordCount); if (recordCount.toInt()>0) { m_rowCount = recordCount.toInt(); return false; } info.dataType = CallbackInfo::IsEmpty; emit getCallbackData(info,isEmpty); return isEmpty.toBool(); } } } //namespace LimeReport