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 "AnnotationTableObject.h"
23 
24 #include <QCoreApplication>
25 
26 #include <U2Core/AnnotationModification.h>
27 #include <U2Core/AnnotationTableObjectConstraints.h>
28 #include <U2Core/DocumentModel.h>
29 #include <U2Core/GHints.h>
30 #include <U2Core/L10n.h>
31 #include <U2Core/Timer.h>
32 #include <U2Core/U2DbiUtils.h>
33 #include <U2Core/U2FeatureUtils.h>
34 #include <U2Core/U2ObjectDbi.h>
35 #include <U2Core/U2OpStatusUtils.h>
36 #include <U2Core/U2SafePoints.h>
37 
38 #include "GObjectTypes.h"
39 
40 namespace U2 {
41 
AnnotationTableObject(const QString & objectName,const U2DbiRef & dbiRef,const QVariantMap & hintsMap)42 AnnotationTableObject::AnnotationTableObject(const QString &objectName, const U2DbiRef &dbiRef, const QVariantMap &hintsMap)
43     : GObject(GObjectTypes::ANNOTATION_TABLE, objectName, hintsMap) {
44     U2OpStatusImpl os;
45     const QString folder = hintsMap.value(DocumentFormat::DBI_FOLDER_HINT, U2ObjectDbi::ROOT_FOLDER).toString();
46     U2AnnotationTable table = U2FeatureUtils::createAnnotationTable(objectName, dbiRef, folder, os);
47     SAFE_POINT_OP(os, );
48 
49     entityRef = U2EntityRef(dbiRef, table.id);
50     rootGroup = new AnnotationGroup(table.rootFeature, AnnotationGroup::ROOT_GROUP_NAME, nullptr, this);
51     dataLoaded = true;
52 }
53 
AnnotationTableObject(const QString & objectName,const U2EntityRef & tableRef,const QVariantMap & hintsMap)54 AnnotationTableObject::AnnotationTableObject(const QString &objectName, const U2EntityRef &tableRef, const QVariantMap &hintsMap)
55     : GObject(GObjectTypes::ANNOTATION_TABLE, objectName, hintsMap), rootGroup(nullptr) {
56     entityRef = tableRef;
57 }
58 
~AnnotationTableObject()59 AnnotationTableObject::~AnnotationTableObject() {
60     delete rootGroup;
61 }
62 
getAnnotations() const63 QList<Annotation *> AnnotationTableObject::getAnnotations() const {
64     ensureDataLoaded();
65     return rootGroup->getAnnotations(true);
66 }
67 
hasAnnotations() const68 bool AnnotationTableObject::hasAnnotations() const {
69     ensureDataLoaded();
70     return rootGroup->hasAnnotations();
71 }
72 
getRootGroup()73 AnnotationGroup *AnnotationTableObject::getRootGroup() {
74     ensureDataLoaded();
75     return rootGroup;
76 }
77 
78 typedef QPair<AnnotationGroup *, QList<SharedAnnotationData>> AnnotationGroupData;
79 
addAnnotations(const QList<SharedAnnotationData> & annotations,const QString & groupName)80 QList<Annotation *> AnnotationTableObject::addAnnotations(const QList<SharedAnnotationData> &annotations, const QString &groupName) {
81     QList<Annotation *> result;
82     CHECK(!annotations.isEmpty(), result);
83 
84     ensureDataLoaded();
85 
86     if (groupName.isEmpty()) {
87         QMap<QString, AnnotationGroupData> group2Annotations;
88         for (const SharedAnnotationData &a : qAsConst(annotations)) {
89             if (!group2Annotations.contains(a->name)) {
90                 AnnotationGroup *group = rootGroup->getSubgroup(a->name, true);
91                 group2Annotations[a->name].first = group;
92             }
93             group2Annotations[a->name].second.append(a);
94         }
95         for (const AnnotationGroupData &groupData : qAsConst(group2Annotations)) {
96             result.append(groupData.first->addAnnotations(groupData.second));
97         }
98     } else {
99         AnnotationGroup *group = rootGroup->getSubgroup(groupName, true);
100         result.append(group->addAnnotations(annotations));
101     }
102     return result;
103 }
104 
removeAnnotations(const QList<Annotation * > & annotations)105 void AnnotationTableObject::removeAnnotations(const QList<Annotation *> &annotations) {
106     CHECK(!annotations.isEmpty(), );
107 
108     QMap<AnnotationGroup *, QList<Annotation *>> group2Annotations;
109     foreach (Annotation *ann, annotations) {
110         SAFE_POINT(ann->getGObject() == this, "Unexpected annotation detected", );
111         group2Annotations[ann->getGroup()].append(ann);
112     }
113 
114     foreach (AnnotationGroup *group, group2Annotations.keys()) {
115         group->removeAnnotations(group2Annotations[group]);
116     }
117 }
118 
clone(const U2DbiRef & ref,U2OpStatus & os,const QVariantMap & hints) const119 GObject *AnnotationTableObject::clone(const U2DbiRef &ref, U2OpStatus &os, const QVariantMap &hints) const {
120     ensureDataLoaded();
121 
122     GHintsDefaultImpl gHints(getGHintsMap());
123     gHints.setAll(hints);
124 
125     DbiOperationsBlock opBlock(ref, os);
126     CHECK_OP(os, nullptr);
127 
128     AnnotationTableObject *cln = new AnnotationTableObject(getGObjectName(), ref, gHints.getMap());
129     cln->setIndexInfo(getIndexInfo());
130 
131     QStringList subgroupPaths;
132     rootGroup->getSubgroupPaths(subgroupPaths);
133     AnnotationGroup *clonedRootGroup = cln->getRootGroup();
134     for (const QString &groupPath : qAsConst(subgroupPaths)) {
135         AnnotationGroup *originalGroup = rootGroup->getSubgroup(groupPath, false);
136         SAFE_POINT(originalGroup != nullptr, L10N::nullPointerError("annotation group"), nullptr);
137 
138         AnnotationGroup *clonedGroup = clonedRootGroup->getSubgroup(groupPath, true);
139         QList<SharedAnnotationData> groupData;
140         foreach (const Annotation *a, originalGroup->getAnnotations()) {
141             groupData.append(a->getData());
142         }
143         clonedGroup->addAnnotations(groupData);
144     }
145 
146     return cln;
147 }
148 
getAnnotationsByName(const QString & name) const149 QList<Annotation *> AnnotationTableObject::getAnnotationsByName(const QString &name) const {
150     QList<Annotation *> result;
151 
152     ensureDataLoaded();
153 
154     foreach (Annotation *a, getAnnotations()) {
155         if (a->getName() == name) {
156             result.append(a);
157         }
158     }
159 
160     return result;
161 }
162 
163 namespace {
164 
annotationIntersectsRange(const Annotation * a,const U2Region & range,bool contains)165 bool annotationIntersectsRange(const Annotation *a, const U2Region &range, bool contains) {
166     SAFE_POINT(nullptr != a, L10N::nullPointerError("annotation"), false);
167     if (!contains) {
168         foreach (const U2Region &r, a->getRegions()) {
169             if (r.intersects(range)) {
170                 return true;
171             }
172         }
173         return false;
174     } else {
175         foreach (const U2Region &r, a->getRegions()) {
176             if (!range.contains(r)) {
177                 return false;
178             }
179         }
180         return true;
181     }
182 }
183 
184 }  // namespace
185 
getAnnotationsByRegion(const U2Region & region,bool contains) const186 QList<Annotation *> AnnotationTableObject::getAnnotationsByRegion(const U2Region &region, bool contains) const {
187     QList<Annotation *> result;
188 
189     ensureDataLoaded();
190 
191     foreach (Annotation *a, getAnnotations()) {
192         if (annotationIntersectsRange(a, region, contains)) {
193             result.append(a);
194         }
195     }
196 
197     return result;
198 }
199 
getAnnotationsByType(const U2FeatureType featureType) const200 QList<Annotation *> AnnotationTableObject::getAnnotationsByType(const U2FeatureType featureType) const {
201     QList<Annotation *> result;
202 
203     ensureDataLoaded();
204 
205     foreach (Annotation *a, getAnnotations()) {
206         if (a->getType() == featureType) {
207             result.append(a);
208         }
209     }
210 
211     return result;
212 }
213 
checkConstraints(const GObjectConstraints * c) const214 bool AnnotationTableObject::checkConstraints(const GObjectConstraints *c) const {
215     auto ac = qobject_cast<const AnnotationTableObjectConstraints *>(c);
216     SAFE_POINT(ac != nullptr, "Invalid feature constraints", false);
217 
218     ensureDataLoaded();
219 
220     int fitSize = ac->sequenceSizeToFit;
221     SAFE_POINT(fitSize > 0, "Invalid sequence length provided!", false);
222     QList<Annotation *> annotations = getAnnotations();
223     for (const Annotation *a : qAsConst(annotations)) {
224         const QVector<U2Region> &regions = a->getRegions();
225         for (const U2Region &region : qAsConst(regions)) {
226             SAFE_POINT(region.startPos >= 0, "Invalid annotation region", false);
227             if (region.endPos() > fitSize) {
228                 return false;
229             }
230         }
231     }
232     return true;
233 }
234 
setGObjectName(const QString & newName)235 void AnnotationTableObject::setGObjectName(const QString &newName) {
236     CHECK(name != newName, );
237 
238     ensureDataLoaded();
239     GObject::setGObjectName(newName);
240 }
241 
getRootFeatureId() const242 U2DataId AnnotationTableObject::getRootFeatureId() const {
243     ensureDataLoaded();
244 
245     return rootGroup->id;
246 }
247 
emit_onAnnotationsAdded(const QList<Annotation * > & l)248 void AnnotationTableObject::emit_onAnnotationsAdded(const QList<Annotation *> &l) {
249     emit si_onAnnotationsAdded(l);
250 }
251 
emit_onAnnotationsModified(const AnnotationModification & annotationModification)252 void AnnotationTableObject::emit_onAnnotationsModified(const AnnotationModification &annotationModification) {
253     emit_onAnnotationsModified(QList<AnnotationModification>() << annotationModification);
254 }
255 
emit_onAnnotationsModified(const QList<AnnotationModification> & annotationModifications)256 void AnnotationTableObject::emit_onAnnotationsModified(const QList<AnnotationModification> &annotationModifications) {
257     emit si_onAnnotationsModified(annotationModifications);
258 }
259 
emit_onAnnotationsRemoved(const QList<Annotation * > & a)260 void AnnotationTableObject::emit_onAnnotationsRemoved(const QList<Annotation *> &a) {
261     emit si_onAnnotationsRemoved(a);
262 }
263 
emit_onGroupCreated(AnnotationGroup * g)264 void AnnotationTableObject::emit_onGroupCreated(AnnotationGroup *g) {
265     emit si_onGroupCreated(g);
266 }
267 
emit_onGroupRemoved(AnnotationGroup * p,AnnotationGroup * g)268 void AnnotationTableObject::emit_onGroupRemoved(AnnotationGroup *p, AnnotationGroup *g) {
269     emit si_onGroupRemoved(p, g);
270 }
271 
emit_onGroupRenamed(AnnotationGroup * g)272 void AnnotationTableObject::emit_onGroupRenamed(AnnotationGroup *g) {
273     emit si_onGroupRenamed(g);
274 }
275 
emit_onAnnotationsInGroupRemoved(const QList<Annotation * > & l,AnnotationGroup * gr)276 void AnnotationTableObject::emit_onAnnotationsInGroupRemoved(const QList<Annotation *> &l, AnnotationGroup *gr) {
277     emit si_onAnnotationsInGroupRemoved(l, gr);
278 }
279 
loadDataCore(U2OpStatus & os)280 void AnnotationTableObject::loadDataCore(U2OpStatus &os) {
281     SAFE_POINT(nullptr == rootGroup, "Annotation table is initialized unexpectedly", );
282 
283     U2AnnotationTable table = U2FeatureUtils::getAnnotationTable(entityRef, os);
284     CHECK_OP(os, );
285 
286     rootGroup = U2FeatureUtils::loadAnnotationTable(table.rootFeature, entityRef.dbiRef, this, os);
287 }
288 
289 }  // namespace U2
290