1
2
3
4
5
6
7
8
9
10 package org.dom4j.rule;
11
12 import java.util.HashMap;
13 import java.util.Map;
14
15 import org.dom4j.Attribute;
16 import org.dom4j.Document;
17 import org.dom4j.Element;
18 import org.dom4j.Node;
19
20
21 /*** <p><code>Mode</code> manages a number of RuleSet instances
22 * for the mode in a stylesheet.
23 * It is responsible for finding the correct rule for a given DOM4J Node
24 * using the XSLT processing model uses the smallest possible RuleSet to
25 * reduce the number of Rule evaluations.</p>
26 *
27 * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
28 * @version $Revision: 1.7 $
29 */
30 public class Mode {
31
32 private RuleSet[] ruleSets = new RuleSet[ Pattern.NUMBER_OF_TYPES ];
33
34 /*** Map of exact (local) element names to RuleSet instances */
35 private Map elementNameRuleSets;
36
37 /*** Map of exact (local) attribute names to RuleSet instances */
38 private Map attributeNameRuleSets;
39
40 public Mode() {
41 }
42
43 /*** Runs the actions associated with the given node
44 */
45 public void fireRule( Node node ) throws Exception {
46 if ( node != null ) {
47 Rule rule = getMatchingRule( node );
48 if ( rule != null ) {
49 Action action = rule.getAction();
50 if ( action != null ) {
51 action.run( node );
52 }
53 }
54 }
55 }
56
57 public void applyTemplates( Element element ) throws Exception {
58 for ( int i = 0, size = element.attributeCount(); i < size; i++ ) {
59 Attribute attribute = element.attribute(i);
60 fireRule( attribute );
61 }
62 for ( int i = 0, size = element.nodeCount(); i < size; i++ ) {
63 Node node = element.node(i);
64 fireRule( node );
65 }
66 }
67
68 public void applyTemplates( Document document ) throws Exception {
69 for ( int i = 0, size = document.nodeCount(); i < size; i++ ) {
70 Node node = document.node(i);
71 fireRule( node );
72 }
73 }
74
75
76 public void addRule(Rule rule) {
77 int matchType = rule.getMatchType();
78 String name = rule.getMatchesNodeName();
79 if ( name != null ) {
80 if ( matchType == Node.ELEMENT_NODE ) {
81 elementNameRuleSets = addToNameMap(
82 elementNameRuleSets, name, rule
83 );
84 }
85 else if ( matchType == Node.ATTRIBUTE_NODE ) {
86 attributeNameRuleSets = addToNameMap(
87 attributeNameRuleSets, name, rule
88 );
89 }
90 }
91 if ( matchType >= Pattern.NUMBER_OF_TYPES ) {
92 matchType = Pattern.ANY_NODE;
93 }
94 if ( matchType == Pattern.ANY_NODE ) {
95
96 for ( int i = 1, size = ruleSets.length; i < size; i++ ) {
97 RuleSet ruleSet = ruleSets[i];
98 if ( ruleSet != null ) {
99 ruleSet.addRule( rule );
100 }
101 }
102 }
103 getRuleSet( matchType ).addRule( rule );
104 }
105
106 public void removeRule(Rule rule) {
107 int matchType = rule.getMatchType();
108 String name = rule.getMatchesNodeName();
109 if ( name != null ) {
110 if ( matchType == Node.ELEMENT_NODE ) {
111 removeFromNameMap( elementNameRuleSets, name, rule );
112 }
113 else if ( matchType == Node.ATTRIBUTE_NODE ) {
114 removeFromNameMap( attributeNameRuleSets, name, rule );
115 }
116 }
117 if ( matchType >= Pattern.NUMBER_OF_TYPES ) {
118 matchType = Pattern.ANY_NODE;
119 }
120 getRuleSet( matchType ).removeRule( rule );
121 if ( matchType != Pattern.ANY_NODE ) {
122 getRuleSet( Pattern.ANY_NODE ).removeRule( rule );
123 }
124 }
125
126 /*** Performs an XSLT processing model match for the rule
127 * which matches the given Node the best.
128 *
129 * @param node is the DOM4J Node to match against
130 * @return the matching Rule or no rule if none matched
131 */
132 public Rule getMatchingRule(Node node) {
133 int matchType = node.getNodeType();
134 if ( matchType == Node.ELEMENT_NODE ) {
135 if ( elementNameRuleSets != null ) {
136 String name = node.getName();
137 RuleSet ruleSet = (RuleSet) elementNameRuleSets.get( name );
138 if ( ruleSet != null ) {
139 Rule answer = ruleSet.getMatchingRule( node );
140 if ( answer != null ) {
141 return answer;
142 }
143 }
144 }
145 }
146 else if ( matchType == Node.ATTRIBUTE_NODE ) {
147 if ( attributeNameRuleSets != null ) {
148 String name = node.getName();
149 RuleSet ruleSet = (RuleSet) attributeNameRuleSets.get( name );
150 if ( ruleSet != null ) {
151 Rule answer = ruleSet.getMatchingRule( node );
152 if ( answer != null ) {
153 return answer;
154 }
155 }
156 }
157 }
158 if ( matchType < 0 || matchType >= ruleSets.length ) {
159 matchType = Pattern.ANY_NODE;
160 }
161 Rule answer = null;
162 RuleSet ruleSet = ruleSets[ matchType ];
163 if ( ruleSet != null ) {
164
165 answer = ruleSet.getMatchingRule( node );
166 }
167 if ( answer == null && matchType != Pattern.ANY_NODE ) {
168
169 ruleSet = ruleSets[ Pattern.ANY_NODE ];
170 if ( ruleSet != null ) {
171 answer = ruleSet.getMatchingRule( node );
172 }
173 }
174 return answer;
175 }
176
177
178 /*** @return the RuleSet for the given matching type.
179 * This method will never return null, a new instance will be created.
180 */
181 protected RuleSet getRuleSet( int matchType ) {
182 RuleSet ruleSet = ruleSets[ matchType ];
183 if ( ruleSet == null ) {
184 ruleSet = new RuleSet();
185 ruleSets[ matchType ] = ruleSet;
186
187
188 if ( matchType != Pattern.ANY_NODE ) {
189 RuleSet allRules = ruleSets[ Pattern.ANY_NODE ];
190 if ( allRules != null ) {
191 ruleSet.addAll( allRules );
192 }
193 }
194 }
195 return ruleSet;
196 }
197
198
199 /*** Adds the Rule to a RuleSet for the given name.
200 * @return the Map (which will be created if the given map was null
201 */
202 protected Map addToNameMap( Map map, String name, Rule rule ) {
203 if ( map == null ) {
204 map = new HashMap();
205 }
206 RuleSet ruleSet = (RuleSet) map.get( name );
207 if ( ruleSet == null ) {
208 ruleSet = new RuleSet();
209 map.put( name, ruleSet );
210 }
211 ruleSet.addRule( rule );
212 return map;
213 }
214
215 protected void removeFromNameMap( Map map, String name, Rule rule ) {
216 if ( map != null ) {
217 RuleSet ruleSet = (RuleSet) map.get( name );
218 if ( ruleSet != null ) {
219 ruleSet.removeRule( rule );
220 }
221 }
222 }
223
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272