Project

General

Profile

Statistics
| Branch: | Revision:

root / src / sim / minixpath.cc @ 3e29b8a0

History | View | Annotate | Download (10.7 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  MINIXPATH.CC - part of
3
//                 OMNeT++/OMNEST
4
//              Discrete System Simulation in C++
5
//
6
// Contents:
7
//   class MiniXPath
8
//
9
//==========================================================================
10
11
/*--------------------------------------------------------------*
12
  Copyright (C) 2002-2008 Andras Varga
13
  Copyright (C) 2006-2008 OpenSim Ltd.
14

15
  This file is distributed WITHOUT ANY WARRANTY. See the file
16
  `license' for details on this and other legal matters.
17
*--------------------------------------------------------------*/
18
19
#include <string.h>
20
#include "opp_ctype.h"
21
#include "platmisc.h"
22
#include "minixpath.h"
23
#include "cexception.h"
24
25
USING_NAMESPACE
26
27
28
29
MiniXPath::MiniXPath(cXMLElement::ParamResolver *resolver)
30
{
31
    this->resolver = resolver;
32
}
33
34
inline void trim(const char *&s, const char *&end)
35
{
36
    while ((*s==' ' || *s=='\t') && s<=end)
37
        s++;
38
    while (end>s && (*(end-1)==' ' || *(end-1)=='\t'))
39
        end--;
40
}
41
42
bool MiniXPath::parseTagNameFromStepExpr(std::string& tagname, const char *stepexpr, int len)
43
{
44
    const char *lbracket = strchr(stepexpr, '[');
45
    if (!lbracket || lbracket-stepexpr>=len)
46
        tagname.assign(stepexpr,len);
47
    else
48
        tagname.assign(stepexpr,lbracket-stepexpr);
49
    return true;
50
}
51
52
bool MiniXPath::parseBracketedNum(int& n, const char *s, int len)
53
{
54
    const char *end = s+len;
55
    trim(s, end);
56
    if (*s!='[' || *(end-1)!=']')
57
        return false;
58
    s++; end--; // cut brackets
59
    trim(s, end);
60
    for (const char *s1=s; s1<end; s1++)
61
        if (!opp_isdigit(*s1))
62
            return false;
63
    n = atoi(s);
64
    return true;
65
}
66
67
bool MiniXPath::parseConstant(std::string& value, const char *s, int len)
68
{
69
    // we get the part after the equal sign in "[@attrname=...]", try to
70
    // match 'value', "value" or $PARAM
71
    const char *end = s+len;
72
    trim(s, end);
73
    if (*s=='\'' && *(end-1)=='\'')
74
    {
75
        value.assign(s+1, end-s-2);
76
        return true;
77
    }
78
    else if (*s=='"' && *(end-1)=='"')
79
    {
80
        value.assign(s+1, end-s-2);
81
        return true;
82
    }
83
    else if (*s=='$' && resolver!=NULL)
84
    {
85
        return resolver->resolve(std::string(s+1,end-s-1).c_str(), value);
86
    }
87
    return false;
88
}
89
90
bool MiniXPath::parseBracketedAttrEquals(std::string& attr, std::string& value, const char *s, int len)
91
{
92
    // try to match "[@attrname='value']"
93
    if (len<7)
94
        return false;
95
    const char *end = s+len;
96
    trim(s, end);
97
    if (*s!='[' || *(end-1)!=']')
98
        return false;
99
    s++; end--; // cut brackets
100
    trim(s, end);
101
    if (*s!='@')
102
        return false;
103
    const char *equalsign = strchr(s+1, '=');
104
    if (!equalsign || equalsign>=end)
105
        return false;
106
    const char *endattr = equalsign;
107
    trim(s, endattr);
108
    attr.assign(s+1, endattr-s-1);
109
    return parseConstant(value, equalsign+1, end-equalsign-1);
110
}
111
112
cXMLElement *MiniXPath::getFirstSiblingWithAttribute(cXMLElement *firstsibling, const char *tagname, const char *attr, const char *attrvalue)
113
{
114
    for (cXMLElement *child=firstsibling; child; child=child->getNextSibling())
115
    {
116
        if (!tagname || !strcasecmp(child->getTagName(),tagname))
117
        {
118
            const char *val = child->getAttribute(attr);
119
            if (val && (!attrvalue || !strcmp(val,attrvalue)))
120
                return child;
121
        }
122
    }
123
    return NULL;
124
}
125
126
cXMLElement *MiniXPath::getNthSibling(cXMLElement *firstsibling, const char *tagname, int n)
127
{
128
    for (cXMLElement *child=firstsibling; child; child=child->getNextSibling())
129
        if (!tagname || !strcasecmp(child->getTagName(),tagname))
130
            if (n--==0)
131
                return child;
132
    return NULL;
133
}
134
135
cXMLElement *MiniXPath::recursiveMatch(cXMLElement *node, const char *pathexpr)
136
{
137
    cXMLElement *res = matchStep(node, pathexpr);
138
    if (res)
139
        return res;
140
    for (cXMLElement *child=node->getFirstChild(); child; child=child->getNextSibling())
141
    {
142
        res = recursiveMatch(child, pathexpr);
143
        if (res)
144
            return res;
145
    }
146
    return NULL;
147
}
148
149
// handle "/" or "//" at front of pattern
150
cXMLElement *MiniXPath::matchSeparator(cXMLElement *node, const char *seppathexpr)
151
{
152
    ASSERT(!*seppathexpr || *seppathexpr=='/');
153
    if (!*seppathexpr)
154
        return node; // end of pattern
155
    else if (*(seppathexpr+1)=='/')
156
        return recursiveMatch(node, seppathexpr+2); // separator is "//"  -- match in any depth
157
    else
158
        return matchStep(node, seppathexpr+1); // separator is "/"  -- match a child
159
}
160
161
// "node": the current node (".") whose children we'll try to match
162
cXMLElement *MiniXPath::matchStep(cXMLElement *node, const char *pathexpr)
163
{
164
    // find end of first pattern step
165
    const char *sep = strchr(pathexpr, '/');
166
    if (!sep) sep = pathexpr+strlen(pathexpr);
167
168
    const char *stepexpr = pathexpr;
169
    int steplen = sep-pathexpr;
170
171
    // match first pattern step.
172
    // might be one of: ".", "..", "*", "tagname", "tagname[n]", "tagname[@attr='value']"
173
    int n;
174
    std::string tagname, attr, value;
175
    if (!strncmp(stepexpr,".",steplen))
176
    {
177
        return matchSeparator(node, sep);
178
    }
179
    else if (!strncmp(stepexpr,"..",steplen))
180
    {
181
        if (node->getParentNode() && node->getParentNode()!=docNode)
182
            return matchSeparator(node->getParentNode(), sep);
183
        return NULL;
184
    }
185
    else if (!strncmp(stepexpr,"*",steplen))
186
    {
187
        for (cXMLElement *child=node->getFirstChild(); child; child=child->getNextSibling())
188
        {
189
            cXMLElement *res = matchSeparator(child, sep);
190
            if (res)
191
                return res;
192
        }
193
        return NULL;
194
    }
195
    else if (stepexpr[0]=='*' && parseBracketedNum(n, stepexpr+1, steplen-1))
196
    {
197
        cXMLElement *nthnode = getNthSibling(node->getFirstChild(), NULL, n);
198
        if (!nthnode)
199
            return NULL;
200
        return matchSeparator(nthnode, sep);
201
    }
202
    else if (stepexpr[0]=='*' && parseBracketedAttrEquals(attr, value, stepexpr+1, steplen-1))
203
    {
204
        for (cXMLElement *child=getFirstSiblingWithAttribute(node->getFirstChild(), NULL, attr.c_str(), value.c_str());
205
             child;
206
             child=getFirstSiblingWithAttribute(child->getNextSibling(), NULL, attr.c_str(), value.c_str()))
207
        {
208
            cXMLElement *res = matchSeparator(child, sep);
209
            if (res)
210
                return res;
211
        }
212
        return NULL;
213
    }
214
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && steplen==(int)tagname.length())
215
    {
216
        for (cXMLElement *child=getNthSibling(node->getFirstChild(), tagname.c_str(), 0);
217
             child;
218
             child=getNthSibling(child->getNextSibling(), tagname.c_str(), 0))
219
        {
220
            cXMLElement *res = matchSeparator(child, sep);
221
            if (res)
222
                return res;
223
        }
224
        return NULL;
225
    }
226
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && parseBracketedNum(n, stepexpr+tagname.length(), steplen-tagname.length()))
227
    {
228
        cXMLElement *nthnode = getNthSibling(node->getFirstChild(), tagname.c_str(), n);
229
        if (!nthnode)
230
            return NULL;
231
        return matchSeparator(nthnode, sep);
232
    }
233
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && parseBracketedAttrEquals(attr, value, stepexpr+tagname.length(), steplen-tagname.length()))
234
    {
235
        for (cXMLElement *child=getFirstSiblingWithAttribute(node->getFirstChild(), tagname.c_str(), attr.c_str(), value.c_str());
236
             child;
237
             child=getFirstSiblingWithAttribute(child->getNextSibling(), tagname.c_str(), attr.c_str(), value.c_str()))
238
        {
239
            cXMLElement *res = matchSeparator(child, sep);
240
            if (res)
241
                return res;
242
        }
243
        return NULL;
244
    }
245
    else
246
    {
247
        throw cRuntimeError("cXMLElement::getElementByPath(): invalid path expression `%s'", pathexpr);
248
    }
249
}
250
251
/* this function is currently unused
252
bool MiniXPath::nodeMatchesStepExpr(cXMLElement *node, const char *stepexpr, int steplen)
253
{
254
    // match first pattern step.
255
    // might be one of: ".", "..", "*", "tagname", "tagname[n]", "tagname[@attr='value']"
256
    int n;
257
    std::string tagname, attr, value;
258
    if (!strncmp(stepexpr,".",steplen))
259
    {
260
        return true;
261
    }
262
    else if (!strncmp(stepexpr,"..",steplen))
263
    {
264
        return false;
265
    }
266
    else if (!strncmp(stepexpr,"*",steplen))
267
    {
268
        return true;
269
    }
270
    else if (stepexpr[0]=='*' && parseBracketedNum(n, stepexpr+1, steplen-1))
271
    {
272
        return n==0;
273
    }
274
    else if (stepexpr[0]=='*' && parseBracketedAttrEquals(attr, value, stepexpr+1, steplen-1))
275
    {
276
        const char *attrvalue = node->getAttribute(attr.c_str());
277
        return attrvalue && value==attrvalue;
278
    }
279
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && steplen==(int)tagname.length())
280
    {
281
        return tagname==node->getTagName();
282
    }
283
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && parseBracketedNum(n, stepexpr+tagname.length(), steplen-tagname.length()))
284
    {
285
        return tagname==node->getTagName() && n==0;
286
    }
287
    else if (parseTagNameFromStepExpr(tagname, stepexpr, steplen) && parseBracketedAttrEquals(attr, value, stepexpr+tagname.length(), steplen-tagname.length()))
288
    {
289
        const char *attrvalue = node->getAttribute(attr.c_str());
290
        return tagname==node->getTagName() && attrvalue && value==attrvalue;
291
    }
292
    else
293
    {
294
        throw cRuntimeError("cXMLElement::getElementByPath(): invalid step in expression `%s'", stepexpr);
295
    }
296
}
297
*/
298
299
cXMLElement *MiniXPath::matchPathExpression(cXMLElement *contextNode, const char *pathexpr, cXMLElement *documentNode)
300
{
301
    this->docNode = documentNode;
302
    if (pathexpr[0]=='/')
303
    {
304
        // we need the document node if path starts with "/"
305
        if (documentNode==NULL)
306
            throw cRuntimeError("Mini XPath engine: cannot evaluate a path starting with '/' "
307
                                "if the documentNode optional parameter is not supplied");
308
309
        // plain "/" or "/." or "/./." doesn't match anything (try with any XPath interpreter)
310
        while (pathexpr[0]=='/' && pathexpr[1]=='.' && pathexpr[2]=='/')
311
            pathexpr += 2;
312
        if (!pathexpr[0] || (pathexpr[0]=='/' && !pathexpr[1]) || (pathexpr[0]=='/' && pathexpr[1]=='.' && !pathexpr[2]))
313
            return NULL;
314
315
        // match
316
        return matchSeparator(docNode, pathexpr);
317
    }
318
    else
319
    {
320
        // plain ".", "./." or "././." matches the context node itself
321
        while (pathexpr[0]=='.' && pathexpr[1]=='/' && pathexpr[2]!='/')
322
            pathexpr += 2;
323
        if (!pathexpr[0])
324
            return NULL;  // plain "./" is nothing
325
        if (pathexpr[0]=='.' && !pathexpr[1])
326
            return contextNode;
327
328
        // match
329
        return matchStep(contextNode, pathexpr);
330
    }
331
}