View Javadoc

1   /*
2    * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
3    * 
4    * This software is open source. 
5    * See the bottom of this file for the licence.
6    * 
7    * $Id: Mode.java,v 1.7 2004/06/25 08:03:39 maartenc Exp $
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              // add rule to all other RuleSets if they exist
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             // try rules that match this kind of node first
165             answer = ruleSet.getMatchingRule( node );
166         }
167         if ( answer == null && matchType != Pattern.ANY_NODE ) {
168             // try general rules that match any kind of node
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             // add the patterns that match any node
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  * Redistribution and use of this software and associated documentation
231  * ("Software"), with or without modification, are permitted provided
232  * that the following conditions are met:
233  *
234  * 1. Redistributions of source code must retain copyright
235  *    statements and notices.  Redistributions must also contain a
236  *    copy of this document.
237  *
238  * 2. Redistributions in binary form must reproduce the
239  *    above copyright notice, this list of conditions and the
240  *    following disclaimer in the documentation and/or other
241  *    materials provided with the distribution.
242  *
243  * 3. The name "DOM4J" must not be used to endorse or promote
244  *    products derived from this Software without prior written
245  *    permission of MetaStuff, Ltd.  For written permission,
246  *    please contact dom4j-info@metastuff.com.
247  *
248  * 4. Products derived from this Software may not be called "DOM4J"
249  *    nor may "DOM4J" appear in their names without prior written
250  *    permission of MetaStuff, Ltd. DOM4J is a registered
251  *    trademark of MetaStuff, Ltd.
252  *
253  * 5. Due credit should be given to the DOM4J Project - 
254  *    http://www.dom4j.org
255  *
256  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
257  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
258  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
259  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
260  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
261  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
262  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
263  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
264  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
265  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
266  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
267  * OF THE POSSIBILITY OF SUCH DAMAGE.
268  *
269  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
270  *
271  * $Id: Mode.java,v 1.7 2004/06/25 08:03:39 maartenc Exp $
272  */