/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Functionality of function analysis 
*
*/


#include "la.hpp"

// ----------------------------------------------------------------------------------------------------------

/**
 * This flags affects to the function comparison policy.
 */
// Default policy (=exact match):
#define COMPARE_FLAG_NONE                       0x00000000

// This flag means that adding a 'const' qualifier to a function
// parameter does not generate BBC break issue:
#define COMPARE_FLAG_ALLOW_CONST_ADDITION       0x00000001

// ----------------------------------------------------------------------------------------------------------

// Forward declaration
class ParameterList;

// ParameterElement class represents a parameter element, which has an actual
// value and optional sub-parameters.
class ParameterElement
{
public:
    // Default constructor and destructor
    ParameterElement();
    virtual ~ParameterElement();

    // Copy constructor
    ParameterElement(const ParameterElement& rhs);
    // Assignment operator
    const ParameterElement& operator= (const ParameterElement& rhs);

    // Methods for getting the element value.
    string& Value() { return val; };
    const string& Value() const { return val; };

    // Methods for getting the sub-parameters.
    ParameterList* SubParameters() { return subPars; };
    const ParameterList* SubParameters() const { return subPars; };

    // Sets the sub-parameters. Takes the ownership to the given 
    // sub-parameter list pointer.
    void SetSubParameters( ParameterList* pPars );

    // Clears the value and sub-parameters.
    void Clear();

private:
    string val;
    ParameterList* subPars;
};

// ----------------------------------------------------------------------------------------------------------

typedef vector<ParameterElement> FunctionParameter;

class ParameterList : public vector<FunctionParameter>
{};

ParameterElement::ParameterElement() 
{
    subPars = new ParameterList(); 
}
ParameterElement::~ParameterElement()
{
    delete subPars;
}
ParameterElement::ParameterElement(const ParameterElement& rhs)
{
    val = rhs.val;
    if( rhs.subPars )
        subPars = new ParameterList(*(rhs.subPars));
    else
        subPars = 0;
}

void ParameterElement::Clear() 
{
    val.clear();
    if( subPars )
        subPars->clear();
}

const ParameterElement& ParameterElement::operator= (const ParameterElement& rhs)
    {
        if( this != &rhs )
        {
            val = rhs.val;
            delete subPars;
            if( rhs.subPars )
                subPars = new ParameterList(*(rhs.subPars));
            else
                subPars = 0;
        }
        return *this;
    }

void ParameterElement::SetSubParameters( ParameterList* pPars )
{
    delete subPars;
    subPars = pPars;
}

// ----------------------------------------------------------------------------------------------------------

/**
 * This structure represents the C++ cv-qualifier.
 */
struct CVQualifier
{
    static const string constQualifier;
    static const string volatileQualifier;
    static const string constVolatileQualifier;

    CVQualifier() { isConst = false; isVolatile = false; };

    /**
     * Set function gets <code>std::string</code> as an input and sets const 
     * and/or volatile flags according to the input.
     * @param cvStr String representing the const and / or volatile qualifier
     * i.e "const", "volatile" or "const volatile".
     */
    void Set(string const& cvStr)
    {
        if( cvStr == constQualifier )
        {
            isConst = true;         
            isVolatile = false;
        }
        else if( cvStr == volatileQualifier )
        {
            isConst = false;
            isVolatile = true;
        }
        else if( cvStr == constVolatileQualifier )
        {
            isConst = true;
            isVolatile = true;
        }        
        else
        {
            isConst = false;
            isVolatile = false;
        }
    };

    /**
     * Sets const and/or volatile flags
     * @param c 'const' qualifier. If TRUE, isConst flag is set to TRUE
     * @param v 'volatile' qualifier. If TRUE, isVolatile flag is set to TRUE
     */
    void Set( bool c, bool v )
    {
        isConst = c;
        isVolatile = v;
    };
    bool isConst;
    bool isVolatile;
};

// ----------------------------------------------------------------------------------------------------------

const string CVQualifier::constQualifier = string("const");
const string CVQualifier::volatileQualifier = string("volatile");
const string CVQualifier::constVolatileQualifier = string("const volatile");

// ----------------------------------------------------------------------------------------------------------


// This represents the function signature
struct FuncSignature
{    
    string nestedName; // name part
    ParameterList parameters; // parameters
    CVQualifier cvQualifier; // cv-qualifier of function
};

// ----------------------------------------------------------------------------------------------------------


bool AreParametersCompatible(const FunctionParameter& basePar, const FunctionParameter& currPar, unsigned int CompareFlags = COMPARE_FLAG_NONE);
string::const_iterator RemoveSpaces(string::const_iterator start, string::const_iterator end, string::const_iterator& result);
string::size_type ReadCharsInTag(const string& sig, string& argElem, string::size_type startPos, pair<char, char> const& tag);


// ----------------------------------------------------------------------------------------------------------

/**
 * Reads characters inside the given tag. Starts looking for the
 * starting tag at startPos. 
 * @param sig Reference to the string representing the function signature. 
 * @param argElem Reference to the string in which the parameter list is read.
 * @param startPos Tag starting point (e.g. '<' or '(') is startet to be searched
 *        after this location.
 * @param tag This object defines the starting and ending characters of the tag (e.g. '<' and '>').
 * @return Size of the string between the start and end characters of the tag.
 */
string::size_type ReadCharsInTag(const string& sig, string& argElem, string::size_type startPos, pair<char, char> const& tag)
{
    string::size_type sigLen = sig.size();
    if( sigLen == 0 )
    {
        return 0;
    }

    if( sig.at(startPos) != tag.first )
    {
        startPos = sig.find(tag.first, startPos);
    }

    string::size_type currPos = startPos;

    int tagCnt = 0; // This counter will be increased by 1 when tag starts and
                    // decreased by 1 when the tag ends. When it reaches zero,
                    // the tag has been completely read.

    do
    {            
        const char current = sig.at(currPos); // throws, if out of bounds

        if( current == tag.first )
            ++tagCnt; // tag starts
        else if( current == tag.second )
            --tagCnt; // tag ends

        ++currPos;
    }
    while( tagCnt > 0 );

    string::size_type len = currPos - startPos;

    if( len > 0 )
    {
        argElem.append(sig.begin() + startPos, sig.begin() + currPos);
    }

    return len;
}

// ----------------------------------------------------------------------------------------------------------

/**
 * ParseParameterList. Recursively parses the given sub-string into
 * parameter list object.
 * @param funcSignature Reference to the string representing the function 
 *        signature.
 * @param parBegin Location where the parameter list starts.
 * @param parEnd Location where the parameter list ends.
 * @param parameters Parameter list of the function is returned here.
 */
void ParseParameterList(const string& funcSignature, 
                        string::size_type parBegin, 
                        string::size_type parEnd, 
                        ParameterList& parameters)
{    
    FunctionParameter par; // Object holding the current function parameter during parsing
    ParameterElement parElem; // Object holding the current parameter element during parsing
    
    while( parBegin <= parEnd )
    {
        char current = funcSignature.at(parBegin);
        switch( current )
        {
        case '(':
        case '<':
            {
                // Character '(' or '<' means that a new parameter list starts.
                // New parameter list is stored as a sub-parameter list of the current parameter.

                if( parElem.Value().size() > 0 )
                {
                    par.push_back(parElem); // Put the previous element in the list...
                    parElem.Clear(); // ...and clear the object for the next round.
                }

                string parListStr; // String representing the sub-parameterlist is stored here.
                int len = 0;
                char tagEndMark = current == '(' ? ')' : '>';

                // Put the value "<>" or "()"
                parElem.Value().push_back(current);
                parElem.Value().push_back(tagEndMark);

                // Read sub-parameters...
                len += ReadCharsInTag( funcSignature, parListStr, parBegin, make_pair(current, tagEndMark) );
                // ...and parse them
                if( parElem.SubParameters() == 0 )
                {
                    parElem.SetSubParameters(new ParameterList());
                }
                ParseParameterList(parListStr, 1, len-1, *(parElem.SubParameters()) );

                par.push_back(parElem); // Put the current element in the list...
                parElem.Clear(); // ...and clear the object for the next round.

                parBegin += len;
                break;
            }        
        case ' ':        
            {
                // space means that we have a new parameter element

                if( parElem.Value().size() > 0 )
                {
                    par.push_back(parElem); // Put the previous element in the list...
                    parElem.Clear(); // ...and clear the object for the next round.
                }
                ++parBegin;
                break;
            }
        case ',':
        case ')':
        case '>':
            {
                // Parameter completed

                if( parElem.Value().size() > 0 )
                {
                    par.push_back(parElem); // Put the previous element in the list...
                    parElem.Clear(); //...and clear the object for the next round.
                }
                if( par.size() > 0 )
                {
                    // Put the parameter in the parameter list...
                    parameters.push_back(par);
                    par.clear(); //...and clear the object for the next round.
                }
                ++parBegin;
                break;
            }
        case '*':
        case '&':
            {
                // We have a pointer or reference

                if( parElem.Value().size() > 0 )
                {
                    par.push_back(parElem); // Put the previous element in the list...
                    parElem.Clear(); //...and clear the object for the next round.
                }
                // Put also the current element in the list (i.e. '*' or '&')
                parElem.Value().push_back(current);
                par.push_back(parElem);
                parElem.Clear(); // ...and clear the object for the next round.
                ++parBegin;
                break;
            }
        default:
            {
                parElem.Value().push_back(funcSignature.at(parBegin));
                ++parBegin;
                break;
            }
        }        
    }
}

// ----------------------------------------------------------------------------------------------------------

/**
 * ParseFunctionSignature
 * Parses function signature and returns following elements for the given 
 * function: 
 *  - name
 *  - parameters, which are splitted in elements and sub-parameters
 *  - cv-qualifier of the function
 *
 * Parameters are splitted in following elements: 
 *  - type (int, char, etc...)
 *  - cv-qualifier (const and/or volatile)
 *  - pointer symbol (*)
 *  - reference symbol (&)
 *  - parameter list "()" 
 *  - template parameter list "<>".
 *
 * Parameter lists are further splitted into sub-parameters, which also consists
 * of elements and sub-parameters.
 *
 * @param funcSignature Reference to the string representing the function signature
 * @param signature Parsed function signature is returned in this object.
 */
void ParseFunctionSignature( const string& funcSignature, FuncSignature& signature )
{    
    // Find the start and end positions of the "main" parameter list
    string::size_type openBracketIndex = funcSignature.find_first_of('(');
    string::size_type closeBracketIndex = funcSignature.find_last_of(')');
        
    if(openBracketIndex == string::npos || closeBracketIndex == string::npos )
    {     
        // No parameter list, so the given string may be for example 
        // "virtual table for MyClass"
        signature.nestedName = funcSignature;
        return;
    }

    // Remove preceding and trailing spaces from the function name part:
    string::const_iterator start;
    string::const_iterator end = RemoveSpaces(  
                                    funcSignature.begin(), 
                                    funcSignature.begin() + openBracketIndex,
                                    start);

    // Store the name part
    signature.nestedName.append(start, end); 

    // Parse function parameters
    ParseParameterList(funcSignature, openBracketIndex+1, closeBracketIndex, signature.parameters);

    // Parse the cv-qualifier of the function.
    end = RemoveSpaces(funcSignature.begin() + (closeBracketIndex+1), funcSignature.end(), start);
    signature.cvQualifier.Set(string(start,end));
}

// ----------------------------------------------------------------------------------------------------------

/**
 * AreFunctionsCompatible
 * Compares two function signatures for backward binary compatibility. 
 * This function first parses the function signature into name, parameter list
 * and cv-qualifier parts. The name part is compared first and if names don't 
 * match, return false. Then parameter lists are given to 
 * <code>AreParametersCompatible</code> function for comparison. 
 * And finally cv-qualifiers of functions are compared. This comparison function
 * allows changing non-const function to const (i.e adding 'const'-qualifier for 
 * a function).
 * @param baselineFunc Reference to the string representing the baseline 
 *        platform's function signature
 * @param currentFunc Reference to the string representing the current
 *        platform's function signature
 * @return bool value indicating whether the two functions are backward binary
 *         compatible.
 */
TypeOfSeverity AreFunctionsCompatible(const string& baselineFunc, const string& currentFunc)
{ 
	TypeOfSeverity retSeverity = NO_BREAK;
    // First split functions into name part, parameter list and possible cv-qualifier:
    FuncSignature baseFunc;
    FuncSignature currFunc;

    ParseFunctionSignature(baselineFunc, baseFunc);
    ParseFunctionSignature(currentFunc, currFunc);
    
      // Check the const qualifier of the function:
    if( baseFunc.cvQualifier.isConst != currFunc.cvQualifier.isConst )
	  {        
		    // const qualifier of the function has been removed(either const to non-const or vise versa)
		    // Results in SC break    
		    retSeverity = CONFIRMED_SC_BREAK;
		goto EXIT_POINT;
	  }
	  
	  // Check the number of parameters:
    if( baseFunc.parameters.size() != currFunc.parameters.size() )
    {
        retSeverity = POSSIBLE_BC_CONFIRMED_SC_BREAK; // Number of parameters does not match.
		goto EXIT_POINT;
    }

    // Then check name part:
    if( baseFunc.nestedName.compare(currFunc.nestedName) != 0 )
    {     
        retSeverity = POSSIBLE_BC_SC_BREAK; // Names do not match.
		goto EXIT_POINT;
    }

    // Check the parameters:       
    for( unsigned int i = 0; i < baseFunc.parameters.size(); ++i )
    {
        if( AreParametersCompatible(baseFunc.parameters[i], currFunc.parameters[i], COMPARE_FLAG_ALLOW_CONST_ADDITION) == false )
        {            
            retSeverity = POSSIBLE_BC_SC_BREAK; // Parameters do not match
			goto EXIT_POINT;
        }
    }

    if( baseFunc.cvQualifier.isVolatile != currFunc.cvQualifier.isVolatile )
    {
        retSeverity = POSSIBLE_SC_BREAK; // volatile qualifier does not match
		goto EXIT_POINT;
    }

EXIT_POINT:
    return retSeverity;
}

// ----------------------------------------------------------------------------------------------------------

/**
 * Removes preceding and trailing spaces from the given string
 * @return iterator to the end of the trimmed string.
 * @param start Iterator to the beginning of the string.
 * @param end Iterator to the end of the string.
 * @param result Iterator to the beginning of the trimmed string. 
 */
string::const_iterator RemoveSpaces(string::const_iterator start, 
                                    string::const_iterator end, 
                                    string::const_iterator& result)
{
    // Remove spaces from the beginning of the string:
    result = start;
    while( result != end && *result == ' ' )
    {
        ++result;
    }

    // Remove spaces from the end of the string:
    string::const_iterator resultEnd = end;
    while( resultEnd != result && *(resultEnd-1) == ' ' )
    {
        --resultEnd;
    }

    return resultEnd;
}

// ----------------------------------------------------------------------------------------------------------

/**
 * AreParametersCompatible
 * Checks if two function parameters are compatible with each other. 
 * Allows added const qualifiers in current version of the parameter.
 * This function loops through parameters, that are splitted into elements,
 * and compares them element by element. If the elements differ and current 
 * platform's element is 'const' qualifier, skip the current platform's element
 * and compare next one to the baseline platform's element. 
 * @param basePar Reference to the parameter object representing the parameter
 *        of the baseline platform's function.
 * @param currPar Reference to the parameter object representing the parameter
 *        of the current platform's function.
 * @param compareFlags Comparison policy flags that should be used when comparing
 *        the parameters. 
 * @return bool value indicating if the two parameters are backward binary compatible.
 */
bool AreParametersCompatible(   const FunctionParameter& basePar,
                                const FunctionParameter& currPar,
                                unsigned int compareFlags )
{      
    const string cvQualConst("const"); // const qualifier
    const string ptrStr("*"); // pointer
    const string refStr("&"); // reference
    if( basePar.size() > currPar.size() )
    {
        // Something has been removed:
        return false;
    }

    vector<ParameterElement>::const_iterator baseElem = basePar.begin();
    vector<ParameterElement>::const_iterator currElem = currPar.begin();
    while( baseElem != basePar.end() && currElem != currPar.end() )
    {
        if( baseElem->Value().compare(currElem->Value()) != 0 )
        {
            if( currElem->Value().compare(cvQualConst) == 0 &&
                (compareFlags & COMPARE_FLAG_ALLOW_CONST_ADDITION) )
            {       
                // COMPARE_FLAG_ALLOW_CONST_ADDITION used, so it is acceptable that
                // 'const' qualifier has been added to current parameter. So let's skip
                // the 'const' qualifier -element and compare next elements to see if 
                // the parameter is otherwise compatible.
                ++currElem;             
                continue;
            }
            else
            {
                return false; // Parameter elements do not match
            }
        }
        
        // Now, lets take the sub-parameters (if any) and call this function recursively:
        const ParameterList* baseSubPars = baseElem->SubParameters();
        const ParameterList* currSubPars = currElem->SubParameters();
        if( baseSubPars != 0 &&  currSubPars != 0 )
        {
            if( baseSubPars->size() != currSubPars->size() )
                return false; // Number of sub-parameters do not match

            for( unsigned int subI = 0; subI < baseSubPars->size(); ++subI )
            {
                // Here we are dealing with sub-parameters (e.g parameter list of a function 
                // pointer parameter), and no 'const addition' flags etc. are used anymore.
                if( AreParametersCompatible( baseSubPars->at(subI), currSubPars->at(subI)) == false )
                    return false;
            }
        }
        else if( baseSubPars != currSubPars )
        {
            return false; // Other one's subparameter list pointer is NULL
        }

        ++baseElem;
        ++currElem;
    }

    // Check for the const pointer. Actually in this case the 'const' qualifier
    // is left out from the mangled/demangled signature, but just to be sure...
    // For example: 'int *' changed to 'int * const'
    if( baseElem == basePar.end() && currElem != currPar.end() )
    {
        if( currElem->Value().compare(cvQualConst) == 0 )
        {            
            ++currElem;
        }
    }

    // Check that all the elements have been "consumed":
    return baseElem == basePar.end() && currElem == currPar.end();
}

// ----------------------------------------------------------------------------------------------------------
