Mercurial > genshi > mirror
comparison genshi/template/match.py @ 687:3d7288f373bd experimental-match-fastpaths
land first cut at fast-path matching - needs some cleanup
author | aflett |
---|---|
date | Mon, 25 Feb 2008 20:44:04 +0000 |
parents | |
children | 1240ada13334 |
comparison
equal
deleted
inserted
replaced
686:92efe764ec81 | 687:3d7288f373bd |
---|---|
1 from genshi.core import START | |
2 from genshi.path import CHILD, LocalNameTest | |
3 | |
4 from copy import copy | |
5 | |
6 def is_simple_path(path): | |
7 """ | |
8 Is the path merely a tag match like "foo"? | |
9 """ | |
10 if len(path.paths) == 1 and len(path.paths[0]) == 1: | |
11 axis, nodetest, predicates = path.paths[0][0] | |
12 if (axis is CHILD and | |
13 not predicates and | |
14 isinstance(nodetest, LocalNameTest)): | |
15 return True | |
16 | |
17 return False | |
18 | |
19 | |
20 class MatchSet(object): | |
21 | |
22 def __init__(self, parent=None, exclude=None): | |
23 """ | |
24 If a parent is given, it means this is a wrapper around another | |
25 set. Just copy references to member variables in parent, but | |
26 also set exclude | |
27 """ | |
28 self.parent = parent | |
29 if parent is None: | |
30 self.tag_templates = {} | |
31 self.other_templates = [] | |
32 self.exclude = [] | |
33 if exclude is not None: | |
34 self.exclude.append(exclude) | |
35 else: | |
36 self.tag_templates = parent.tag_templates | |
37 self.other_templates = parent.other_templates | |
38 self.exclude = copy(parent.exclude) | |
39 if exclude is not None: | |
40 self.exclude.append(exclude) | |
41 | |
42 def add(self, match_template): | |
43 """ | |
44 match_template is a tuple the form | |
45 test, path, template, hints, namespace, directives | |
46 """ | |
47 path = match_template[1] | |
48 | |
49 if is_simple_path(path): | |
50 # special cache of tag | |
51 tag_name = path.paths[0][0][1].name | |
52 # setdefault is wasteful | |
53 if tag_name not in self.tag_templates: | |
54 self.tag_templates[tag_name] = [match_template] | |
55 else: | |
56 self.tag_templates[tag_name].append(match_template) | |
57 | |
58 else: | |
59 self.other_templates.append(match_template) | |
60 | |
61 def remove(self, match_template): | |
62 """ | |
63 Permanently remove a match_template - mainly for match_once | |
64 """ | |
65 path = match_template[1] | |
66 | |
67 if is_simple_path(path): | |
68 tag_name = path.paths[0][0][1].name | |
69 if tag_name in self.tag_templates: | |
70 template_list = self.tag_templates[tag_name] | |
71 template_list.remove(match_template) | |
72 if not template_list: | |
73 del self.tag_templates[tag_name] | |
74 | |
75 else: | |
76 self.other_templates.remove(match_template) | |
77 | |
78 def single_match(cls, match_template): | |
79 """ | |
80 Factory for creating a MatchSet with just one match | |
81 """ | |
82 match_set = cls() | |
83 match_set.add(match_template) | |
84 return match_set | |
85 single_match = classmethod(single_match) | |
86 | |
87 def with_exclusion(self, exclude): | |
88 """ | |
89 Factory for creating a MatchSet based on another MatchSet, but | |
90 with certain templates excluded | |
91 """ | |
92 cls = self.__class__ | |
93 new_match_set = cls(parent=self, exclude=exclude) | |
94 return new_match_set | |
95 | |
96 def find_matches(self, event): | |
97 """ | |
98 Return a list of all valid templates that can be used for the given event. | |
99 """ | |
100 kind, data, pos = event[:3] | |
101 | |
102 # todo: get the order right | |
103 if kind is START: | |
104 tag, attrs = data | |
105 if tag.localname in self.tag_templates: | |
106 for template in self.tag_templates[tag.localname]: | |
107 if template not in self.exclude: | |
108 yield template | |
109 | |
110 for template in self.other_templates: | |
111 if template not in self.exclude: | |
112 yield template | |
113 | |
114 | |
115 def __nonzero__(self): | |
116 """ | |
117 allow this to behave as a list | |
118 """ | |
119 return bool(self.tag_templates or self.other_templates) | |
120 | |
121 def __iter__(self): | |
122 """ | |
123 I don't think we really need this, but it lets us behave like a list | |
124 """ | |
125 for template_list in self.tag_templates.iteritems(): | |
126 for template in template_list: | |
127 yield template | |
128 for template in self.other_templates: | |
129 yield template | |
130 | |
131 def __str__(self): | |
132 parent = "" | |
133 if self.parent: | |
134 parent = ": child of 0x%x" % id(self.parent) | |
135 | |
136 exclude = "" | |
137 if self.exclude: | |
138 exclude = " / excluding %d items" % len(self.exclude) | |
139 | |
140 return "<MatchSet 0x%x %d tag templates, %d other templates%s%s>" % (id(self), len(self.tag_templates), len(self.other_templates), parent, exclude) |