idlimport.cpp 15.7 KB
Newer Older
Oliver Kellogg's avatar
Oliver Kellogg committed
1
2
3
4
/***************************************************************************
 *   This program 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 2 of the License, or     *
5
 *   (at your option) any later version.                                   *
Oliver Kellogg's avatar
Oliver Kellogg committed
6
 *                                                                         *
Oliver Kellogg's avatar
Oliver Kellogg committed
7
 *   copyright (C) 2005-2014                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
Oliver Kellogg's avatar
Oliver Kellogg committed
9
10
 ***************************************************************************/

11
12
// own header
#include "idlimport.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
13

14
// app includes
15
16
#include "attribute.h"
#include "classifier.h"
17
#include "debug_utils.h"
18
#include "enum.h"
19
#include "import_utils.h"
20
21
22
23
24
25
#include "operation.h"
#include "package.h"
#include "uml.h"
#include "umldoc.h"
#include "umlpackagelist.h"

26
27
28
// kde includes
#include <KStandardDirs>

29
// qt includes
30
31
32
#include <QProcess>
#include <QRegExp>
#include <QStringList>
33
34

#include <stdio.h>
35

36
37
38
39
QString IDLImport::m_preProcessor;
QStringList IDLImport::m_preProcessorArguments;
bool IDLImport::m_preProcessorChecked = false;

40
IDLImport::IDLImport(CodeImpThread* thread) : NativeImportBase(QLatin1String("//"), thread)
41
{
42
    m_isOneway = m_isReadonly = m_isAttribute = false;
43
    setMultiLineComment(QLatin1String("/*"), QLatin1String("*/"));
44
45
46
47
48
49
50
51

    // we do not want to find the executable on each imported file
    if (m_preProcessorChecked) {
        m_enabled = !m_preProcessor.isEmpty();
        return; 
    }

    QStringList arguments;
52
    QString executable = KStandardDirs::findExe(QLatin1String("cpp"));
53
    if (!executable.isEmpty()) {
54
        arguments << QLatin1String("-C");   // -C means "preserve comments"
55
    }
56
#ifdef Q_OS_WIN
57
    else {
58
        executable = KStandardDirs::findExe(QLatin1String("cl"));
59
        if (executable.isEmpty()) {
Ralf Habacker's avatar
Ralf Habacker committed
60
            QString path = QLatin1String(qgetenv("VS100COMNTOOLS").constData());
61
            if (!path.isEmpty())
62
                executable = KStandardDirs::findExe(QLatin1String("cl"), path + QLatin1String("/../../VC/bin"));
63
64
        }
        if (!executable.isEmpty()) {
65
            arguments << QLatin1String("-E");   // -E means "preprocess to stdout"
66
67
68
69
70
71
72
73
74
75
76
77
        }
    }
#endif
    if (!executable.isEmpty()) {
        m_preProcessor = executable;
        m_preProcessorArguments = arguments;
    }
    else {
        uError() << "Cannot find any of the supported preprocessors (gcc, Microsoft Visual Studio 2010)";
        m_enabled = false;
    }
    m_preProcessorChecked = true;
78
}
Oliver Kellogg's avatar
Oliver Kellogg committed
79

80
81
IDLImport::~IDLImport()
{
Oliver Kellogg's avatar
Oliver Kellogg committed
82
83
}

84
/// Check for split type names (e.g. unsigned long long)
85
86
QString IDLImport::joinTypename()
{
87
    QString typeName = m_source[m_srcIndex];
88
89
90
91
92
93
    if (m_source[m_srcIndex] == QLatin1String("unsigned"))
        typeName += QLatin1Char(' ') + advance();
    if (m_source[m_srcIndex] == QLatin1String("long") &&
            (m_source[m_srcIndex + 1] == QLatin1String("long") ||
             m_source[m_srcIndex + 1] == QLatin1String("double")))
        typeName += QLatin1Char(' ') + advance();
Oliver Kellogg's avatar
Oliver Kellogg committed
94
    return typeName;
95
96
}

97
98
99
/**
 * Override operation from NativeImportBase.
 */
100
101
bool IDLImport::preprocess(QString& line)
{
Oliver Kellogg's avatar
Oliver Kellogg committed
102
    // Ignore C preprocessor generated lines.
103
    if (line.startsWith(QLatin1Char('#')))
104
        return true;  // done
105
106
107
108

/**
 * Override operation from NativeImportBase.
 */
109
110
    return NativeImportBase::preprocess(line);
}
111

112
113
114
/**
 * Implement abstract operation from NativeImportBase.
 */
115
116
void IDLImport::fillSource(const QString& word)
{
117
118
    QString lexeme;
    const uint len = word.length();
119
    for (uint i = 0; i < len; ++i) {
120
        QChar c = word[i];
121
        if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
122
            lexeme += c;
123
        } else if (c == QLatin1Char(':') && i < len-1 && word[i + 1] == QLatin1Char(':')) {
124
            // compress scoped name into lexeme
125
            lexeme += QLatin1String("::");
126
            i++;
127
        } else if (c == QLatin1Char('<')) {
128
129
130
            // compress sequence or bounded string into lexeme
            do {
                lexeme += word[i];
131
            } while (word[i] != QLatin1Char('>') && ++i < len);
132
133
134
        } else {
            if (!lexeme.isEmpty()) {
                m_source.append(lexeme);
135
                lexeme.clear();
Oliver Kellogg's avatar
Oliver Kellogg committed
136
            }
137
            m_source.append(QString(c));
Oliver Kellogg's avatar
Oliver Kellogg committed
138
139
        }
    }
140
141
    if (!lexeme.isEmpty())
        m_source.append(lexeme);
142
143
}

144
145
146
147
/**
 * Reimplement operation from NativeImportBase.
 * Need to do this because we use the external C preprocessor.
 */
148
bool IDLImport::parseFile(const QString& filename)
149
{
150
    if (filename.contains(QLatin1Char('/'))) {
151
        QString path = filename;
152
        path.remove(QRegExp(QLatin1String("/[^/]+$")));
153
        uDebug() << "adding path " << path;
154
155
        Import_Utils::addIncludePath(path);
    }
156
    const QStringList includePaths = Import_Utils::includePathList();
157

158
159
160
161
162
    if (m_preProcessor.isEmpty()) { 
        uError() << "no preprocessor installed, could not import file";
        return false;
    }
    QStringList arguments(m_preProcessorArguments);
163
164

    QProcess p(UMLApp::app());
165
    for (QStringList::ConstIterator pathIt = includePaths.begin();
Oliver Kellogg's avatar
Oliver Kellogg committed
166
167
            pathIt != includePaths.end(); ++pathIt) {
        QString path = (*pathIt);
168
        arguments << QLatin1String("-I") + path;
169
170
    }
    arguments << filename;
171
172
    uDebug() << "importIDL: " << m_preProcessor << arguments;
    p.start(m_preProcessor, arguments);
173
174
    if (!p.waitForStarted()) {
        uError() << "could not run preprocessor";
175
        return false;
Oliver Kellogg's avatar
Oliver Kellogg committed
176
    }
177
178
179

    if (!p.waitForFinished()) {
        uError() << "could not run preprocessor";
180
        return false;
Oliver Kellogg's avatar
Oliver Kellogg committed
181
    }
182
183
184
185
186
    int exitCode = p.exitCode();
    if (exitCode  != 0) {
        uError() << "preprocessor returned error" << exitCode;
        return false;
    }
187
188
189
190

    QByteArray out = p.readAllStandardOutput();
    QTextStream data(out);

191
192
    // Scan the input file into the QStringList m_source.
    m_source.clear();
193
194
    while (!data.atEnd()) {
        NativeImportBase::scan(data.readLine());
Oliver Kellogg's avatar
Oliver Kellogg committed
195
    }
196

197
    // Parse the QStringList m_source.
198
    m_scope.clear();
199
    pushScope(0); // global scope
200
    const int srcLength = m_source.count();
201
    for (m_srcIndex = 0; m_srcIndex < srcLength; ++m_srcIndex) {
202
        const QString& keyword = m_source[m_srcIndex];
203
        //uDebug() << QLatin1Char('"') << keyword << QLatin1Char('"');
204
        if (keyword.startsWith(m_singleLineCommentIntro)) {
205
            m_comment = keyword.mid(m_singleLineCommentIntro.length());
Oliver Kellogg's avatar
Oliver Kellogg committed
206
207
            continue;
        }
208
209
210
        if (! parseStmt())
            skipStmt();
        m_currentAccess = Uml::Visibility::Public;
211
        m_comment.clear();
212
    }
213
    return true;
214
215
}

216
217
218
/**
 * Implement abstract operation from NativeImportBase.
 */
219
220
bool IDLImport::parseStmt()
{
221
    const QString& keyword = m_source[m_srcIndex];
222
    const int srcLength = m_source.count();
223
    uDebug() << "keyword is " << keyword;
224
    if (keyword == QLatin1String("module")) {
225
        const QString& name = advance();
226
        UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Package,
227
228
229
                        name, currentScope(), m_comment);
        pushScope(static_cast<UMLPackage*>(ns));
        currentScope()->setStereotype(QLatin1String("CORBAModule"));
230
        if (advance() != QLatin1String("{")) {
231
            uError() << "importIDL: unexpected: " << m_source[m_srcIndex];
232
            skipStmt(QLatin1String("{"));
Oliver Kellogg's avatar
Oliver Kellogg committed
233
        }
234
235
        return true;
    }
236
    if (keyword == QLatin1String("interface")) {
237
        const QString& name = advance();
238
        UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Class,
239
                        name, currentScope(), m_comment);
240
        m_klass = static_cast<UMLClassifier*>(ns);
241
        m_klass->setStereotype(QLatin1String("CORBAInterface"));
242
243
        m_klass->setAbstract(m_isAbstract);
        m_isAbstract = false;
244
        m_comment.clear();
245
        if (advance() == QLatin1String(";"))   // forward declaration
246
            return true;
247
        pushScope(m_klass);
248
249
        if (m_source[m_srcIndex] == QLatin1String(":")) {
            while (++m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String("{")) {
250
251
                const QString& baseName = m_source[m_srcIndex];
                Import_Utils::createGeneralization(m_klass, baseName);
252
                if (advance() != QLatin1String(","))
Oliver Kellogg's avatar
Oliver Kellogg committed
253
254
255
                    break;
            }
        }
256
        if (m_source[m_srcIndex] != QLatin1String("{")) {
257
            uError() << "importIDL: ignoring excess chars at " << name;
258
            skipStmt(QLatin1String("{"));
Oliver Kellogg's avatar
Oliver Kellogg committed
259
        }
260
261
        return true;
    }
262
    if (keyword == QLatin1String("struct") || keyword == QLatin1String("exception")) {
263
        const QString& name = advance();
264
        UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Class,
265
                        name, currentScope(), m_comment);
266
        m_klass = static_cast<UMLClassifier*>(ns);
267
        pushScope(m_klass);
268
269
        if (keyword == QLatin1String("struct"))
            m_klass->setStereotype(QLatin1String("CORBAStruct"));
270
        else
271
272
            m_klass->setStereotype(QLatin1String("CORBAException"));
        if (advance() != QLatin1String("{")) {
273
            uError() << "importIDL: expecting '{' at " << name;
274
            skipStmt(QLatin1String("{"));
Oliver Kellogg's avatar
Oliver Kellogg committed
275
        }
276
277
        return true;
    }
278
    if (keyword == QLatin1String("union")) {
279
280
281
        // mostly TBD.
        const QString& name = advance();
        Import_Utils::createUMLObject(UMLObject::ot_Class,
282
                        name, currentScope(), m_comment, QLatin1String("CORBAUnion"));
283
        skipStmt(QLatin1String("}"));
284
285
286
        m_srcIndex++;  // advance to ';'
        return true;
    }
287
    if (keyword == QLatin1String("enum")) {
288
        const QString& name = advance();
289
        UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Enum,
290
                        name, currentScope(), m_comment);
291
292
        UMLEnum *enumType = static_cast<UMLEnum*>(ns);
        m_srcIndex++;  // skip name
293
        while (++m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String("}")) {
294
            Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]);
295
            if (advance() != QLatin1String(","))
296
                break;
Oliver Kellogg's avatar
Oliver Kellogg committed
297
        }
298
299
300
        skipStmt();
        return true;
    }
301
    if (keyword == QLatin1String("typedef")) {
302
        const QString& oldType = advance();
303
        const QString& newType = advance();
304
305
        uDebug() << "oldType is " << oldType
                 << ", newType is " << newType
306
307
                 << ", scopeIndex is " << scopeIndex();
        Import_Utils::createUMLObject(UMLObject::ot_Class, newType, currentScope(),
308
                                     m_comment, QLatin1String("CORBATypedef") /* stereotype */);
309
310
311
312
        // @todo How do we convey the existingType ?
        skipStmt();
        return true;
    }
313
    if (keyword == QLatin1String("const")) {
314
315
316
        skipStmt();
        return true;
    }
317
    if (keyword == QLatin1String("custom")) {
318
319
        return true;
    }
320
    if (keyword == QLatin1String("abstract")) {
321
322
323
        m_isAbstract = true;
        return true;
    }
324
    if (keyword == QLatin1String("valuetype")) {
325
        const QString& name = advance();
326
        UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Class,
327
                        name, currentScope(), m_comment);
328
        m_klass = static_cast<UMLClassifier*>(ns);
329
330
        m_klass->setAbstract(m_isAbstract);
        m_isAbstract = false;
331
        if (advance() == QLatin1String(";"))   // forward declaration
332
            return true;
333
        pushScope(m_klass);
334
335
        if (m_source[m_srcIndex] == QLatin1String(":")) {
            if (advance() == QLatin1String("truncatable"))
336
                m_srcIndex++;
337
            while (m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String("{")) {
338
339
                const QString& baseName = m_source[m_srcIndex];
                Import_Utils::createGeneralization(m_klass, baseName);
340
                if (advance() != QLatin1String(","))
Oliver Kellogg's avatar
Oliver Kellogg committed
341
                    break;
342
                m_srcIndex++;
Oliver Kellogg's avatar
Oliver Kellogg committed
343
344
            }
        }
345
        if (m_source[m_srcIndex] != QLatin1String("{")) {
346
            uError() << "importIDL: ignoring excess chars at "
347
            << name;
348
            skipStmt(QLatin1String("{"));
349
350
351
        }
        return true;
    }
352
    if (keyword == QLatin1String("public")) {
353
354
        return true;
    }
355
    if (keyword == QLatin1String("private")) {
356
357
358
        m_currentAccess = Uml::Visibility::Private;
        return true;
    }
359
    if (keyword == QLatin1String("readonly")) {
360
361
362
        m_isReadonly = true;
        return true;
    }
363
    if (keyword == QLatin1String("attribute")) {
364
365
366
        m_isAttribute = true;
        return true;
    }
367
    if (keyword == QLatin1String("oneway")) {
368
369
370
        m_isOneway = true;
        return true;
    }
371
    if (keyword == QLatin1String("}")) {
372
373
        if (scopeIndex())
            m_klass = dynamic_cast<UMLClassifier*>(popScope());
374
        else
375
            uError() << "importIDL: too many }";
376
377
378
        m_srcIndex++;  // skip ';'
        return true;
    }
379
    if (keyword == QLatin1String(";"))
380
381
382
383
384
        return true;
    // At this point, we expect `keyword' to be a type name
    // (of a member of struct or valuetype, or return type
    // of an operation.) Up next is the name of the attribute
    // or operation.
385
    if (! keyword.contains(QRegExp(QLatin1String("^\\w")))) {
386
        uError() << "importIDL: ignoring " << keyword;
387
388
389
390
        return false;
    }
    QString typeName = joinTypename();
    QString name = advance();
391
    if (name.contains(QRegExp(QLatin1String("\\W")))) {
392
        uError() << "importIDL: expecting name in " << name;
393
394
395
396
        return false;
    }
    // At this point we most definitely need a class.
    if (m_klass == NULL) {
397
        uError() << "importIDL: no class set for " << name;
398
399
400
        return false;
    }
    QString nextToken = advance();
401
    if (nextToken == QLatin1String("(")) {
402
403
404
        // operation
        UMLOperation *op = Import_Utils::makeOperation(m_klass, name);
        m_srcIndex++;
405
        while (m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String(")")) {
406
407
408
409
            const QString &direction = m_source[m_srcIndex++];
            QString typeName = joinTypename();
            const QString &parName = advance();
            UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName);
410
            Uml::ParameterDirection::Enum dir;
411
412
413
            if (Model_Utils::stringToDirection(direction, dir))
                att->setParmKind(dir);
            else
414
                uError() << "importIDL: expecting parameter direction at "
415
                << direction;
416
            if (advance() != QLatin1String(","))
417
                break;
418
419
420
421
422
            m_srcIndex++;
        }
        Import_Utils::insertMethod(m_klass, op, Uml::Visibility::Public, typeName,
                                  false, false, false, false, m_comment);
        if (m_isOneway) {
423
            op->setStereotype(QLatin1String("oneway"));
424
425
426
427
428
429
430
            m_isOneway = false;
        }
        skipStmt();  // skip possible "raises" clause
        return true;
    }
    // At this point we know it's some kind of attribute declaration.
    while (1) {
431
        while (nextToken != QLatin1String(",") && nextToken != QLatin1String(";")) {
432
            name += nextToken;  // add possible array dimensions to `name'
433
            nextToken = advance();
Oliver Kellogg's avatar
Oliver Kellogg committed
434
        }
435
436
437
        UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, typeName, m_comment);
        UMLAttribute *attr = static_cast<UMLAttribute*>(o);
        if (m_isReadonly) {
438
            attr->setStereotype(QLatin1String("readonly"));
439
            m_isReadonly = false;
Oliver Kellogg's avatar
Oliver Kellogg committed
440
        }
441
        if (nextToken != QLatin1String(","))
442
443
444
            break;
        name = advance();
        nextToken = advance();
Oliver Kellogg's avatar
Oliver Kellogg committed
445
    }
446
    return true;
447
448
}