Changeset View
Changeset View
Standalone View
Standalone View
src/lib/plugin/desktopfileparser.cpp
Show First 20 Lines • Show All 200 Lines • ▼ Show 20 Line(s) | 200 | if (line == "[Desktop Entry]") { | |||
---|---|---|---|---|---|
201 | return true; | 201 | return true; | ||
202 | } | 202 | } | ||
203 | } | 203 | } | ||
204 | qCWarning(DESKTOPPARSER) << "Error: Could not find [Desktop Entry] group in " << path; | 204 | qCWarning(DESKTOPPARSER) << "Error: Could not find [Desktop Entry] group in " << path; | ||
205 | return false; | 205 | return false; | ||
206 | } | 206 | } | ||
207 | 207 | | |||
208 | 208 | | |||
209 | QByteArray readTypeEntryForCurrentGroup(QFile &df, QByteArray *nextGroup) | 209 | QByteArray readTypeEntryForCurrentGroup(QFile &df, QByteArray *nextGroup, QByteArray *pName) | ||
210 | { | 210 | { | ||
211 | QByteArray group = *nextGroup; | 211 | QByteArray group = *nextGroup; | ||
212 | QByteArray type; | 212 | QByteArray type; | ||
213 | if (group.isEmpty()) { | 213 | if (group.isEmpty()) { | ||
214 | qCWarning(DESKTOPPARSER, "Read empty .desktop file group name! Invalid file?"); | 214 | qCWarning(DESKTOPPARSER, "Read empty .desktop file group name! Invalid file?"); | ||
215 | } | 215 | } | ||
216 | while (!df.atEnd()) { | 216 | while (!df.atEnd()) { | ||
217 | QByteArray line = df.readLine().trimmed(); | 217 | QByteArray line = df.readLine().trimmed(); | ||
Show All 11 Lines | 222 | if (line.startsWith('[')) { | |||
229 | break; | 229 | break; | ||
230 | } | 230 | } | ||
231 | 231 | | |||
232 | const static QRegularExpression typeEntryRegex( | 232 | const static QRegularExpression typeEntryRegex( | ||
233 | QStringLiteral("^Type\\s*=\\s*(.*)$")); | 233 | QStringLiteral("^Type\\s*=\\s*(.*)$")); | ||
234 | const auto match = typeEntryRegex.match(QString::fromUtf8(line)); | 234 | const auto match = typeEntryRegex.match(QString::fromUtf8(line)); | ||
235 | if (match.hasMatch()) { | 235 | if (match.hasMatch()) { | ||
236 | type = match.captured(1).toUtf8(); | 236 | type = match.captured(1).toUtf8(); | ||
237 | } else if (pName) { | ||||
238 | const static QRegularExpression nameEntryRegex( | ||||
239 | QStringLiteral("^X-KDE-ServiceType\\s*=\\s*(.*)$")); | ||||
240 | const auto nameMatch = nameEntryRegex.match(QString::fromUtf8(line)); | ||||
241 | if (nameMatch.hasMatch()) { | ||||
242 | *pName = nameMatch.captured(1).toUtf8(); | ||||
243 | } | ||||
237 | } | 244 | } | ||
238 | } | 245 | } | ||
239 | return type; | 246 | return type; | ||
240 | } | 247 | } | ||
241 | 248 | | |||
242 | bool tokenizeKeyValue(QFile &df, const QString &src, QByteArray &key, QString &value, int &lineNr) | 249 | bool tokenizeKeyValue(QFile &df, const QString &src, QByteArray &key, QString &value, int &lineNr) | ||
243 | { | 250 | { | ||
244 | const QByteArray line = df.readLine().trimmed(); | 251 | const QByteArray line = df.readLine().trimmed(); | ||
Show All 40 Lines | |||||
285 | } | 292 | } | ||
286 | 293 | | |||
287 | static QString locateRelativeServiceType(const QString &relPath) | 294 | static QString locateRelativeServiceType(const QString &relPath) | ||
288 | { | 295 | { | ||
289 | return QStandardPaths::locate(QStandardPaths::GenericDataLocation, | 296 | return QStandardPaths::locate(QStandardPaths::GenericDataLocation, | ||
290 | QStringLiteral("kservicetypes5/") + relPath); | 297 | QStringLiteral("kservicetypes5/") + relPath); | ||
291 | } | 298 | } | ||
292 | 299 | | |||
293 | static QVector<CustomPropertyDefinition>* parseServiceTypesFile(const QString &inputPath) | 300 | static ServiceTypeDefinition* parseServiceTypesFile(const QString &inputPath) | ||
294 | { | 301 | { | ||
295 | int lineNr = 0; | 302 | int lineNr = 0; | ||
296 | QString path = inputPath; | 303 | QString path = inputPath; | ||
297 | if (QDir::isRelativePath(path)) { | 304 | if (QDir::isRelativePath(path)) { | ||
298 | path = locateRelativeServiceType(path); | 305 | path = locateRelativeServiceType(path); | ||
299 | QString rcPath; | 306 | QString rcPath; | ||
300 | if (path.isEmpty()) { | 307 | if (path.isEmpty()) { | ||
301 | rcPath = QStringLiteral(":/kservicetypes5/") + inputPath; | 308 | rcPath = QStringLiteral(":/kservicetypes5/") + inputPath; | ||
Show All 9 Lines | |||||
311 | QFile df(path); | 318 | QFile df(path); | ||
312 | if (!df.exists()) { | 319 | if (!df.exists()) { | ||
313 | qCCritical(DESKTOPPARSER) << "Service type file" << path << "does not exist"; | 320 | qCCritical(DESKTOPPARSER) << "Service type file" << path << "does not exist"; | ||
314 | return nullptr; | 321 | return nullptr; | ||
315 | } | 322 | } | ||
316 | if (!readUntilDesktopEntryGroup(df, path, lineNr)) { | 323 | if (!readUntilDesktopEntryGroup(df, path, lineNr)) { | ||
317 | return nullptr; | 324 | return nullptr; | ||
318 | } | 325 | } | ||
319 | QVector<CustomPropertyDefinition> result; | 326 | ServiceTypeDefinition result; | ||
320 | // TODO: passing nextGroup by pointer is inefficient as it will make deep copies every time | 327 | // TODO: passing nextGroup by pointer is inefficient as it will make deep copies every time | ||
321 | // Not exactly performance critical code though so low priority | 328 | // Not exactly performance critical code though so low priority | ||
322 | QByteArray nextGroup = "Desktop Entry"; | 329 | QByteArray nextGroup = "Desktop Entry"; | ||
323 | // Type must be ServiceType now | 330 | // Type must be ServiceType now | ||
324 | QByteArray typeStr = readTypeEntryForCurrentGroup(df, &nextGroup); | 331 | QByteArray typeStr = readTypeEntryForCurrentGroup(df, &nextGroup, &result.m_serviceTypeName); | ||
325 | if (typeStr != QByteArrayLiteral("ServiceType")) { | 332 | if (typeStr != QByteArrayLiteral("ServiceType")) { | ||
326 | qCWarning(DESKTOPPARSER) << path << "is not a valid service type: Type entry should be 'ServiceType', got" | 333 | qCWarning(DESKTOPPARSER) << path << "is not a valid service type: Type entry should be 'ServiceType', got" | ||
327 | << typeStr << "instead."; | 334 | << typeStr << "instead."; | ||
328 | return nullptr; | 335 | return nullptr; | ||
329 | } | 336 | } | ||
330 | while (!df.atEnd()) { | 337 | while (!df.atEnd()) { | ||
331 | QByteArray currentGroup = nextGroup; | 338 | QByteArray currentGroup = nextGroup; | ||
332 | typeStr = readTypeEntryForCurrentGroup(df, &nextGroup); | 339 | typeStr = readTypeEntryForCurrentGroup(df, &nextGroup, nullptr); | ||
333 | if (!currentGroup.startsWith(QByteArrayLiteral("PropertyDef::"))) { | 340 | if (!currentGroup.startsWith(QByteArrayLiteral("PropertyDef::"))) { | ||
334 | qCWarning(DESKTOPPARSER) << "Skipping invalid group" << currentGroup << "in service type" << path; | 341 | qCWarning(DESKTOPPARSER) << "Skipping invalid group" << currentGroup << "in service type" << path; | ||
335 | continue; | 342 | continue; | ||
336 | } | 343 | } | ||
337 | if (typeStr.isEmpty()) { | 344 | if (typeStr.isEmpty()) { | ||
338 | qCWarning(DESKTOPPARSER) << "Could not find Type= key in group" << currentGroup; | 345 | qCWarning(DESKTOPPARSER) << "Could not find Type= key in group" << currentGroup; | ||
339 | continue; | 346 | continue; | ||
340 | } | 347 | } | ||
341 | QByteArray propertyName = currentGroup.mid(qstrlen("PropertyDef::")); | 348 | QByteArray propertyName = currentGroup.mid(qstrlen("PropertyDef::")); | ||
342 | QVariant::Type type = QVariant::nameToType(typeStr.constData()); | 349 | QVariant::Type type = QVariant::nameToType(typeStr.constData()); | ||
343 | switch (type) { | 350 | switch (type) { | ||
344 | case QVariant::String: | 351 | case QVariant::String: | ||
345 | case QVariant::StringList: | 352 | case QVariant::StringList: | ||
346 | case QVariant::Int: | 353 | case QVariant::Int: | ||
347 | case QVariant::Double: | 354 | case QVariant::Double: | ||
348 | case QVariant::Bool: | 355 | case QVariant::Bool: | ||
349 | qCDebug(DESKTOPPARSER) << "Found property definition" << propertyName << "with type" << typeStr; | 356 | qCDebug(DESKTOPPARSER) << "Found property definition" << propertyName << "with type" << typeStr; | ||
350 | result.push_back(CustomPropertyDefinition(propertyName, type)); | 357 | result.m_propertyDefs.push_back(CustomPropertyDefinition(propertyName, type)); | ||
351 | break; | 358 | break; | ||
352 | case QVariant::Invalid: | 359 | case QVariant::Invalid: | ||
353 | qCWarning(DESKTOPPARSER) << "Property type" << typeStr << "is not a known QVariant type." | 360 | qCWarning(DESKTOPPARSER) << "Property type" << typeStr << "is not a known QVariant type." | ||
354 | " Found while parsing property definition for" << propertyName << "in" << path; | 361 | " Found while parsing property definition for" << propertyName << "in" << path; | ||
355 | break; | 362 | break; | ||
356 | default: | 363 | default: | ||
357 | qCWarning(DESKTOPPARSER) << "Unsupported property type" << typeStr << "for property" << propertyName | 364 | qCWarning(DESKTOPPARSER) << "Unsupported property type" << typeStr << "for property" << propertyName | ||
358 | << "found in" << path << "\nOnly QString, QStringList, int, double and bool are supported."; | 365 | << "found in" << path << "\nOnly QString, QStringList, int, double and bool are supported."; | ||
359 | } | 366 | } | ||
360 | } | 367 | } | ||
361 | return new QVector<CustomPropertyDefinition>(result); | 368 | return new ServiceTypeDefinition(result); | ||
362 | } | 369 | } | ||
363 | 370 | | |||
364 | // a lazy map of service type definitions | 371 | // a lazy map of service type definitions | ||
365 | typedef QCache<QString, QVector<CustomPropertyDefinition>> ServiceTypesHash; | 372 | typedef QCache<QString /*path*/, ServiceTypeDefinition> ServiceTypesHash; | ||
366 | Q_GLOBAL_STATIC(ServiceTypesHash, s_serviceTypes) | 373 | Q_GLOBAL_STATIC(ServiceTypesHash, s_serviceTypes) | ||
367 | // access must be guarded by serviceTypesMutex as this code could be executed by multiple threads | 374 | // access must be guarded by serviceTypesMutex as this code could be executed by multiple threads | ||
368 | QBasicMutex s_serviceTypesMutex; | 375 | QBasicMutex s_serviceTypesMutex; | ||
369 | } // end of anonymous namespace | 376 | } // end of anonymous namespace | ||
370 | 377 | | |||
371 | 378 | | |||
372 | ServiceTypeDefinition::ServiceTypeDefinition() | 379 | ServiceTypeDefinitions ServiceTypeDefinitions::fromFiles(const QStringList &paths) | ||
373 | { | | |||
374 | } | | |||
375 | | ||||
376 | ServiceTypeDefinition ServiceTypeDefinition::fromFiles(const QStringList &paths) | | |||
377 | { | 380 | { | ||
378 | ServiceTypeDefinition ret; | 381 | ServiceTypeDefinitions ret; | ||
379 | ret.m_definitions.reserve(paths.size()); | 382 | ret.m_definitions.reserve(paths.size()); | ||
380 | // as we might modify the cache we need to acquire a mutex here | 383 | // as we might modify the cache we need to acquire a mutex here | ||
381 | for (const QString &serviceTypePath : paths) { | 384 | for (const QString &serviceTypePath : paths) { | ||
382 | bool added = ret.addFile(serviceTypePath); | 385 | bool added = ret.addFile(serviceTypePath); | ||
383 | if (!added) { | 386 | if (!added) { | ||
384 | #ifdef BUILDING_DESKTOPTOJSON_TOOL | 387 | #ifdef BUILDING_DESKTOPTOJSON_TOOL | ||
385 | exit(1); // this is a fatal error when using kcoreaddons_desktop_to_json() | 388 | exit(1); // this is a fatal error when using kcoreaddons_desktop_to_json() | ||
386 | #endif | 389 | #endif | ||
387 | } | 390 | } | ||
388 | } | 391 | } | ||
389 | return ret; | 392 | return ret; | ||
390 | } | 393 | } | ||
391 | 394 | | |||
392 | bool ServiceTypeDefinition::addFile(const QString& path) | 395 | bool ServiceTypeDefinitions::addFile(const QString& path) | ||
393 | { | 396 | { | ||
394 | QMutexLocker lock(&s_serviceTypesMutex); | 397 | QMutexLocker lock(&s_serviceTypesMutex); | ||
395 | QVector<CustomPropertyDefinition>* def = s_serviceTypes->object(path); | 398 | ServiceTypeDefinition* def = s_serviceTypes->object(path); | ||
396 | 399 | | |||
397 | if (def) { | 400 | if (def) { | ||
398 | // in cache but we still must make our own copy | 401 | // in cache but we still must make our own copy | ||
399 | m_definitions << *def; | 402 | m_definitions << *def; | ||
400 | } | 403 | } else { | ||
anthonyfieroni: Where def is deleted? | |||||
dfaure: It's owned by the QCache. | |||||
401 | else { | | |||
402 | // not found in cache -> we need to parse the file | 404 | // not found in cache -> we need to parse the file | ||
403 | qCDebug(DESKTOPPARSER) << "About to parse service type file" << path; | 405 | qCDebug(DESKTOPPARSER) << "About to parse service type file" << path; | ||
404 | def = parseServiceTypesFile(path); | 406 | def = parseServiceTypesFile(path); | ||
405 | if (!def) { | 407 | if (!def) { | ||
406 | return false; | 408 | return false; | ||
407 | } | 409 | } | ||
408 | 410 | | |||
409 | m_definitions << *def; // This must *precede* insert call, insert might delete | 411 | m_definitions << *def; // This must *precede* insert call, insert might delete | ||
410 | s_serviceTypes->insert(path, def); | 412 | s_serviceTypes->insert(path, def); | ||
411 | } | 413 | } | ||
412 | return true; | 414 | return true; | ||
413 | } | 415 | } | ||
414 | 416 | | |||
415 | QJsonValue ServiceTypeDefinition::parseValue(const QByteArray &key, const QString &value) const | 417 | QJsonValue ServiceTypeDefinitions::parseValue(const QByteArray &key, const QString &value) const | ||
416 | { | 418 | { | ||
417 | // check whether the key has a special type associated with it | 419 | // check whether the key has a special type associated with it | ||
418 | for (const CustomPropertyDefinition &propertyDef : qAsConst(m_definitions)) { | 420 | for (const auto &def : m_definitions) { | ||
421 | for (const CustomPropertyDefinition &propertyDef : def.m_propertyDefs) { | ||||
419 | if (propertyDef.key == key) { | 422 | if (propertyDef.key == key) { | ||
420 | return propertyDef.fromString(value); | 423 | return propertyDef.fromString(value); | ||
421 | } | 424 | } | ||
422 | } | 425 | } | ||
426 | } | ||||
423 | qCDebug(DESKTOPPARSER) << "Unknown property type for key" << key << "-> falling back to string"; | 427 | qCDebug(DESKTOPPARSER) << "Unknown property type for key" << key << "-> falling back to string"; | ||
424 | return QJsonValue(value); | 428 | return QJsonValue(value); | ||
425 | } | 429 | } | ||
426 | 430 | | |||
427 | void DesktopFileParser::convertToJson(const QByteArray &key, ServiceTypeDefinition &serviceTypes, const QString &value, | 431 | bool ServiceTypeDefinitions::hasServiceType(const QByteArray &serviceTypeName) const | ||
432 | { | ||||
433 | const auto it = std::find_if(m_definitions.begin(), m_definitions.end(), [&serviceTypeName](const ServiceTypeDefinition &def) { | ||||
434 | return def.m_serviceTypeName == serviceTypeName; | ||||
435 | }); | ||||
436 | return it != m_definitions.end(); | ||||
437 | } | ||||
438 | | ||||
439 | void DesktopFileParser::convertToJson(const QByteArray &key, ServiceTypeDefinitions &serviceTypes, const QString &value, | ||||
428 | QJsonObject &json, QJsonObject &kplugin, int lineNr) | 440 | QJsonObject &json, QJsonObject &kplugin, int lineNr) | ||
429 | { | 441 | { | ||
430 | /* The following keys are recognized (and added to a "KPlugin" object): | 442 | /* The following keys are recognized (and added to a "KPlugin" object): | ||
431 | 443 | | |||
432 | Icon=mypluginicon | 444 | Icon=mypluginicon | ||
433 | Type=Service | 445 | Type=Service | ||
434 | ServiceTypes=KPluginInfo | 446 | ServiceTypes=KPluginInfo | ||
435 | MimeType=text/plain;image/png | 447 | MimeType=text/plain;image/png | ||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Line(s) | 533 | } else { | |||
523 | json[QString::fromUtf8(key)] = serviceTypes.parseValue(key, value); | 535 | json[QString::fromUtf8(key)] = serviceTypes.parseValue(key, value); | ||
524 | } | 536 | } | ||
525 | } | 537 | } | ||
526 | 538 | | |||
527 | bool DesktopFileParser::convert(const QString &src, const QStringList &serviceTypes, QJsonObject &json, QString *libraryPath) | 539 | bool DesktopFileParser::convert(const QString &src, const QStringList &serviceTypes, QJsonObject &json, QString *libraryPath) | ||
528 | { | 540 | { | ||
529 | QFile df(src); | 541 | QFile df(src); | ||
530 | int lineNr = 0; | 542 | int lineNr = 0; | ||
531 | ServiceTypeDefinition serviceTypeDef = ServiceTypeDefinition::fromFiles(serviceTypes); | 543 | ServiceTypeDefinitions serviceTypeDef = ServiceTypeDefinitions::fromFiles(serviceTypes); | ||
532 | readUntilDesktopEntryGroup(df, src, lineNr); | 544 | readUntilDesktopEntryGroup(df, src, lineNr); | ||
533 | DESKTOPTOJSON_VERBOSE_DEBUG << "Found [Desktop Entry] group in line" << lineNr; | 545 | DESKTOPTOJSON_VERBOSE_DEBUG << "Found [Desktop Entry] group in line" << lineNr; | ||
534 | auto startPos = df.pos(); | 546 | auto startPos = df.pos(); | ||
535 | 547 | | |||
536 | //parse it a first time to know servicetype | 548 | //parse it a first time to know servicetype | ||
537 | while (!df.atEnd()) { | 549 | while (!df.atEnd()) { | ||
538 | QByteArray key; | 550 | QByteArray key; | ||
539 | QString value; | 551 | QString value; | ||
540 | if (!tokenizeKeyValue(df, src, key, value, lineNr)) { | 552 | if (!tokenizeKeyValue(df, src, key, value, lineNr)) { | ||
541 | break; | 553 | break; | ||
542 | } | 554 | } | ||
543 | // some .desktop files still use the legacy ServiceTypes= key | 555 | // some .desktop files still use the legacy ServiceTypes= key | ||
544 | if (key == QByteArrayLiteral("X-KDE-ServiceTypes") || key == QByteArrayLiteral("ServiceTypes")) { | 556 | if (key == QByteArrayLiteral("X-KDE-ServiceTypes") || key == QByteArrayLiteral("ServiceTypes")) { | ||
545 | const QString dotDesktop = QStringLiteral(".desktop"); | 557 | const QString dotDesktop = QStringLiteral(".desktop"); | ||
546 | const QChar slashChar(QLatin1Char('/')); | 558 | const QChar slashChar(QLatin1Char('/')); | ||
547 | const auto serviceList = deserializeList(value); | 559 | const auto serviceList = deserializeList(value); | ||
548 | 560 | | |||
549 | for (const auto &service : serviceList) { | 561 | for (const auto &service : serviceList) { | ||
562 | if (!serviceTypeDef.hasServiceType(service.toLatin1())) { | ||||
550 | // Make up the filename from the service type name. This assumes consistent naming... | 563 | // Make up the filename from the service type name. This assumes consistent naming... | ||
551 | QString absFileName = locateRelativeServiceType( | 564 | QString absFileName = locateRelativeServiceType( | ||
552 | service.toLower().replace(slashChar, QLatin1Char('-')) + dotDesktop); | 565 | service.toLower().replace(slashChar, QLatin1Char('-')) + dotDesktop); | ||
553 | if (absFileName.isEmpty()) { | 566 | if (absFileName.isEmpty()) { | ||
554 | absFileName = locateRelativeServiceType( | 567 | absFileName = locateRelativeServiceType( | ||
555 | service.toLower().remove(slashChar) + dotDesktop); | 568 | service.toLower().remove(slashChar) + dotDesktop); | ||
556 | } | 569 | } | ||
557 | if (absFileName.isEmpty()) { | 570 | if (absFileName.isEmpty()) { | ||
558 | qCWarning(DESKTOPPARSER) << "Unable to find service type for service" << service << "listed in" << src; | 571 | qCWarning(DESKTOPPARSER) << "Unable to find service type for service" << service << "listed in" << src; | ||
559 | } else { | 572 | } else { | ||
560 | serviceTypeDef.addFile(absFileName); | 573 | serviceTypeDef.addFile(absFileName); | ||
561 | } | 574 | } | ||
562 | } | 575 | } | ||
576 | } | ||||
563 | break; | 577 | break; | ||
564 | } | 578 | } | ||
565 | } | 579 | } | ||
566 | lineNr=0; | 580 | lineNr=0; | ||
567 | df.seek(startPos); | 581 | df.seek(startPos); | ||
568 | 582 | | |||
569 | QJsonObject kplugin; // the "KPlugin" key of the metadata | 583 | QJsonObject kplugin; // the "KPlugin" key of the metadata | ||
570 | //QJsonObject json; | 584 | //QJsonObject json; | ||
Show All 25 Lines |
Where def is deleted?