source: branches/vendor/trolltech/qt/current/tools/linguist/shared/java.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 13 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 20.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "translator.h"
43
44#include <QtCore/QDebug>
45#include <QtCore/QFile>
46#include <QtCore/QRegExp>
47#include <QtCore/QStack>
48#include <QtCore/QStack>
49#include <QtCore/QString>
50#include <QtCore/QTextCodec>
51
52#include <ctype.h>
53
54QT_BEGIN_NAMESPACE
55
56enum { Tok_Eof, Tok_class, Tok_return, Tok_tr,
57       Tok_translate, Tok_Ident, Tok_Package,
58       Tok_Comment, Tok_String, Tok_Colon, Tok_Dot,
59       Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen,
60       Tok_RightParen, Tok_Comma, Tok_Semicolon,
61       Tok_Integer, Tok_Plus, Tok_PlusPlus, Tok_PlusEq };
62
63class Scope
64{
65    public:
66        QString name;
67        enum Type {Clazz, Function, Other} type;
68        int line;
69
70        Scope(const QString & name, Type type, int line) :
71                name(name),
72                type(type),
73                line(line)
74        {}
75
76        ~Scope()
77        {}
78};
79
80/*
81  The tokenizer maintains the following global variables. The names
82  should be self-explanatory.
83*/
84
85static QString yyFileName;
86static QChar yyCh;
87static QString yyIdent;
88static QString yyComment;
89static QString yyString;
90
91
92static qlonglong yyInteger;
93static int yyParenDepth;
94static int yyLineNo;
95static int yyCurLineNo;
96static int yyParenLineNo;
97static int yyTok;
98
99// the string to read from and current position in the string
100static QString yyInStr;
101static int yyInPos;
102
103// The parser maintains the following global variables.
104static QString yyPackage;
105static QStack<Scope*> yyScope;
106static QString yyDefaultContext;
107
108static QChar getChar()
109{
110    if (yyInPos >= yyInStr.size())
111        return EOF;
112    QChar c = yyInStr[yyInPos++];
113    if (c.unicode() == '\n')
114        ++yyCurLineNo;
115    return c.unicode();
116}
117
118static int getToken()
119{
120    const char tab[] = "bfnrt\"\'\\";
121    const char backTab[] = "\b\f\n\r\t\"\'\\";
122
123    yyIdent.clear();
124    yyComment.clear();
125    yyString.clear();
126
127    while ( yyCh != EOF ) {
128        yyLineNo = yyCurLineNo;
129
130        if ( yyCh.isLetter() || yyCh.toLatin1() == '_' ) {
131            do {
132                yyIdent.append(yyCh);
133                yyCh = getChar();
134            } while ( yyCh.isLetterOrNumber() || yyCh.toLatin1() == '_' );
135
136            if (yyTok != Tok_Dot) {
137                switch ( yyIdent.at(0).toLatin1() ) {
138                    case 'r':
139                        if ( yyIdent == QLatin1String("return") )
140                            return Tok_return;
141                        break;
142                     case 'c':
143                        if ( yyIdent == QLatin1String("class") )
144                            return Tok_class;
145                    break;
146                }
147            }
148            switch ( yyIdent.at(0).toLatin1() ) {
149            case 'T':
150                // TR() for when all else fails
151                if ( yyIdent == QLatin1String("TR") )
152                    return Tok_tr;
153                break;
154            case 'p':
155                if( yyIdent == QLatin1String("package") )
156                    return Tok_Package;
157                break;
158            case 't':
159                if ( yyIdent == QLatin1String("tr") )
160                    return Tok_tr;
161                if ( yyIdent == QLatin1String("translate") )
162                    return Tok_translate;
163                }
164            return Tok_Ident;
165        } else {
166            switch ( yyCh.toLatin1() ) {
167
168            case '/':
169                yyCh = getChar();
170                if ( yyCh == QLatin1Char('/') ) {
171                    do {
172                        yyCh = getChar();
173                        if (yyCh == EOF)
174                            break;
175                        yyComment.append(yyCh);
176                    } while (yyCh != QLatin1Char('\n'));
177                    return Tok_Comment;
178
179                } else if ( yyCh == QLatin1Char('*') ) {
180                    bool metAster = false;
181                    bool metAsterSlash = false;
182
183                    while ( !metAsterSlash ) {
184                        yyCh = getChar();
185                        if ( yyCh == EOF ) {
186                            qFatal( "%s: Unterminated Java comment starting at"
187                                    " line %d\n",
188                                    qPrintable(yyFileName), yyLineNo );
189
190                            return Tok_Comment;
191                        }
192
193                        yyComment.append( yyCh );
194
195                        if ( yyCh == QLatin1Char('*') )
196                            metAster = true;
197                        else if ( metAster && yyCh == QLatin1Char('/') )
198                            metAsterSlash = true;
199                        else
200                            metAster = false;
201                    }
202                    yyComment.chop(2);
203                    yyCh = getChar();
204
205                    return Tok_Comment;
206                }
207                break;
208            case '"':
209                yyCh = getChar();
210
211                while ( yyCh != EOF && yyCh != QLatin1Char('\n') && yyCh != QLatin1Char('"') ) {
212                    if ( yyCh == QLatin1Char('\\') ) {
213                        yyCh = getChar();
214                        if ( yyCh == QLatin1Char('u') ) {
215                            yyCh = getChar();
216                            uint unicode(0);
217                            for (int i = 4; i > 0; --i) {
218                                unicode = unicode << 4;
219                                if( yyCh.isDigit() ) {
220                                    unicode += yyCh.digitValue();
221                                }
222                                else {
223                                    int sub(yyCh.toLower().toAscii() - 87);
224                                    if( sub > 15 || sub < 10) {
225                                        qFatal( "%s:%d: Invalid Unicode",
226                                            qPrintable(yyFileName), yyLineNo );
227                                    }
228                                    unicode += sub;
229                                }
230                                yyCh = getChar();
231                            }
232                            yyString.append(QChar(unicode));
233                        }
234                        else if ( yyCh == QLatin1Char('\n') ) {
235                            yyCh = getChar();
236                        }
237                        else {
238                            yyString.append( QLatin1Char(backTab[strchr( tab, yyCh.toAscii() ) - tab]) );
239                            yyCh = getChar();
240                        }
241                    } else {
242                        yyString.append(yyCh);
243                        yyCh = getChar();
244                    }
245                }
246
247                if ( yyCh != QLatin1Char('"') )
248                    qFatal( "%s:%d: Unterminated string",
249                        qPrintable(yyFileName), yyLineNo );
250
251                yyCh = getChar();
252
253                return Tok_String;
254
255            case ':':
256                yyCh = getChar();
257                return Tok_Colon;
258            case '\'':
259                yyCh = getChar();
260
261                if ( yyCh == QLatin1Char('\\') )
262                    yyCh = getChar();
263                do {
264                    yyCh = getChar();
265                } while ( yyCh != EOF && yyCh != QLatin1Char('\'') );
266                yyCh = getChar();
267                break;
268            case '{':
269                yyCh = getChar();
270                return Tok_LeftBrace;
271            case '}':
272                yyCh = getChar();
273                return Tok_RightBrace;
274            case '(':
275                if (yyParenDepth == 0)
276                    yyParenLineNo = yyCurLineNo;
277                yyParenDepth++;
278                yyCh = getChar();
279                return Tok_LeftParen;
280            case ')':
281                if (yyParenDepth == 0)
282                    yyParenLineNo = yyCurLineNo;
283                yyParenDepth--;
284                yyCh = getChar();
285                return Tok_RightParen;
286            case ',':
287                yyCh = getChar();
288                return Tok_Comma;
289            case '.':
290                yyCh = getChar();
291                return Tok_Dot;
292            case ';':
293                yyCh = getChar();
294                return Tok_Semicolon;
295            case '+':
296                yyCh = getChar();
297                if (yyCh == QLatin1Char('+')) {
298                    yyCh = getChar();
299                    return Tok_PlusPlus;
300                }
301                if( yyCh == QLatin1Char('=') ){
302                    yyCh = getChar();
303                    return Tok_PlusEq;
304                }
305                return Tok_Plus;
306            case '0':
307            case '1':
308            case '2':
309            case '3':
310            case '4':
311            case '5':
312            case '6':
313            case '7':
314            case '8':
315            case '9':
316                {
317                    QByteArray ba;
318                    ba += yyCh.toLatin1();
319                    yyCh = getChar();
320                    bool hex = yyCh == QLatin1Char('x');
321                    if ( hex ) {
322                        ba += yyCh.toLatin1();
323                        yyCh = getChar();
324                    }
325                    while ( hex ? isxdigit(yyCh.toLatin1()) : yyCh.isDigit() ) {
326                        ba += yyCh.toLatin1();
327                        yyCh = getChar();
328                    }
329                    bool ok;
330                    yyInteger = ba.toLongLong(&ok);
331                    if (ok) return Tok_Integer;
332                    break;
333                }
334            default:
335                yyCh = getChar();
336            }
337        }
338    }
339    return Tok_Eof;
340}
341
342static bool match( int t )
343{
344    bool matches = ( yyTok == t );
345    if ( matches )
346        yyTok = getToken();
347    return matches;
348}
349
350static bool matchString( QString &s )
351{
352    if ( yyTok != Tok_String )
353        return false;
354
355    s = yyString;
356    yyTok = getToken();
357    while ( yyTok == Tok_Plus ) {
358        yyTok = getToken();
359        if (yyTok == Tok_String)
360            s += yyString;
361        else {
362            qWarning( "%s:%d: String used in translation can only contain strings"
363                " concatenated with other strings, not expressions or numbers.",
364                qPrintable(yyFileName), yyLineNo );
365            return false;
366        }
367        yyTok = getToken();
368    }
369    return true;
370}
371
372static bool matchInteger( qlonglong *number)
373{
374    bool matches = (yyTok == Tok_Integer);
375    if (matches) {
376        yyTok = getToken();
377        *number = yyInteger;
378    }
379    return matches;
380}
381
382static bool matchStringOrNull(QString &s)
383{
384    bool matches = matchString(s);
385    qlonglong num = 0;
386    if (!matches) matches = matchInteger(&num);
387    return matches && num == 0;
388}
389
390/*
391 * match any expression that can return a number, which can be
392 * 1. Literal number (e.g. '11')
393 * 2. simple identifier (e.g. 'm_count')
394 * 3. simple function call (e.g. 'size()' )
395 * 4. function call on an object (e.g. 'list.size()')
396 * 5. function call on an object (e.g. 'list->size()')
397 *
398 * Other cases:
399 * size(2,4)
400 * list().size()
401 * list(a,b).size(2,4)
402 * etc...
403 */
404static bool matchExpression()
405{
406    if (match(Tok_Integer)) {
407        return true;
408    }
409
410    int parenlevel = 0;
411    while (match(Tok_Ident) || parenlevel > 0) {
412        if (yyTok == Tok_RightParen) {
413            if (parenlevel == 0) break;
414            --parenlevel;
415            yyTok = getToken();
416        } else if (yyTok == Tok_LeftParen) {
417            yyTok = getToken();
418            if (yyTok == Tok_RightParen) {
419                yyTok = getToken();
420            } else {
421                ++parenlevel;
422            }
423        } else if (yyTok == Tok_Ident) {
424            continue;
425        } else if (parenlevel == 0) {
426            return false;
427        }
428    }
429    return true;
430}
431
432static const QString context()
433{
434      QString context(yyPackage);
435      bool innerClass = false;
436      for (int i = 0; i < yyScope.size(); ++i) {
437         if (yyScope.at(i)->type == Scope::Clazz) {
438             if (innerClass)
439                 context.append(QLatin1String("$"));
440             else
441                 context.append(QLatin1String("."));
442
443             context.append(yyScope.at(i)->name);
444             innerClass = true;
445         }
446     }
447     return context.isEmpty() ? yyDefaultContext : context;
448}
449
450static void recordMessage(
451    Translator *tor, const QString &context, const QString &text, const QString &comment,
452    const QString &extracomment, bool plural)
453{
454    TranslatorMessage msg(
455        context, text, comment, QString(),
456        yyFileName, yyLineNo, QStringList(),
457        TranslatorMessage::Unfinished, plural);
458    msg.setExtraComment(extracomment.simplified());
459    tor->extend(msg);
460}
461
462static void parse( Translator *tor )
463{
464    QString text;
465    QString com;
466    QString extracomment;
467
468    yyCh = getChar();
469
470    yyTok = getToken();
471    while ( yyTok != Tok_Eof ) {
472        switch ( yyTok ) {
473        case Tok_class:
474            yyTok = getToken();
475            if(yyTok == Tok_Ident) {
476                yyScope.push(new Scope(yyIdent, Scope::Clazz, yyLineNo));
477            }
478            else {
479                qFatal( "%s:%d: Class must be followed by a classname",
480                                          qPrintable(yyFileName), yyLineNo );
481            }
482            while (!match(Tok_LeftBrace)) {
483                yyTok = getToken();
484            }
485            break;
486
487        case Tok_tr:
488            yyTok = getToken();
489            if ( match(Tok_LeftParen) && matchString(text) ) {
490                com.clear();
491                bool plural = false;
492
493                if ( match(Tok_RightParen) ) {
494                    // no comment
495                } else if (match(Tok_Comma) && matchStringOrNull(com)) {   //comment
496                    if ( match(Tok_RightParen)) {
497                        // ok,
498                    } else if (match(Tok_Comma)) {
499                        plural = true;
500                    }
501                }
502                if (!text.isEmpty())
503                    recordMessage(tor, context(), text, com, extracomment, plural);
504            }
505            break;
506        case Tok_translate:
507            {
508                QString contextOverride;
509                yyTok = getToken();
510                if ( match(Tok_LeftParen) &&
511                     matchString(contextOverride) &&
512                     match(Tok_Comma) &&
513                     matchString(text) ) {
514
515                    com.clear();
516                    bool plural = false;
517                    if (!match(Tok_RightParen)) {
518                        // look for comment
519                        if ( match(Tok_Comma) && matchStringOrNull(com)) {
520                            if (!match(Tok_RightParen)) {
521                                if (match(Tok_Comma) && matchExpression() && match(Tok_RightParen)) {
522                                    plural = true;
523                                } else {
524                                    break;
525                                }
526                            }
527                        } else {
528                            break;
529                        }
530                    }
531                    if (!text.isEmpty())
532                        recordMessage(tor, contextOverride, text, com, extracomment, plural);
533                }
534            }
535            break;
536
537        case Tok_Ident:
538            yyTok = getToken();
539            break;
540
541        case Tok_Comment:
542            if (yyComment.startsWith(QLatin1Char(':'))) {
543                yyComment.remove(0, 1);
544                extracomment.append(yyComment);
545            }
546            yyTok = getToken();
547            break;
548
549        case Tok_RightBrace:
550            if ( yyScope.isEmpty() ) {
551                qFatal( "%s:%d: Unbalanced right brace in Java code\n",
552                        qPrintable(yyFileName), yyLineNo );
553            }
554            else
555                delete (yyScope.pop());
556            extracomment.clear();
557            yyTok = getToken();
558            break;
559
560         case Tok_LeftBrace:
561            yyScope.push(new Scope(QString(), Scope::Other, yyLineNo));
562            yyTok = getToken();
563            break;
564
565        case Tok_Semicolon:
566            extracomment.clear();
567            yyTok = getToken();
568            break;
569
570        case Tok_Package:
571            yyTok = getToken();
572            while(!match(Tok_Semicolon)) {
573                switch(yyTok) {
574                    case Tok_Ident:
575                        yyPackage.append(yyIdent);
576                        break;
577                    case Tok_Dot:
578                        yyPackage.append(QLatin1String("."));
579                        break;
580                    default:
581                         qFatal( "%s:%d: Package keyword should be followed by com.package.name;",
582                                          qPrintable(yyFileName), yyLineNo );
583                         break;
584                }
585                yyTok = getToken();
586            }
587            break;
588
589        default:
590            yyTok = getToken();
591        }
592    }
593
594    if ( !yyScope.isEmpty() )
595        qFatal( "%s:%d: Unbalanced braces in Java code\n",
596                 qPrintable(yyFileName), yyScope.top()->line );
597    else if ( yyParenDepth != 0 )
598        qFatal( "%s:%d: Unbalanced parentheses in Java code\n",
599                 qPrintable(yyFileName), yyParenLineNo );
600}
601
602
603bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd)
604{
605    //void LupdateApplication::fetchtr_java( const QString &fileName, Translator *tor,
606    //const QString &defaultContext, bool mustExist, const QByteArray &codecForSource )
607
608    yyDefaultContext = cd.m_defaultContext;
609    yyInPos = -1;
610    yyFileName = cd.m_sourceFileName;
611    yyPackage.clear();
612    yyScope.clear();
613    yyTok = -1;
614    yyParenDepth = 0;
615    yyCurLineNo = 0;
616    yyParenLineNo = 1;
617
618    QTextStream ts(&dev);
619    QByteArray codecName;
620    if (!cd.m_codecForSource.isEmpty())
621        codecName = cd.m_codecForSource;
622    else
623        codecName = translator.codecName(); // Just because it should be latin1 already
624    ts.setCodec(QTextCodec::codecForName(codecName));
625    ts.setAutoDetectUnicode(true);
626    yyInStr = ts.readAll();
627    yyInPos = 0;
628    yyFileName = cd.m_sourceFileName;
629    yyCurLineNo = 1;
630    yyParenLineNo = 1;
631    yyCh = getChar();
632
633    parse(&translator);
634
635    // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it.
636    translator.setCodecName("UTF-8");
637    return true;
638}
639
640int initJava()
641{
642    Translator::FileFormat format;
643    format.extension = QLatin1String("java");
644    format.fileType = Translator::FileFormat::SourceCode;
645    format.priority = 0;
646    format.description = QObject::tr("Java source files");
647    format.loader = &loadJava;
648    format.saver = 0;
649    Translator::registerFileFormat(format);
650    return 1;
651}
652
653Q_CONSTRUCTOR_FUNCTION(initJava)
654
655QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.