Initial commit
[profile/ivi/openjade.git] / style / Pattern.cxx
1 // Copyright (c) 1997 James Clark
2 // See the file copying.txt for copying permission.
3
4 #include "stylelib.h"
5 #include "Pattern.h"
6 #include "macros.h"
7 #include "Vector.h"
8 #include "Interpreter.h"
9
10 #ifdef DSSSL_NAMESPACE
11 namespace DSSSL_NAMESPACE {
12 #endif
13
14 Pattern::Pattern()
15 : trivial_(0)
16 {
17 }
18
19 Pattern::Pattern(IList<Element> &ancestors)
20 : trivial_(computeTrivial(ancestors))
21 {
22   ancestors_.swap(ancestors);
23 }
24
25 bool Pattern::computeTrivial(const IList<Element> &ancestors)
26 {
27   IListIter<Element> iter(ancestors);
28   if (iter.done())
29     return 1;
30   if (!iter.cur()->trivial())
31     return 0;
32   iter.next();
33   if (!iter.done())
34     return 0;
35   return 1;
36 }
37
38 Pattern::Element::Element(const StringC &gi)
39 : gi_(gi), minRepeat_(1), maxRepeat_(1)
40 {
41 }
42
43 bool Pattern::Element::matches(const NodePtr &nd, MatchContext &context) const
44 {
45   if (gi_.size()) {
46     if (!nd->hasGi(GroveString(gi_.data(), gi_.size())))
47       return 0;
48   }
49   else {
50     GroveString tem;
51     if (nd->getGi(tem) != accessOK)
52       return 0;
53   }
54   for (IListIter<Qualifier> iter(qualifiers_); !iter.done(); iter.next())
55     if (!iter.cur()->satisfies(nd, context))
56       return 0;
57   return 1;
58 }
59
60 bool Pattern::Element::trivial() const
61 {
62   if (minRepeat_ > 1)
63     return 0;
64   for (IListIter<Qualifier> iter(qualifiers_); !iter.done(); iter.next())
65     if (!iter.cur()->vacuous())
66       return 0;
67   return 1;
68 }
69
70 void Pattern::Element::contributeSpecificity(int *s) const
71 {
72   if (gi_.size())
73     s[giSpecificity] += minRepeat_;
74   for (IListIter<Qualifier> iter(qualifiers_); !iter.done(); iter.next())
75     iter.cur()->contributeSpecificity(s);
76   if (minRepeat_ != maxRepeat_)
77     s[repeatSpecificity] -= 1;
78 }
79
80 Pattern::Qualifier::~Qualifier()
81 {
82 }
83
84 bool Pattern::Qualifier::vacuous() const
85 {
86   return 0;
87 }
88
89 bool Pattern::Qualifier::matchAttribute(const StringC &name,
90                                         const StringC &value,
91                                         const NodePtr &nd,
92                                         MatchContext &context)
93 {
94   NamedNodeListPtr atts;
95   if (nd->getAttributes(atts) != accessOK)
96     return 0;
97   NodePtr att;
98   if (atts->namedNode(GroveString(name.data(), name.size()), att) != accessOK)
99     return 0;
100   bool implied;
101   if (att->getImplied(implied) == accessOK && implied)
102     return 0;
103   GroveString tokens;
104   if (att->tokens(tokens) == accessOK) {
105     if (tokens.size() != value.size())
106       return 0;
107     NodePtr node;
108     NamedNodeListPtr normalizer;
109     if (att->firstChild(node) != accessOK
110         || node->getEntity(node) != accessOK
111         || node->getGroveRoot(node) != accessOK
112         || node->getEntities(normalizer) != accessOK)
113       normalizer = atts;
114     StringC tem(value);
115     tem.resize(normalizer->normalize(&tem[0], tem.size()));
116     if (tokens != GroveString(tem.data(), tem.size()))
117       return 0;
118   }
119   else {
120     NodePtr tem;
121     StringC s;
122     if (att->firstChild(tem) == accessOK) {
123       do {
124         GroveString chunk;
125         if (tem->charChunk(context, chunk) == accessOK)
126           s.append(chunk.data(), chunk.size());
127         } while (tem.assignNextChunkSibling() == accessOK);
128     }
129     if (s != value)
130       return 0;
131   }
132   return 1;
133 }
134
135 Pattern::ChildrenQualifier::ChildrenQualifier(IList<Element> &children)
136 {
137   children.swap(children_);
138 }
139
140 bool Pattern::ChildrenQualifier::satisfies(const NodePtr &nd,
141                                            MatchContext &context) const
142 {
143   ASSERT(!children_.empty());
144   NodePtr child;
145   if (nd->firstChild(child) != accessOK)
146     return 0;
147   Vector<const Element *> toMatch;
148   for (IListIter<Element> iter(children_); !iter.done(); iter.next())
149     toMatch.push_back(iter.cur());
150   do {
151     size_t j = 0;
152     for (size_t i = 0; i < toMatch.size(); i++) {
153       if (!toMatch[i]->matches(child, context)) {
154         if (j != i)
155           toMatch[j] = toMatch[i];
156         j++;
157       }
158     }
159     if (j == 0)
160       return 1;
161     toMatch.resize(j);
162   } while (child.assignNextChunkSibling() == accessOK);
163   return 0;
164 }
165
166 void Pattern::ChildrenQualifier::contributeSpecificity(int *s) const
167 {
168   for (IListIter<Element> iter(children_); !iter.done(); iter.next())
169     iter.cur()->contributeSpecificity(s);
170 }
171
172 Pattern::IdQualifier::IdQualifier(const StringC &id)
173 : id_(id)
174 {
175 }
176
177 bool Pattern::IdQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
178 {
179   GroveString nodeId;
180   if (nd->getId(nodeId) == accessOK) {
181     size_t len = id_.size();
182     if (nodeId.size() == len) {
183       StringC tem(id_);
184       Interpreter::normalizeGeneralName(nd, tem);
185       GroveString patternId(tem.data(), tem.size());
186       if (patternId == nodeId)
187         return 1;
188     }
189   }
190   const Vector<StringC> &idAtts = context.idAttributeNames();
191   for (size_t i = 0; i < idAtts.size(); i++)
192     if (matchAttribute(idAtts[i], id_, nd, context))
193       return 1;
194   return 0;
195 }
196
197 void Pattern::IdQualifier::contributeSpecificity(int *s) const
198 {
199   s[idSpecificity] += 1;
200 }
201
202 Pattern::ClassQualifier::ClassQualifier(const StringC &cls)
203 : class_(cls)
204 {
205 }
206
207 bool Pattern::ClassQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
208 {
209   const Vector<StringC> &classAtts = context.classAttributeNames();
210   for (size_t i = 0; i < classAtts.size(); i++)
211     if (matchAttribute(classAtts[i], class_, nd, context))
212       return 1;
213   return 0;
214 }
215
216 void Pattern::ClassQualifier::contributeSpecificity(int *s) const
217 {
218   s[classSpecificity] += 1;
219 }
220
221 Pattern::AttributeHasValueQualifier::AttributeHasValueQualifier(const StringC &name)
222 : name_(name)
223 {
224 }
225
226 bool Pattern::AttributeHasValueQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
227 {
228   NamedNodeListPtr atts;
229   if (nd->getAttributes(atts) != accessOK)
230     return 0;
231   NodePtr att;
232   if (atts->namedNode(GroveString(name_.data(), name_.size()), att) != accessOK)
233     return 0;
234   bool implied;
235   if (att->getImplied(implied) == accessOK && implied)
236     return 0;
237   return 1;
238 }
239
240 void Pattern::AttributeHasValueQualifier::contributeSpecificity(int *s) const
241 {
242   s[attributeSpecificity] += 1;
243 }
244
245 Pattern::AttributeMissingValueQualifier::AttributeMissingValueQualifier(const StringC &name)
246 : name_(name)
247 {
248 }
249
250 bool Pattern::AttributeMissingValueQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
251 {
252   NamedNodeListPtr atts;
253   if (nd->getAttributes(atts) != accessOK)
254     return 1;
255   NodePtr att;
256   if (atts->namedNode(GroveString(name_.data(), name_.size()), att) != accessOK)
257     return 1;
258   bool implied;
259   if (att->getImplied(implied) == accessOK && implied)
260     return 1;
261   return 0;
262 }
263
264 void Pattern::AttributeMissingValueQualifier::contributeSpecificity(int *s) const
265 {
266   s[attributeSpecificity] += 1;
267 }
268
269 Pattern::AttributeQualifier::AttributeQualifier(const StringC &name, const StringC &value)
270 : name_(name), value_(value)
271 {
272 }
273
274 bool Pattern::AttributeQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
275 {
276   return matchAttribute(name_, value_, nd, context);
277 }
278
279 void Pattern::AttributeQualifier::contributeSpecificity(int *s) const
280 {
281   s[attributeSpecificity] += 1;
282 }
283
284 void Pattern::PositionQualifier::contributeSpecificity(int *s) const
285 {
286   s[positionSpecificity] += 1;
287 }
288
289 bool Pattern::FirstOfTypeQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
290 {
291   GroveString ndType;
292   nd->getGi(ndType);
293   NodePtr tem;
294   if (nd->firstSibling(tem) != accessOK)
295     return 1; // must be document element
296   while (*tem != *nd) {
297     GroveString temType;
298     if (tem->getGi(temType) == accessOK && temType == ndType)
299       return 0;
300     tem.assignNextChunkSibling();
301   }
302   return 1;
303 }
304
305 bool Pattern::LastOfTypeQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
306 {
307   NodePtr tem;
308   if (nd->nextChunkSibling(tem) != accessOK)
309     return 1;
310   GroveString ndType;
311   nd->getGi(ndType);
312   do {
313     GroveString temType;
314     if (tem->getGi(temType) == accessOK && temType == ndType)
315       return 0;
316   } while (tem.assignNextChunkSibling() == accessOK);
317   return 1;
318 }
319
320 bool Pattern::FirstOfAnyQualifier::satisfies(const NodePtr &nd, MatchContext &) const
321 {
322   NodePtr tem;
323   if (nd->firstSibling(tem) != accessOK)
324     return 1; // must be document element
325   while (*tem != *nd) {
326     GroveString temType;
327     if (tem->getGi(temType) == accessOK)
328       return 0;
329     tem.assignNextChunkSibling();
330   }
331   return 1;
332 }
333
334 bool Pattern::LastOfAnyQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
335 {
336   NodePtr tem;
337   if (nd->nextChunkSibling(tem) != accessOK)
338     return 1;
339   GroveString ndType;
340   nd->getGi(ndType);
341   do {
342     GroveString temType;
343     if (tem->getGi(temType) == accessOK)
344       return 0;
345   } while (tem.assignNextChunkSibling() == accessOK);
346   return 1;
347 }
348
349 void Pattern::OnlyQualifier::contributeSpecificity(int *s) const
350 {
351   s[onlySpecificity] += 1;
352 }
353
354 bool Pattern::OnlyOfTypeQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
355 {
356   GroveString ndType;
357   nd->getGi(ndType);
358   NodePtr tem;
359   if (nd->firstSibling(tem) != accessOK)
360     return 1; // must be document element
361   unsigned count = 0;
362   do {
363     GroveString temType;
364     if (tem->getGi(temType) == accessOK && temType == ndType) {
365       if (count++)
366         return 0;
367     }
368   } while (tem.assignNextChunkSibling() == accessOK);
369   return 1;
370 }
371
372 bool Pattern::OnlyOfAnyQualifier::satisfies(const NodePtr &nd, MatchContext &context) const
373 {
374   NodePtr tem;
375   if (nd->firstSibling(tem) != accessOK)
376     return 1; // must be document element
377   unsigned count = 0;
378   do {
379     GroveString temType;
380     if (tem->getGi(temType) == accessOK) {
381       if (count++)
382         return 0;
383     }
384   } while (tem.assignNextChunkSibling() == accessOK);
385   return 1;
386 }
387
388 bool Pattern::VacuousQualifier::vacuous() const
389 {
390   return 1;
391 }
392
393 Pattern::PriorityQualifier::PriorityQualifier(long n)
394 : n_(n)
395 {
396 }
397
398 void Pattern::PriorityQualifier::contributeSpecificity(int *s) const
399 {
400   s[prioritySpecificity] += n_;
401 }
402
403 bool Pattern::PriorityQualifier::satisfies(const NodePtr &, MatchContext &) const
404 {
405   return 1;
406 }
407
408 Pattern::ImportanceQualifier::ImportanceQualifier(long n)
409 : n_(n)
410 {
411 }
412
413 void Pattern::ImportanceQualifier::contributeSpecificity(int *s) const
414 {
415   s[importanceSpecificity] += n_;
416 }
417
418 bool Pattern::ImportanceQualifier::satisfies(const NodePtr &, MatchContext &) const
419 {
420   return 1;
421 }
422
423 bool Pattern::matchAncestors1(const IListIter<Element> &ancestors,
424                               const NodePtr &node,
425                               MatchContext &context)
426 {
427   const Element &r = *ancestors.cur();
428   NodePtr tem(node);
429   for (Repeat i = 0; i < r.minRepeat(); i++) {
430     if (!tem || !r.matches(tem, context))
431       return 0;
432     if (tem->getParent(tem) != accessOK)
433       tem.clear();
434   }
435   Repeat i = r.minRepeat();
436   for (;;) {
437     IListIter<Element> up(ancestors);
438     up.next();
439     if (matchAncestors(up, tem, context))
440       break;
441     if (i == r.maxRepeat() || !tem || !r.matches(tem, context))
442       return 0;
443     i++;
444     if (tem->getParent(tem) != accessOK)
445       tem.clear();
446   }
447   return 1;
448 }
449
450 void Pattern::computeSpecificity(int *s) const
451 {
452   for (int i = 0; i < nSpecificity; i++)
453     s[i] = 0;
454   for (IListIter<Element> iter(ancestors_); !iter.done(); iter.next())
455     iter.cur()->contributeSpecificity(s);
456 }
457
458 int Pattern::compareSpecificity(const Pattern &pattern1, const Pattern &pattern2)
459 {
460   int s1[nSpecificity];
461   int s2[nSpecificity];
462   int i;  // declare here to avoid gcc bug
463   pattern1.computeSpecificity(s1);
464   pattern2.computeSpecificity(s2);
465   for (i = 0; i < nSpecificity; i++) {
466     if (s1[i] != s2[i])
467       return s1[i] > s2[i] ? -1 : 1;
468   }
469   return 0;
470 }
471
472 #ifdef DSSSL_NAMESPACE
473 }
474 #endif