From da6cd92b2225c8d44d82f7e3c619f2969ae58d5d Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 3 Jan 2017 11:14:32 +0200 Subject: [PATCH] =?UTF-8?q?Resolved=20issue=20#606.=20Mac=20OS=20X.=20Can?= =?UTF-8?q?=E2=80=99t=20type=20in=20measurements=20due=20to=20digit=20coun?= =?UTF-8?q?t=20limitation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --HG-- branch : develop --- ChangeLog.txt | 1 + src/libs/qmuparser/qmudef.cpp | 275 ++++++++++++++++++ src/libs/qmuparser/qmudef.h | 21 ++ src/libs/qmuparser/qmuformulabase.cpp | 30 +- src/libs/qmuparser/qmuformulabase.h | 8 +- src/libs/qmuparser/qmuparser.cpp | 16 +- src/libs/qmuparser/qmuparser.h | 4 +- src/libs/qmuparser/qmuparser.pri | 3 +- src/libs/qmuparser/qmuparser.pro | 2 +- src/libs/qmuparser/qmuparserbase.cpp | 134 +++++---- src/libs/qmuparser/qmuparserbase.h | 19 +- src/libs/qmuparser/qmuparserdef.h | 9 +- src/libs/qmuparser/qmuparsertest.cpp | 7 +- src/libs/qmuparser/qmuparsertest.h | 3 +- src/libs/qmuparser/qmuparsertokenreader.cpp | 10 +- src/libs/qmuparser/qmuparsertokenreader.h | 4 +- src/libs/qmuparser/qmutokenparser.cpp | 4 +- src/libs/vpatterndb/calculator.cpp | 2 +- src/libs/vpatterndb/vtranslatevars.cpp | 15 +- src/test/ValentinaTest/ValentinaTest.pro | 8 +- src/test/ValentinaTest/qttestmainlambda.cpp | 5 + src/test/ValentinaTest/tst_qmutokenparser.cpp | 97 +++++- src/test/ValentinaTest/tst_qmutokenparser.h | 12 +- src/test/ValentinaTest/tst_readval.cpp | 153 ++++++++++ src/test/ValentinaTest/tst_readval.h | 58 ++++ src/test/ValentinaTest/tst_vtranslatevars.cpp | 174 +++++++++++ src/test/ValentinaTest/tst_vtranslatevars.h | 60 ++++ 27 files changed, 1025 insertions(+), 109 deletions(-) create mode 100644 src/libs/qmuparser/qmudef.cpp create mode 100644 src/test/ValentinaTest/tst_readval.cpp create mode 100644 src/test/ValentinaTest/tst_readval.h create mode 100644 src/test/ValentinaTest/tst_vtranslatevars.cpp create mode 100644 src/test/ValentinaTest/tst_vtranslatevars.h diff --git a/ChangeLog.txt b/ChangeLog.txt index e72c5e1b5..689dfa1b7 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -48,6 +48,7 @@ - [#582] Issue with standard path to shared data on Linux. - [#595] GapWidth affecting to the margins. - [#589] Valentina lock up if not enough space for label. +- [#606] Mac OS X. Can’t type in measurements due to digit count limitation. # Version 0.4.6 - [#594] Broken export on Mac. diff --git a/src/libs/qmuparser/qmudef.cpp b/src/libs/qmuparser/qmudef.cpp new file mode 100644 index 000000000..a588db1e9 --- /dev/null +++ b/src/libs/qmuparser/qmudef.cpp @@ -0,0 +1,275 @@ +/*************************************************************************************************** + ** + ** Copyright (C) 2016 Roman Telezhynskyi + ** + ** Permission is hereby granted, free of charge, to any person obtaining a copy of this + ** software and associated documentation files (the "Software"), to deal in the Software + ** without restriction, including without limitation the rights to use, copy, modify, + ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + ** permit persons to whom the Software is furnished to do so, subject to the following conditions: + ** + ** The above copyright notice and this permission notice shall be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ** + ******************************************************************************************************/ + +#include "qmudef.h" + +#include +#include + +enum State +{ + Init = 0, + Sign = 1, + Thousand = 2, + Mantissa = 3, + Dot = 4, + Abscissa = 5, + ExpMark = 6, + ExpSign = 7, + Exponent = 8, + Done = 9 +}; + +enum InputToken +{ + InputSign = 1, + InputThousand = 2, + InputDigit = 3, + InputDot = 4, + InputExp = 5 +}; + +static const QChar QmuEOF = QChar(static_cast(0xffff)); //guaranteed not to be a character. + +//--------------------------------------------------------------------------------------------------------------------- +static QChar GetChar(const QString &formula, int &index) +{ + if (index >= formula.size()) + { + return QmuEOF; + } + + return formula.at(index++); +} + +//--------------------------------------------------------------------------------------------------------------------- +static QChar EatWhiteSpace(const QString &formula, int &index) +{ + QChar c; + do + { + c = GetChar(formula, index); + } + while ( c != QmuEOF && c.isSpace() ); + + return c; +} + +//--------------------------------------------------------------------------------------------------------------------- +static int CheckChar(QChar &c, const QLocale &locale, const QChar &decimal, const QChar &thousand) +{ + INIT_LOCALE_VARIABLES(locale); + + if (c == positiveSign) + { + c = '+'; + return InputToken::InputSign; + } + else if (c == negativeSign) + { + c = '-'; + return InputToken::InputSign; + } + else if (c == sign0) + { + c = '0'; + return InputToken::InputDigit; + } + else if (c == sign1) + { + c = '1'; + return InputToken::InputDigit; + } + else if (c == sign2) + { + c = '2'; + return InputToken::InputDigit; + } + else if (c == sign3) + { + c = '3'; + return InputToken::InputDigit; + } + else if (c == sign4) + { + c = '4'; + return InputToken::InputDigit; + } + else if (c == sign5) + { + c = '5'; + return InputToken::InputDigit; + } + else if (c == sign6) + { + c = '6'; + return InputToken::InputDigit; + } + else if (c == sign7) + { + c = '7'; + return InputToken::InputDigit; + } + else if (c == sign8) + { + c = '8'; + return InputToken::InputDigit; + } + else if (c == sign9) + { + c = '9'; + return InputToken::InputDigit; + } + else if (c == decimal) + { + return InputToken::InputDot; + } + else if (c == thousand) + { + return InputToken::InputThousand; + } + else if (c == expLower) + { + c = 'e'; + return InputToken::InputExp; + } + else if (c == expUpper) + { + c = 'E'; + return InputToken::InputExp; + } + else + { + return 0; + } + + return 0; +} + +//--------------------------------------------------------------------------------------------------------------------- +int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand) +{ + // Must not be equal + if (decimal == thousand || formula.isEmpty()) + { + val = 0; + return -1; + } + + INIT_LOCALE_VARIABLES(locale); + + QSet reserved; + reserved << positiveSign + << negativeSign + << sign0 + << sign1 + << sign2 + << sign3 + << sign4 + << sign5 + << sign6 + << sign7 + << sign8 + << sign9 + << expUpper + << expLower; + + if (reserved.contains(decimal) || reserved.contains(thousand)) + { + val = 0; + return -1; + } + + // row - current state, column - new state + static uchar table[9][6] = + { + /* None InputSign InputThousand InputDigit InputDot InputExp */ + { 0, State::Sign, 0, State::Mantissa, State::Dot, 0, }, // Init + { 0, 0, 0, State::Mantissa, State::Dot, 0, }, // Sign + { 0, 0, 0, State::Mantissa, 0, 0, }, // Thousand + { State::Done, State::Done, State::Thousand, State::Mantissa, State::Dot, State::ExpMark,}, // Mantissa + { 0, 0, 0, State::Abscissa, 0, 0, }, // Dot + { State::Done, State::Done, 0, State::Abscissa, 0, State::ExpMark,}, // Abscissa + { 0, State::ExpSign, 0, State::Exponent, 0, 0, }, // ExpMark + { 0, 0, 0, State::Exponent, 0, 0, }, // ExpSign + { State::Done, 0, 0, State::Exponent, 0, State::Done } // Exponent + }; + + int state = State::Init; // parse state + int input; // input token + QString buf; + + int index = 0; // start position + QChar c = EatWhiteSpace(formula, index); + + while ( true ) + { + input = CheckChar(c, locale, decimal, thousand); + + state = table[state][input]; + + if (state == 0) + { + val = 0; + return -1; + } + else if (state == Done) + { + // Convert to C locale + QLocale cLocale(QLocale::C); + const QChar cDecimal = cLocale.decimalPoint(); + const QChar cThousand = cLocale.groupSeparator(); + if (locale != cLocale && (cDecimal != decimal || cThousand != thousand)) + { + if (decimal == cThousand) + {// Handle reverse to C locale case: thousand '.', decimal ',' + const QChar tmpThousand = '@'; + buf.replace(thousand, tmpThousand); + buf.replace(decimal, cDecimal); + buf.replace(tmpThousand, cThousand); + } + else + { + buf.replace(thousand, cThousand); + buf.replace(decimal, cDecimal); + } + } + + bool ok = false; + const double d = cLocale.toDouble(buf, &ok); + if (ok) + { + val = d; + return buf.size(); + } + else + { + val = 0; + return -1; + } + } + + buf.append(c); + c = GetChar(formula, index); + } + + return -1; +} diff --git a/src/libs/qmuparser/qmudef.h b/src/libs/qmuparser/qmudef.h index 40b17d15e..70c883119 100644 --- a/src/libs/qmuparser/qmudef.h +++ b/src/libs/qmuparser/qmudef.h @@ -90,6 +90,25 @@ QT_WARNING_DISABLE_GCC("-Wattributes") #ifdef Q_CC_MSVC #include #endif /* Q_CC_MSVC */ + +class QLocale; + +#define INIT_LOCALE_VARIABLES(locale) \ +const QChar positiveSign = (locale).positiveSign(); \ +const QChar negativeSign = (locale).negativeSign(); \ +const QChar sign0 = (locale).toString(0).at(0); \ +const QChar sign1 = (locale).toString(1).at(0); \ +const QChar sign2 = (locale).toString(2).at(0); \ +const QChar sign3 = (locale).toString(3).at(0); \ +const QChar sign4 = (locale).toString(4).at(0); \ +const QChar sign5 = (locale).toString(5).at(0); \ +const QChar sign6 = (locale).toString(6).at(0); \ +const QChar sign7 = (locale).toString(7).at(0); \ +const QChar sign8 = (locale).toString(8).at(0); \ +const QChar sign9 = (locale).toString(9).at(0); \ +const QChar expUpper = (locale).exponential().toUpper(); \ +const QChar expLower = (locale).exponential().toLower() \ + //--------------------------------------------------------------------------------------------------------------------- inline QString NameRegExp() { @@ -119,4 +138,6 @@ static inline bool QmuFuzzyComparePossibleNulls(double p1, double p2) } } +int ReadVal(const QString &formula, qreal &val, const QLocale &locale, const QChar &decimal, const QChar &thousand); + #endif // QMUDEF_H diff --git a/src/libs/qmuparser/qmuformulabase.cpp b/src/libs/qmuparser/qmuformulabase.cpp index 4d24505ca..fd8f3a965 100644 --- a/src/libs/qmuparser/qmuformulabase.cpp +++ b/src/libs/qmuparser/qmuformulabase.cpp @@ -46,13 +46,13 @@ QmuFormulaBase::~QmuFormulaBase() //--------------------------------------------------------------------------------------------------------------------- /** - * @brief InitCharacterSets init character set for parser. + * @brief InitCharSets init character set for parser. * * QMuParser require setting character set for legal characters. Because we try make our expresion language independent * we set all posible unique characters from all alphabets. * */ -void QmuFormulaBase::InitCharacterSets() +void QmuFormulaBase::InitCharSets() { //String with all unique symbols for supported alphabets. //See script alphabets.py for generation and more information. @@ -68,9 +68,18 @@ void QmuFormulaBase::InitCharacterSets() << "ЪƯخγвŅԴŪضλкԼĴσтÅՄنъÍՌRӕՔZÝŜbåդﻩjíլļrӵմzýռپêЅքćچЍďӱҒЕůėژșΘØҚНğńءΠFҢХħΨҪ" << "ЭųįҶرҲеԷňعθҺнԿفπÂхՇψÊэšՏÒUəÚѝŻşҤӑâeէŐımկòuշÕúտŔ"; + INIT_LOCALE_VARIABLES(m_locale); + // Defining identifier character sets - DefineNameChars(QStringLiteral("0123456789_@#'") + symbols.join("")); - DefineOprtChars(symbols.join("") + QStringLiteral("+-*^/?<>=!$%&|~_")); + const QString nameChars = QString() + sign0 + sign1 + sign2 + sign3 + sign4 + sign5 + sign6 + sign7 + sign8 + + sign9 + QLatin1String("_@#'") + symbols.join(""); + DefineNameChars(nameChars); + + const QString oprtChars = symbols.join("") + positiveSign + negativeSign + QLatin1String("*^/?<>=!$%&|~'_"); + DefineOprtChars(oprtChars); + + const QString infixOprtChars = QString() + positiveSign + negativeSign + QLatin1String("*^/?<>=!$%&|~'_"); + DefineInfixOprtChars(infixOprtChars); } //--------------------------------------------------------------------------------------------------------------------- @@ -94,12 +103,13 @@ void QmuFormulaBase::SetSepForTr(bool osSeparator, bool fromUser) { if (fromUser) { + const QLocale loc = QLocale(); + setLocale(loc); + SetArgSep(';'); if (osSeparator) { - const QLocale loc = QLocale::system(); - SetDecSep(loc.decimalPoint().toLatin1()); - SetThousandsSep(loc.groupSeparator().toLatin1()); - SetArgSep(';'); + setDecimalPoint(loc.decimalPoint()); + setThousandsSeparator(loc.groupSeparator()); return; } } @@ -114,8 +124,8 @@ void QmuFormulaBase::SetSepForTr(bool osSeparator, bool fromUser) void QmuFormulaBase::SetSepForEval() { SetArgSep(';'); - SetThousandsSep(','); - SetDecSep('.'); + setThousandsSeparator(','); + setDecimalPoint('.'); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/libs/qmuparser/qmuformulabase.h b/src/libs/qmuparser/qmuformulabase.h index 0ff10d950..6324f4cc8 100644 --- a/src/libs/qmuparser/qmuformulabase.h +++ b/src/libs/qmuparser/qmuformulabase.h @@ -41,14 +41,14 @@ public: QmuFormulaBase(); virtual ~QmuFormulaBase() Q_DECL_OVERRIDE; + virtual void InitCharSets() Q_DECL_OVERRIDE; + + static void RemoveAll(QMap &map, const QString &val); + protected: - void InitCharacterSets(); static qreal* AddVariable(const QString &a_szName, void *a_pUserData); void SetSepForTr(bool osSeparator, bool fromUser); void SetSepForEval(); - - static void RemoveAll(QMap &map, const QString &val); - private: Q_DISABLE_COPY(QmuFormulaBase) }; diff --git a/src/libs/qmuparser/qmuparser.cpp b/src/libs/qmuparser/qmuparser.cpp index 707d35271..160481467 100644 --- a/src/libs/qmuparser/qmuparser.cpp +++ b/src/libs/qmuparser/qmuparser.cpp @@ -225,23 +225,19 @@ qreal QmuParser::Max(const qreal *a_afArg, int a_iArgc) * @param [out] a_fVal Pointer where the value should be stored in case one is found. * @return 1 if a value was found 0 otherwise. */ -int QmuParser::IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale) +int QmuParser::IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale, const QChar &decimal, + const QChar &thousand) { qreal fVal(0); - std::wstring a_szExprStd = a_szExpr.toStdWString(); - stringstream_type stream(a_szExprStd); - stream.seekg(0); // todo: check if this really is necessary - stream.imbue(s_locale); - stream >> fVal; - stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + const int pos = ReadVal(a_szExpr, fVal, locale, decimal, thousand); - if (iEnd==static_cast(-1)) + if (pos == -1) { return 0; } - *a_iPos += static_cast(iEnd); + *a_iPos += pos; *a_fVal = fVal; return 1; } @@ -340,7 +336,7 @@ void QmuParser::InitConst() */ void QmuParser::InitOprt() { - DefineInfixOprt("-", UnaryMinus); + DefineInfixOprt(m_locale.negativeSign(), UnaryMinus); } //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/libs/qmuparser/qmuparser.h b/src/libs/qmuparser/qmuparser.h index 2a25d9be2..b0553ca10 100644 --- a/src/libs/qmuparser/qmuparser.h +++ b/src/libs/qmuparser/qmuparser.h @@ -27,7 +27,6 @@ #include #include -#include "../qmuparser/qmuparser_global.h" #include "qmuparser_global.h" #include "qmuparserbase.h" @@ -59,7 +58,8 @@ namespace qmu virtual void OnDetectVar(const QString &pExpr, int &nStart, int &nEnd) Q_DECL_OVERRIDE; qreal Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon = 0) const; protected: - static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale); + static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale, + const QChar &decimal, const QChar &thousand); // hyperbolic functions static qreal Sinh(qreal); static qreal Cosh(qreal); diff --git a/src/libs/qmuparser/qmuparser.pri b/src/libs/qmuparser/qmuparser.pri index 81885d723..42ee28d42 100644 --- a/src/libs/qmuparser/qmuparser.pri +++ b/src/libs/qmuparser/qmuparser.pri @@ -11,7 +11,8 @@ SOURCES += \ $$PWD/qmuparsertest.cpp \ $$PWD/qmutranslation.cpp \ $$PWD/qmuformulabase.cpp \ - $$PWD/qmutokenparser.cpp + $$PWD/qmutokenparser.cpp \ + $$PWD/qmudef.cpp win32-msvc*:SOURCES += $$PWD/stable.cpp diff --git a/src/libs/qmuparser/qmuparser.pro b/src/libs/qmuparser/qmuparser.pro index 004568674..46fd30a4b 100644 --- a/src/libs/qmuparser/qmuparser.pro +++ b/src/libs/qmuparser/qmuparser.pro @@ -39,7 +39,7 @@ OBJECTS_DIR = obj include(qmuparser.pri) -VERSION = 2.4.1 +VERSION = 2.5.0 # Allow MAC OS X to find library inside a bundle macx:QMAKE_SONAME_PREFIX = @rpath diff --git a/src/libs/qmuparser/qmuparserbase.cpp b/src/libs/qmuparser/qmuparserbase.cpp index 04994717c..abbbe3783 100644 --- a/src/libs/qmuparser/qmuparserbase.cpp +++ b/src/libs/qmuparser/qmuparserbase.cpp @@ -62,11 +62,31 @@ const QStringList QmuParserBase::c_DefaultOprt = QStringList()<< "<=" << ">=" << * @brief Constructor. */ QmuParserBase::QmuParserBase() - :s_locale(std::locale(std::locale::classic(), new change_dec_sep('.'))), - m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), m_vStringVarBuf(), m_pTokenReader(), - m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_StrVarDef(), m_VarDef(), - m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_nIfElseCounter(0), m_vStackBuffer(), - m_nFinalResultIdx(0), m_Tokens(QMap()), m_Numbers(QMap()), allowSubexpressions(true) + : m_locale(QLocale::c()), + m_decimalPoint(QLocale::c().decimalPoint()), + m_thousandsSeparator(QLocale::c().groupSeparator()), + m_pParseFormula(&QmuParserBase::ParseString), + m_vRPN(), + m_vStringBuf(), + m_vStringVarBuf(), + m_pTokenReader(), + m_FunDef(), + m_PostOprtDef(), + m_InfixOprtDef(), + m_OprtDef(), + m_ConstDef(), + m_StrVarDef(), + m_VarDef(), + m_bBuiltInOp(true), + m_sNameChars(), + m_sOprtChars(), + m_sInfixOprtChars(), + m_nIfElseCounter(0), + m_vStackBuffer(), + m_nFinalResultIdx(0), + m_Tokens(QMap()), + m_Numbers(QMap()), + allowSubexpressions(true) { InitTokenReader(); } @@ -78,11 +98,31 @@ QmuParserBase::QmuParserBase() * Tha parser can be safely copy constructed but the bytecode is reset during copy construction. */ QmuParserBase::QmuParserBase(const QmuParserBase &a_Parser) - :s_locale(a_Parser.getLocale()), m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), - m_vStringVarBuf(), m_pTokenReader(), m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), - m_StrVarDef(), m_VarDef(), m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), - m_nIfElseCounter(0), m_vStackBuffer(), m_nFinalResultIdx(0), m_Tokens(QMap()), - m_Numbers(QMap()), allowSubexpressions(true) + : m_locale(a_Parser.getLocale()), + m_decimalPoint(a_Parser.getDecimalPoint()), + m_thousandsSeparator(a_Parser.getThousandsSeparator()), + m_pParseFormula(&QmuParserBase::ParseString), + m_vRPN(), + m_vStringBuf(), + m_vStringVarBuf(), + m_pTokenReader(), + m_FunDef(), + m_PostOprtDef(), + m_InfixOprtDef(), + m_OprtDef(), + m_ConstDef(), + m_StrVarDef(), + m_VarDef(), + m_bBuiltInOp(true), + m_sNameChars(), + m_sOprtChars(), + m_sInfixOprtChars(), + m_nIfElseCounter(0), + m_vStackBuffer(), + m_nFinalResultIdx(0), + m_Tokens(QMap()), + m_Numbers(QMap()), + allowSubexpressions(true) { m_pTokenReader.reset(new token_reader_type(this)); Assign(a_Parser); @@ -150,38 +190,6 @@ void QmuParserBase::Assign(const QmuParserBase &a_Parser) m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; } -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief Set the decimal separator. - * @param cDecSep Decimal separator as a character value. - * @sa SetThousandsSep - * - * By default muparser uses the "C" locale. The decimal separator of this - * locale is overwritten by the one provided here. - */ -// cppcheck-suppress unusedFunction -void QmuParserBase::SetDecSep(char_type cDecSep) -{ - char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); - s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); -} - -//--------------------------------------------------------------------------------------------------------------------- -/** - * @brief Sets the thousands operator. - * @param cThousandsSep The thousands separator as a character - * @sa SetDecSep - * - * By default muparser uses the "C" locale. The thousands separator of this - * locale is overwritten by the one provided here. - */ -// cppcheck-suppress unusedFunction -void QmuParserBase::SetThousandsSep(char_type cThousandsSep) -{ - char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); - s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); -} - //--------------------------------------------------------------------------------------------------------------------- /** * @brief Resets the locale. @@ -191,8 +199,10 @@ void QmuParserBase::SetThousandsSep(char_type cThousandsSep) // cppcheck-suppress unusedFunction void QmuParserBase::ResetLocale() { - s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); - SetArgSep(','); + setLocale(QLocale::c()); + m_decimalPoint = m_locale.decimalPoint(); + m_thousandsSeparator = m_locale.groupSeparator(); + SetArgSep(';'); } //--------------------------------------------------------------------------------------------------------------------- @@ -228,15 +238,41 @@ void QmuParserBase::setAllowSubexpressions(bool value) } //--------------------------------------------------------------------------------------------------------------------- -std::locale QmuParserBase::getLocale() const +QLocale QmuParserBase::getLocale() const { - return s_locale; + return m_locale; } //--------------------------------------------------------------------------------------------------------------------- -void QmuParserBase::setLocale(const std::locale &value) +void QmuParserBase::setLocale(const QLocale &value) { - s_locale = value; + m_locale = value; + InitCharSets(); + InitOprt(); +} + +//--------------------------------------------------------------------------------------------------------------------- +QChar QmuParserBase::getDecimalPoint() const +{ + return m_decimalPoint; +} + +//--------------------------------------------------------------------------------------------------------------------- +void QmuParserBase::setDecimalPoint(const QChar &c) +{ + m_decimalPoint = c; +} + +//--------------------------------------------------------------------------------------------------------------------- +QChar QmuParserBase::getThousandsSeparator() const +{ + return m_thousandsSeparator; +} + +//--------------------------------------------------------------------------------------------------------------------- +void QmuParserBase::setThousandsSeparator(const QChar &c) +{ + m_thousandsSeparator = c; } //--------------------------------------------------------------------------------------------------------------------- @@ -1319,7 +1355,7 @@ void QmuParserBase::CreateRPN() const for (;;) { - opt = m_pTokenReader->ReadNextToken(s_locale); + opt = m_pTokenReader->ReadNextToken(m_locale, m_decimalPoint, m_thousandsSeparator); switch (opt.GetCode()) { diff --git a/src/libs/qmuparser/qmuparserbase.h b/src/libs/qmuparser/qmuparserbase.h index 6c1e86b24..45d263222 100644 --- a/src/libs/qmuparser/qmuparserbase.h +++ b/src/libs/qmuparser/qmuparserbase.h @@ -31,11 +31,10 @@ #include #include #include -#include #include #include +#include -#include "../qmuparser/qmuparser_global.h" #include "qmuparser_global.h" #include "qmuparserbytecode.h" #include "qmuparsercallback.h" @@ -81,8 +80,6 @@ public: int GetNumResults() const; void SetExpr(const QString &a_sExpr); void SetVarFactory(facfun_type a_pFactory, void *pUserData = nullptr); - void SetDecSep(char_type cDecSep); - void SetThousandsSep(char_type cThousandsSep = 0); void ResetLocale(); void EnableOptimizer(bool a_bIsOn=true); void EnableBuiltInOprt(bool a_bIsOn=true); @@ -128,12 +125,20 @@ public: void setAllowSubexpressions(bool value); - std::locale getLocale() const; - void setLocale(const std::locale &value); + QLocale getLocale() const; + void setLocale(const QLocale &value); + + QChar getDecimalPoint() const; + void setDecimalPoint(const QChar &c); + + QChar getThousandsSeparator() const; + void setThousandsSeparator(const QChar &c); protected: static const QStringList c_DefaultOprt; - std::locale s_locale; ///< The locale used by the parser + QLocale m_locale;///< The locale used by the parser + QChar m_decimalPoint; + QChar m_thousandsSeparator; static bool g_DbgDumpCmdCode; static bool g_DbgDumpStack; void Init(); diff --git a/src/libs/qmuparser/qmuparserdef.h b/src/libs/qmuparser/qmuparserdef.h index 3b57223aa..9008ccf0a 100644 --- a/src/libs/qmuparser/qmuparserdef.h +++ b/src/libs/qmuparser/qmuparserdef.h @@ -32,8 +32,8 @@ @brief This file contains standard definitions used by the parser. */ -#define QMUP_VERSION "2.2.4" -#define QMUP_VERSION_DATE "20140504; GC" +#define QMUP_VERSION "2.5.0" +#define QMUP_VERSION_DATE "20170101; GC" #define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -64,6 +64,8 @@ /** @brief Definition of the basic parser string type. */ #define QMUP_STRING_TYPE std::wstring +class QLocale; + namespace qmu { //------------------------------------------------------------------------------ @@ -275,7 +277,8 @@ typedef qreal ( *strfun_type2 ) ( const QString &, qreal ); typedef qreal ( *strfun_type3 ) ( const QString &, qreal, qreal ); /** @brief Callback used for functions that identify values in a string. */ -typedef int ( *identfun_type ) ( const QString &sExpr, int *nPos, qreal *fVal, const std::locale &s_locale ); +typedef int ( *identfun_type ) ( const QString &sExpr, int *nPos, qreal *fVal, const QLocale &locale, + const QChar &decimal, const QChar &thousand ); /** @brief Callback used for variable creation factory functions. */ typedef qreal* ( *facfun_type ) ( const QString &, void* ); diff --git a/src/libs/qmuparser/qmuparsertest.cpp b/src/libs/qmuparser/qmuparsertest.cpp index b17b727a5..26c76f117 100644 --- a/src/libs/qmuparser/qmuparsertest.cpp +++ b/src/libs/qmuparser/qmuparsertest.cpp @@ -74,9 +74,12 @@ QmuParserTester::QmuParserTester(QObject *parent) } //--------------------------------------------------------------------------------------------------------------------- -int QmuParserTester::IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale ) +int QmuParserTester::IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale, + const QChar &decimal, const QChar &thousand ) { - Q_UNUSED(s_locale) + Q_UNUSED(locale) + Q_UNUSED(decimal) + Q_UNUSED(thousand) if ( a_szExpr.data()[1] == 0 || ( a_szExpr.data()[0] != '0' || a_szExpr.data()[1] != 'x' ) ) { return 0; diff --git a/src/libs/qmuparser/qmuparsertest.h b/src/libs/qmuparser/qmuparsertest.h index 6ed2ae9f0..d50573f4f 100644 --- a/src/libs/qmuparser/qmuparsertest.h +++ b/src/libs/qmuparser/qmuparsertest.h @@ -293,7 +293,8 @@ private: } // Custom value recognition - static int IsHexVal (const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const std::locale &s_locale); + static int IsHexVal (const QString &a_szExpr, int *a_iPos, qreal *a_fVal, const QLocale &locale, + const QChar &decimal, const QChar &thousand); // cppcheck-suppress functionStatic int TestNames(); diff --git a/src/libs/qmuparser/qmuparsertokenreader.cpp b/src/libs/qmuparser/qmuparsertokenreader.cpp index 111c6ab18..552bdf817 100644 --- a/src/libs/qmuparser/qmuparsertokenreader.cpp +++ b/src/libs/qmuparser/qmuparsertokenreader.cpp @@ -213,7 +213,8 @@ void QmuParserTokenReader::ReInit() /** * @brief Read the next token from the string. */ -QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const std::locale &s_locale) +QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const QLocale &locale, const QChar &decimal, + const QChar &thousand) { assert ( m_pParser ); @@ -245,7 +246,7 @@ QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const std:: { return SaveBeforeReturn ( tok ); // Check for function argument separators } - if ( IsValTok ( tok, s_locale ) ) + if ( IsValTok ( tok, locale, decimal, thousand ) ) { return SaveBeforeReturn ( tok ); // Check for values / constant tokens } @@ -779,7 +780,8 @@ bool QmuParserTokenReader::IsPostOpTok ( token_type &a_Tok ) * @param a_Tok [out] If a value token is found it will be placed here. * @return true if a value token has been found. */ -bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const std::locale &s_locale ) +bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const QLocale &locale, const QChar &decimal, + const QChar &thousand ) { assert ( m_pConstDef ); assert ( m_pParser ); @@ -815,7 +817,7 @@ bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const std::locale &s_lo for ( item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item ) { int iStart = m_iPos; - if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal, s_locale ) == 1 ) + if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal, locale, decimal, thousand ) == 1 ) { // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2 strTok = m_strFormula.mid ( iStart, m_iPos-iStart ); diff --git a/src/libs/qmuparser/qmuparsertokenreader.h b/src/libs/qmuparser/qmuparsertokenreader.h index fc04776e3..b023e6b63 100644 --- a/src/libs/qmuparser/qmuparsertokenreader.h +++ b/src/libs/qmuparser/qmuparsertokenreader.h @@ -66,7 +66,7 @@ public: QChar GetArgSep() const; void IgnoreUndefVar(bool bIgnore); void ReInit(); - token_type ReadNextToken(const std::locale &s_locale); + token_type ReadNextToken(const QLocale &locale, const QChar &decimal, const QChar &thousand); private: /** @@ -111,7 +111,7 @@ private: bool IsFunTok(token_type &a_Tok); bool IsPostOpTok(token_type &a_Tok); bool IsOprt(token_type &a_Tok); - bool IsValTok(token_type &a_Tok, const std::locale &s_locale); + bool IsValTok(token_type &a_Tok, const QLocale &locale, const QChar &decimal, const QChar &thousand); bool IsVarTok(token_type &a_Tok); bool IsStrVarTok(token_type &a_Tok); bool IsUndefVarTok(token_type &a_Tok); diff --git a/src/libs/qmuparser/qmutokenparser.cpp b/src/libs/qmuparser/qmutokenparser.cpp index 47bc6cbe3..0dd8efd09 100644 --- a/src/libs/qmuparser/qmutokenparser.cpp +++ b/src/libs/qmuparser/qmutokenparser.cpp @@ -34,7 +34,7 @@ namespace qmu //--------------------------------------------------------------------------------------------------------------------- QmuTokenParser::QmuTokenParser() { - InitCharacterSets(); + InitCharSets(); setAllowSubexpressions(false);//Only one expression per time } @@ -57,7 +57,7 @@ QmuTokenParser::QmuTokenParser() QmuTokenParser::QmuTokenParser(const QString &formula, bool osSeparator, bool fromUser) :QmuFormulaBase() { - InitCharacterSets(); + InitCharSets(); setAllowSubexpressions(false);//Only one expression per time SetVarFactory(AddVariable, this); diff --git a/src/libs/vpatterndb/calculator.cpp b/src/libs/vpatterndb/calculator.cpp index 0ab8c11f8..7f8ac74f8 100644 --- a/src/libs/vpatterndb/calculator.cpp +++ b/src/libs/vpatterndb/calculator.cpp @@ -53,7 +53,7 @@ Calculator::Calculator() :QmuFormulaBase() { - InitCharacterSets(); + InitCharSets(); setAllowSubexpressions(false);//Only one expression per time SetSepForEval(); diff --git a/src/libs/vpatterndb/vtranslatevars.cpp b/src/libs/vpatterndb/vtranslatevars.cpp index 9328a70d6..e3b51c091 100644 --- a/src/libs/vpatterndb/vtranslatevars.cpp +++ b/src/libs/vpatterndb/vtranslatevars.cpp @@ -780,6 +780,11 @@ QString VTranslateVars::FormulaFromUser(const QString &formula, bool osSeparator } continue; } + + if (tValues.at(i) == QLocale().negativeSign()) + {// unary minus + newFormula.replace(tKeys.at(i), 1, '-'); + } } QLocale loc = QLocale(); // User locale @@ -916,6 +921,11 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator) } continue; } + + if (tValues.at(i) == QChar('-')) + {// unary minus + newFormula.replace(tKeys.at(i), 1, QLocale().negativeSign()); + } } QLocale loc = QLocale();// User locale @@ -936,11 +946,6 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator) loc = QLocale();// To user locale QString dStr = loc.toString(d);// Number string in user locale - const QChar thSep = loc.groupSeparator(); - if (thSep.isSpace()) - { - dStr.remove(thSep);// Remove thousand separator - } newFormula.replace(nKeys.at(i), nValues.at(i).length(), dStr); const int bias = nValues.at(i).length() - dStr.length(); if (bias != 0) diff --git a/src/test/ValentinaTest/ValentinaTest.pro b/src/test/ValentinaTest/ValentinaTest.pro index 3f5c649de..d4aab2df2 100644 --- a/src/test/ValentinaTest/ValentinaTest.pro +++ b/src/test/ValentinaTest/ValentinaTest.pro @@ -53,7 +53,9 @@ SOURCES += \ tst_vcubicbezierpath.cpp \ tst_vgobject.cpp \ tst_vsplinepath.cpp \ - tst_vpointf.cpp + tst_vpointf.cpp \ + tst_readval.cpp \ + tst_vtranslatevars.cpp win32-msvc*:SOURCES += stable.cpp @@ -77,7 +79,9 @@ HEADERS += \ tst_vcubicbezierpath.h \ tst_vgobject.h \ tst_vsplinepath.h \ - tst_vpointf.h + tst_vpointf.h \ + tst_readval.h \ + tst_vtranslatevars.h # Set using ccache. Function enable_ccache() defined in common.pri. $$enable_ccache() diff --git a/src/test/ValentinaTest/qttestmainlambda.cpp b/src/test/ValentinaTest/qttestmainlambda.cpp index 02cf31d74..60997e2ee 100644 --- a/src/test/ValentinaTest/qttestmainlambda.cpp +++ b/src/test/ValentinaTest/qttestmainlambda.cpp @@ -47,8 +47,11 @@ #include "tst_vgobject.h" #include "tst_vsplinepath.h" #include "tst_vpointf.h" +#include "tst_readval.h" +#include "tst_vtranslatevars.h" #include "../vmisc/def.h" +#include "../qmuparser/qmudef.h" int main(int argc, char** argv) { @@ -82,6 +85,8 @@ int main(int argc, char** argv) ASSERT_TEST(new TST_VCubicBezierPath()); ASSERT_TEST(new TST_VGObject()); ASSERT_TEST(new TST_VPointF()); + ASSERT_TEST(new TST_ReadVal()); + ASSERT_TEST(new TST_VTranslateVars()); return status; } diff --git a/src/test/ValentinaTest/tst_qmutokenparser.cpp b/src/test/ValentinaTest/tst_qmutokenparser.cpp index 9c9cb6cf3..001ce91a1 100644 --- a/src/test/ValentinaTest/tst_qmutokenparser.cpp +++ b/src/test/ValentinaTest/tst_qmutokenparser.cpp @@ -28,12 +28,14 @@ #include "tst_qmutokenparser.h" #include "../qmuparser/qmutokenparser.h" +#include "../vmisc/logging.h" #include //--------------------------------------------------------------------------------------------------------------------- TST_QmuTokenParser::TST_QmuTokenParser(QObject *parent) - :QObject(parent) + : QObject(parent), + m_systemLocale(QLocale::system()) { } @@ -56,6 +58,7 @@ void TST_QmuTokenParser::IsSingle_data() QTest::newRow("Correct C locale 15500") << "15500" << true; QTest::newRow("Correct C locale 15,500") << "15,500" << true; QTest::newRow("Correct C locale 15,500.1") << "15,500.1" << true; + QTest::newRow("Correct C locale 15500.1") << "15500.1" << true; QTest::newRow("Not C locale 15,5") << "15,5" << false; QTest::newRow("Not C locale 15.500,1") << "15.500,1" << false; } @@ -68,3 +71,95 @@ void TST_QmuTokenParser::IsSingle() QCOMPARE(qmu::QmuTokenParser::IsSingle(formula), result); } + +//--------------------------------------------------------------------------------------------------------------------- +void TST_QmuTokenParser::TokenFromUser_data() +{ + QTest::addColumn("formula"); + QTest::addColumn("result"); + QTest::addColumn("locale"); + + const QList allLocales = + QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + for(int i = 0; i < allLocales.size(); ++i) + { + const QLocale locale = allLocales.at(i); + PrepareVal(1000.5, locale); + PrepareVal(-1000.5, locale); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_QmuTokenParser::TokenFromUser() +{ + QFETCH(QString, formula); + QFETCH(bool, result); + QFETCH(QLocale, locale); + + QLocale::setDefault(locale); + + QCOMPARE(IsSingleFromUser(formula), result); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_QmuTokenParser::cleanupTestCase() +{ + QLocale::setDefault(m_systemLocale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_QmuTokenParser::PrepareVal(qreal val, const QLocale &locale) +{ + const QString formula = locale.toString(val); + QString string = formula; + QString tag = QString("%1. String '%2'").arg(locale.name()).arg(string); + QTest::newRow(qUtf8Printable(tag)) << string << true << locale; + + string = formula+QLatin1String("+"); + tag = QString("%1. String '%2'").arg(locale.name()).arg(string); + QTest::newRow(qUtf8Printable(tag)) << string << false << locale; + + string = formula+QLatin1String("+")+formula; + tag = QString("%1. String '%2'").arg(locale.name()).arg(string); + QTest::newRow(qUtf8Printable(tag)) << string << false << locale; + + string = formula+QString("+б"); + tag = QString("%1. String '%2'").arg(locale.name()).arg(string); + QTest::newRow(qUtf8Printable(tag)) << string << false << locale; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool TST_QmuTokenParser::IsSingleFromUser(const QString &formula) +{ + if (formula.isEmpty()) + { + return false;// if don't know say no + } + + QMap tokens; + QMap numbers; + + try + { + QScopedPointer cal(new qmu::QmuTokenParser(formula, true, true)); + tokens = cal->GetTokens();// Tokens (variables, measurements) + numbers = cal->GetNumbers();// All numbers in expression + } + catch (const qmu::QmuParserError &e) + { + Q_UNUSED(e) + return false;// something wrong with formula, say no + } + + // Remove "-" from tokens list if exist. If don't do that unary minus operation will broken. + qmu::QmuFormulaBase::RemoveAll(tokens, QLocale().negativeSign()); + + if (tokens.isEmpty() && numbers.size() == 1) + { + return true; + } + else + { + return false; + } +} diff --git a/src/test/ValentinaTest/tst_qmutokenparser.h b/src/test/ValentinaTest/tst_qmutokenparser.h index 22930d27d..a72d8bde9 100644 --- a/src/test/ValentinaTest/tst_qmutokenparser.h +++ b/src/test/ValentinaTest/tst_qmutokenparser.h @@ -29,18 +29,26 @@ #ifndef TST_QMUTOKENPARSER_H #define TST_QMUTOKENPARSER_H +#include #include class TST_QmuTokenParser : public QObject { Q_OBJECT public: - Q_DISABLE_COPY(TST_QmuTokenParser) explicit TST_QmuTokenParser(QObject *parent = nullptr); - private slots: void IsSingle_data(); void IsSingle(); + void TokenFromUser_data(); + void TokenFromUser(); + void cleanupTestCase(); +private: + Q_DISABLE_COPY(TST_QmuTokenParser) + QLocale m_systemLocale; + + void PrepareVal(qreal val, const QLocale &locale); + bool IsSingleFromUser(const QString &formula); }; #endif // TST_QMUTOKENPARSER_H diff --git a/src/test/ValentinaTest/tst_readval.cpp b/src/test/ValentinaTest/tst_readval.cpp new file mode 100644 index 000000000..e451630a7 --- /dev/null +++ b/src/test/ValentinaTest/tst_readval.cpp @@ -0,0 +1,153 @@ +/************************************************************************ + ** + ** @file + ** @author Roman Telezhynskyi + ** @date 30 12, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#include "tst_readval.h" +#include "../qmuparser/qmudef.h" +#include "../vmisc/logging.h" + +#include +#include + +//--------------------------------------------------------------------------------------------------------------------- +TST_ReadVal::TST_ReadVal(QObject *parent) + : QObject(parent), + m_systemLocale(QLocale::system()) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::TestReadVal_data() +{ + QTest::addColumn("formula"); + QTest::addColumn("expCount"); + QTest::addColumn("expVal"); + QTest::addColumn("locale"); + + const QList allLocales = + QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + for(int i = 0; i < allLocales.size(); ++i) + { + const QLocale locale = allLocales.at(i); + PrepareVal(1., locale); + PrepareVal(1.0, locale); + PrepareVal(-1.0, locale); + PrepareVal(1.5, locale); + PrepareVal(-1.5, locale); + PrepareVal(1000.0, locale); + PrepareVal(-1000.0, locale); + PrepareVal(1000.5, locale); + PrepareVal(-1000.5, locale); + PrepareVal(std::numeric_limits::max(), locale); + PrepareVal(-std::numeric_limits::max(), locale); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::TestReadVal() +{ + TestVal(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::TestInvalidData_data() +{ + QTest::addColumn("formula"); + QTest::addColumn("expCount"); + QTest::addColumn("expVal"); + QTest::addColumn("locale"); + + // Test invalid values + const QLocale locale = QLocale::c(); + PrepareString(QString(), locale); + PrepareString(QString("-1.000.5"), locale); + PrepareString(QString("1.000.5"), locale); + PrepareString(QString("-1.000,5"), locale); + PrepareString(QString("1.000,5"), locale); + PrepareString(QString("-1.0.00,5"), locale); + PrepareString(QString("1.0.00,5"), locale); + PrepareString(QString("7,5"), locale); + PrepareString(QString("-7,5"), locale); + PrepareString(QString("- 7,5"), locale); + PrepareString(QString("- 7.5"), locale); + PrepareString(QString("1,0,00.5"), locale); + PrepareString(QString("1,,000.5"), locale); + PrepareString(QString(",5"), locale); + PrepareString(QString(","), locale); + PrepareString(QString("."), locale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::TestInvalidData() +{ + TestVal(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::cleanupTestCase() +{ + QLocale::setDefault(m_systemLocale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::PrepareVal(qreal val, const QLocale &locale) +{ + const QString string = locale.toString(val); + bool ok = false; + const double d = locale.toDouble(string, &ok); + PrepareString(string, locale, d, string.size()); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::PrepareString(const QString &str, const QLocale &locale, qreal val, int count) +{ + const QString tag = QString("%1. String '%2'").arg(locale.name()).arg(str); + QTest::newRow(qUtf8Printable(tag)) << str << count << val << locale; +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_ReadVal::TestVal() +{ + QFETCH(QString, formula); + QFETCH(int, expCount); + QFETCH(qreal, expVal); + QFETCH(QLocale, locale); + + qreal resVal = 0; + QLocale::setDefault(locale); + + const int resCount = ReadVal(formula, resVal, locale, locale.decimalPoint(), locale.groupSeparator()); + + QString errorMsg = QString("Conversion failed. Locale: '%1'.").arg(locale.name()); + QVERIFY2(resCount == expCount, qUtf8Printable(errorMsg)); + + if (resCount != -1) + { + QString errorMsg = QString("Unexpected result. Locale: '%1'.").arg(locale.name()); + QVERIFY2(QmuFuzzyComparePossibleNulls(resVal, expVal), qUtf8Printable(errorMsg)); + } +} diff --git a/src/test/ValentinaTest/tst_readval.h b/src/test/ValentinaTest/tst_readval.h new file mode 100644 index 000000000..abc78e93e --- /dev/null +++ b/src/test/ValentinaTest/tst_readval.h @@ -0,0 +1,58 @@ +/************************************************************************ + ** + ** @file + ** @author Roman Telezhynskyi + ** @date 30 12, 2016 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2016 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef TST_READVAL_H +#define TST_READVAL_H + +#include + +#include +#include + +class TST_ReadVal : public QObject +{ + Q_OBJECT +public: + explicit TST_ReadVal(QObject *parent = nullptr); +private slots: + void TestReadVal_data(); + void TestReadVal(); + void TestInvalidData_data(); + void TestInvalidData(); + void cleanupTestCase(); +private: + Q_DISABLE_COPY(TST_ReadVal) + QLocale m_systemLocale; + + void PrepareVal(qreal val, const QLocale &locale); + void PrepareString(const QString &str, const QLocale &locale, qreal val=0, int count=-1); + + void TestVal(); +}; + +#endif // TST_READVAL_H diff --git a/src/test/ValentinaTest/tst_vtranslatevars.cpp b/src/test/ValentinaTest/tst_vtranslatevars.cpp new file mode 100644 index 000000000..f62492f3f --- /dev/null +++ b/src/test/ValentinaTest/tst_vtranslatevars.cpp @@ -0,0 +1,174 @@ +/************************************************************************ + ** + ** @file + ** @author Roman Telezhynskyi + ** @date 2 1, 2017 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2017 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#include "tst_vtranslatevars.h" +#include "../vmisc/logging.h" +#include "../vpatterndb/vtranslatevars.h" +#include "../qmuparser/qmuparsererror.h" + +#include + +//--------------------------------------------------------------------------------------------------------------------- +TST_VTranslateVars::TST_VTranslateVars(QObject *parent) + : QObject(parent), + m_trMs(nullptr), + m_systemLocale(QLocale::system()) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::initTestCase() +{ + m_trMs = new VTranslateVars(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::TestFormulaFromUser_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + QTest::addColumn("locale"); + + const QList allLocales = + QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + for(int i = 0; i < allLocales.size(); ++i) + { + PrepareValFromUser(1000.5, allLocales.at(i)); + PrepareValFromUser(-1000.5, allLocales.at(i)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::TestFormulaFromUser() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QFETCH(QLocale, locale); + + QLocale::setDefault(locale); + + QString result; + try + { + result = m_trMs->FormulaFromUser(input, true); + } + catch (qmu::QmuParserError &e)// In case something bad will happen + { + Q_UNUSED(e) + result = input; + } + + QCOMPARE(result, output); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::TestFormulaToUser_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + QTest::addColumn("locale"); + + const QList allLocales = + QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + for(int i = 0; i < allLocales.size(); ++i) + { + PrepareValToUser(1000.5, allLocales.at(i)); + PrepareValToUser(-1000.5, allLocales.at(i)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::TestFormulaToUser() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QFETCH(QLocale, locale); + + QLocale::setDefault(locale); + + QString result; + try + { + result = m_trMs->FormulaToUser(input, true); + } + catch (qmu::QmuParserError &e)// In case something bad will happen + { + Q_UNUSED(e) + result = input; + } + + QCOMPARE(result, output); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::cleanupTestCase() +{ + delete m_trMs; + QLocale::setDefault(m_systemLocale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::PrepareValFromUser(double d, const QLocale &locale) +{ + const QString formulaToSystem = QLocale::c().toString(d); + const QString formulaFromUser = locale.toString(d); + + PrepareVal(formulaFromUser, formulaToSystem, locale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::PrepareValToUser(double d, const QLocale &locale) +{ + const QString formulaFromSystem = QLocale::c().toString(d); + const QString formulaToUser = locale.toString(d); + + PrepareVal(formulaFromSystem, formulaToUser, locale); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_VTranslateVars::PrepareVal(const QString &inputFormula, const QString &outputFormula, const QLocale &locale) +{ + QString inputString = inputFormula; + QString outputString = outputFormula; + + auto PREPARE_CASE = [locale](const QString &inputString, const QString &outputString) + { + QString tag = QString("%1. String '%2'").arg(locale.name()).arg(inputString); + QTest::newRow(qUtf8Printable(tag)) << inputString << outputString << locale; + }; + + PREPARE_CASE(inputString, outputString); + + inputString = inputFormula+QLatin1String("+")+inputFormula; + outputString = outputFormula+QLatin1String("+")+outputFormula; + PREPARE_CASE(inputString, outputString); + + inputString = inputFormula+QString("+a"); + outputString = outputFormula+QString("+a"); + PREPARE_CASE(inputString, outputString); +} diff --git a/src/test/ValentinaTest/tst_vtranslatevars.h b/src/test/ValentinaTest/tst_vtranslatevars.h new file mode 100644 index 000000000..90657bd1a --- /dev/null +++ b/src/test/ValentinaTest/tst_vtranslatevars.h @@ -0,0 +1,60 @@ +/************************************************************************ + ** + ** @file + ** @author Roman Telezhynskyi + ** @date 2 1, 2017 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2017 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef TST_VTRANSLATEVARS_H +#define TST_VTRANSLATEVARS_H + +#include +#include +#include + +class VTranslateVars; + +class TST_VTranslateVars : public QObject +{ + Q_OBJECT +public: + explicit TST_VTranslateVars(QObject *parent = nullptr); +private slots: + void initTestCase(); + void TestFormulaFromUser_data(); + void TestFormulaFromUser(); + void TestFormulaToUser_data(); + void TestFormulaToUser(); + void cleanupTestCase(); +private: + Q_DISABLE_COPY(TST_VTranslateVars) + VTranslateVars *m_trMs; + QLocale m_systemLocale; + + void PrepareValFromUser(double d, const QLocale &locale); + void PrepareValToUser(double d, const QLocale &locale); + void PrepareVal(const QString &inputFormula, const QString &outputFormula, const QLocale &locale); +}; + +#endif // TST_VTRANSLATEVARS_H