tools/porting/src/tokenreplacements.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the qt3to4 porting application of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "tokenreplacements.h"
       
    43 #include "logger.h"
       
    44 #include "portingrules.h"
       
    45 
       
    46 QT_BEGIN_NAMESPACE
       
    47 using namespace TokenEngine;
       
    48 
       
    49 void addLogSourceEntry(const QString &text, const TokenContainer &tokenContainer, const int index)
       
    50 {
       
    51     Logger *logger = Logger::instance();
       
    52     int line = tokenContainer.line(index);
       
    53     int col = tokenContainer.column(index);
       
    54     SourcePointLogEntry *logEntry =
       
    55                 new SourcePointLogEntry(QLatin1String("Info"), QLatin1String("Porting"),
       
    56                                         logger->globalState.value(QLatin1String("currentFileName")),
       
    57                                         line, col, text);
       
    58     logger->addEntry(logEntry);
       
    59 }
       
    60 
       
    61 void addLogWarning(const QString &text)
       
    62 {
       
    63      Logger::instance()->addEntry(new PlainLogEntry(QLatin1String("Warning"), QLatin1String("Porting"), text));
       
    64 }
       
    65 
       
    66 QualifiedNameParser::QualifiedNameParser(const TokenContainer &tokenContainer, const int tokenIndex)
       
    67 :tokenContainer(tokenContainer)
       
    68 ,currentIndex(tokenIndex)
       
    69 {
       
    70     Q_ASSERT(isValidIndex(currentIndex));
       
    71 }
       
    72 
       
    73 bool QualifiedNameParser::isPartOfQualifiedName()
       
    74 {
       
    75     return ((nextScopeToken(Left) != -1) || (nextScopeToken(Right) != -1));
       
    76 }
       
    77 
       
    78 
       
    79 bool QualifiedNameParser::isValidIndex(int index)
       
    80 {
       
    81     return (index < tokenContainer.count() && index >= 0);
       
    82 }
       
    83 
       
    84 /*
       
    85     A qualifier is a the leftmost or middle part of a qualified name
       
    86 */
       
    87 bool QualifiedNameParser::isQualifier()
       
    88 {
       
    89     return (nextScopeToken(Right) != -1);
       
    90 }
       
    91 
       
    92 /*
       
    93     A name is a the rightmost part of a qualified name.
       
    94 */
       
    95 bool QualifiedNameParser::isName()
       
    96 {
       
    97     return (nextScopeToken(Left) != -1);
       
    98 }
       
    99 
       
   100 /*
       
   101     Peek for a qualifier or name in the given direction
       
   102 */
       
   103 int QualifiedNameParser::peek(Direction direction)
       
   104 {
       
   105     return nextScopeToken(direction);
       
   106 }
       
   107 
       
   108 /*
       
   109     Look for a qualifier or name in the given direction,update
       
   110     current position if found.
       
   111 */
       
   112 int QualifiedNameParser::move(Direction direction)
       
   113 {
       
   114     int tokenIndex = nextScopeToken(direction);
       
   115     if(tokenIndex != -1)
       
   116         currentIndex = tokenIndex;
       
   117     return tokenIndex;
       
   118 }
       
   119 
       
   120 /*
       
   121     Looks for "::" starting at currentIndex, returns the token index
       
   122     for it if found. If the first non-whitespace token found is something else,
       
   123     -1 is returned.
       
   124 */
       
   125 int QualifiedNameParser::findScopeOperator(Direction direction)
       
   126 {
       
   127     int tokenIndex = currentIndex;
       
   128     QByteArray tokenText;
       
   129     //loop until we get a token containg text or we pass the beginning/end of the source
       
   130     tokenIndex += direction;
       
   131     while(tokenText.isEmpty() && isValidIndex(tokenIndex)) {
       
   132         tokenText = tokenContainer.text(tokenIndex).trimmed();
       
   133         if(tokenText==QByteArray("::"))
       
   134            return tokenIndex;
       
   135         tokenIndex += direction;
       
   136     }
       
   137     return -1;
       
   138 }
       
   139 /*
       
   140     Walks a qualified name. Returns the token index
       
   141     for the next identifer in the qualified name, or -1 if its not found.
       
   142 */
       
   143 int QualifiedNameParser::nextScopeToken(Direction direction)
       
   144 {
       
   145     int tokenIndex  = findScopeOperator(direction);
       
   146     if (tokenIndex == -1)
       
   147         return -1;
       
   148     QByteArray tokenText;
       
   149    //loop until we get a token containg text or we pass the start of the source
       
   150     tokenIndex += direction;
       
   151     while(tokenText.isEmpty() && isValidIndex(tokenIndex)) {
       
   152        tokenText = tokenContainer.text(tokenIndex).trimmed();
       
   153        tokenIndex += direction;
       
   154     }
       
   155     return tokenIndex - direction;
       
   156 }
       
   157 
       
   158 /////////////////////
       
   159 GenericTokenReplacement::GenericTokenReplacement(QByteArray oldToken, QByteArray newToken)
       
   160 :oldToken(oldToken)
       
   161 ,newToken(newToken)
       
   162 {}
       
   163 
       
   164 QByteArray GenericTokenReplacement::getReplaceKey()
       
   165 {
       
   166     return QByteArray(oldToken);
       
   167 }
       
   168 
       
   169 bool GenericTokenReplacement::doReplace(const TokenContainer &tokenContainer,
       
   170                             int index, TextReplacements &textReplacements)
       
   171 {
       
   172     QByteArray tokenText = tokenContainer.text(index);
       
   173     if(tokenText == oldToken){
       
   174         addLogSourceEntry(QString::fromLatin1(tokenText + QByteArray(" -> ") + newToken), tokenContainer, index);
       
   175         TokenEngine::Token token = tokenContainer.token(index);
       
   176         textReplacements.insert(newToken, token.start, token.length);
       
   177         return true;
       
   178     }
       
   179     return false;
       
   180 
       
   181 }
       
   182 
       
   183 ///////////////////
       
   184 ClassNameReplacement::ClassNameReplacement(QByteArray oldToken, QByteArray newToken)
       
   185 :oldToken(oldToken)
       
   186 ,newToken(newToken)
       
   187 {}
       
   188 
       
   189 QByteArray ClassNameReplacement::getReplaceKey()
       
   190 {
       
   191     return QByteArray(oldToken);
       
   192 }
       
   193 
       
   194 /*
       
   195     Replace a class name token. If the class name is a scope specifier (a "qualifier")
       
   196     in a qualified name, we check if qualified name will be replaced by a porting rule.
       
   197     If so, we don't do the class name replacement.
       
   198 */
       
   199 bool ClassNameReplacement::doReplace(const TokenContainer &tokenContainer, int index, TextReplacements &textReplacements)
       
   200 {
       
   201     QByteArray tokenText = tokenContainer.text(index);
       
   202     if(tokenText != oldToken)
       
   203         return false;
       
   204 
       
   205     QualifiedNameParser nameParser(tokenContainer, index);
       
   206     if(nameParser.isPartOfQualifiedName() &&
       
   207        nameParser.peek(QualifiedNameParser::Right) != -1) {
       
   208         int nameTokenIndex = nameParser.peek(QualifiedNameParser::Right);
       
   209         QByteArray name = tokenContainer.text(nameTokenIndex); 
       
   210         TextReplacements textReplacements;
       
   211         QList<TokenReplacement*> tokenReplacements
       
   212             = PortingRules::instance()->getTokenReplacementRules();
       
   213         bool changed = false;
       
   214         foreach(TokenReplacement *tokenReplacement, tokenReplacements) {
       
   215             changed = tokenReplacement->doReplace(tokenContainer, nameTokenIndex, textReplacements);
       
   216             if(changed)
       
   217                 break;
       
   218         }
       
   219         if(changed)
       
   220             return false;
       
   221     }
       
   222     addLogSourceEntry(QString::fromLatin1(tokenText + QByteArray(" -> ") + newToken), tokenContainer, index);
       
   223     TokenEngine::Token token = tokenContainer.token(index);
       
   224     textReplacements.insert(newToken, token.start, token.length);
       
   225     return true;
       
   226 }
       
   227 
       
   228 ///////////////////
       
   229 
       
   230 ScopedTokenReplacement::ScopedTokenReplacement(const QByteArray &oldToken,
       
   231                                                const QByteArray &newToken)
       
   232 :newScopedName(newToken)
       
   233 {
       
   234     Q_ASSERT(oldToken.contains(QByteArray("::")));
       
   235 
       
   236     // Split oldToken into scope and name parts.
       
   237     oldName = oldToken.mid(oldToken.lastIndexOf(':')+1);
       
   238     oldScope = oldToken.mid(0, oldToken.indexOf(':'));
       
   239 
       
   240     // Split newToken into scope and name parts, execept if we have a spcial
       
   241     // case like Qt::WType_Modal -> (Qt::WType_Dialog | Qt::WShowModal)
       
   242     if (newToken.count(QByteArray("::")) != 1 || newToken.contains(QByteArray("("))) {
       
   243         newName = newToken;
       
   244     } else {
       
   245         newName = newToken.mid(newToken.lastIndexOf(':')+1);
       
   246         newScope = newToken.mid(0, newToken.indexOf(':'));
       
   247     }
       
   248 
       
   249     strictMode = Logger::instance()->globalState.contains(QString::fromLatin1("strictMode"));
       
   250 }
       
   251 
       
   252 bool ScopedTokenReplacement::doReplace(const TokenContainer &tokenContainer, int sourceIndex, TextReplacements &textReplacements)
       
   253 {
       
   254     const QByteArray sourceName = tokenContainer.text(sourceIndex);
       
   255 
       
   256     // Check if the token texts matches.
       
   257     if (sourceName != oldName)
       
   258         return false;
       
   259 
       
   260     // Get token attributes. The attributes are created by the the C++ parser/analyzer.
       
   261     const TokenAttributes *attributes = tokenContainer.tokenAttributes();
       
   262     // If the declaration attribute is set we don't replace.
       
   263     if (!attributes->attribute(sourceIndex, "declaration").isEmpty())
       
   264         return false;
       
   265     // If the unknown (undeclared) attribute is set we don't replace.
       
   266     if (!attributes->attribute(sourceIndex, "unknown").isEmpty())
       
   267         return false;
       
   268     // If nameUse is set we test if the nameUse refers to the correct declaration.
       
   269     // This is done by checking the parentScope attriute, wich returns the scope
       
   270     // for the declaration associated with this name use.
       
   271     const bool haveNameUseInfo = !attributes->attribute(sourceIndex, "nameUse").isEmpty();
       
   272     if (haveNameUseInfo) {
       
   273         if (attributes->attribute(sourceIndex, "parentScope") != oldScope)
       
   274             return false;
       
   275     // If the user has specified -strict, we don't replace tokens when we don't have name use info.
       
   276     } else if (strictMode) {
       
   277         return false;
       
   278     }
       
   279 
       
   280     // The token might have a qualifier, and in that case we need to check if
       
   281     // we should replace the qualifier as well.
       
   282     QualifiedNameParser nameParser(tokenContainer, sourceIndex);
       
   283 
       
   284     // This is a pretty special case, it means that in a qualified
       
   285     // name like aaa::bbb the replacement rule has been triggered for
       
   286     // the aaa part. Since this is not what we'd normally use a
       
   287     // ScopedReplacement for, we just return here.
       
   288     if (nameParser.isQualifier())
       
   289         return false;
       
   290 
       
   291     // If the token is unqualified, just replace it.
       
   292     if (!nameParser.isPartOfQualifiedName()) {
       
   293         // If we have no name use info we try to avoid replacements of
       
   294         // e.g. Vertical with QSizePolicy::Vertically. Unqualified tokens
       
   295         // can't happen for classes one does not usually inherit from, so
       
   296         // we only let them pass for stuff that people usually inherited from.
       
   297         if (!haveNameUseInfo && newScope != "Qt" && newScope != "QFrame" && newScope != "QValidator")
       
   298             return false;
       
   299 
       
   300         const Token sourceToken = tokenContainer.token(sourceIndex);
       
   301         addLogSourceEntry(QString::fromLatin1(sourceName + QByteArray(" -> ") + newScopedName), tokenContainer, sourceIndex);
       
   302         textReplacements.insert(newScopedName, sourceToken.start, sourceName.size());
       
   303         return true;
       
   304     }
       
   305 
       
   306     // Peek left for the qualifer token.
       
   307     const int sourceScopeIndex = nameParser.peek(QualifiedNameParser::Left);
       
   308     if (sourceScopeIndex == -1) {
       
   309         return false;
       
   310     }
       
   311 
       
   312     const Token sourceNameToken = tokenContainer.token(sourceIndex);
       
   313     const Token sourceScopeToken = tokenContainer.token(sourceScopeIndex);
       
   314     const QByteArray sourceScope = tokenContainer.text(sourceScopeIndex);
       
   315 
       
   316     // If we have no name use info and the source and old scopes don't match,
       
   317     // we generally dont't do a replace, unless the old scope is Qt and
       
   318     // the source scope inherits Qt. For example, QWidget::ButtonState should
       
   319     // be renamed to Qt::ButtonState.
       
   320     if (!haveNameUseInfo && sourceScope != oldScope) {
       
   321         if (oldScope != "Qt")
       
   322             return false;
       
   323         // Check if sourceScope inherits the Qt class.
       
   324         if (!PortingRules::instance()->getInheritsQt().contains(QString::fromLatin1(sourceScope.constData()))) //TODO optimize: linear search
       
   325             return false;
       
   326     }
       
   327 
       
   328     // Spcecial cases, such as QIODevice::Offset -> Q_LONGLONG
       
   329     // or Qt::WType_Modal -> (Qt::WType_Dialog | Qt::WShowModal).
       
   330     if (newScope.isEmpty()) {
       
   331         addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray("::") + sourceName +
       
   332                           QByteArray(" -> ") + newScopedName).constData()), tokenContainer, sourceIndex);
       
   333         const int qualiferLength = sourceNameToken.start - sourceScopeToken.start;
       
   334         const int length = qualiferLength + sourceNameToken.length;
       
   335         textReplacements.insert(newName, sourceScopeToken.start, length);
       
   336         return true;
       
   337     }
       
   338 
       
   339     // If the old and new scopes are equal, we replace the name part only.
       
   340     if (newScope == sourceScope) {
       
   341         // If the names are equal, there is no need to do anything.
       
   342         if (newName == sourceName)
       
   343             return true;
       
   344         addLogSourceEntry(QString::fromLatin1((sourceName + QByteArray(" -> ") + newName).constData()), tokenContainer, sourceIndex);
       
   345         textReplacements.insert(newName, sourceNameToken.start, sourceNameToken.length);
       
   346         return true;
       
   347     }
       
   348 
       
   349     // If the names are equal, replace scope only.
       
   350     if (newName == sourceName) {
       
   351         addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray(" -> ") + newScope).constData()), tokenContainer, sourceScopeIndex);
       
   352         textReplacements.insert(newScope, sourceScopeToken.start, sourceScopeToken.length);
       
   353         return true;
       
   354     }
       
   355 
       
   356     // Replace scope and name.
       
   357     addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray("::") + sourceName +
       
   358                       QByteArray(" -> ") + newScopedName).constData()),
       
   359                       tokenContainer, sourceScopeIndex);
       
   360     textReplacements.insert(newScope, sourceScopeToken.start, sourceScopeToken.length);
       
   361     textReplacements.insert(newName, sourceNameToken.start, sourceNameToken.length);
       
   362     return true;
       
   363 }
       
   364 
       
   365 QByteArray ScopedTokenReplacement::getReplaceKey()
       
   366 {
       
   367    return oldName;
       
   368 }
       
   369 
       
   370 
       
   371 QT_END_NAMESPACE