File indexing completed on 2024-05-12 05:46:43

0001 // This file is part of PyKrita, Krita' Python scripting plugin.
0002 //
0003 // Copyright (C) 2013 Alex Turbov <i.zaufi@gmail.com>
0004 //
0005 // This library is free software; you can redistribute it and/or
0006 // modify it under the terms of the GNU Library General Public
0007 // License as published by the Free Software Foundation; either
0008 // version 2 of the License, or (at your option) version 3.
0009 //
0010 // This library is distributed in the hope that it will be useful,
0011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013 // Library General Public License for more details.
0014 //
0015 // You should have received a copy of the GNU Library General Public License
0016 // along with this library; see the file COPYING.LIB.  If not, write to
0017 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018 // Boston, MA 02110-1301, USA.
0019 
0020 #ifndef __VERSION_CHECKER_H__
0021 # define  __VERSION_CHECKER_H__
0022 
0023 #include <libs/global/kis_debug.h>
0024 #include "utilities.h"
0025 # include <QtCore/QString>
0026 # include <QtCore/QStringList>
0027 # include <QtCore/QtGlobal>
0028 
0029 namespace PyKrita
0030 {
0031 
0032 /**
0033  * \brief Class \c version
0034  */
0035 class version
0036 {
0037     enum type {
0038         undefined = -1,
0039         zero = 0
0040     };
0041 
0042 public:
0043     /// Default constructor
0044     explicit version(const int _major = zero, const int _minor = zero, const int patch = zero)
0045         : m_major(_major)
0046         , m_minor(_minor)
0047         , m_patch(patch) {
0048     }
0049 
0050     int _major() const {
0051         return m_major;
0052     }
0053     int _minor() const {
0054         return m_minor;
0055     }
0056     int patch() const {
0057         return m_patch;
0058     }
0059 
0060     bool isValid() const {
0061         return _major() != undefined && _minor() != undefined && patch() != undefined;
0062     }
0063 
0064     operator QString() const {
0065         return QString("%1.%2.%3").arg(_major()).arg(_minor()).arg(patch());
0066     }
0067 
0068     static version fromString(const QString& version_str) {
0069         int tmp[3] = {zero, zero, zero};
0070         QStringList parts = version_str.split('.');
0071         for (
0072             unsigned long i = 0
0073                               ; i < qMin(static_cast<unsigned long>(sizeof(tmp) / sizeof(int)), static_cast<unsigned long>(parts.size()))
0074             ; ++i
0075         ) {
0076             bool ok;
0077             const int num = parts[i].toInt(&ok);
0078             if (ok)
0079                 tmp[i] = num;
0080             else {
0081                 tmp[i] = undefined;
0082                 break;
0083             }
0084         }
0085         return version(tmp[0], tmp[1], tmp[2]);
0086     };
0087 
0088     static version fromPythonObject(PyObject* version_obj)
0089     {
0090         version v = tryObtainVersionFromTuple(version_obj);
0091         if (!v.isValid()) {
0092             // PEP396 requires __version__ to be a tuple of integers,
0093             // but some modules use a string instead.
0094             v = tryObtainVersionFromString(version_obj);
0095         }
0096         return v;
0097     }
0098 
0099     static version invalid() {
0100         static version s_bad(undefined, undefined, undefined);
0101         return s_bad;
0102     }
0103 
0104 private:
0105     int m_major;
0106     int m_minor;
0107     int m_patch;
0108 
0109 
0110     static version tryObtainVersionFromTuple(PyObject* version_obj)
0111     {
0112         Q_ASSERT("Sanity check" && version_obj);
0113 
0114         if (PyTuple_Check(version_obj) == 0)
0115             return version::invalid();
0116 
0117         int version_info[3] = {0, 0, 0};
0118         for (unsigned i = 0; i < PyTuple_Size(version_obj); ++i) {
0119             PyObject* v = PyTuple_GetItem(version_obj, i);
0120             if (v && PyLong_Check(v))
0121                 version_info[i] = PyLong_AsLong(v);
0122             else
0123                 version_info[i] = -1;
0124         }
0125         if (version_info[0] != -1 && version_info[1] != -1 && version_info[2] != -1)
0126             return ::PyKrita::version(version_info[0], version_info[1], version_info[2]);
0127 
0128         return version::invalid();
0129     }
0130 
0131 /**
0132  * Try to parse version string as a simple triplet X.Y.Z.
0133  *
0134  * \todo Some modules has letters in a version string...
0135  * For example current \c pytz version is \e "2013d".
0136  */
0137     static version tryObtainVersionFromString(PyObject* version_obj)
0138     {
0139         Q_ASSERT("Sanity check" && version_obj);
0140 
0141         if (!Python::isUnicode(version_obj))
0142             return version::invalid();
0143 
0144         QString version_str = Python::unicode(version_obj);
0145         if (version_str.isEmpty())
0146             return version::invalid();
0147 
0148         return version::fromString(version_str);
0149     }
0150 
0151 
0152 };
0153 
0154 inline bool operator==(const version& left, const version& right)
0155 {
0156     return left._major() == right._major()
0157            && left._minor() == right._minor()
0158            && left.patch() == right.patch()
0159            ;
0160 }
0161 
0162 inline bool operator!=(const version& left, const version& right)
0163 {
0164     return !(left == right);
0165 }
0166 
0167 inline bool operator<(const version& left, const version& right)
0168 {
0169     return left._major() < right._major()
0170            || (left._major() == right._major() && left._minor() < right._minor())
0171            || (left._major() == right._major() && left._minor() == right._minor() && left.patch() < right.patch())
0172            ;
0173 }
0174 
0175 inline bool operator>(const version& left, const version& right)
0176 {
0177     return left._major() > right._major()
0178            || (left._major() == right._major() && left._minor() > right._minor())
0179            || (left._major() == right._major() && left._minor() == right._minor() && left.patch() > right.patch())
0180            ;
0181 }
0182 
0183 inline bool operator<=(const version& left, const version& right)
0184 {
0185     return left == right || left < right;
0186 }
0187 
0188 inline bool operator>=(const version& left, const version& right)
0189 {
0190     return left == right || left > right;
0191 }
0192 
0193 
0194 /**
0195 * \brief Class \c version_checker
0196 */
0197 class version_checker
0198 {
0199 public:
0200     enum operation {
0201         invalid
0202         , undefined
0203         , less
0204         , less_or_equal
0205         , greater
0206         , greater_or_equal
0207         , not_equal
0208         , equal
0209         , last__
0210     };
0211 
0212     /// Default constructor
0213     explicit version_checker(const operation op = invalid)
0214         : m_op(op) {
0215     }
0216 
0217     bool isValid() const {
0218         return m_op != invalid;
0219     }
0220 
0221     bool isEmpty() const {
0222         return m_op == undefined;
0223     }
0224 
0225     void bind_second(const version& rhs) {
0226         m_rhs = rhs;
0227     }
0228 
0229     bool operator()(const version& left) {
0230         switch (m_op) {
0231         case less:
0232             return left < m_rhs;
0233         case greater:
0234             return left > m_rhs;
0235         case equal:
0236             return left == m_rhs;
0237         case not_equal:
0238             return left != m_rhs;
0239         case less_or_equal:
0240             return left <= m_rhs;
0241         case greater_or_equal:
0242             return left >= m_rhs;
0243         default:
0244             Q_ASSERT(!"Sanity check");
0245             break;
0246         }
0247         return false;
0248     }
0249 
0250     version required() const {
0251         return m_rhs;
0252     }
0253 
0254     QString operationToString() const {
0255         QString result;
0256         switch (m_op) {
0257         case less:
0258             result = " < ";
0259             break;
0260         case greater:
0261             result = " > ";
0262             break;
0263         case equal:
0264             result = " = ";
0265             break;
0266         case not_equal:
0267             result = " != ";
0268             break;
0269         case less_or_equal:
0270             result = " <= ";
0271             break;
0272         case greater_or_equal:
0273             result = " >= ";
0274             break;
0275         default:
0276             Q_ASSERT(!"Sanity check");
0277             break;
0278         }
0279         return result;
0280     }
0281 
0282     static version_checker fromString(const QString& version_info) {
0283         version_checker checker(invalid);
0284         if (version_info.isEmpty())
0285             return checker;
0286 
0287         bool lookup_next_char = false;
0288         int strip_lead_pos = 0;
0289         switch (version_info.at(0).toLatin1()) {
0290         case '<':
0291             checker.m_op = less;
0292             lookup_next_char = true;
0293             break;
0294         case '>':
0295             checker.m_op = greater;
0296             lookup_next_char = true;
0297             break;
0298         case '=':
0299             strip_lead_pos = 1;
0300             checker.m_op = equal;
0301             break;
0302         default:
0303             strip_lead_pos = 0;
0304             checker.m_op = equal;
0305             break;
0306         }
0307         if (lookup_next_char) {
0308             if (version_info.at(1).toLatin1() == '=') {
0309                 // NOTE Shift state
0310                 checker.m_op = operation(int(checker.m_op) + 1);
0311                 strip_lead_pos = 2;
0312             } else {
0313                 strip_lead_pos = 1;
0314             }
0315         }
0316         //
0317         QString rhs_str = version_info.mid(strip_lead_pos).trimmed();
0318         version rhs = version::fromString(rhs_str);
0319         if (rhs.isValid())
0320             checker.bind_second(rhs);
0321         else
0322             checker.m_op = invalid;
0323         return checker;
0324     }
0325 
0326 private:
0327     operation m_op;
0328     version m_rhs;
0329 };
0330 
0331 }                                                           // namespace PyKrita
0332 #endif                                                      //  __VERSION_CHECKER_H__