001/*
002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
003 * 
004 * This software is open source. 
005 * See the bottom of this file for the licence.
006 * 
007 * $Id: Mode.java,v 1.4 2001/04/04 18:08:49 jstrachan Exp $
008 */
009
010package org.dom4j.rule;
011
012import java.util.HashMap;
013import java.util.Map;
014
015import org.dom4j.Attribute;
016import org.dom4j.Document;
017import org.dom4j.Element;
018import org.dom4j.Node;
019
020
021/** <p><code>Mode</code> manages a number of RuleSet instances 
022  * for the mode in a stylesheet. 
023  * It is responsible for finding the correct rule for a given DOM4J Node 
024  * using the XSLT processing model uses the smallest possible RuleSet to 
025  * reduce the number of Rule evaluations.</p>
026  *
027  * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
028  * @version $Revision: 1.4 $
029  */
030public class Mode {
031
032    private RuleSet[] ruleSets = new RuleSet[ Pattern.NUMBER_OF_TYPES ];
033    
034    /** Map of exact (local) element names to RuleSet instances */
035    private Map elementNameRuleSets;
036    
037    /** Map of exact (local) attribute names to RuleSet instances */
038    private Map attributeNameRuleSets;
039
040    public Mode() {
041    }
042
043    /** Runs the actions associated with the given node 
044      */
045    public void fireRule( Node node ) throws Exception {
046        if ( node != null ) {
047            Rule rule = getMatchingRule( node );
048            if ( rule != null ) {
049                Action action = rule.getAction();
050                if ( action != null ) {
051                    action.run( node );
052                }
053            }
054        }
055    }
056    
057    public void applyTemplates( Element element ) throws Exception {
058        for ( int i = 0, size = element.attributeCount(); i < size; i++ ) {
059            Attribute attribute = element.attribute(i);
060            fireRule( attribute );
061        }
062        for ( int i = 0, size = element.nodeCount(); i < size; i++ ) {
063            Node node = element.node(i);
064            fireRule( node );
065        }
066    }
067    
068    public void applyTemplates( Document document ) throws Exception {
069        for ( int i = 0, size = document.nodeCount(); i < size; i++ ) {
070            Node node = document.node(i);
071            fireRule( node );
072        }
073    }
074    
075    
076    public void addRule(Rule rule) {
077        int matchType = rule.getMatchType();
078        String name = rule.getMatchesNodeName();
079        if ( name != null ) {
080            if ( matchType == Node.ELEMENT_NODE ) {
081                elementNameRuleSets = addToNameMap( 
082                    elementNameRuleSets, name, rule 
083                );
084            }
085            else if ( matchType == Node.ATTRIBUTE_NODE ) { 
086                attributeNameRuleSets = addToNameMap( 
087                    attributeNameRuleSets, name, rule 
088                );
089            }
090        }
091        if ( matchType >= Pattern.NUMBER_OF_TYPES ) {
092            matchType = Pattern.ANY_NODE;
093        }
094        if ( matchType == Pattern.ANY_NODE ) {
095            // add rule to all other RuleSets if they exist
096            for ( int i = 1, size = ruleSets.length; i < size; i++ ) {
097                RuleSet ruleSet = ruleSets[i];
098                if ( ruleSet != null ) {
099                    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://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 (C) MetaStuff, Ltd. All Rights Reserved.
270 *
271 * $Id: Mode.java,v 1.4 2001/04/04 18:08:49 jstrachan Exp $
272 */