001/* $Id: CallParamRule.java 471661 2006-11-06 08:09:25Z skitching $
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements.  See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License.  You may obtain a copy of the License at
009 * 
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 * 
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */ 
018
019
020package org.apache.commons.digester;
021
022
023import org.xml.sax.Attributes;
024
025import org.apache.commons.collections.ArrayStack;
026
027
028/**
029 * <p>Rule implementation that saves a parameter for use by a surrounding 
030 * <code>CallMethodRule<code>.</p>
031 *
032 * <p>This parameter may be:
033 * <ul>
034 * <li>from an attribute of the current element
035 * See {@link #CallParamRule(int paramIndex, String attributeName)}
036 * <li>from current the element body
037 * See {@link #CallParamRule(int paramIndex)}
038 * <li>from the top object on the stack. 
039 * See {@link #CallParamRule(int paramIndex, boolean fromStack)}
040 * <li>the current path being processed (separate <code>Rule</code>). 
041 * See {@link PathCallParamRule}
042 * </ul>
043 * </p>
044 */
045
046public class CallParamRule extends Rule {
047
048    // ----------------------------------------------------------- Constructors
049
050
051    /**
052     * Construct a "call parameter" rule that will save the body text of this
053     * element as the parameter value.
054     *
055     * <p>Note that if the element is empty the an <i>empty string</i> is 
056     * passed to the target method, not null. And if automatic type conversion
057     * is being applied (ie if the target function takes something other than 
058     * a string as a parameter) then the conversion will fail if the converter
059     * class does not accept an empty string as valid input.</p>
060     *
061     * @param digester The associated Digester
062     * @param paramIndex The zero-relative parameter number
063     *
064     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
065     * Use {@link #CallParamRule(int paramIndex)} instead.
066     */
067    public CallParamRule(Digester digester, int paramIndex) {
068
069        this(paramIndex);
070
071    }
072
073
074    /**
075     * Construct a "call parameter" rule that will save the value of the
076     * specified attribute as the parameter value.
077     *
078     * @param digester The associated Digester
079     * @param paramIndex The zero-relative parameter number
080     * @param attributeName The name of the attribute to save
081     *
082     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
083     * Use {@link #CallParamRule(int paramIndex, String attributeName)} instead.
084     */
085    public CallParamRule(Digester digester, int paramIndex,
086                         String attributeName) {
087
088        this(paramIndex, attributeName);
089
090    }
091
092    /**
093     * Construct a "call parameter" rule that will save the body text of this
094     * element as the parameter value.
095     *
096     * <p>Note that if the element is empty the an <i>empty string</i> is 
097     * passed to the target method, not null. And if automatic type conversion
098     * is being applied (ie if the target function takes something other than 
099     * a string as a parameter) then the conversion will fail if the converter
100     * class does not accept an empty string as valid input.</p>
101     *
102     * @param paramIndex The zero-relative parameter number
103     */
104    public CallParamRule(int paramIndex) {
105
106        this(paramIndex, null);
107
108    }
109
110
111    /**
112     * Construct a "call parameter" rule that will save the value of the
113     * specified attribute as the parameter value.
114     *
115     * @param paramIndex The zero-relative parameter number
116     * @param attributeName The name of the attribute to save
117     */
118    public CallParamRule(int paramIndex,
119                         String attributeName) {
120
121        this.paramIndex = paramIndex;
122        this.attributeName = attributeName;
123
124    }
125
126
127    /**
128     * Construct a "call parameter" rule.
129     *
130     * @param paramIndex The zero-relative parameter number
131     * @param fromStack should this parameter be taken from the top of the stack?
132     */    
133    public CallParamRule(int paramIndex, boolean fromStack) {
134    
135        this.paramIndex = paramIndex;  
136        this.fromStack = fromStack;
137
138    }
139    
140    /**
141     * Constructs a "call parameter" rule which sets a parameter from the stack.
142     * If the stack contains too few objects, then the parameter will be set to null.
143     *
144     * @param paramIndex The zero-relative parameter number
145     * @param stackIndex the index of the object which will be passed as a parameter. 
146     * The zeroth object is the top of the stack, 1 is the next object down and so on.
147     */    
148    public CallParamRule(int paramIndex, int stackIndex) {
149    
150        this.paramIndex = paramIndex;  
151        this.fromStack = true;
152        this.stackIndex = stackIndex;
153    }
154 
155    // ----------------------------------------------------- Instance Variables
156
157
158    /**
159     * The attribute from which to save the parameter value
160     */
161    protected String attributeName = null;
162
163
164    /**
165     * The zero-relative index of the parameter we are saving.
166     */
167    protected int paramIndex = 0;
168
169
170    /**
171     * Is the parameter to be set from the stack?
172     */
173    protected boolean fromStack = false;
174    
175    /**
176     * The position of the object from the top of the stack
177     */
178    protected int stackIndex = 0;
179
180    /** 
181     * Stack is used to allow nested body text to be processed.
182     * Lazy creation.
183     */
184    protected ArrayStack bodyTextStack;
185
186    // --------------------------------------------------------- Public Methods
187
188
189    /**
190     * Process the start of this element.
191     *
192     * @param attributes The attribute list for this element
193     */
194    public void begin(Attributes attributes) throws Exception {
195
196        Object param = null;
197        
198        if (attributeName != null) {
199        
200            param = attributes.getValue(attributeName);
201            
202        } else if(fromStack) {
203        
204            param = digester.peek(stackIndex);
205            
206            if (digester.log.isDebugEnabled()) {
207            
208                StringBuffer sb = new StringBuffer("[CallParamRule]{");
209                sb.append(digester.match);
210                sb.append("} Save from stack; from stack?").append(fromStack);
211                sb.append("; object=").append(param);
212                digester.log.debug(sb.toString());
213            }   
214        }
215        
216        // Have to save the param object to the param stack frame here.
217        // Can't wait until end(). Otherwise, the object will be lost.
218        // We can't save the object as instance variables, as 
219        // the instance variables will be overwritten
220        // if this CallParamRule is reused in subsequent nesting.
221        
222        if(param != null) {
223            Object parameters[] = (Object[]) digester.peekParams();
224            parameters[paramIndex] = param;
225        }
226    }
227
228
229    /**
230     * Process the body text of this element.
231     *
232     * @param bodyText The body text of this element
233     */
234    public void body(String bodyText) throws Exception {
235
236        if (attributeName == null && !fromStack) {
237            // We must wait to set the parameter until end
238            // so that we can make sure that the right set of parameters
239            // is at the top of the stack
240            if (bodyTextStack == null) {
241                bodyTextStack = new ArrayStack();
242            }
243            bodyTextStack.push(bodyText.trim());
244        }
245
246    }
247    
248    /**
249     * Process any body texts now.
250     */
251    public void end(String namespace, String name) {
252        if (bodyTextStack != null && !bodyTextStack.empty()) {
253            // what we do now is push one parameter onto the top set of parameters
254            Object parameters[] = (Object[]) digester.peekParams();
255            parameters[paramIndex] = bodyTextStack.pop();
256        }
257    }
258
259    /**
260     * Render a printable version of this Rule.
261     */
262    public String toString() {
263
264        StringBuffer sb = new StringBuffer("CallParamRule[");
265        sb.append("paramIndex=");
266        sb.append(paramIndex);
267        sb.append(", attributeName=");
268        sb.append(attributeName);
269        sb.append(", from stack=");
270        sb.append(fromStack);
271        sb.append("]");
272        return (sb.toString());
273
274    }
275
276
277}