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