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