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 "SharedDbUrlUtils.h"
23 
24 #include <U2Core/AppContext.h>
25 #include <U2Core/Folder.h>
26 #include <U2Core/Settings.h>
27 #include <U2Core/U2DbiUtils.h>
28 #include <U2Core/U2ObjectDbi.h>
29 #include <U2Core/U2ObjectTypeUtils.h>
30 #include <U2Core/U2SafePoints.h>
31 
32 #include <U2Lang/BaseTypes.h>
33 
34 namespace U2 {
35 
36 const QString SharedDbUrlUtils::DB_PROVIDER_SEP = ">";
37 const QString SharedDbUrlUtils::DB_URL_SEP = ",";
38 const QString SharedDbUrlUtils::DB_OBJ_ID_SEP = ":";
39 
40 namespace {
41 
42 const QString CONN_SETTINGS_PATH = "/shared_database/recent_connections/";
43 
checkFolderPath(const QString & folderPath)44 bool checkFolderPath(const QString &folderPath) {
45     return !folderPath.isEmpty() && folderPath.startsWith(U2ObjectDbi::ROOT_FOLDER);
46 }
47 
disassembleObjectId(const QString & objUrl,QStringList & idParts)48 bool disassembleObjectId(const QString &objUrl, QStringList &idParts) {
49     SAFE_POINT(SharedDbUrlUtils::isDbObjectUrl(objUrl), "Invalid shared DB object URL", false);
50     const QString fullObjId = objUrl.mid(objUrl.indexOf(SharedDbUrlUtils::DB_URL_SEP) + 1);
51     idParts.clear();
52     const int firstSepPos = fullObjId.indexOf(SharedDbUrlUtils::DB_OBJ_ID_SEP);
53     SAFE_POINT(-1 != firstSepPos, "Invalid object DB URL", false);
54     idParts.append(fullObjId.left(firstSepPos));
55 
56     const int secondSepPos = fullObjId.indexOf(SharedDbUrlUtils::DB_OBJ_ID_SEP, firstSepPos + 1);
57     SAFE_POINT(-1 != secondSepPos, "Invalid object DB URL", false);
58     SAFE_POINT(fullObjId.size() - 2 >= secondSepPos, "Invalid object DB URL", false);
59     idParts.append(fullObjId.mid(firstSepPos + 1, secondSepPos - firstSepPos - 1));
60     idParts.append(fullObjId.mid(secondSepPos + 1));
61 
62     return true;
63 }
64 
str2Int(const QString & str,qint64 & res)65 bool str2Int(const QString &str, qint64 &res) {
66     bool conversionOk = false;
67     res = str.toLongLong(&conversionOk);
68     return conversionOk;
69 }
70 
str2DataType(const QString & str,U2DataType & res)71 bool str2DataType(const QString &str, U2DataType &res) {
72     bool conversionOk = false;
73     // hope that "U2DataType" typedef won't change
74     SAFE_POINT(sizeof(U2DataType) == sizeof(qint16), "Unexpected data type detected", false);
75     res = str.toUShort(&conversionOk);
76     return conversionOk;
77 }
78 
objId2Str(const U2DataId & objId,const QString & objName)79 QString objId2Str(const U2DataId &objId, const QString &objName) {
80     const qint64 idNumber = U2DbiUtils::toDbiId(objId);
81     const U2DataType objType = U2DbiUtils::toType(objId);
82     return QString::number(idNumber) + SharedDbUrlUtils::DB_OBJ_ID_SEP + QString::number(objType) + SharedDbUrlUtils::DB_OBJ_ID_SEP + objName;
83 }
84 
85 }  // namespace
86 
createDbUrl(const U2DbiRef & dbiRef)87 QString SharedDbUrlUtils::createDbUrl(const U2DbiRef &dbiRef) {
88     SAFE_POINT(dbiRef.isValid(), "Invalid DBI reference", QString());
89     return dbiRef.dbiFactoryId + DB_PROVIDER_SEP + dbiRef.dbiId;
90 }
91 
validateDbUrl(const QString & dbUrl)92 bool SharedDbUrlUtils::validateDbUrl(const QString &dbUrl) {
93     QString hostName;
94     int portNum;
95     QString dbName;
96     return U2DbiUtils::parseDbiUrl(dbUrl, hostName, portNum, dbName);
97 }
98 
createDbFolderUrl(const Folder & folder,const U2DataType & compatibleType)99 QString SharedDbUrlUtils::createDbFolderUrl(const Folder &folder, const U2DataType &compatibleType) {
100     Document *doc = folder.getDocument();
101     CHECK(nullptr != doc, QString());
102     const U2DbiRef dbiRef = doc->getDbiRef();
103     CHECK(dbiRef.isValid(), QString());
104 
105     const QString folderPath = folder.getFolderPath();
106     CHECK(checkFolderPath(folderPath), QString());
107 
108     return dbiRef.dbiFactoryId + DB_PROVIDER_SEP + dbiRef.dbiId + DB_URL_SEP + QString::number(compatibleType) + DB_OBJ_ID_SEP + folderPath;
109 }
110 
createDbFolderUrl(const QString & dbUrl,const QString & path,const U2DataType & compatibleType)111 QString SharedDbUrlUtils::createDbFolderUrl(const QString &dbUrl, const QString &path, const U2DataType &compatibleType) {
112     SAFE_POINT(validateDbUrl(dbUrl), "Invalid DB URL", QString());
113     SAFE_POINT(!path.isEmpty(), "Invalid DB folder path", QString());
114 
115     return dbUrl + DB_URL_SEP + QString::number(compatibleType) + DB_OBJ_ID_SEP + path;
116 }
117 
isDbFolderUrl(const QString & url)118 bool SharedDbUrlUtils::isDbFolderUrl(const QString &url) {
119     const int dbProviderSepPos = url.indexOf(DB_PROVIDER_SEP);
120     if (dbProviderSepPos < 1) {
121         return false;
122     }
123 
124     const int dbUrlSepPos = url.indexOf(DB_URL_SEP, dbProviderSepPos);
125     if (-1 == dbUrlSepPos) {
126         return false;
127     }
128 
129     const int dbObjTypeSepPos = url.indexOf(DB_OBJ_ID_SEP, dbUrlSepPos);
130     if (-1 == dbObjTypeSepPos || url.size() - 2 < dbObjTypeSepPos || U2ObjectDbi::ROOT_FOLDER != url[dbObjTypeSepPos + 1]) {
131         return false;
132     } else {
133         return true;
134     }
135 }
136 
createDbObjectUrl(const GObject * obj)137 QString SharedDbUrlUtils::createDbObjectUrl(const GObject *obj) {
138     SAFE_POINT(nullptr != obj, "Invalid object", QString());
139     const U2EntityRef entityRef = obj->getEntityRef();
140     return createDbObjectUrl(entityRef.dbiRef, entityRef.entityId, obj->getGObjectName());
141 }
142 
createDbObjectUrl(const U2DbiRef & dbiRef,const U2DataId & objId,const QString & objName)143 QString SharedDbUrlUtils::createDbObjectUrl(const U2DbiRef &dbiRef, const U2DataId &objId, const QString &objName) {
144     SAFE_POINT(dbiRef.isValid(), "Invalid database reference detected", QString());
145     SAFE_POINT(!objId.isEmpty(), "Invalid DB object reference detected", QString());
146     SAFE_POINT(!objName.isEmpty(), "Invalid DB object name detected", QString());
147 
148     return dbiRef.dbiFactoryId + DB_PROVIDER_SEP + dbiRef.dbiId + DB_URL_SEP + objId2Str(objId, objName);
149 }
150 
createDbObjectUrl(const QString & dbUrl,qint64 objId,const QString & objType,const QString & objName)151 QString SharedDbUrlUtils::createDbObjectUrl(const QString &dbUrl, qint64 objId, const QString &objType, const QString &objName) {
152     SAFE_POINT(validateDbUrl(dbUrl), "Invalid DB URL", QString());
153     const U2DataType objDataType = BaseTypes::toDataType(objType);
154     SAFE_POINT(U2Type::Unknown != objDataType, "Invalid object type detected", QString());
155     SAFE_POINT(!objName.isEmpty(), "Invalid DB object name", QString());
156 
157     return dbUrl + DB_URL_SEP + QString::number(objId) + DB_OBJ_ID_SEP + QString::number(objDataType) + DB_OBJ_ID_SEP + objName;
158 }
159 
160 namespace {
161 
getSeparatorIndices(const QString & entityUrl,int & dbProviderSepPos,int & dbUrlSepPos)162 bool getSeparatorIndices(const QString &entityUrl, int &dbProviderSepPos, int &dbUrlSepPos) {
163     dbProviderSepPos = entityUrl.indexOf(SharedDbUrlUtils::DB_PROVIDER_SEP);
164     CHECK(dbProviderSepPos >= 1, false);
165 
166     dbUrlSepPos = entityUrl.indexOf(SharedDbUrlUtils::DB_URL_SEP, dbProviderSepPos);
167     CHECK(dbUrlSepPos != -1, false);
168 
169     return true;
170 }
171 
172 }  // namespace
173 
isDbObjectUrl(const QString & url)174 bool SharedDbUrlUtils::isDbObjectUrl(const QString &url) {
175     int dbProviderSepPos = -1;
176     int dbUrlSepPos = -1;
177     CHECK(getSeparatorIndices(url, dbProviderSepPos, dbUrlSepPos), false);
178 
179     const int dbObjIdSepPos = url.indexOf(DB_OBJ_ID_SEP, dbUrlSepPos);
180     CHECK(-1 != dbObjIdSepPos, false);
181 
182     const int dbObjTypeSepPos = url.indexOf(DB_OBJ_ID_SEP, dbObjIdSepPos + 1);
183     if (-1 == dbObjTypeSepPos || url.size() - 2 < dbObjTypeSepPos) {
184         return false;
185     } else {
186         return true;
187     }
188 }
189 
getDbRefFromEntityUrl(const QString & url)190 U2DbiRef SharedDbUrlUtils::getDbRefFromEntityUrl(const QString &url) {
191     int dbProviderSepPos = -1;
192     int dbUrlSepPos = -1;
193     getSeparatorIndices(url, dbProviderSepPos, dbUrlSepPos);
194     CHECK(-1 != dbProviderSepPos, U2DbiRef());
195 
196     return U2DbiRef(url.left(dbProviderSepPos), url.mid(dbProviderSepPos + 1, -1 != dbUrlSepPos ? dbUrlSepPos - dbProviderSepPos - 1 : -1));
197 }
198 
getKnownDbs()199 QVariantMap SharedDbUrlUtils::getKnownDbs() {
200     QVariantMap result;
201     Settings *appSettings = AppContext::getSettings();
202     const QStringList recentList = appSettings->getAllKeys(CONN_SETTINGS_PATH);
203     foreach (const QString &shortName, recentList) {
204         // TODO: fix this when other DB providers emerge
205         result.insert(shortName, MYSQL_DBI_ID + DB_PROVIDER_SEP + appSettings->getValue(CONN_SETTINGS_PATH + shortName).toString());
206     }
207     return result;
208 }
209 
getDbUrlFromEntityUrl(const QString & url)210 QString SharedDbUrlUtils::getDbUrlFromEntityUrl(const QString &url) {
211     int dbProviderSepPos = -1;
212     int dbUrlSepPos = -1;
213     CHECK(getSeparatorIndices(url, dbProviderSepPos, dbUrlSepPos), QString());
214 
215     return url.left(dbUrlSepPos);
216 }
217 
getDbShortNameFromEntityUrl(const QString & url)218 QString SharedDbUrlUtils::getDbShortNameFromEntityUrl(const QString &url) {
219     const QString dbUrl = getDbRefFromEntityUrl(url).dbiId;
220     CHECK(!dbUrl.isEmpty(), url);
221     Settings *appSettings = AppContext::getSettings();
222     const QStringList recentList = appSettings->getAllKeys(CONN_SETTINGS_PATH);
223     foreach (const QString &shortName, recentList) {
224         if (dbUrl == appSettings->getValue(CONN_SETTINGS_PATH + shortName).toString()) {
225             return shortName;
226         }
227     }
228     return dbUrl;
229 }
230 
saveNewDbConnection(const QString & connectionName,const QString & connectionUrl)231 void SharedDbUrlUtils::saveNewDbConnection(const QString &connectionName, const QString &connectionUrl) {
232     SAFE_POINT(!connectionName.isEmpty() && !connectionUrl.isEmpty(), "Unexpected DB connection", );
233     AppContext::getSettings()->setValue(CONN_SETTINGS_PATH + connectionName, connectionUrl);
234 }
235 
getObjectIdByUrl(const QString & objUrl)236 U2DataId SharedDbUrlUtils::getObjectIdByUrl(const QString &objUrl) {
237     QStringList objIdParts;
238     CHECK(disassembleObjectId(objUrl, objIdParts), U2DataId());
239 
240     qint64 idNumber = 0;
241     CHECK(str2Int(objIdParts[0], idNumber), U2DataId());
242 
243     U2DataType dataType = U2Type::Unknown;
244     CHECK(str2DataType(objIdParts[1], dataType), U2DataId());
245 
246     return U2DbiUtils::toU2DataId(idNumber, dataType);
247 }
248 
getObjectNumberIdByUrl(const QString & url)249 qint64 SharedDbUrlUtils::getObjectNumberIdByUrl(const QString &url) {
250     QStringList objIdParts;
251     CHECK(disassembleObjectId(url, objIdParts), -1);
252 
253     qint64 idNumber = 0;
254     CHECK(str2Int(objIdParts[0], idNumber), -1);
255 
256     return idNumber;
257 }
258 
getDbObjectTypeByUrl(const QString & objUrl)259 GObjectType SharedDbUrlUtils::getDbObjectTypeByUrl(const QString &objUrl) {
260     QStringList objIdParts;
261     CHECK(disassembleObjectId(objUrl, objIdParts), GObjectType());
262 
263     U2DataType dataType = U2Type::Unknown;
264     CHECK(str2DataType(objIdParts[1], dataType), GObjectType());
265 
266     return U2ObjectTypeUtils::toGObjectType(dataType);
267 }
268 
getDbSerializedObjectTypeByUrl(const QString & url)269 QString SharedDbUrlUtils::getDbSerializedObjectTypeByUrl(const QString &url) {
270     QStringList objIdParts;
271     CHECK(disassembleObjectId(url, objIdParts), QString());
272 
273     U2DataType dataType = U2Type::Unknown;
274     CHECK(str2DataType(objIdParts[1], dataType), QString());
275     return BaseTypes::toTypeId(dataType);
276 }
277 
getDbObjectNameByUrl(const QString & objUrl)278 QString SharedDbUrlUtils::getDbObjectNameByUrl(const QString &objUrl) {
279     QStringList objIdParts;
280     CHECK(disassembleObjectId(objUrl, objIdParts), QString());
281 
282     return objIdParts[2];
283 }
284 
getObjEntityRefByUrl(const QString & url)285 U2EntityRef SharedDbUrlUtils::getObjEntityRefByUrl(const QString &url) {
286     return U2EntityRef(getDbRefFromEntityUrl(url), getObjectIdByUrl(url));
287 }
288 
getDbFolderPathByUrl(const QString & url)289 QString SharedDbUrlUtils::getDbFolderPathByUrl(const QString &url) {
290     SAFE_POINT(isDbFolderUrl(url), "Invalid DB folder URL", QString());
291 
292     const QString path = url.mid(url.indexOf(DB_OBJ_ID_SEP, url.indexOf(DB_URL_SEP) + 1) + 1);
293     SAFE_POINT(!path.isEmpty(), "Invalid shared DB folder URL", QString());
294 
295     return path;
296 }
297 
298 namespace {
299 
getDbFolderDataTypeStr(const QString & url)300 QString getDbFolderDataTypeStr(const QString &url) {
301     SAFE_POINT(SharedDbUrlUtils::isDbFolderUrl(url), "Invalid DB folder URL", QString());
302 
303     const int dbUrlSepPos = url.indexOf(SharedDbUrlUtils::DB_URL_SEP);
304     const QString typeStr = url.mid(dbUrlSepPos + 1, url.indexOf(SharedDbUrlUtils::DB_OBJ_ID_SEP, dbUrlSepPos + 1) - dbUrlSepPos - 1);
305     SAFE_POINT(!typeStr.isEmpty(), "Invalid shared DB folder data type", QString());
306 
307     return typeStr;
308 }
309 
310 }  // namespace
311 
getDbFolderDataTypeByUrl(const QString & url)312 U2DataType SharedDbUrlUtils::getDbFolderDataTypeByUrl(const QString &url) {
313     bool ok = false;
314     const U2DataType result = getDbFolderDataTypeStr(url).toUShort(&ok);
315     SAFE_POINT(ok, "Invalid DB folder data type", U2Type::Unknown);
316     return result;
317 }
318 
getDbFolderSerializedDataTypeByUrl(const QString & url)319 QString SharedDbUrlUtils::getDbFolderSerializedDataTypeByUrl(const QString &url) {
320     return BaseTypes::toTypeId(getDbFolderDataTypeByUrl(url));
321 }
322 
323 }  // namespace U2
324