001/* $Id: VariableExpansionTestCase.java 1212599 2011-12-09 19:46:42Z 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
019package org.apache.commons.digester3.substitution;
020
021import static org.junit.Assert.*;
022
023import java.io.IOException;
024import java.io.StringReader;
025import java.util.HashMap;
026import java.util.LinkedList;
027
028import org.apache.commons.digester3.CallMethodRule;
029import org.apache.commons.digester3.Digester;
030import org.apache.commons.digester3.SimpleTestBean;
031import org.apache.commons.digester3.substitution.MultiVariableExpander;
032import org.apache.commons.digester3.substitution.VariableSubstitutor;
033import org.junit.Test;
034import org.xml.sax.SAXException;
035
036/**
037 * <p>
038 * Test Case for the variable expansion facility in Digester.
039 *
040 * @author Simon Kitching
041 */
042public class VariableExpansionTestCase
043{
044
045    // --------------------------------------------------- Overall Test Methods
046
047    // method used in tests4
048    private LinkedList<SimpleTestBean> simpleTestBeans = new LinkedList<SimpleTestBean>();
049
050    public void addSimpleTestBean( SimpleTestBean bean )
051    {
052        simpleTestBeans.add( bean );
053    }
054
055    // implementation of source shared by the variable expander and
056    // is updatable during digesting via an Ant-like property element
057    private HashMap<String, Object> mutableSource = new HashMap<String, Object>();
058
059    /**
060     * Used in test case "testExpansionWithMutableSource", where the set of variables available to be substituted into
061     * the xml is updated as the xml is parsed.
062     */
063    public void addProperty( String key, String value )
064    {
065        mutableSource.put( key, value );
066    }
067
068    /**
069     * Creates a Digester configured to show Ant-like capability.
070     *
071     * @return a Digester with rules and variable substitutor
072     */
073    private Digester createDigesterThatCanDoAnt()
074    {
075        Digester digester = new Digester();
076
077        MultiVariableExpander expander = new MultiVariableExpander();
078        expander.addSource( "$", mutableSource );
079        digester.setSubstitutor( new VariableSubstitutor( expander ) );
080
081        int useRootObj = -1;
082        Class<?>[] callerArgTypes = new Class[] { String.class, String.class };
083        CallMethodRule caller = new CallMethodRule( useRootObj, "addProperty", callerArgTypes.length, callerArgTypes );
084        digester.addRule( "root/property", caller );
085        digester.addCallParam( "root/property", 0, "name" );
086        digester.addCallParam( "root/property", 1, "value" );
087
088        digester.addObjectCreate( "root/bean", SimpleTestBean.class );
089        digester.addSetProperties( "root/bean" );
090        digester.addSetNext( "root/bean", "addSimpleTestBean" );
091        return digester;
092    }
093
094    // ------------------------------------------------ Individual Test Methods
095
096    /**
097     * Test that by default no expansion occurs.
098     */
099    @Test
100    public void testNoExpansion()
101        throws SAXException, IOException
102    {
103
104        String xml = "<root alpha='${attr1}' beta='var{attr2}'/>";
105        StringReader input = new StringReader( xml );
106        Digester digester = new Digester();
107
108        // Configure the digester as required
109        digester.addObjectCreate( "root", SimpleTestBean.class );
110        digester.addSetProperties( "root" );
111
112        // Parse our test input.
113        SimpleTestBean root = digester.parse( input );
114
115        assertNotNull( "Digester returned no object", root );
116
117        assertEquals( "${attr1}", root.getAlpha() );
118        assertEquals( "var{attr2}", root.getBeta() );
119    }
120
121    /**
122     * Test that a MultiVariableExpander with no sources does no expansion.
123     */
124    @Test
125    public void testExpansionWithNoSource()
126        throws SAXException, IOException
127    {
128
129        String xml = "<root alpha='${attr1}' beta='var{attr2}'/>";
130        StringReader input = new StringReader( xml );
131        Digester digester = new Digester();
132
133        // Configure the digester as required
134        MultiVariableExpander expander = new MultiVariableExpander();
135        digester.setSubstitutor( new VariableSubstitutor( expander ) );
136        digester.addObjectCreate( "root", SimpleTestBean.class );
137        digester.addSetProperties( "root" );
138
139        // Parse our test input.
140        SimpleTestBean root = digester.parse( input );
141
142        assertNotNull( "Digester returned no object", root );
143
144        assertEquals( "${attr1}", root.getAlpha() );
145        assertEquals( "var{attr2}", root.getBeta() );
146    }
147
148    /**
149     * Test that a MultiVariableExpander with multiple sources works. It also tests that expansion works ok where
150     * multiple elements exist.
151     */
152    @Test
153    public void testExpansionWithMultipleSources()
154        throws SAXException, IOException
155    {
156
157        String xml =
158            "<root>" + "<bean alpha='${attr1}' beta='var{attr1}'/>" + "<bean alpha='${attr2}' beta='var{attr2}'/>"
159                + "</root>";
160
161        StringReader input = new StringReader( xml );
162        Digester digester = new Digester();
163
164        // Configure the digester as required
165        HashMap<String, Object> source1 = new HashMap<String, Object>();
166        source1.put( "attr1", "source1.attr1" );
167        source1.put( "attr2", "source1.attr2" ); // should not be used
168
169        HashMap<String, Object> source2 = new HashMap<String, Object>();
170        source2.put( "attr1", "source2.attr1" ); // should not be used
171        source2.put( "attr2", "source2.attr2" );
172
173        MultiVariableExpander expander = new MultiVariableExpander();
174        expander.addSource( "$", source1 );
175        expander.addSource( "var", source2 );
176
177        digester.setSubstitutor( new VariableSubstitutor( expander ) );
178        digester.addObjectCreate( "root/bean", SimpleTestBean.class );
179        digester.addSetProperties( "root/bean" );
180        digester.addSetNext( "root/bean", "addSimpleTestBean" );
181
182        // Parse our test input.
183        this.simpleTestBeans.clear();
184        digester.push( this );
185        digester.parse( input );
186
187        assertEquals( 2, this.simpleTestBeans.size() );
188
189        {
190            SimpleTestBean bean = this.simpleTestBeans.get( 0 );
191            assertEquals( "source1.attr1", bean.getAlpha() );
192            assertEquals( "source2.attr1", bean.getBeta() );
193        }
194
195        {
196            SimpleTestBean bean = this.simpleTestBeans.get( 1 );
197            assertEquals( "source1.attr2", bean.getAlpha() );
198            assertEquals( "source2.attr2", bean.getBeta() );
199        }
200    }
201
202    /**
203     * Test expansion of text in element bodies.
204     */
205    @Test
206    public void testBodyExpansion()
207        throws SAXException, IOException
208    {
209
210        String xml = "<root>" + "Twas noun{1} and the noun{2}" + " did verb{1} and verb{2} in the noun{3}" + "</root>";
211
212        StringReader input = new StringReader( xml );
213        Digester digester = new Digester();
214
215        // Configure the digester as required
216        HashMap<String, Object> nouns = new HashMap<String, Object>();
217        nouns.put( "1", "brillig" );
218        nouns.put( "2", "slithy toves" );
219        nouns.put( "3", "wabe" );
220
221        HashMap<String, Object> verbs = new HashMap<String, Object>();
222        verbs.put( "1", "gyre" );
223        verbs.put( "2", "gimble" );
224
225        MultiVariableExpander expander = new MultiVariableExpander();
226        expander.addSource( "noun", nouns );
227        expander.addSource( "verb", verbs );
228        digester.setSubstitutor( new VariableSubstitutor( expander ) );
229
230        digester.addObjectCreate( "root", SimpleTestBean.class );
231        digester.addCallMethod( "root", "setAlpha", 0 );
232
233        // Parse our test input.
234        SimpleTestBean root = digester.parse( input );
235
236        assertNotNull( "Digester returned no object", root );
237
238        assertEquals( "Twas brillig and the slithy toves" + " did gyre and gimble in the wabe", root.getAlpha() );
239    }
240
241    /**
242     * Test that an unknown variable causes a RuntimeException.
243     */
244    @Test
245    public void testExpansionException()
246        throws IOException
247    {
248
249        String xml = "<root alpha='${attr1}'/>";
250        StringReader input = new StringReader( xml );
251        Digester digester = new Digester();
252
253        // Configure the digester as required
254        MultiVariableExpander expander = new MultiVariableExpander();
255        expander.addSource( "$", new HashMap<String, Object>() );
256        digester.setSubstitutor( new VariableSubstitutor( expander ) );
257
258        digester.addObjectCreate( "root", SimpleTestBean.class );
259        digester.addSetProperties( "root" );
260
261        // Parse our test input.
262        try
263        {
264            digester.parse( input );
265            fail( "Exception expected due to unknown variable." );
266        }
267        catch ( SAXException e )
268        {
269            // expected, due to reference to undefined variable
270        }
271    }
272
273    /**
274     * First of two tests added to verify that the substitution framework is capable of processing Ant-like properties.
275     * The tests above essentially verify that if a property was pre-set (e.g. using the "-D" option to Ant), then the
276     * property could be expanded via a variable used either in an attribute or in body text. This test shows that if
277     * properties were also set while processing a document, you could still perform variable expansion (i.e. just like
278     * using the "property" task in Ant).
279     *
280     * @throws IOException
281     * @throws SAXException
282     */
283    @Test
284    public void testExpansionWithMutableSource()
285        throws SAXException, IOException
286    {
287        String xml = "<root>" + "<property name='attr' value='prop.value'/>" + "<bean alpha='${attr}'/>" + "</root>";
288        StringReader input = new StringReader( xml );
289        Digester digester = createDigesterThatCanDoAnt();
290
291        simpleTestBeans.clear();
292        digester.push( this );
293        digester.parse( input );
294
295        assertEquals( 1, simpleTestBeans.size() );
296        SimpleTestBean bean = simpleTestBeans.get( 0 );
297        assertEquals( "prop.value", bean.getAlpha() );
298    }
299
300    /**
301     * Second of two tests added to verify that the substitution framework is capable of processing Ant-like properties.
302     * This test shows that if properties were also set while processing a document, the resulting variables could also
303     * be expanded within a property element. This is thus effectively a "closure" test, since it shows that the
304     * mechanism used to bind properties is also capable of having property values that are driven by property
305     * variables.
306     *
307     * @throws IOException
308     * @throws SAXException
309     */
310    @Test
311    public void testExpansionOfPropertyInProperty()
312        throws SAXException, IOException
313    {
314        String xml =
315            "<root>" + "<property name='attr1' value='prop.value1'/>"
316                + "<property name='attr2' value='substituted-${attr1}'/>" + "<bean alpha='${attr2}'/>" + "</root>";
317        StringReader input = new StringReader( xml );
318        Digester digester = createDigesterThatCanDoAnt();
319
320        simpleTestBeans.clear();
321        digester.push( this );
322        digester.parse( input );
323
324        assertEquals( 1, simpleTestBeans.size() );
325        SimpleTestBean bean = simpleTestBeans.get( 0 );
326        assertEquals( "substituted-prop.value1", bean.getAlpha() );
327    }
328
329}