Some notes.
[emufront] / src / db / dbmediaimagecontainer.cpp
1 // EmuFront
2 // Copyright 2010 Mikko Keinänen
3 //
4 // This file is part of EmuFront.
5 //
6 //
7 // EmuFront is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2 as published by
9 // the Free Software Foundation and appearing in the file gpl.txt included in the
10 // packaging of this file.
11 //
12 // EmuFront is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with EmuFront.  If not, see <http://www.gnu.org/licenses/>.
19
20 #include <QDebug>
21 #include <QSqlRecord>
22 #include <QSqlQuery>
23 #include <QSqlRelationalTableModel>
24 #include <QSqlError>
25 #include "dbmediaimagecontainer.h"
26 #include "dbmediaimage.h"
27 //#include "dbsetup.h"
28 #include "dbfilepath.h"
29
30
31 DbMediaImageContainer::DbMediaImageContainer(QObject *parent)
32     : DbFile(parent) // DbQueryModelManager(parent)
33 {
34     dbMediaImage = new DbMediaImage(parent);
35     dbFilePath = new DbFilePath(parent);
36     tableName = DbMediaImageContainer::DB_TABLE_MEDIAIMAGECONTAINER;
37     //dbFile = new DbFile(parent);
38 }
39
40 bool DbMediaImageContainer::updateDataObjectToModel(const EmuFrontObject *efo)
41 {
42     // TODO
43     return false;
44 }
45
46 int DbMediaImageContainer::insertDataObjectToModel(const EmuFrontObject *efo)
47 {
48     const MediaImageContainer *mic
49         = dynamic_cast<const MediaImageContainer *>(efo);
50
51     if (!mic->getFilePath())
52         throw new EmuFrontException("Cannot install media image "
53             "container to database without a file path object!");
54
55     // check if this media image container is already in the database
56
57     // Two possible solutions:
58     // * multiple media image containers with matching checksum will not be stored at all
59     //   (only the first instance will be stored)
60     // * multiple media image containers with matching checksum will be stored
61     //   if each instance is in a different file path
62
63     EmuFrontObject *o = getFileByChecksum(mic->getCheckSum());
64     int fileId = o ? o->getId() : -1;
65     if (fileId >= 0) {
66         // ok, we have a matching file, we could still create a new media image instance to the database
67         qDebug() << "Media image container already in db with id " << fileId << ".";
68         delete o;
69         return fileId;
70    }
71
72     QMap<QString, EmuFrontObject*> images = mic->getMediaImages();
73     QList<int> ids = dbMediaImage->storeMediaImages(images);
74
75     qDebug() << "Stored " << ids.count() << " media images.";
76
77     if (ids.count() <= 0)
78         return -1;
79
80     /* Contained Media images successfully stored to db,
81         storing media image container also */
82
83     try {
84
85         // Insert MediaImageContainer first as a EmuFrontFile object to file table.
86
87         // File id is used to store the media image container instance to database,
88         // file id is also the media image container id
89         fileId = DbFile::insertDataObjectToModel(mic);
90
91         qDebug() << "Inserted media image container to file table with id " << fileId << ".";
92
93         if (fileId < 0) {
94             // TODO: note we most surely need to catch the exception
95             // in the calling code block and clean
96             // all the media image and ...containers from
97             // the memory!
98             throw new EmuFrontException(
99                     QString(tr("Inserting media image container %1 to file database failed"))
100                     .arg(mic->getName()));
101         }
102
103         // Insert to mediaimagecontainer table
104
105         QSqlQuery q;
106         q.prepare("INSERT INTO mediaimagecontainer "
107                   "(fileid, filepathid, updatetime) "
108                   "VALUES (:fileid, :filepathid, :updatetime)");
109         q.bindValue(":fileid", fileId);
110         q.bindValue(":filepathid", mic->getFilePath()->getId());
111         q.bindValue(":updatetime", DatabaseManager::getCurrentTimeStamp());
112         if (!q.exec()){
113             DbFile::deleteDataObject(fileId);
114             throw new EmuFrontException("Failed inserting media image to database!");
115         }
116         qDebug() << "Inserted media image container " << fileId << " to mediaimagecontainer table.";
117         linkMediaImagesWithContainer(fileId, ids);
118         qDebug() << "Linked media image container with media images.";
119     } catch (EmuFrontException e) {
120         dbMediaImage->removeOrphanedMediaImages(ids);
121         throw e;
122     }
123
124     return fileId;
125 }
126
127 bool DbMediaImageContainer::deleteDataObjectFromModel(QModelIndex *i)
128 {
129     // TODO
130     return false;
131 }
132
133 QString DbMediaImageContainer::constructSelect(QString whereClause) const
134 {
135     // TODO, for a usual search we need a "light" version of this select
136     // and MediaImageContainer (only id, name)
137     QString select = QString("SELECT file.id, file.name, file.checksum, file.size, "
138                 "        filepath.id, filepath.name, "
139                 "        setup.id, "
140                 "        platform.id, platform.name, "
141                 "        mediatype.id, mediatype.name "
142                 "FROM mediaimagecontainer "
143                 "INNER JOIN file ON mediaimagecontainer.fileid = file.id "
144                 "INNER JOIN filepath ON mediaimagecontainer.filepathid = filepath.id "
145                 "INNER JOIN setup ON filepath.setupid = setup.id "
146                 "INNER JOIN platform ON setup.platformid = platform.id "
147                 "INNER JOIN mediatype ON setup.mediatypeid = mediatype.id "
148                 "%1 "
149                 "ORDER BY file.name").arg(whereClause);
150     qDebug() << select;
151     return select;
152 }
153
154 QString DbMediaImageContainer::constructFilterById(int id) const
155 {
156     return QString("file.id = %1").arg(id);
157 }
158
159 QString DbMediaImageContainer::constructSelectById(int id) const
160 {
161     return constructSelect(
162         QString("WHERE %1").arg(constructFilterById(id))
163         );
164 }
165
166 EmuFrontObject* DbMediaImageContainer::recordToDataObject(const QSqlRecord *rec)
167 {
168     // TODO: checks!
169     MediaImageContainer *mic = 0;
170     if (!rec) return mic;
171     int id = rec->value(MIC_FileId).toInt();
172     QString name = rec->value(MIC_FileName).toString();
173     QString checksum = rec->value(MIC_FileCheckSum).toString();
174     int size = rec->value(MIC_FileSize).toInt();
175     int fpId = rec->value(MIC_FilePathId).toInt();
176     FilePathObject *fpo
177         = dynamic_cast<FilePathObject*>(dbFilePath->getDataObject(fpId));
178     //int supId = rec->value(MIC_SetupId).toInt();
179     //Setup *sup = dbSetup->getDataObject(supId)
180     QMap<QString, EmuFrontObject*> images = dbMediaImage->getMediaImages(id);
181
182     mic = new MediaImageContainer(
183        id, name, checksum, size, images, fpo
184     );
185     return mic;
186 }
187
188 QSqlQueryModel* DbMediaImageContainer::getData()
189 {
190     QSqlQueryModel *model = new QSqlQueryModel(this);
191     if (sqlTableModel){
192         model->setQuery(sqlTableModel->query());
193     }
194     else
195         model->setQuery(constructSelect());
196     model->setHeaderData(MIC_FileId, Qt::Horizontal, tr("File id"));
197     model->setHeaderData(MIC_FileName, Qt::Horizontal, tr("File Name"));
198     model->setHeaderData(MIC_FileCheckSum, Qt::Horizontal, tr("File checksum"));
199     model->setHeaderData(MIC_FileSize, Qt::Horizontal, tr("File Size"));
200     model->setHeaderData(MIC_FilePathId, Qt::Horizontal, tr("File path id"));
201     model->setHeaderData(MIC_FilePathName, Qt::Horizontal, tr("File path name"));
202     model->setHeaderData(MIC_SetupId, Qt::Horizontal, tr("Setup id"));
203     model->setHeaderData(MIC_PlatformId, Qt::Horizontal, tr("Platform id"));
204     model->setHeaderData(MIC_PlatformName, Qt::Horizontal, tr("Platform name"));
205     model->setHeaderData(MIC_MediaTypeId, Qt::Horizontal, tr("Media type id"));
206     model->setHeaderData(MIC_MediaTypeName, Qt::Horizontal, tr("Media type name"));
207     return model;
208 }
209
210 /* Returns the id of a media image container with a given cheksum or -1 if not found */
211 int DbMediaImageContainer::getMediaImageContainer(QString checksum) const
212 {
213     QSqlQuery q;
214     q.prepare("SELECT id FROM file WHERE checksum=:checksum");
215     q.bindValue(":checksum", checksum);
216     int id = -1;
217     if (q.next())
218         id = q.value(0).toInt();
219     return id;
220 }
221
222
223 /**
224 * Stores media image containers, including the media images included
225 * to database.
226 */
227 void DbMediaImageContainer::storeContainers(QList<MediaImageContainer *> lst, FilePathObject *fpo)
228 {
229     qDebug() << "Storing media image containers to database.";
230     foreach(MediaImageContainer *mic, lst)
231     {
232         qDebug() << "Media image container " << mic->getName();
233         int micFileId = insertDataObjectToModel(mic);
234     }
235 }
236
237 void DbMediaImageContainer::linkMediaImagesWithContainer(int micId, QList<int> miIds)
238 {
239     if (micId < 0 || miIds.count() <= 0)
240         return;
241
242     QSqlQuery q;
243     q.prepare("INSERT INTO mediaimagecontainer_mediaimage "
244         "(mediaimagecontainerid, mediaimageid) "
245         "VALUES (:micid, :miid) ");
246     q.bindValue(":micid", micId);
247
248     foreach(int miid, miIds) {
249         qDebug() << "Linking media image container " << micId << " to media image " << miid  << ".";
250         q.bindValue(":miid", miid);
251         if (!q.exec()) {
252             throw new EmuFrontException(QString("Failed linking media "
253                 "image container %1 to a media image %2").arg(micId).arg(miid));
254         }
255     }
256 }
257
258 void DbMediaImageContainer::filter(int mediaTypeId, int platformId)
259 {
260     qDebug() << "Filtering media images with media type " << mediaTypeId
261         << " and platform " << platformId;
262     QList<QString> filters;
263     if (mediaTypeId >= 0)
264         filters.append(QString("mediatype.id=%1").arg(mediaTypeId));
265     if (platformId >= 0)
266         filters.append(QString("platform.id=%1").arg(platformId));
267     filterDataObjects(filters);
268 }
269
270 QString DbMediaImageContainer::getCountRefsSelect(int id) const
271 {
272     /* we need to count file references to give media image container */
273     /* example:
274         select count(*) from mediaimagecontainer
275         INNER JOIN mediaimagecontainer_mediaimage
276         ON mediaimagecontainer_mediaimage.mediaimagecontainerid
277         = mediaimagecontainer.fileid
278         WHERE mediaimagecontainer.fileid=589;
279     */
280     return QString("SELECT count(*) FROM mediaimagecontainer "
281               "INNER JOIN mediaimagecontainer_mediaimage "
282               "ON mediaimagecontainer_mediaimage.mediaimagecontainerid "
283               "    =mediaimagecontainer.fileid "
284               "WHERE mediaimagecontainer.fileid=%1").arg(id);
285 }
286
287 QString DbMediaImageContainer::getDeleteObjectSql() const
288 {
289        // The trigger will take care of deleting
290        // the reference from the mediaimagecontainer
291        // and mediaimage_mediaimagecontainer tables.
292        // there is also a trigger that will delete
293        // all the files linked to mediaimagecontainer
294        // using mediaimage_mediaimagecontainer (the actual
295        // mediaimages).
296        return QString("DELETE FROM file WHERE id=:id");
297 }