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}