src/hbcore/cssparser/hbexpressionparser_p.cpp
changeset 30 80e4d18b72f5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/cssparser/hbexpressionparser_p.cpp	Mon Oct 04 00:38:12 2010 +0300
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbexpressionparser_p.h"
+#include "hbhash_p.h"
+
+#include <QStack>
+
+
+static bool parseExpressionOperand(
+    const QString &str,
+    HbExpressionParser::Token &type,
+    int &value)
+{
+    QString valueString = str;
+    if (valueString.startsWith("var(") && valueString.endsWith(")")) {
+        // remove var( and last )
+        type = HbExpressionParser::Variable;
+        value = hbHash(valueString.midRef(4, valueString.size()-5));
+    } else {
+        if (valueString.endsWith(QLatin1String("un"), Qt::CaseInsensitive)) {
+            valueString.chop(2);
+            type = HbExpressionParser::LengthInUnits;
+        } else if (valueString.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
+            valueString.chop(2);
+            type = HbExpressionParser::LengthInPixels;
+        } else if (valueString.endsWith(QLatin1String("mm"), Qt::CaseInsensitive)) {
+            valueString.chop(2);
+            type = HbExpressionParser::LengthInMillimeters;
+        } else {
+            // assume pixels
+            type = HbExpressionParser::LengthInPixels;
+        }
+        bool ok(true);
+        value = HbExpressionParser::toFixed(valueString.toFloat(&ok));
+        if (!ok) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static int precedence(HbExpressionParser::Token op)
+{
+    switch (op) {
+        case HbExpressionParser::Addition:
+        case HbExpressionParser::Subtraction:
+            return 1;
+        case HbExpressionParser::Multiplication:
+        case HbExpressionParser::Division:
+            return 2;
+        case HbExpressionParser::Negation:
+            return 3;
+        default:
+            return 0;
+    }
+}
+
+static bool isLeft(HbExpressionParser::Token op)
+{
+    switch (op) {
+        case HbExpressionParser::Addition:
+        case HbExpressionParser::Subtraction:
+        case HbExpressionParser::Multiplication:
+        case HbExpressionParser::Division:
+            return true;
+        case HbExpressionParser::Negation:
+        default:
+            return false;
+    }
+}
+
+
+bool HbExpressionParser::parse(const QString &str, QList<int> &tokens)
+{
+    tokens.clear();
+
+    // Parse the infix notation to "tempTokens"
+    QList<int> tempTokens;
+
+    const QChar SPACE(' ');
+    const QChar OPENPARENTHESIS('(');
+    const QChar CLOSEPARENTHESIS(')');
+    const QChar PLUS('+');
+    const QChar MINUS('-');
+    const QChar STAR('*');
+    const QChar SLASH('/');
+
+    int position = 0;
+
+    if (str.startsWith("expr(") && str.endsWith(")")) {
+        position = 4;
+    } else if (str.startsWith("-expr(") && str.endsWith(")")) {
+        position = 5;
+        tempTokens.append(LengthInPixels);
+        tempTokens.append(toFixed(-1.0));
+        tempTokens.append(Multiplication);
+    }
+
+    int begin = -1;
+    int nestingLevel = 0; 
+    bool parseVariable = false;
+    bool endMark = false;
+    int operatorCount = 1;//there can only be 2 sequental operators if the latter one is negation
+    while (position < str.size()) {
+        endMark = false;
+        bool appendCloseParenthesis(false);
+        if (str.at(position) == SPACE) {
+            endMark = true;
+        } else if ((str.at(position) == OPENPARENTHESIS) && !parseVariable) {
+            tempTokens.append(OpenParenthesis);
+            nestingLevel++;
+            position++;
+            operatorCount = 1;
+            continue;
+        } else if (str.at(position) == CLOSEPARENTHESIS) {
+            if (parseVariable) {
+                parseVariable = false;
+                position++;
+                continue;
+            }
+            appendCloseParenthesis = true;
+            nestingLevel--;
+            endMark = true;
+        } else if ((str.at(position) == MINUS) && !parseVariable) {
+            endMark = true;
+        } else if ((str.at(position) == PLUS) ||
+                   (str.at(position) == STAR) ||
+                   (str.at(position) == SLASH)) {
+            endMark = true;
+        }
+
+        if (endMark) {
+            if (begin >= 0) {
+                // parse operand
+                QString valueString = str.mid(begin, position - begin);
+                if (operatorCount==0) {
+                    // an operand without operator
+                    return false;
+                }
+                HbExpressionParser::Token type;
+                int val;
+                if ( !parseExpressionOperand(valueString, type, val ) ) {
+                    return false;
+                }
+                tempTokens.append(type);
+                tempTokens.append(val);
+                operatorCount = 0;
+            }
+            begin = -1;
+            if (str.at(position) == PLUS) {
+                if (operatorCount > 0) {
+                    return false;
+                }
+                tempTokens.append(Addition);
+                operatorCount++;
+            } else if (str.at(position) == MINUS) {
+                if (operatorCount == 1) {
+                    tempTokens.append(Negation);
+                } else if (operatorCount > 1) {
+                    return false;
+                } else {
+                    tempTokens.append(Subtraction);
+                }
+                operatorCount++;
+            } else if (str.at(position) == STAR) {
+                if (operatorCount > 0) {
+                    return false;
+                }
+                tempTokens.append(Multiplication);
+                operatorCount++;
+            } else if (str.at(position) == SLASH) {
+                if (operatorCount > 0) {
+                    return false;
+                }
+                tempTokens.append(Division);
+                operatorCount++;
+            }
+            if (appendCloseParenthesis) {
+                tempTokens.append(CloseParenthesis);                
+                if (operatorCount != 0) {
+                    // an operator without operand
+                    return false;
+                }
+            }
+            position++;
+            continue;
+        }
+
+        if (begin == -1) {
+            begin = position;
+        }
+
+        // flag variable parsing (variable syntax contains parenthesis)
+        if ((str.at(position) == QChar('v')) && !parseVariable) {
+            parseVariable = true;
+            position++;
+            continue;
+        }
+        position++;
+    }
+
+    // check for unmatching parentheses
+    if (nestingLevel != 0) {
+        return false;
+    }
+
+    // parse last value
+    if (begin >= 0) {
+        // parse operand
+        if (operatorCount==0) {
+            // an operand without operator
+            return false;
+        }
+        QString valueString = str.mid(begin, position - begin);
+        HbExpressionParser::Token type;
+        int val;
+        if ( !parseExpressionOperand(valueString, type, val ) ) {
+            return false;
+        }
+        tempTokens.append(type);
+        tempTokens.append(val);
+    } else {
+        if (operatorCount!=0) {
+            // an operator without operand
+            return false;
+        }
+    }
+
+    if (tempTokens.count()<2) {
+        return false;
+    }
+
+    // Convert the infix notation to RPN with "shunting-yard" algorithm
+
+    QStack<Token> operatorStack;
+    for (int i=0; i<tempTokens.count(); i++) {
+        Token t = (Token)tempTokens.at(i);
+        switch (t) {
+            case Variable:
+            case LengthInPixels:
+            case LengthInUnits:
+            case LengthInMillimeters:
+                tokens.append(tempTokens.at(i));
+                i++;
+                tokens.append(tempTokens.at(i));
+                break;
+            case OpenParenthesis:
+                operatorStack.push(t);
+                break;
+            case CloseParenthesis:
+                while (operatorStack.count()) {
+                    Token op = operatorStack.pop();
+                    if (op == OpenParenthesis) {
+                        break;
+                    } else {
+                        tokens.append(op);
+                    }
+                }
+                break;
+            case Addition:
+            case Subtraction:
+            case Multiplication:
+            case Division:
+            case Negation:
+                while (operatorStack.count()) {
+                    Token op = operatorStack.top();
+                    if ((isLeft(t) && (precedence(t) <= precedence(op))) ||
+                        (!isLeft(t) && (precedence(t) < precedence(op)))) {
+                        tokens.append(op);
+                        operatorStack.pop();
+                    } else {
+                        break;
+                    }
+                }
+                operatorStack.push(t);
+                break;
+            default:
+                break;
+        }
+    }
+    while (operatorStack.count()) {
+        tokens.append(operatorStack.pop());
+    }
+
+    return true;
+}
+
+int HbExpressionParser::toFixed(float f)
+{
+    Q_ASSERT( sizeof(quint32) == sizeof(float) );
+    quint32 u;
+    memcpy(&u, &f, sizeof(quint32));
+    return u;
+}
+
+int HbExpressionParser::toFixed(double d)
+{
+    return toFixed((float)d);
+}
+
+qreal HbExpressionParser::fromFixed(int i)
+{
+    float f;
+    memcpy(&f, &i, sizeof(float));
+    return f;
+}