Project

General

Profile

Statistics
| Branch: | Revision:

root / src / nedxml / nedresourcecache.cc @ 79bb12dc

History | View | Annotate | Download (18.2 KB)

1 01873262 Georg Kunz
//==========================================================================
2
// NEDRESOURCECACHE.CC -
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
//==========================================================================
8
9
/*--------------------------------------------------------------*
10
  Copyright (C) 2002-2008 Andras Varga
11
  Copyright (C) 2006-2008 OpenSim Ltd.
12

13
  This file is distributed WITHOUT ANY WARRANTY. See the file
14
  `license' for details on this and other legal matters.
15
*--------------------------------------------------------------*/
16
17
#include <stdio.h>
18
#include <string.h>
19
#include "nederror.h"
20
#include "nedexception.h"
21
#include "nedresourcecache.h"
22
#include "nedparser.h"
23
#include "nedxmlparser.h"
24
#include "neddtdvalidator.h"
25
#include "nedsyntaxvalidator.h"
26
#include "nedcrossvalidator.h"
27
#include "fileutil.h"
28
#include "stringutil.h"
29
#include "fileglobber.h"
30
#include "patternmatcher.h"
31
32
USING_NAMESPACE
33
34
//TODO collect errors in a NEDErrorStore?
35
36
NEDResourceCache::NEDResourceCache()
37
{
38
}
39
40
NEDResourceCache::~NEDResourceCache()
41
{
42
    for (NEDFileMap::iterator i = files.begin(); i!=files.end(); ++i)
43
        delete i->second;
44
    for (NEDTypeInfoMap::iterator i = nedTypes.begin(); i!=nedTypes.end(); ++i)
45
        delete i->second;
46
}
47
48
void NEDResourceCache::registerBuiltinDeclarations()
49
{
50
    // NED code to define built-in types
51
    const char *nedcode = NEDParser::getBuiltInDeclarations();
52
53
    NEDErrorStore errors;
54
    NEDParser parser(&errors);
55
    NEDElement *tree = parser.parseNEDText(nedcode, "built-in-declarations");
56
    if (errors.containsError())
57
    {
58
        delete tree;
59
        throw NEDException("error during parsing of internal NED declarations");
60
    }
61
62
    //TODO check errors, run validation perhaps
63
64
    // note: file must be called package.ned so that @namespace("") takes effect
65
    addFile("/[built-in-declarations]/package.ned", tree);
66
}
67
68
69
int NEDResourceCache::loadNedSourceFolder(const char *foldername)
70
{
71
    try
72
    {
73
        std::string canonicalFolderName = tidyFilename(toAbsolutePath(foldername).c_str(), true);
74
        std::string rootPackageName = determineRootPackageName(foldername);
75
        folderPackages[canonicalFolderName] = rootPackageName;
76
        return doLoadNedSourceFolder(foldername, rootPackageName.c_str());
77
    }
78
    catch (std::exception& e)
79
    {
80
        throw NEDException("Error loading NED sources from `%s': %s", foldername, e.what());
81
    }
82
}
83
84
int NEDResourceCache::doLoadNedSourceFolder(const char *foldername, const char *expectedPackage)
85
{
86
    PushDir pushDir(foldername);
87
    int count = 0;
88
89
    FileGlobber globber("*");
90
    const char *filename;
91
    while ((filename=globber.getNext())!=NULL)
92
    {
93
        if (filename[0] == '.')
94
        {
95
            continue;  // ignore ".", "..", and dotfiles
96
        }
97
        if (isDirectory(filename))
98
        {
99
            count += doLoadNedSourceFolder(filename, expectedPackage==NULL ? NULL : opp_join(".", expectedPackage, filename).c_str());
100
        }
101
        else if (opp_stringendswith(filename, ".ned"))
102
        {
103
            doLoadNedFileOrText(filename, NULL, expectedPackage, false);
104
            count++;
105
        }
106
    }
107
    return count;
108
}
109
110
void NEDResourceCache::doLoadNedFileOrText(const char *nedfname, const char *nedtext, const char *expectedPackage, bool isXML)
111
{
112
    Assert(nedfname);
113
    if (getFile(nedfname))
114
        return;  // already loaded
115
116
    // parse file
117
    std::string nedfname2 = nedtext ? nedfname : tidyFilename(toAbsolutePath(nedfname).c_str()); // so that NedFileElement stores absolute file name
118
    NEDElement *tree = parseAndValidateNedFileOrText(nedfname2.c_str(), nedtext, isXML);
119
    Assert(tree);
120
121
    // check that declared package matches expected package
122
    PackageElement *packageDecl = (PackageElement *)tree->getFirstChildWithTag(NED_PACKAGE);
123
    std::string declaredPackage = packageDecl ? packageDecl->getName() : "";
124
    if (expectedPackage!=NULL && declaredPackage != std::string(expectedPackage))
125
        throw NEDException("NED error in file `%s': declared package `%s' does not match expected package `%s'",
126
                           nedfname, declaredPackage.c_str(), expectedPackage);
127
128
    // register it
129
    try
130
    {
131
        addFile(nedfname2.c_str(), tree);
132
    }
133
    catch (NEDException& e)
134
    {
135
        throw NEDException("NED error: %s", e.what());
136
    }
137
}
138
139
NEDElement *NEDResourceCache::parseAndValidateNedFileOrText(const char *fname, const char *nedtext, bool isXML)
140
{
141
    // load file
142
    NEDElement *tree = 0;
143
    NEDErrorStore errors;
144
    errors.setPrintToStderr(true); //XXX
145
    if (isXML)
146
    {
147
        if (nedtext)
148
            throw NEDException("loadNedText(): parsing XML from string not supported");
149
        tree = parseXML(fname, &errors);
150
    }
151
    else
152
    {
153
        NEDParser parser(&errors);
154
        parser.setParseExpressions(true);
155
        parser.setStoreSource(false);
156
        if (nedtext)
157
            tree = parser.parseNEDText(nedtext, fname);
158
        else
159
            tree = parser.parseNEDFile(fname);
160
    }
161
    if (errors.containsError())
162
    {
163
        delete tree;
164
        throw NEDException("errors while loading or parsing file `%s'", fname);  //FIXME these errors print relative path????
165
    }
166
167
    // DTD validation and additional syntax validation
168
    NEDDTDValidator dtdvalidator(&errors);
169
    dtdvalidator.validate(tree);
170
    if (errors.containsError())
171
    {
172
        delete tree;
173
        throw NEDException("errors during DTD validation of file `%s'", fname);
174
    }
175
176
    NEDSyntaxValidator syntaxvalidator(true, &errors);
177
    syntaxvalidator.validate(tree);
178
    if (errors.containsError())
179
    {
180
        delete tree;
181
        throw NEDException("errors during validation of file `%s'", fname);
182
    }
183
    return tree;
184
}
185
186
void NEDResourceCache::loadNedFile(const char *nedfname, const char *expectedPackage, bool isXML)
187
{
188
    if (!nedfname)
189
        throw NEDException("loadNedFile(): file name is NULL");
190
191
    doLoadNedFileOrText(nedfname, NULL, expectedPackage, isXML);
192
    registerPendingNedTypes();
193
}
194
195
void NEDResourceCache::loadNedText(const char *name, const char *nedtext, const char *expectedPackage, bool isXML)
196
{
197
    if (!name)
198
        throw NEDException("loadNedText(): name is NULL");
199
    if (getFile(name))
200
        throw NEDException("loadNedText(): name `%s' already used", name);
201
202
    doLoadNedFileOrText(name, nedtext, expectedPackage, isXML);
203
    registerPendingNedTypes();
204
}
205
206
bool NEDResourceCache::addFile(const char *fname, NEDElement *node)
207
{
208
    std::string key = tidyFilename(toAbsolutePath(fname).c_str());
209
    NEDFileMap::iterator it = files.find(key);
210
    if (it!=files.end())
211
        return false; // already added
212
213
    files[key] = node;
214
215
    PackageElement *packageDecl = (PackageElement *) node->getFirstChildWithTag(NED_PACKAGE);
216
    std::string packagePrefix = packageDecl ? packageDecl->getName() : "";
217
    if (!packagePrefix.empty())
218
        packagePrefix += ".";
219
220
    collectNedTypesFrom(node, packagePrefix, false);
221
    return true;
222
}
223
224
void NEDResourceCache::collectNedTypesFrom(NEDElement *node, const std::string& namespacePrefix, bool areInnerTypes)
225
{
226
    for (NEDElement *child=node->getFirstChild(); child; child=child->getNextSibling())
227
    {
228
        int tag = child->getTagCode();
229
        if (tag==NED_CHANNEL || tag==NED_CHANNEL_INTERFACE || tag==NED_SIMPLE_MODULE ||
230
            tag==NED_COMPOUND_MODULE || tag==NED_MODULE_INTERFACE ||
231
            tag==NED_ENUM || tag==NED_STRUCT || tag==NED_CLASS || tag==NED_MESSAGE)
232
        {
233
            std::string qname = namespacePrefix + child->getAttribute("name");
234
            if (lookup(qname.c_str()))
235
                throw NEDException("redeclaration of %s %s", child->getTagName(), qname.c_str()); //XXX maybe just NEDError?
236
237
            collectNedType(qname.c_str(), areInnerTypes, child);
238
239
            NEDElement *types = child->getFirstChildWithTag(NED_TYPES);
240
            if (types)
241
                collectNedTypesFrom(types, qname+".", true);
242
        }
243
    }
244
}
245
246
void NEDResourceCache::collectNedType(const char *qname, bool isInnerType, NEDElement *node)
247
{
248
    // we'll process it later, from doneLoadingNedFiles()
249
    pendingList.push_back(PendingNedType(qname, isInnerType, node));
250
}
251
252
bool NEDResourceCache::areDependenciesResolved(const char *qname, NEDElement *node)
253
{
254
    // check that all base types are resolved
255
    NEDLookupContext context = getParentContextOf(qname, node);
256
    for (NEDElement *child=node->getFirstChild(); child; child=child->getNextSibling())
257
    {
258
        if (child->getTagCode()!=NED_EXTENDS && child->getTagCode()!=NED_INTERFACE_NAME)
259
            continue;
260
261
        const char *name = child->getAttribute("name");
262
        std::string qname = resolveNedType(context, name);
263
        if (qname.empty())
264
            return false;
265
    }
266
    return true;
267
}
268
269
void NEDResourceCache::doneLoadingNedFiles()
270
{
271
    // register NED types from all the files we've loaded
272
    registerPendingNedTypes();
273
274
    // if something was missing --> error
275
    if (!pendingList.empty())
276
    {
277
        std::string unresolvedNames;
278
        for (int i=0; i<(int)pendingList.size(); i++)
279
            unresolvedNames += std::string(i==0 ? "" : ", ") + pendingList[i].qname;
280
        if (pendingList.size()==1)
281
            throw NEDException("NED type `%s' could not be fully resolved, due to a missing base type or interface", unresolvedNames.c_str());
282
        else
283
            throw NEDException("The following NED types could not be fully resolved, due to a missing base type or interface: %s", unresolvedNames.c_str());
284
    }
285
}
286
287
void NEDResourceCache::registerPendingNedTypes()
288
{
289
    bool again = true;
290
    while (again)
291
    {
292
        again = false;
293
        for (int i=0; i<(int)pendingList.size(); i++)
294
        {
295
            PendingNedType type = pendingList[i];
296
            if (areDependenciesResolved(type.qname.c_str(), type.node))
297
            {
298
                registerNedType(type.qname.c_str(), type.isInnerType, type.node);
299
                pendingList.erase(pendingList.begin() + i--);
300
                again = true;
301
            }
302
        }
303
    }
304
}
305
306
void NEDResourceCache::registerNedType(const char *qname, bool isInnerType, NEDElement *node)
307
{
308
    NEDTypeInfo *decl = new NEDTypeInfo(this, qname, isInnerType, node);
309
    nedTypes[qname] = decl;
310
    nedTypeNames.clear();  // invalidate
311
}
312
313
NEDTypeInfo *NEDResourceCache::lookup(const char *qname) const
314
{
315
    // hash table lookup
316
    NEDTypeInfoMap::const_iterator i = nedTypes.find(qname);
317
    return i==nedTypes.end() ? NULL : i->second;
318
}
319
320
NEDTypeInfo *NEDResourceCache::getDecl(const char *qname) const
321
{
322
    NEDTypeInfo *decl = lookup(qname);
323
    if (!decl)
324
        throw NEDException("NED declaration '%s' not found", qname);
325
    return decl;
326
}
327
328
NEDElement *NEDResourceCache::getFile(const char *fname) const
329
{
330
    // hash table lookup
331
    std::string key = tidyFilename(toAbsolutePath(fname).c_str());
332
    NEDFileMap::const_iterator i = files.find(key);
333
    return i==files.end() ? NULL : i->second;
334
}
335
336
NedFileElement *NEDResourceCache::getParentPackageNedFile(NedFileElement *nedfile) const
337
{
338
    std::string nedfilename = tidyFilename(toAbsolutePath(nedfile->getFilename()).c_str(), true);
339
    std::string dir, fname;
340
    splitFileName(nedfilename.c_str(), dir, fname);
341
    dir = tidyFilename(dir.c_str(), true);
342
343
    std::string topDir = getNedSourceFolderForFolder(dir.c_str());
344
    if (topDir.empty())
345
        return NULL;
346
347
    if (fname != "package.ned")
348
    {
349
        // get package.ned from same package
350
        NEDElement *e = getFile(tidyFilename(concatDirAndFile(dir.c_str(), "package.ned").c_str(), true).c_str());
351
        if (e)
352
            return (NedFileElement *)e;
353
    }
354
355
    // walk up in search for a package.ned
356
    while (dir != topDir)
357
    {
358
        //printf("%s   ~   %s\n", dir.c_str(), topDir.c_str());
359
360
        // chop last segment
361
        std::string parentDir, dummy;
362
        splitFileName(dir.c_str(), parentDir, dummy);
363
        Assert(dir != parentDir); // we should exit via reaching the NED source folder
364
        dir = tidyFilename(parentDir.c_str(), true);
365
366
        // return package.ned from this dir, if exists
367
        NEDElement *e = getFile(tidyFilename(concatDirAndFile(dir.c_str(), "package.ned").c_str(), true).c_str());
368
        if (e)
369
            return (NedFileElement *)e;
370
    }
371
    return NULL;
372
}
373
374
std::string NEDResourceCache::determineRootPackageName(const char *nedSourceFolderName)
375
{
376
    // determine if a package.ned file exists
377
    std::string packageNedFilename = std::string(nedSourceFolderName) + "/package.ned";
378
    FILE *f = fopen(packageNedFilename.c_str(), "r");
379
    if (!f)
380
        return "";
381
    fclose(f);
382
383
    // read package declaration from it
384
    NEDElement *tree = parseAndValidateNedFileOrText(packageNedFilename.c_str(), NULL, false);
385
    Assert(tree);
386
    PackageElement *packageDecl = (PackageElement *)tree->getFirstChildWithTag(NED_PACKAGE);
387
    std::string result = packageDecl ? packageDecl->getName() : "";
388
    delete tree;
389
    return result;
390
}
391
392
static bool isPathPrefixOf(const char *prefix, const char *path)
393
{
394
    // note: both "prefix" and "path" must be canonical absolute paths for this to work
395
    int pathlen = strlen(path);
396
    int prefixlen = strlen(prefix);
397
    Assert(prefix[prefixlen-1]!='/' && path[pathlen-1]!='/');
398
    if (pathlen == prefixlen)
399
        return strcmp(path, prefix)==0;
400
    else if (pathlen < prefixlen)
401
        return false;  // too short
402
    else if (strncmp(path, prefix, strlen(prefix))!=0)
403
        return false;  // differ
404
    else
405
        return path[prefixlen]=='/';  // e.g. "/tmp/foo" is not prefix of "/tmp/foolish"
406
}
407
408
std::string NEDResourceCache::getNedSourceFolderForFolder(const char *folder) const
409
{
410
    // find NED source folder which is a prefix of folder.
411
    // note: this is unambiguous because nested NED source folders are not allowed
412
    std::string folderName = tidyFilename(toAbsolutePath(folder).c_str(), true);
413
    for (StringMap::const_iterator it = folderPackages.begin(); it!=folderPackages.end(); ++it)
414
        if (isPathPrefixOf(it->first.c_str(), folderName.c_str()))
415
            return it->first;
416
    return "";
417
}
418
419
std::string NEDResourceCache::getNedPackageForFolder(const char *folder) const
420
{
421
    std::string sourceFolder = getNedSourceFolderForFolder(folder);
422
    if (sourceFolder.empty())
423
        return "";
424
425
    std::string folderName = tidyFilename(toAbsolutePath(folder).c_str(), true);
426
    std::string suffix = folderName.substr(sourceFolder.size());
427
    if (suffix[0] == '/') suffix = suffix.substr(1);
428
    std::string subpackage = opp_replacesubstring(suffix.c_str(), "/", ".", true);
429
    return opp_join(".", const_cast<StringMap&>(folderPackages)[sourceFolder].c_str(), subpackage.c_str());
430
}
431
432
NEDLookupContext NEDResourceCache::getParentContextOf(const char *qname, NEDElement *node)
433
{
434
    NEDElement *contextnode = node->getParent();
435
    if (contextnode->getTagCode()==NED_TYPES)
436
        contextnode = contextnode->getParent();
437
    const char *lastdot = strrchr(qname, '.');
438
    std::string contextqname = !lastdot ? "" : std::string(qname, lastdot-qname);
439
    return NEDLookupContext(contextnode, contextqname.c_str());
440
}
441
442
std::string NEDResourceCache::resolveNedType(const NEDLookupContext& context, const char *nedtypename, INEDTypeNames *qnames)
443
{
444
    // note: this method is to be kept consistent with NEDResources.lookupNedType() in the Java code
445
    // note2: partially qualified names are not supported: name must be either simplename or fully qualified
446
    if (!strchr(nedtypename, '.'))
447
    {
448
        // no dot: name is an unqualified name (simple name); so, it can be:
449
        // (a) inner type, (b) an exactly imported type, (c) from the same package, (d) a wildcard imported type
450
451
        // inner type?
452
        if (context.element->getTagCode() == NED_COMPOUND_MODULE) {
453
            std::string qname = context.qname;
454
            NEDElement *topLevelCompoundModule = context.element->getParent()->getParentWithTag(NED_COMPOUND_MODULE);
455
            if (topLevelCompoundModule) {
456
                int index = qname.rfind('.');
457
                Assert(index != -1);
458
                qname.replace(index, qname.length() - index, "");
459
            }
460
            qname = qname + "." + nedtypename;
461
            if (qnames->contains(qname.c_str()))
462
                return qname;
463
        }
464
465
        NedFileElement *nedfileNode = dynamic_cast<NedFileElement *>(context.element->getParentWithTag(NED_NED_FILE));
466
467
        // collect imports, for convenience
468
        std::vector<const char *> imports;
469
        for (ImportElement *import = nedfileNode->getFirstImportChild(); import; import = import->getNextImportSibling())
470
            imports.push_back(import->getImportSpec());
471
472
        // exactly imported type?
473
        // try a shortcut first: if the import doesn't contain wildcards
474
        std::string dot_nedtypename = std::string(".")+nedtypename;
475
        for (int i=0; i<(int)imports.size(); i++)
476
            if (qnames->contains(imports[i]) && (opp_stringendswith(imports[i], dot_nedtypename.c_str()) || strcmp(imports[i], nedtypename)==0))
477
                return imports[i];
478
479
        // from the same package?
480
        PackageElement *packageNode = nedfileNode->getFirstPackageChild();
481
        const char *packageName = packageNode ? packageNode->getName() : "";
482
        std::string qname = opp_isempty(packageName) ? nedtypename : std::string(packageName) + "." + nedtypename;
483
        if (qnames->contains(qname.c_str()))
484
            return qname;
485
486
        // try harder, using wildcards
487
        for (int i=0; i<(int)imports.size(); i++) {
488
            if (PatternMatcher::containsWildcards(imports[i])) {
489
                PatternMatcher importpattern(imports[i], true, true, true);
490
                for (int j=0; j<qnames->size(); j++) {
491
                    const char *qname = qnames->get(j);
492
                    if ((opp_stringendswith(qname, dot_nedtypename.c_str()) || strcmp(qname, nedtypename)==0))
493
                        if (importpattern.matches(qname))
494
                            return qname;
495
                }
496
            }
497
        }
498
    }
499
    else {
500
        // fully qualified name?
501
        if (qnames->contains(nedtypename))
502
            return nedtypename;
503
    }
504
505
    return "";
506
}
507
508
const std::vector<std::string>& NEDResourceCache::getTypeNames() const
509
{
510
    if (nedTypeNames.empty() && !nedTypes.empty())
511
    {
512
        // fill in nedTypeNames vector
513
        for (NEDTypeInfoMap::const_iterator i=nedTypes.begin(); i!=nedTypes.end(); ++i)
514
            nedTypeNames.push_back(i->first);
515
    }
516
    return nedTypeNames;
517
}
518