001/* $Id: CallParamRule.java 992060 2010-09-02 19:09:47Z simonetripodi $
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 java.util.Stack;
024
025import org.xml.sax.Attributes;
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    @Deprecated
068    public CallParamRule(Digester digester, int paramIndex) {
069
070        this(paramIndex);
071
072    }
073
074
075    /**
076     * Construct a "call parameter" rule that will save the value of the
077     * specified attribute as the parameter value.
078     *
079     * @param digester The associated Digester
080     * @param paramIndex The zero-relative parameter number
081     * @param attributeName The name of the attribute to save
082     *
083     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
084     * Use {@link #CallParamRule(int paramIndex, String attributeName)} instead.
085     */
086    @Deprecated
087    public CallParamRule(Digester digester, int paramIndex,
088                         String attributeName) {
089
090        this(paramIndex, attributeName);
091
092    }
093
094    /**
095     * Construct a "call parameter" rule that will save the body text of this
096     * element as the parameter value.
097     *
098     * <p>Note that if the element is empty the an <i>empty string</i> is 
099     * passed to the target method, not null. And if automatic type conversion
100     * is being applied (ie if the target function takes something other than 
101     * a string as a parameter) then the conversion will fail if the converter
102     * class does not accept an empty string as valid input.</p>
103     *
104     * @param paramIndex The zero-relative parameter number
105     */
106    public CallParamRule(int paramIndex) {
107
108        this(paramIndex, null);
109
110    }
111
112
113    /**
114     * Construct a "call parameter" rule that will save the value of the
115     * specified attribute as the parameter value.
116     *
117     * @param paramIndex The zero-relative parameter number
118     * @param attributeName The name of the attribute to save
119     */
120    public CallParamRule(int paramIndex,
121                         String attributeName) {
122
123        this.paramIndex = paramIndex;
124        this.attributeName = attributeName;
125
126    }
127
128
129    /**
130     * Construct a "call parameter" rule.
131     *
132     * @param paramIndex The zero-relative parameter number
133     * @param fromStack should this parameter be taken from the top of the stack?
134     */    
135    public CallParamRule(int paramIndex, boolean fromStack) {
136    
137        this.paramIndex = paramIndex;  
138        this.fromStack = fromStack;
139
140    }
141    
142    /**
143     * Constructs a "call parameter" rule which sets a parameter from the stack.
144     * If the stack contains too few objects, then the parameter will be set to null.
145     *
146     * @param paramIndex The zero-relative parameter number
147     * @param stackIndex the index of the object which will be passed as a parameter. 
148     * The zeroth object is the top of the stack, 1 is the next object down and so on.
149     */    
150    public CallParamRule(int paramIndex, int stackIndex) {
151    
152        this.paramIndex = paramIndex;  
153        this.fromStack = true;
154        this.stackIndex = stackIndex;
155    }
156 
157    // ----------------------------------------------------- Instance Variables
158
159
160    /**
161     * The attribute from which to save the parameter value
162     */
163    protected String attributeName = null;
164
165
166    /**
167     * The zero-relative index of the parameter we are saving.
168     */
169    protected int paramIndex = 0;
170
171
172    /**
173     * Is the parameter to be set from the stack?
174     */
175    protected boolean fromStack = false;
176    
177    /**
178     * The position of the object from the top of the stack
179     */
180    protected int stackIndex = 0;
181
182    /** 
183     * Stack is used to allow nested body text to be processed.
184     * Lazy creation.
185     */
186    protected Stack<String> bodyTextStack;
187
188    // --------------------------------------------------------- Public Methods
189
190
191    /**
192     * Process the start of this element.
193     *
194     * @param attributes The attribute list for this element
195     */
196    @Override
197    public void begin(Attributes attributes) throws Exception {
198
199        Object param = null;
200        
201        if (attributeName != null) {
202        
203            param = attributes.getValue(attributeName);
204            
205        } else if(fromStack) {
206        
207            param = digester.peek(stackIndex);
208            
209            if (digester.log.isDebugEnabled()) {
210            
211                StringBuffer sb = new StringBuffer("[CallParamRule]{");
212                sb.append(digester.match);
213                sb.append("} Save from stack; from stack?").append(fromStack);
214                sb.append("; object=").append(param);
215                digester.log.debug(sb.toString());
216            }   
217        }
218        
219        // Have to save the param object to the param stack frame here.
220        // Can't wait until end(). Otherwise, the object will be lost.
221        // We can't save the object as instance variables, as 
222        // the instance variables will be overwritten
223        // if this CallParamRule is reused in subsequent nesting.
224        
225        if(param != null) {
226            Object parameters[] = (Object[]) digester.peekParams();
227            parameters[paramIndex] = param;
228        }
229    }
230
231
232    /**
233     * Process the body text of this element.
234     *
235     * @param bodyText The body text of this element
236     */
237    @Override
238    public void body(String bodyText) throws Exception {
239
240        if (attributeName == null && !fromStack) {
241            // We must wait to set the parameter until end
242            // so that we can make sure that the right set of parameters
243            // is at the top of the stack
244            if (bodyTextStack == null) {
245                bodyTextStack = new Stack<String>();
246            }
247            bodyTextStack.push(bodyText.trim());
248        }
249
250    }
251    
252    /**
253     * Process any body texts now.
254     */
255    @Override
256    public void end(String namespace, String name) {
257        if (bodyTextStack != null && !bodyTextStack.empty()) {
258            // what we do now is push one parameter onto the top set of parameters
259            Object parameters[] = (Object[]) digester.peekParams();
260            parameters[paramIndex] = bodyTextStack.pop();
261        }
262    }
263
264    /**
265     * Render a printable version of this Rule.
266     */
267    @Override
268    public String toString() {
269
270        StringBuffer sb = new StringBuffer("CallParamRule[");
271        sb.append("paramIndex=");
272        sb.append(paramIndex);
273        sb.append(", attributeName=");
274        sb.append(attributeName);
275        sb.append(", from stack=");
276        sb.append(fromStack);
277        sb.append("]");
278        return (sb.toString());
279
280    }
281
282
283}