File indexing completed on 2025-03-09 03:58:42

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-08-01
0007  * Description : camera name helper class
0008  *
0009  * SPDX-FileCopyrightText: 2009-2012 by Andi Clemens <andi dot clemens at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "cameranamehelper.h"
0016 
0017 // Qt includes
0018 
0019 #include <QRegularExpression>
0020 
0021 // KDE includes
0022 
0023 #include <klocalizedstring.h>
0024 
0025 namespace
0026 {
0027 static const KLocalizedString STR_AUTO_DETECTED(ki18nc("in camera model string", "auto-detected"));
0028 
0029 /*
0030  * The constructors used here are written after many experiments.
0031  * REGEXP_MODES expression was failing to give exact match results if the pattern option
0032  * InvertedGreedinessOption was set later in extractCameraNameToken function with QRegularExpression::setPatternOption()
0033  * Maybe a bug in Qt.
0034  * */
0035 static QRegularExpression REGEXP_CAMERA_NAME(
0036     QRegularExpression::anchoredPattern(QLatin1String("(.*)\\s*\\((.*)\\)\\s*")),
0037     QRegularExpression::InvertedGreedinessOption | QRegularExpression::CaseInsensitiveOption);
0038 
0039 static QRegularExpression REGEXP_MODES(
0040     QRegularExpression::anchoredPattern(QLatin1String("(ptp|normal|mtp)(\\s+mode)?")),
0041     QRegularExpression::InvertedGreedinessOption | QRegularExpression::CaseInsensitiveOption);
0042 }
0043 
0044 namespace Digikam
0045 {
0046 
0047 QString CameraNameHelper::createCameraName(const QString& vendor, const QString& product,
0048                                            const QString& mode,   bool autoDetected)
0049 {
0050     if (vendor.isEmpty())
0051     {
0052         return QString();
0053     }
0054 
0055     QString tmp;
0056     QString _vendor  = vendor.simplified();
0057     QString _product = product.simplified();
0058     QString _mode    = mode.simplified().remove(QLatin1Char('(')).remove(QLatin1Char(')'));
0059     tmp              = QString::fromUtf8("%1 %2").arg(_vendor).arg(_product);
0060 
0061     if      (!mode.isEmpty() && mode != STR_AUTO_DETECTED.toString())
0062     {
0063         tmp.append(QLatin1String(" ("));
0064         tmp.append(_mode);
0065         tmp.append(autoDetected ? QString::fromUtf8(", %1)").arg(STR_AUTO_DETECTED.toString())
0066                                 : QLatin1String(")"));
0067     }
0068     else if (autoDetected)
0069     {
0070         tmp.append(QString::fromUtf8(" (%1)").arg(STR_AUTO_DETECTED.toString()));
0071     }
0072 
0073     return tmp.simplified();
0074 }
0075 
0076 QString CameraNameHelper::cameraName(const QString& name)
0077 {
0078     return parseAndFormatCameraName(name, false, false);
0079 }
0080 
0081 QString CameraNameHelper::cameraNameAutoDetected(const QString& name)
0082 {
0083     return parseAndFormatCameraName(name, true, true);
0084 }
0085 
0086 QString CameraNameHelper::parseAndFormatCameraName(const QString& cameraName,
0087                                                    bool parseMode, bool autoDetected)
0088 {
0089     QString vendorAndProduct = extractCameraNameToken(cameraName, VendorAndProduct);
0090 
0091     if (vendorAndProduct.isEmpty())
0092     {
0093         return QString();
0094     }
0095 
0096     QString mode = parseMode ? extractCameraNameToken(cameraName, Mode)
0097                              : QString();
0098 
0099     QString tmp = createCameraName(vendorAndProduct, QString(), mode, autoDetected);
0100 
0101     return (tmp.isEmpty() ? cameraName.simplified()
0102                           : tmp);
0103 }
0104 
0105 QString CameraNameHelper::extractCameraNameToken(const QString& cameraName, Token tokenID)
0106 {
0107     static QRegularExpression REGEXP_AUTODETECTED(QString::fromUtf8("(%1|, %1)").arg(STR_AUTO_DETECTED.toString()));
0108     REGEXP_AUTODETECTED.setPatternOptions(QRegularExpression::InvertedGreedinessOption);
0109 
0110     QRegularExpressionMatch expMatch = REGEXP_CAMERA_NAME.match(cameraName.simplified());
0111 
0112     if (expMatch.hasMatch())
0113     {
0114         QString vendorProduct  = expMatch.captured(1).simplified();
0115         QString tmpMode        = expMatch.captured(2).simplified();
0116         QString clearedTmpMode = tmpMode;
0117         QString mode;
0118         clearedTmpMode.remove(REGEXP_AUTODETECTED);
0119 
0120         if (!tmpMode.isEmpty() && clearedTmpMode.isEmpty())
0121         {
0122             mode = tmpMode;
0123         }
0124         else
0125         {
0126             mode = REGEXP_MODES.match(clearedTmpMode).hasMatch() ? clearedTmpMode
0127                                                            : QLatin1String("");
0128         }
0129 
0130         if (tokenID == VendorAndProduct)
0131         {
0132             return (mode.isEmpty() ? cameraName.simplified()
0133                                    : vendorProduct);
0134         }
0135         else
0136         {
0137             return mode;
0138         }
0139     }
0140     return ((tokenID == VendorAndProduct) ? cameraName.simplified()
0141                                           : QLatin1String(""));
0142 }
0143 
0144 bool CameraNameHelper::sameDevices(const QString& deviceA, const QString& deviceB)
0145 {
0146     if (deviceA.isEmpty() || deviceB.isEmpty())
0147     {
0148         return false;
0149     }
0150 
0151     if (deviceA == deviceB)
0152     {
0153         return true;
0154     }
0155 
0156     // We need to parse the names a little bit. First check if the vendor and name match
0157 
0158     QString vendorAndProductA = extractCameraNameToken(deviceA, VendorAndProduct);
0159     QString vendorAndProductB = extractCameraNameToken(deviceB, VendorAndProduct);
0160     QString cameraNameA       = createCameraName(vendorAndProductA);
0161     QString cameraNameB       = createCameraName(vendorAndProductB);
0162 
0163     // try to clean up the string, if not possible, return false
0164 
0165     if (cameraNameA != cameraNameB)
0166     {
0167         return false;
0168     }
0169 
0170     // is the extracted mode known and equal?
0171 
0172     QString modeA                 = extractCameraNameToken(deviceA, Mode);
0173     QString modeB                 = extractCameraNameToken(deviceB, Mode);
0174     QRegularExpressionMatch match = REGEXP_MODES.match(modeA);
0175     bool isModeAValid             = match.hasMatch();
0176     modeA                         = isModeAValid ? match.captured(1).simplified().toLower() : QLatin1String("");
0177     match                         = REGEXP_MODES.match(modeB);
0178     bool isModeBValid             = match.hasMatch();
0179     modeB                         = isModeBValid ? match.captured(1).simplified().toLower() : QLatin1String("");
0180 
0181     if ((isModeAValid != isModeBValid) || (modeA != modeB))
0182     {
0183         return false;
0184     }
0185 
0186     return true;
0187 }
0188 
0189 } // namespace Digikam