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__