Resolved issue #606. Mac OS X. Can’t type in measurements due to digit count

limitation.

--HG--
branch : develop
This commit is contained in:
Roman Telezhynskyi 2017-01-03 11:14:32 +02:00
parent 290f78651d
commit da6cd92b22
27 changed files with 1025 additions and 109 deletions

View File

@ -48,6 +48,7 @@
- [#582] Issue with standard path to shared data on Linux. - [#582] Issue with standard path to shared data on Linux.
- [#595] GapWidth affecting to the margins. - [#595] GapWidth affecting to the margins.
- [#589] Valentina lock up if not enough space for label. - [#589] Valentina lock up if not enough space for label.
- [#606] Mac OS X. Cant type in measurements due to digit count limitation.
# Version 0.4.6 # Version 0.4.6
- [#594] Broken export on Mac. - [#594] Broken export on Mac.

View File

@ -0,0 +1,275 @@
/***************************************************************************************************
**
** Copyright (C) 2016 Roman Telezhynskyi <dismine(at)gmail.com>
**
** 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 <QLocale>
#include <QSet>
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<ushort>(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<QChar> 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;
}

View File

@ -90,6 +90,25 @@ QT_WARNING_DISABLE_GCC("-Wattributes")
#ifdef Q_CC_MSVC #ifdef Q_CC_MSVC
#include <ciso646> #include <ciso646>
#endif /* Q_CC_MSVC */ #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() 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 #endif // QMUDEF_H

View File

@ -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 * 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. * we set all posible unique characters from all alphabets.
* *
*/ */
void QmuFormulaBase::InitCharacterSets() void QmuFormulaBase::InitCharSets()
{ {
//String with all unique symbols for supported alphabets. //String with all unique symbols for supported alphabets.
//See script alphabets.py for generation and more information. //See script alphabets.py for generation and more information.
@ -68,9 +68,18 @@ void QmuFormulaBase::InitCharacterSets()
<< "ЪƯخγвŅԴŪضλкԼĴσтÅՄنъÍՌRӕՔZÝŜbåդjíլļrӵմzýռپêЅքćچЍďӱҒЕůėژșΘØҚНğńءΠFҢХħΨҪ" << "ЪƯخγвŅԴŪضλкԼĴσтÅՄنъÍՌRӕՔZÝŜbåդjíլļrӵմzýռپêЅքćچЍďӱҒЕůėژșΘØҚНğńءΠFҢХħΨҪ"
<< "ЭųįҶرҲеԷňعθҺнԿفπÂхՇψÊэšՏÒUəÚѝŻşҤӑâeէŐımկòuշÕúտŔ"; << "ЭųįҶرҲеԷňعθҺнԿفπÂхՇψÊэšՏÒUəÚѝŻşҤӑâeէŐımկòuշÕúտŔ";
INIT_LOCALE_VARIABLES(m_locale);
// Defining identifier character sets // Defining identifier character sets
DefineNameChars(QStringLiteral("0123456789_@#'") + symbols.join("")); const QString nameChars = QString() + sign0 + sign1 + sign2 + sign3 + sign4 + sign5 + sign6 + sign7 + sign8 +
DefineOprtChars(symbols.join("") + QStringLiteral("+-*^/?<>=!$%&|~_")); 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) if (fromUser)
{ {
const QLocale loc = QLocale();
setLocale(loc);
SetArgSep(';');
if (osSeparator) if (osSeparator)
{ {
const QLocale loc = QLocale::system(); setDecimalPoint(loc.decimalPoint());
SetDecSep(loc.decimalPoint().toLatin1()); setThousandsSeparator(loc.groupSeparator());
SetThousandsSep(loc.groupSeparator().toLatin1());
SetArgSep(';');
return; return;
} }
} }
@ -114,8 +124,8 @@ void QmuFormulaBase::SetSepForTr(bool osSeparator, bool fromUser)
void QmuFormulaBase::SetSepForEval() void QmuFormulaBase::SetSepForEval()
{ {
SetArgSep(';'); SetArgSep(';');
SetThousandsSep(','); setThousandsSeparator(',');
SetDecSep('.'); setDecimalPoint('.');
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------

View File

@ -41,14 +41,14 @@ public:
QmuFormulaBase(); QmuFormulaBase();
virtual ~QmuFormulaBase() Q_DECL_OVERRIDE; virtual ~QmuFormulaBase() Q_DECL_OVERRIDE;
protected: virtual void InitCharSets() Q_DECL_OVERRIDE;
void InitCharacterSets();
static qreal* AddVariable(const QString &a_szName, void *a_pUserData);
void SetSepForTr(bool osSeparator, bool fromUser);
void SetSepForEval();
static void RemoveAll(QMap<int, QString> &map, const QString &val); static void RemoveAll(QMap<int, QString> &map, const QString &val);
protected:
static qreal* AddVariable(const QString &a_szName, void *a_pUserData);
void SetSepForTr(bool osSeparator, bool fromUser);
void SetSepForEval();
private: private:
Q_DISABLE_COPY(QmuFormulaBase) Q_DISABLE_COPY(QmuFormulaBase)
}; };

View File

@ -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. * @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. * @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); qreal fVal(0);
std::wstring a_szExprStd = a_szExpr.toStdWString(); const int pos = ReadVal(a_szExpr, fVal, locale, decimal, thousand);
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
if (iEnd==static_cast<stringstream_type::pos_type>(-1)) if (pos == -1)
{ {
return 0; return 0;
} }
*a_iPos += static_cast<int>(iEnd); *a_iPos += pos;
*a_fVal = fVal; *a_fVal = fVal;
return 1; return 1;
} }
@ -340,7 +336,7 @@ void QmuParser::InitConst()
*/ */
void QmuParser::InitOprt() void QmuParser::InitOprt()
{ {
DefineInfixOprt("-", UnaryMinus); DefineInfixOprt(m_locale.negativeSign(), UnaryMinus);
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------

View File

@ -27,7 +27,6 @@
#include <QtGlobal> #include <QtGlobal>
#include <locale> #include <locale>
#include "../qmuparser/qmuparser_global.h"
#include "qmuparser_global.h" #include "qmuparser_global.h"
#include "qmuparserbase.h" #include "qmuparserbase.h"
@ -59,7 +58,8 @@ namespace qmu
virtual void OnDetectVar(const QString &pExpr, int &nStart, int &nEnd) Q_DECL_OVERRIDE; 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; qreal Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon = 0) const;
protected: 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 // hyperbolic functions
static qreal Sinh(qreal); static qreal Sinh(qreal);
static qreal Cosh(qreal); static qreal Cosh(qreal);

View File

@ -11,7 +11,8 @@ SOURCES += \
$$PWD/qmuparsertest.cpp \ $$PWD/qmuparsertest.cpp \
$$PWD/qmutranslation.cpp \ $$PWD/qmutranslation.cpp \
$$PWD/qmuformulabase.cpp \ $$PWD/qmuformulabase.cpp \
$$PWD/qmutokenparser.cpp $$PWD/qmutokenparser.cpp \
$$PWD/qmudef.cpp
win32-msvc*:SOURCES += $$PWD/stable.cpp win32-msvc*:SOURCES += $$PWD/stable.cpp

View File

@ -39,7 +39,7 @@ OBJECTS_DIR = obj
include(qmuparser.pri) include(qmuparser.pri)
VERSION = 2.4.1 VERSION = 2.5.0
# Allow MAC OS X to find library inside a bundle # Allow MAC OS X to find library inside a bundle
macx:QMAKE_SONAME_PREFIX = @rpath macx:QMAKE_SONAME_PREFIX = @rpath

View File

@ -62,11 +62,31 @@ const QStringList QmuParserBase::c_DefaultOprt = QStringList()<< "<=" << ">=" <<
* @brief Constructor. * @brief Constructor.
*/ */
QmuParserBase::QmuParserBase() QmuParserBase::QmuParserBase()
:s_locale(std::locale(std::locale::classic(), new change_dec_sep<char_type>('.'))), : m_locale(QLocale::c()),
m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), m_vStringVarBuf(), m_pTokenReader(), m_decimalPoint(QLocale::c().decimalPoint()),
m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_StrVarDef(), m_VarDef(), m_thousandsSeparator(QLocale::c().groupSeparator()),
m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_nIfElseCounter(0), m_vStackBuffer(), m_pParseFormula(&QmuParserBase::ParseString),
m_nFinalResultIdx(0), m_Tokens(QMap<int, QString>()), m_Numbers(QMap<int, QString>()), allowSubexpressions(true) 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<int, QString>()),
m_Numbers(QMap<int, QString>()),
allowSubexpressions(true)
{ {
InitTokenReader(); InitTokenReader();
} }
@ -78,11 +98,31 @@ QmuParserBase::QmuParserBase()
* Tha parser can be safely copy constructed but the bytecode is reset during copy construction. * Tha parser can be safely copy constructed but the bytecode is reset during copy construction.
*/ */
QmuParserBase::QmuParserBase(const QmuParserBase &a_Parser) QmuParserBase::QmuParserBase(const QmuParserBase &a_Parser)
:s_locale(a_Parser.getLocale()), m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), : m_locale(a_Parser.getLocale()),
m_vStringVarBuf(), m_pTokenReader(), m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_decimalPoint(a_Parser.getDecimalPoint()),
m_StrVarDef(), m_VarDef(), m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_thousandsSeparator(a_Parser.getThousandsSeparator()),
m_nIfElseCounter(0), m_vStackBuffer(), m_nFinalResultIdx(0), m_Tokens(QMap<int, QString>()), m_pParseFormula(&QmuParserBase::ParseString),
m_Numbers(QMap<int, QString>()), allowSubexpressions(true) 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<int, QString>()),
m_Numbers(QMap<int, QString>()),
allowSubexpressions(true)
{ {
m_pTokenReader.reset(new token_reader_type(this)); m_pTokenReader.reset(new token_reader_type(this));
Assign(a_Parser); Assign(a_Parser);
@ -150,38 +190,6 @@ void QmuParserBase::Assign(const QmuParserBase &a_Parser)
m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; 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<char_type> >(s_locale).thousands_sep();
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(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<char_type> >(s_locale).decimal_point();
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
/** /**
* @brief Resets the locale. * @brief Resets the locale.
@ -191,8 +199,10 @@ void QmuParserBase::SetThousandsSep(char_type cThousandsSep)
// cppcheck-suppress unusedFunction // cppcheck-suppress unusedFunction
void QmuParserBase::ResetLocale() void QmuParserBase::ResetLocale()
{ {
s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>('.')); setLocale(QLocale::c());
SetArgSep(','); 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 (;;) for (;;)
{ {
opt = m_pTokenReader->ReadNextToken(s_locale); opt = m_pTokenReader->ReadNextToken(m_locale, m_decimalPoint, m_thousandsSeparator);
switch (opt.GetCode()) switch (opt.GetCode())
{ {

View File

@ -31,11 +31,10 @@
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
#include <QtGlobal> #include <QtGlobal>
#include <locale>
#include <memory> #include <memory>
#include <string> #include <string>
#include <QLocale>
#include "../qmuparser/qmuparser_global.h"
#include "qmuparser_global.h" #include "qmuparser_global.h"
#include "qmuparserbytecode.h" #include "qmuparserbytecode.h"
#include "qmuparsercallback.h" #include "qmuparsercallback.h"
@ -81,8 +80,6 @@ public:
int GetNumResults() const; int GetNumResults() const;
void SetExpr(const QString &a_sExpr); void SetExpr(const QString &a_sExpr);
void SetVarFactory(facfun_type a_pFactory, void *pUserData = nullptr); void SetVarFactory(facfun_type a_pFactory, void *pUserData = nullptr);
void SetDecSep(char_type cDecSep);
void SetThousandsSep(char_type cThousandsSep = 0);
void ResetLocale(); void ResetLocale();
void EnableOptimizer(bool a_bIsOn=true); void EnableOptimizer(bool a_bIsOn=true);
void EnableBuiltInOprt(bool a_bIsOn=true); void EnableBuiltInOprt(bool a_bIsOn=true);
@ -128,12 +125,20 @@ public:
void setAllowSubexpressions(bool value); void setAllowSubexpressions(bool value);
std::locale getLocale() const; QLocale getLocale() const;
void setLocale(const std::locale &value); void setLocale(const QLocale &value);
QChar getDecimalPoint() const;
void setDecimalPoint(const QChar &c);
QChar getThousandsSeparator() const;
void setThousandsSeparator(const QChar &c);
protected: protected:
static const QStringList c_DefaultOprt; 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_DbgDumpCmdCode;
static bool g_DbgDumpStack; static bool g_DbgDumpStack;
void Init(); void Init();

View File

@ -32,8 +32,8 @@
@brief This file contains standard definitions used by the parser. @brief This file contains standard definitions used by the parser.
*/ */
#define QMUP_VERSION "2.2.4" #define QMUP_VERSION "2.5.0"
#define QMUP_VERSION_DATE "20140504; GC" #define QMUP_VERSION_DATE "20170101; GC"
#define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" #define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -64,6 +64,8 @@
/** @brief Definition of the basic parser string type. */ /** @brief Definition of the basic parser string type. */
#define QMUP_STRING_TYPE std::wstring #define QMUP_STRING_TYPE std::wstring
class QLocale;
namespace qmu namespace qmu
{ {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -275,7 +277,8 @@ typedef qreal ( *strfun_type2 ) ( const QString &, qreal );
typedef qreal ( *strfun_type3 ) ( const QString &, qreal, qreal ); typedef qreal ( *strfun_type3 ) ( const QString &, qreal, qreal );
/** @brief Callback used for functions that identify values in a string. */ /** @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. */ /** @brief Callback used for variable creation factory functions. */
typedef qreal* ( *facfun_type ) ( const QString &, void* ); typedef qreal* ( *facfun_type ) ( const QString &, void* );

View File

@ -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' ) ) if ( a_szExpr.data()[1] == 0 || ( a_szExpr.data()[0] != '0' || a_szExpr.data()[1] != 'x' ) )
{ {
return 0; return 0;

View File

@ -293,7 +293,8 @@ private:
} }
// Custom value recognition // 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 // cppcheck-suppress functionStatic
int TestNames(); int TestNames();

View File

@ -213,7 +213,8 @@ void QmuParserTokenReader::ReInit()
/** /**
* @brief Read the next token from the string. * @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 ); assert ( m_pParser );
@ -245,7 +246,7 @@ QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const std::
{ {
return SaveBeforeReturn ( tok ); // Check for function argument separators 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 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. * @param a_Tok [out] If a value token is found it will be placed here.
* @return true if a value token has been found. * @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_pConstDef );
assert ( m_pParser ); 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 ) for ( item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item )
{ {
int iStart = m_iPos; 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 // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2
strTok = m_strFormula.mid ( iStart, m_iPos-iStart ); strTok = m_strFormula.mid ( iStart, m_iPos-iStart );

View File

@ -66,7 +66,7 @@ public:
QChar GetArgSep() const; QChar GetArgSep() const;
void IgnoreUndefVar(bool bIgnore); void IgnoreUndefVar(bool bIgnore);
void ReInit(); void ReInit();
token_type ReadNextToken(const std::locale &s_locale); token_type ReadNextToken(const QLocale &locale, const QChar &decimal, const QChar &thousand);
private: private:
/** /**
@ -111,7 +111,7 @@ private:
bool IsFunTok(token_type &a_Tok); bool IsFunTok(token_type &a_Tok);
bool IsPostOpTok(token_type &a_Tok); bool IsPostOpTok(token_type &a_Tok);
bool IsOprt(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 IsVarTok(token_type &a_Tok);
bool IsStrVarTok(token_type &a_Tok); bool IsStrVarTok(token_type &a_Tok);
bool IsUndefVarTok(token_type &a_Tok); bool IsUndefVarTok(token_type &a_Tok);

View File

@ -34,7 +34,7 @@ namespace qmu
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
QmuTokenParser::QmuTokenParser() QmuTokenParser::QmuTokenParser()
{ {
InitCharacterSets(); InitCharSets();
setAllowSubexpressions(false);//Only one expression per time setAllowSubexpressions(false);//Only one expression per time
} }
@ -57,7 +57,7 @@ QmuTokenParser::QmuTokenParser()
QmuTokenParser::QmuTokenParser(const QString &formula, bool osSeparator, bool fromUser) QmuTokenParser::QmuTokenParser(const QString &formula, bool osSeparator, bool fromUser)
:QmuFormulaBase() :QmuFormulaBase()
{ {
InitCharacterSets(); InitCharSets();
setAllowSubexpressions(false);//Only one expression per time setAllowSubexpressions(false);//Only one expression per time
SetVarFactory(AddVariable, this); SetVarFactory(AddVariable, this);

View File

@ -53,7 +53,7 @@
Calculator::Calculator() Calculator::Calculator()
:QmuFormulaBase() :QmuFormulaBase()
{ {
InitCharacterSets(); InitCharSets();
setAllowSubexpressions(false);//Only one expression per time setAllowSubexpressions(false);//Only one expression per time
SetSepForEval(); SetSepForEval();

View File

@ -780,6 +780,11 @@ QString VTranslateVars::FormulaFromUser(const QString &formula, bool osSeparator
} }
continue; continue;
} }
if (tValues.at(i) == QLocale().negativeSign())
{// unary minus
newFormula.replace(tKeys.at(i), 1, '-');
}
} }
QLocale loc = QLocale(); // User locale QLocale loc = QLocale(); // User locale
@ -916,6 +921,11 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator)
} }
continue; continue;
} }
if (tValues.at(i) == QChar('-'))
{// unary minus
newFormula.replace(tKeys.at(i), 1, QLocale().negativeSign());
}
} }
QLocale loc = QLocale();// User locale QLocale loc = QLocale();// User locale
@ -936,11 +946,6 @@ QString VTranslateVars::FormulaToUser(const QString &formula, bool osSeparator)
loc = QLocale();// To user locale loc = QLocale();// To user locale
QString dStr = loc.toString(d);// Number string in 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); newFormula.replace(nKeys.at(i), nValues.at(i).length(), dStr);
const int bias = nValues.at(i).length() - dStr.length(); const int bias = nValues.at(i).length() - dStr.length();
if (bias != 0) if (bias != 0)

View File

@ -53,7 +53,9 @@ SOURCES += \
tst_vcubicbezierpath.cpp \ tst_vcubicbezierpath.cpp \
tst_vgobject.cpp \ tst_vgobject.cpp \
tst_vsplinepath.cpp \ tst_vsplinepath.cpp \
tst_vpointf.cpp tst_vpointf.cpp \
tst_readval.cpp \
tst_vtranslatevars.cpp
win32-msvc*:SOURCES += stable.cpp win32-msvc*:SOURCES += stable.cpp
@ -77,7 +79,9 @@ HEADERS += \
tst_vcubicbezierpath.h \ tst_vcubicbezierpath.h \
tst_vgobject.h \ tst_vgobject.h \
tst_vsplinepath.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. # Set using ccache. Function enable_ccache() defined in common.pri.
$$enable_ccache() $$enable_ccache()

View File

@ -47,8 +47,11 @@
#include "tst_vgobject.h" #include "tst_vgobject.h"
#include "tst_vsplinepath.h" #include "tst_vsplinepath.h"
#include "tst_vpointf.h" #include "tst_vpointf.h"
#include "tst_readval.h"
#include "tst_vtranslatevars.h"
#include "../vmisc/def.h" #include "../vmisc/def.h"
#include "../qmuparser/qmudef.h"
int main(int argc, char** argv) 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_VCubicBezierPath());
ASSERT_TEST(new TST_VGObject()); ASSERT_TEST(new TST_VGObject());
ASSERT_TEST(new TST_VPointF()); ASSERT_TEST(new TST_VPointF());
ASSERT_TEST(new TST_ReadVal());
ASSERT_TEST(new TST_VTranslateVars());
return status; return status;
} }

View File

@ -28,12 +28,14 @@
#include "tst_qmutokenparser.h" #include "tst_qmutokenparser.h"
#include "../qmuparser/qmutokenparser.h" #include "../qmuparser/qmutokenparser.h"
#include "../vmisc/logging.h"
#include <QtTest> #include <QtTest>
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
TST_QmuTokenParser::TST_QmuTokenParser(QObject *parent) 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 15500") << "15500" << true;
QTest::newRow("Correct C locale 15,500") << "15,500" << 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 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,5") << "15,5" << false;
QTest::newRow("Not C locale 15.500,1") << "15.500,1" << 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); QCOMPARE(qmu::QmuTokenParser::IsSingle(formula), result);
} }
//---------------------------------------------------------------------------------------------------------------------
void TST_QmuTokenParser::TokenFromUser_data()
{
QTest::addColumn<QString>("formula");
QTest::addColumn<bool>("result");
QTest::addColumn<QLocale>("locale");
const QList<QLocale> 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<int, QString> tokens;
QMap<int, QString> numbers;
try
{
QScopedPointer<qmu::QmuTokenParser> 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;
}
}

View File

@ -29,18 +29,26 @@
#ifndef TST_QMUTOKENPARSER_H #ifndef TST_QMUTOKENPARSER_H
#define TST_QMUTOKENPARSER_H #define TST_QMUTOKENPARSER_H
#include <QLocale>
#include <QObject> #include <QObject>
class TST_QmuTokenParser : public QObject class TST_QmuTokenParser : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Q_DISABLE_COPY(TST_QmuTokenParser)
explicit TST_QmuTokenParser(QObject *parent = nullptr); explicit TST_QmuTokenParser(QObject *parent = nullptr);
private slots: private slots:
void IsSingle_data(); void IsSingle_data();
void IsSingle(); 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 #endif // TST_QMUTOKENPARSER_H

View File

@ -0,0 +1,153 @@
/************************************************************************
**
** @file
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "tst_readval.h"
#include "../qmuparser/qmudef.h"
#include "../vmisc/logging.h"
#include <QtTest>
#include <limits>
//---------------------------------------------------------------------------------------------------------------------
TST_ReadVal::TST_ReadVal(QObject *parent)
: QObject(parent),
m_systemLocale(QLocale::system())
{
}
//---------------------------------------------------------------------------------------------------------------------
void TST_ReadVal::TestReadVal_data()
{
QTest::addColumn<QString>("formula");
QTest::addColumn<int>("expCount");
QTest::addColumn<qreal>("expVal");
QTest::addColumn<QLocale>("locale");
const QList<QLocale> 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<double>::max(), locale);
PrepareVal(-std::numeric_limits<double>::max(), locale);
}
}
//---------------------------------------------------------------------------------------------------------------------
void TST_ReadVal::TestReadVal()
{
TestVal();
}
//---------------------------------------------------------------------------------------------------------------------
void TST_ReadVal::TestInvalidData_data()
{
QTest::addColumn<QString>("formula");
QTest::addColumn<int>("expCount");
QTest::addColumn<qreal>("expVal");
QTest::addColumn<QLocale>("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));
}
}

View File

@ -0,0 +1,58 @@
/************************************************************************
**
** @file
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef TST_READVAL_H
#define TST_READVAL_H
#include <QtCore/qglobal.h>
#include <QObject>
#include <QLocale>
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

View File

@ -0,0 +1,174 @@
/************************************************************************
**
** @file
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "tst_vtranslatevars.h"
#include "../vmisc/logging.h"
#include "../vpatterndb/vtranslatevars.h"
#include "../qmuparser/qmuparsererror.h"
#include <QtTest>
//---------------------------------------------------------------------------------------------------------------------
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<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<QLocale>("locale");
const QList<QLocale> 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<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<QLocale>("locale");
const QList<QLocale> 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);
}

View File

@ -0,0 +1,60 @@
/************************************************************************
**
** @file
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @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
** <https://bitbucket.org/dismine/valentina> 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 <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef TST_VTRANSLATEVARS_H
#define TST_VTRANSLATEVARS_H
#include <QtCore/qglobal.h>
#include <QLocale>
#include <QObject>
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