1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "DbiDocumentFormat.h"
23 
24 #include <U2Core/AppContext.h>
25 #include <U2Core/AssemblyObject.h>
26 #include <U2Core/DNASequenceObject.h>
27 #include <U2Core/GObjectUtils.h>
28 #include <U2Core/IOAdapter.h>
29 #include <U2Core/U2DbiUtils.h>
30 #include <U2Core/U2ObjectDbi.h>
31 #include <U2Core/U2ObjectRelationsDbi.h>
32 #include <U2Core/U2OpStatusUtils.h>
33 #include <U2Core/U2SafePoints.h>
34 
35 namespace U2 {
36 
DbiDocumentFormat(const U2DbiFactoryId & _id,const DocumentFormatId & _formatId,const QString & _formatName,const QStringList & exts,DocumentFormatFlags flags,QObject * p)37 DbiDocumentFormat::DbiDocumentFormat(const U2DbiFactoryId &_id, const DocumentFormatId &_formatId, const QString &_formatName, const QStringList &exts, DocumentFormatFlags flags, QObject *p)
38     : DocumentFormat(p, _formatId, flags, exts) {
39     id = _id;
40     formatName = _formatName;
41     formatDescription = tr("ugenedb is a internal UGENE database file format");
42     supportedObjectTypes += GObjectTypes::ASSEMBLY;
43     supportedObjectTypes += GObjectTypes::MULTIPLE_CHROMATOGRAM_ALIGNMENT;
44     supportedObjectTypes += GObjectTypes::SEQUENCE;
45     supportedObjectTypes += GObjectTypes::ANNOTATION_TABLE;
46     formatFlags |= DocumentFormatFlag_NoPack;
47     formatFlags |= DocumentFormatFlag_NoFullMemoryLoad;
48     formatFlags |= DocumentFormatFlag_DirectWriteOperations;
49 }
50 
renameObjectsIfNamesEqual(QList<GObject * > & objs)51 static void renameObjectsIfNamesEqual(QList<GObject *> &objs) {
52     for (int i = 0; i < objs.size(); ++i) {
53         int howManyEquals = 0;
54         for (int j = i + 1; j < objs.size(); ++j) {
55             if (objs[i]->getGObjectName() == objs[j]->getGObjectName()) {
56                 objs[j]->setGObjectName(QString("%1 %2").arg(objs[j]->getGObjectName()).arg(++howManyEquals));
57             }
58         }
59     }
60 }
61 
loadDocument(IOAdapter * io,const U2DbiRef & dstDbiRef,const QVariantMap & fs,U2OpStatus & os)62 Document *DbiDocumentFormat::loadDocument(IOAdapter *io, const U2DbiRef &dstDbiRef, const QVariantMap &fs, U2OpStatus &os) {
63     // 1. open db
64     // 2. read all objects
65     // 3. if there is a DEEP_COPY_OBJECT hint, all objects are cloned to the db defined by dstDbiRef
66     // 3. close db
67     QString url = io->getURL().getURLString();
68     U2DbiRef srcDbiRef(id, url);
69     DbiConnection handle(srcDbiRef, true, os);
70     CHECK_OP(os, nullptr);
71 
72     U2ObjectDbi *odbi = handle.dbi->getObjectDbi();
73     QList<U2DataId> objectIds = odbi->getObjects(U2ObjectDbi::ROOT_FOLDER, 0, U2DbiOptions::U2_DBI_NO_LIMIT, os);
74     CHECK_OP(os, nullptr);
75 
76     QList<GObject *> objects = prepareObjects(handle, objectIds);
77     if (fs.value(DEEP_COPY_OBJECT, false).toBool()) {
78         QList<GObject *> clonedObjects = cloneObjects(objects, dstDbiRef, fs, os);
79         qDeleteAll(objects);
80         CHECK_OP_EXT(os, qDeleteAll(clonedObjects), nullptr);
81         objects = clonedObjects;
82     } else {
83         renameObjectsIfNamesEqual(objects);
84     }
85 
86     QString lockReason = handle.dbi->isReadOnly() ? "The database is read-only" : "";
87     Document *d = new Document(this, io->getFactory(), io->getURL(), dstDbiRef, objects, fs, lockReason);
88     d->setDocumentOwnsDbiResources(false);
89     d->setModificationTrack(false);
90 
91     return d;
92 }
93 
prepareObjects(DbiConnection & handle,const QList<U2DataId> & objectIds)94 QList<GObject *> DbiDocumentFormat::prepareObjects(DbiConnection &handle, const QList<U2DataId> &objectIds) {
95     QList<GObject *> objects;
96     U2EntityRef ref;
97     ref.dbiRef = handle.dbi->getDbiRef();
98 
99     QMap<U2DataId, GObject *> match;
100 
101     for (const U2DataId &dataId : qAsConst(objectIds)) {
102         U2OpStatus2Log status;
103         ref.entityId = dataId;
104 
105         U2Object object;
106         handle.dbi->getObjectDbi()->getObject(object, dataId, status);
107         CHECK_OPERATION(!status.isCoR(), continue);
108 
109         if (object.visualName.isEmpty()) {
110             object.visualName = "Unnamed object";
111         }
112 
113         GObject *gobject = GObjectUtils::createObject(ref.dbiRef, dataId, object.visualName);
114         CHECK_OPERATION(gobject != nullptr, continue);
115 
116         match[dataId] = gobject;
117         objects << gobject;
118     }
119 
120     if (handle.dbi->getObjectRelationsDbi() != nullptr) {
121         const QList<U2DataId> matchKeys = match.keys();
122         for (const U2DataId &dataId : qAsConst(matchKeys)) {
123             U2OpStatus2Log status;
124             GObject *srcObj = match.value(dataId, nullptr);
125             SAFE_POINT(srcObj != nullptr, "Source object is NULL", QList<GObject *>());
126             QList<GObjectRelation> gRelations;
127             QList<U2ObjectRelation> relations = handle.dbi->getObjectRelationsDbi()->getObjectRelations(dataId, status);
128             foreach (const U2ObjectRelation &r, relations) {
129                 GObject *relatedObject = match[r.referencedObject];
130                 if (relatedObject == nullptr) {
131                     continue;
132                 }
133                 // SANGER_TODO: dbiId - url, should not be left like this
134                 GObjectReference relatedRef(handle.dbi->getDbiId(), relatedObject->getGObjectName(), relatedObject->getGObjectType(), relatedObject->getEntityRef());
135                 GObjectRelation gRelation(relatedRef, r.relationRole);
136                 gRelations << gRelation;
137             }
138             srcObj->setObjectRelations(gRelations);
139         }
140     }
141 
142     return objects;
143 }
cloneObjects(const QList<GObject * > & srcObjects,const U2DbiRef & dstDbiRef,const QVariantMap & hints,U2OpStatus & os)144 QList<GObject *> DbiDocumentFormat::cloneObjects(const QList<GObject *> &srcObjects, const U2DbiRef &dstDbiRef, const QVariantMap &hints, U2OpStatus &os) {
145     QList<GObject *> clonedObjects;
146     CHECK_EXT(dstDbiRef.isValid(), os.setError(tr("Invalid destination database reference")), clonedObjects);
147 
148     int number = 0;
149     int total = srcObjects.size();
150     foreach (GObject *srcObject, srcObjects) {
151         U2OpStatusMapping mapping(number, (number + 1) / total);
152         U2OpStatusChildImpl childOs(&os, mapping);
153         GObject *clonedObject = srcObject->clone(dstDbiRef, childOs, hints);
154         CHECK_OP(os, clonedObjects);
155         clonedObjects << clonedObject;
156         number++;
157     }
158 
159     return clonedObjects;
160 }
161 
storeDocument(Document * d,IOAdapter * ioAdapter,U2OpStatus & os)162 void DbiDocumentFormat::storeDocument(Document *d, IOAdapter *ioAdapter, U2OpStatus &os) {
163     const QString url = ioAdapter->getURL().getURLString();
164 
165     const U2DbiRef dstDbiRef(id, url);
166     DbiConnection dstCon(dstDbiRef, true, os);
167     CHECK_OP(os, );
168     Q_UNUSED(dstCon);
169 
170     // The relations should be saved
171     QMap<GObject *, GObject *> clonedObjects;
172     QMap<GObjectReference, GObjectReference> match;
173 
174     foreach (GObject *object, d->getObjects()) {
175         if (!supportedObjectTypes.contains(object->getGObjectType())) {
176             continue;
177         }
178 
179         U2DbiRef srcDbiRef = object->getEntityRef().dbiRef;
180         if (srcDbiRef == dstDbiRef) {
181             // do not need to import
182             continue;
183         }
184 
185         GObject *resultObject = object->clone(dstDbiRef, os);
186         CHECK_OP(os, );
187 
188         clonedObjects[object] = resultObject;
189         match[GObjectReference(object, false)] = GObjectReference(url, resultObject->getGObjectName(), resultObject->getGObjectType(), resultObject->getEntityRef());
190     }
191 
192     foreach (GObject *initialObj, clonedObjects.keys()) {
193         GObject *cloned = clonedObjects[initialObj];
194         QList<GObjectRelation> relations;
195         QList<GObjectRelation> initialRelations = initialObj->getObjectRelations();
196         for (const GObjectRelation &r : qAsConst(initialRelations)) {
197             if (match.contains(r.ref)) {
198                 relations << GObjectRelation(match[r.ref], r.role);
199             }
200         }
201         cloned->setObjectRelations(relations);
202     }
203 
204     qDeleteAll(clonedObjects);
205     clonedObjects.clear();
206 }
207 
checkRawData(const QByteArray & rawData,const GUrl & url) const208 FormatCheckResult DbiDocumentFormat::checkRawData(const QByteArray &rawData, const GUrl &url) const {
209     U2DbiFactory *f = AppContext::getDbiRegistry()->getDbiFactoryById(id);
210     if (f != nullptr) {
211         QHash<QString, QString> props;
212         props[U2DbiOptions::U2_DBI_OPTION_URL] = url.getURLString();
213         U2OpStatusImpl os;
214         FormatCheckResult r = f->isValidDbi(props, rawData, os);
215         if (!os.hasError()) {
216             return r;
217         }
218     }
219     return FormatDetection_NotMatched;
220 }
221 
222 }  // namespace U2
223