View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.commons.text.lookup;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotNull;
21  import static org.junit.jupiter.api.Assertions.assertSame;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.Properties;
30  import java.util.Set;
31  
32  import javax.xml.XMLConstants;
33  
34  import org.junit.jupiter.api.Test;
35  import org.junitpioneer.jupiter.DefaultLocale;
36  import org.junitpioneer.jupiter.SetSystemProperty;
37  
38  /**
39   * Tests {@link StringLookupFactory}.
40   */
41  class StringLookupFactoryTest {
42  
43      public static void assertDefaultKeys(final Map<String, StringLookup> stringLookupMap) {
44          // included
45          assertMappedLookups(stringLookupMap,
46                  "base64",
47                  StringLookupFactory.KEY_BASE64_DECODER,
48                  StringLookupFactory.KEY_BASE64_ENCODER,
49                  StringLookupFactory.KEY_CONST,
50                  StringLookupFactory.KEY_DATE,
51                  StringLookupFactory.KEY_ENV,
52                  StringLookupFactory.KEY_FILE,
53                  StringLookupFactory.KEY_JAVA,
54                  StringLookupFactory.KEY_LOCALHOST,
55                  StringLookupFactory.KEY_LOOPBACK_ADDRESS,
56                  StringLookupFactory.KEY_PROPERTIES,
57                  StringLookupFactory.KEY_RESOURCE_BUNDLE,
58                  StringLookupFactory.KEY_SYS,
59                  StringLookupFactory.KEY_URL_DECODER,
60                  StringLookupFactory.KEY_URL_ENCODER,
61                  StringLookupFactory.KEY_XML,
62                  StringLookupFactory.KEY_XML_DECODER,
63                  StringLookupFactory.KEY_XML_ENCODER);
64      }
65  
66      private static void assertMappedLookups(final Map<String, StringLookup> lookupMap, final String... keys) {
67          final Set<String> remainingKeys = new HashSet<>(lookupMap.keySet());
68          for (final String key : keys) {
69              final String normalizedKey = StringLookupFactory.toKey(key);
70              assertNotNull(normalizedKey, () -> "Expected map to contain string lookup for key " + key);
71              remainingKeys.remove(normalizedKey);
72          }
73          assertTrue(remainingKeys.isEmpty(), () -> "Unexpected keys in lookup map: " + remainingKeys);
74      }
75  
76      private static void checkDefaultStringLookupsHolder(final Properties props, final String... keys) {
77          final StringLookupFactory.DefaultStringLookupsHolder holder = new StringLookupFactory.DefaultStringLookupsHolder(props);
78          final Map<String, StringLookup> lookupMap = holder.getDefaultStringLookups();
79          assertMappedLookups(lookupMap, keys);
80      }
81  
82      /**
83       * Main method used to verify the default string lookups resolved during JVM execution.
84       * @param args
85       */
86      public static void main(final String[] args) {
87          final Map<String, StringLookup> lookupMap = new HashMap<>();
88          StringLookupFactory.INSTANCE.addDefaultStringLookups(lookupMap);
89          System.out.println("Default string lookups");
90          for (final String key : lookupMap.keySet()) {
91              System.out.println("- " + key);
92          }
93      }
94  
95      @Test
96      void testAddDefaultStringLookupsMap() {
97          final Map<String, StringLookup> stringLookupMap = new HashMap<>();
98          StringLookupFactory.INSTANCE.addDefaultStringLookups(stringLookupMap);
99          assertDefaultKeys(stringLookupMap);
100     }
101 
102     @Test
103     void testAddDefaultStringLookupsNull() {
104         StringLookupFactory.INSTANCE.addDefaultStringLookups(null);
105     }
106 
107     @Test
108     void testClear() {
109         // this will clear out the global cache in ConstantStringLookup
110         StringLookupFactory.clear();
111     }
112 
113     /**
114      * Tests that we return the singleton.
115      */
116     @Test
117     void testDefault() {
118         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
119         final Map<String, StringLookup> stringLookupMap = new HashMap<>();
120         stringLookupFactory.addDefaultStringLookups(stringLookupMap);
121         assertTrue(stringLookupMap.containsKey("base64"));
122         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_BASE64_ENCODER.toLowerCase(Locale.ROOT)));
123         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_CONST.toLowerCase(Locale.ROOT)));
124         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_DATE));
125         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_ENV.toLowerCase(Locale.ROOT)));
126         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_FILE.toLowerCase(Locale.ROOT)));
127         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_JAVA.toLowerCase(Locale.ROOT)));
128         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_LOCALHOST.toLowerCase(Locale.ROOT)));
129         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_LOOPBACK_ADDRESS.toLowerCase(Locale.ROOT)));
130         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_PROPERTIES.toLowerCase(Locale.ROOT)));
131         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_RESOURCE_BUNDLE.toLowerCase(Locale.ROOT)));
132         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_SYS.toLowerCase(Locale.ROOT)));
133         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_DECODER.toLowerCase(Locale.ROOT)));
134         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_ENCODER.toLowerCase(Locale.ROOT)));
135         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML.toLowerCase(Locale.ROOT)));
136         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML_DECODER.toLowerCase(Locale.ROOT)));
137         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML_ENCODER.toLowerCase(Locale.ROOT)));
138     }
139 
140     @Test
141     void testDefaultStringLookupsHolder_allLookups() {
142         final Properties props = new Properties();
143         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "BASE64_DECODER BASE64_ENCODER const, date, dns, environment "
144                 + "file ,java, local_host properties, resource_bundle,script,system_properties url url_decoder  , url_encoder, xml");
145         // @formatter:off
146         checkDefaultStringLookupsHolder(props,
147                 "base64",
148                 StringLookupFactory.KEY_BASE64_DECODER,
149                 StringLookupFactory.KEY_BASE64_ENCODER,
150                 StringLookupFactory.KEY_CONST,
151                 StringLookupFactory.KEY_DATE,
152                 StringLookupFactory.KEY_ENV,
153                 StringLookupFactory.KEY_FILE,
154                 StringLookupFactory.KEY_JAVA,
155                 StringLookupFactory.KEY_LOCALHOST,
156                 StringLookupFactory.KEY_LOOPBACK_ADDRESS,
157                 StringLookupFactory.KEY_PROPERTIES,
158                 StringLookupFactory.KEY_RESOURCE_BUNDLE,
159                 StringLookupFactory.KEY_SYS,
160                 StringLookupFactory.KEY_URL_DECODER,
161                 StringLookupFactory.KEY_URL_ENCODER,
162                 StringLookupFactory.KEY_XML,
163 
164                 StringLookupFactory.KEY_DNS,
165                 StringLookupFactory.KEY_URL,
166                 StringLookupFactory.KEY_SCRIPT);
167         // @formatter:on
168     }
169 
170     @Test
171     void testDefaultStringLookupsHolder_givenSingleLookup() {
172         final Properties props = new Properties();
173         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "base64_encoder");
174         checkDefaultStringLookupsHolder(props, "base64", StringLookupFactory.KEY_BASE64_ENCODER);
175     }
176 
177     @Test
178     @DefaultLocale(language = "tr", country = "TR")
179     void testDefaultStringLookupsHolder_givenSingleLookup_localeIndependent() {
180         // Turkish upper-cases 'i' (U+0069) to dotted 'İ' (U+0130), so "file" would fold to "FİLE"
181         // and fail to resolve against the DefaultStringLookup enum without Locale.ROOT.
182         final Properties props = new Properties();
183         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "file");
184         checkDefaultStringLookupsHolder(props, StringLookupFactory.KEY_FILE);
185     }
186 
187     @Test
188     void testDefaultStringLookupsHolder_givenSingleLookup_weirdString() {
189         final Properties props = new Properties();
190         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, " \n \t  ,, DnS , , ");
191         checkDefaultStringLookupsHolder(props, StringLookupFactory.KEY_DNS);
192     }
193 
194     @Test
195     void testDefaultStringLookupsHolder_invalidLookupsDefinition() {
196         final Properties props = new Properties();
197         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "base64_encoder nope");
198         final Exception exc = assertThrows(IllegalArgumentException.class, () -> new StringLookupFactory.DefaultStringLookupsHolder(props));
199         assertEquals("Invalid default string lookups definition: base64_encoder nope", exc.getMessage());
200     }
201 
202     @Test
203     void testDefaultStringLookupsHolder_lookupsPropertyEmptyAndBlank() {
204         final Properties propsWithNull = new Properties();
205         propsWithNull.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "");
206         checkDefaultStringLookupsHolder(propsWithNull);
207         final Properties propsWithBlank = new Properties();
208         propsWithBlank.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, " ");
209         checkDefaultStringLookupsHolder(propsWithBlank);
210     }
211 
212     @Test
213     void testDefaultStringLookupsHolder_lookupsPropertyNotPresent() {
214         // @formatter:off
215         checkDefaultStringLookupsHolder(new Properties(),
216                 "base64",
217                 StringLookupFactory.KEY_BASE64_DECODER,
218                 StringLookupFactory.KEY_BASE64_ENCODER,
219                 StringLookupFactory.KEY_CONST,
220                 StringLookupFactory.KEY_DATE,
221                 StringLookupFactory.KEY_ENV,
222                 StringLookupFactory.KEY_FILE,
223                 StringLookupFactory.KEY_JAVA,
224                 StringLookupFactory.KEY_LOCALHOST,
225                 StringLookupFactory.KEY_LOOPBACK_ADDRESS,
226                 StringLookupFactory.KEY_PROPERTIES,
227                 StringLookupFactory.KEY_RESOURCE_BUNDLE,
228                 StringLookupFactory.KEY_SYS,
229                 StringLookupFactory.KEY_URL_DECODER,
230                 StringLookupFactory.KEY_URL_ENCODER,
231                 StringLookupFactory.KEY_XML,
232                 StringLookupFactory.KEY_XML_DECODER,
233                 StringLookupFactory.KEY_XML_ENCODER);
234         // @formatter:on
235     }
236 
237     @Test
238     void testDefaultStringLookupsHolder_multipleLookups() {
239         final Properties props = new Properties();
240         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "dns, url script ");
241         // @formatter:off
242         checkDefaultStringLookupsHolder(props,
243                 StringLookupFactory.KEY_DNS,
244                 StringLookupFactory.KEY_URL,
245                 StringLookupFactory.KEY_SCRIPT);
246         // @formatter:on
247     }
248 
249     /**
250      * Tests that we return the singleton.
251      */
252     @Test
253     void testDeprecatedSingletons() {
254         assertSame(StringLookupFactory.INSTANCE_BASE64_DECODER, StringLookupFactory.INSTANCE.base64StringLookup());
255     }
256 
257     /**
258      * Tests that we return the singleton.
259      */
260     @Test
261     void testSingletons() {
262         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
263         assertSame(StringLookupFactory.INSTANCE_BASE64_DECODER, stringLookupFactory.base64DecoderStringLookup());
264         assertSame(StringLookupFactory.INSTANCE_BASE64_ENCODER, stringLookupFactory.base64EncoderStringLookup());
265         assertSame(ConstantStringLookup.INSTANCE, stringLookupFactory.constantStringLookup());
266         assertSame(DateStringLookup.INSTANCE, stringLookupFactory.dateStringLookup());
267         assertSame(DnsStringLookup.INSTANCE, stringLookupFactory.dnsStringLookup());
268         assertSame(StringLookupFactory.INSTANCE_ENVIRONMENT_VARIABLES, stringLookupFactory.environmentVariableStringLookup());
269         assertSame(InterpolatorStringLookup.INSTANCE, stringLookupFactory.interpolatorStringLookup());
270         assertSame(JavaPlatformStringLookup.INSTANCE, stringLookupFactory.javaPlatformStringLookup());
271         assertSame(InetAddressStringLookup.LOCAL_HOST, stringLookupFactory.localHostStringLookup());
272         assertSame(InetAddressStringLookup.LOOPACK_ADDRESS, stringLookupFactory.loopbackAddressStringLookup());
273         assertSame(StringLookupFactory.INSTANCE_NULL, stringLookupFactory.nullStringLookup());
274         assertSame(ResourceBundleStringLookup.INSTANCE, stringLookupFactory.resourceBundleStringLookup());
275         assertSame(ScriptStringLookup.INSTANCE, stringLookupFactory.scriptStringLookup());
276         assertSame(StringLookupFactory.INSTANCE_SYSTEM_PROPERTIES, stringLookupFactory.systemPropertyStringLookup());
277         assertSame(UrlDecoderStringLookup.INSTANCE, stringLookupFactory.urlDecoderStringLookup());
278         assertSame(UrlEncoderStringLookup.INSTANCE, stringLookupFactory.urlEncoderStringLookup());
279         assertSame(UrlStringLookup.INSTANCE, stringLookupFactory.urlStringLookup());
280         assertSame(XmlStringLookup.INSTANCE, stringLookupFactory.xmlStringLookup());
281         assertSame(XmlDecoderStringLookup.INSTANCE, stringLookupFactory.xmlDecoderStringLookup());
282         assertSame(XmlEncoderStringLookup.INSTANCE, stringLookupFactory.xmlEncoderStringLookup());
283     }
284 
285     @Test
286     void testXmlStringLookup() {
287         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
288         final HashMap<String, Boolean> features = new HashMap<>(1);
289         features.put(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
290         XmlStringLookupTest.assertLookup(stringLookupFactory.xmlStringLookup(features));
291         XmlStringLookupTest.assertLookup(stringLookupFactory.xmlStringLookup(new HashMap<>()));
292     }
293 
294     @Test
295     void testXmlStringLookupExternalEntityOff() {
296         assertThrows(IllegalArgumentException.class,
297                 () -> StringLookupFactory.INSTANCE.xmlStringLookup().apply(XmlStringLookupTest.DOC_DIR + "document-entity-ref.xml:/document/content"));
298     }
299 
300     @Test
301     @SetSystemProperty(key = "XmlStringLookup.secure", value = "false")
302     void testXmlStringLookupExternalEntityOn() {
303         final String key = XmlStringLookupTest.DOC_DIR + "document-entity-ref.xml:/document/content";
304         assertEquals(XmlStringLookupTest.DATA, StringLookupFactory.INSTANCE.xmlStringLookup(XmlStringLookupTest.EMPTY_MAP).apply(key).trim());
305     }
306 }