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.SetSystemProperty;
36  
37  /**
38   * Tests {@link StringLookupFactory}.
39   */
40  class StringLookupFactoryTest {
41  
42      public static void assertDefaultKeys(final Map<String, StringLookup> stringLookupMap) {
43          // included
44          assertMappedLookups(stringLookupMap,
45                  "base64",
46                  StringLookupFactory.KEY_BASE64_DECODER,
47                  StringLookupFactory.KEY_BASE64_ENCODER,
48                  StringLookupFactory.KEY_CONST,
49                  StringLookupFactory.KEY_DATE,
50                  StringLookupFactory.KEY_ENV,
51                  StringLookupFactory.KEY_FILE,
52                  StringLookupFactory.KEY_JAVA,
53                  StringLookupFactory.KEY_LOCALHOST,
54                  StringLookupFactory.KEY_LOOPBACK_ADDRESS,
55                  StringLookupFactory.KEY_PROPERTIES,
56                  StringLookupFactory.KEY_RESOURCE_BUNDLE,
57                  StringLookupFactory.KEY_SYS,
58                  StringLookupFactory.KEY_URL_DECODER,
59                  StringLookupFactory.KEY_URL_ENCODER,
60                  StringLookupFactory.KEY_XML,
61                  StringLookupFactory.KEY_XML_DECODER,
62                  StringLookupFactory.KEY_XML_ENCODER);
63      }
64  
65      private static void assertMappedLookups(final Map<String, StringLookup> lookupMap, final String... keys) {
66          final Set<String> remainingKeys = new HashSet<>(lookupMap.keySet());
67          for (final String key : keys) {
68              final String normalizedKey = StringLookupFactory.toKey(key);
69              assertNotNull(normalizedKey, () -> "Expected map to contain string lookup for key " + key);
70              remainingKeys.remove(normalizedKey);
71          }
72          assertTrue(remainingKeys.isEmpty(), () -> "Unexpected keys in lookup map: " + remainingKeys);
73      }
74  
75      private static void checkDefaultStringLookupsHolder(final Properties props, final String... keys) {
76          final StringLookupFactory.DefaultStringLookupsHolder holder = new StringLookupFactory.DefaultStringLookupsHolder(props);
77          final Map<String, StringLookup> lookupMap = holder.getDefaultStringLookups();
78          assertMappedLookups(lookupMap, keys);
79      }
80  
81      /**
82       * Main method used to verify the default string lookups resolved during JVM execution.
83       * @param args
84       */
85      public static void main(final String[] args) {
86          final Map<String, StringLookup> lookupMap = new HashMap<>();
87          StringLookupFactory.INSTANCE.addDefaultStringLookups(lookupMap);
88          System.out.println("Default string lookups");
89          for (final String key : lookupMap.keySet()) {
90              System.out.println("- " + key);
91          }
92      }
93  
94      @Test
95      void testAddDefaultStringLookupsMap() {
96          final Map<String, StringLookup> stringLookupMap = new HashMap<>();
97          StringLookupFactory.INSTANCE.addDefaultStringLookups(stringLookupMap);
98          assertDefaultKeys(stringLookupMap);
99      }
100 
101     @Test
102     void testAddDefaultStringLookupsNull() {
103         StringLookupFactory.INSTANCE.addDefaultStringLookups(null);
104     }
105 
106     @Test
107     void testClear() {
108         // this will clear out the global cache in ConstantStringLookup
109         StringLookupFactory.clear();
110     }
111 
112     /**
113      * Tests that we return the singleton.
114      */
115     @Test
116     void testDefault() {
117         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
118         final Map<String, StringLookup> stringLookupMap = new HashMap<>();
119         stringLookupFactory.addDefaultStringLookups(stringLookupMap);
120         assertTrue(stringLookupMap.containsKey("base64"));
121         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_BASE64_ENCODER.toLowerCase(Locale.ROOT)));
122         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_CONST.toLowerCase(Locale.ROOT)));
123         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_DATE));
124         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_ENV.toLowerCase(Locale.ROOT)));
125         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_FILE.toLowerCase(Locale.ROOT)));
126         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_JAVA.toLowerCase(Locale.ROOT)));
127         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_LOCALHOST.toLowerCase(Locale.ROOT)));
128         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_LOOPBACK_ADDRESS.toLowerCase(Locale.ROOT)));
129         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_PROPERTIES.toLowerCase(Locale.ROOT)));
130         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_RESOURCE_BUNDLE.toLowerCase(Locale.ROOT)));
131         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_SYS.toLowerCase(Locale.ROOT)));
132         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_DECODER.toLowerCase(Locale.ROOT)));
133         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_URL_ENCODER.toLowerCase(Locale.ROOT)));
134         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML.toLowerCase(Locale.ROOT)));
135         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML_DECODER.toLowerCase(Locale.ROOT)));
136         assertTrue(stringLookupMap.containsKey(StringLookupFactory.KEY_XML_ENCODER.toLowerCase(Locale.ROOT)));
137     }
138 
139     @Test
140     void testDefaultStringLookupsHolder_allLookups() {
141         final Properties props = new Properties();
142         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "BASE64_DECODER BASE64_ENCODER const, date, dns, environment "
143                 + "file ,java, local_host properties, resource_bundle,script,system_properties url url_decoder  , url_encoder, xml");
144         // @formatter:off
145         checkDefaultStringLookupsHolder(props,
146                 "base64",
147                 StringLookupFactory.KEY_BASE64_DECODER,
148                 StringLookupFactory.KEY_BASE64_ENCODER,
149                 StringLookupFactory.KEY_CONST,
150                 StringLookupFactory.KEY_DATE,
151                 StringLookupFactory.KEY_ENV,
152                 StringLookupFactory.KEY_FILE,
153                 StringLookupFactory.KEY_JAVA,
154                 StringLookupFactory.KEY_LOCALHOST,
155                 StringLookupFactory.KEY_LOOPBACK_ADDRESS,
156                 StringLookupFactory.KEY_PROPERTIES,
157                 StringLookupFactory.KEY_RESOURCE_BUNDLE,
158                 StringLookupFactory.KEY_SYS,
159                 StringLookupFactory.KEY_URL_DECODER,
160                 StringLookupFactory.KEY_URL_ENCODER,
161                 StringLookupFactory.KEY_XML,
162 
163                 StringLookupFactory.KEY_DNS,
164                 StringLookupFactory.KEY_URL,
165                 StringLookupFactory.KEY_SCRIPT);
166         // @formatter:on
167     }
168 
169     @Test
170     void testDefaultStringLookupsHolder_givenSingleLookup() {
171         final Properties props = new Properties();
172         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "base64_encoder");
173         checkDefaultStringLookupsHolder(props, "base64", StringLookupFactory.KEY_BASE64_ENCODER);
174     }
175 
176     @Test
177     void testDefaultStringLookupsHolder_givenSingleLookup_weirdString() {
178         final Properties props = new Properties();
179         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, " \n \t  ,, DnS , , ");
180         checkDefaultStringLookupsHolder(props, StringLookupFactory.KEY_DNS);
181     }
182 
183     @Test
184     void testDefaultStringLookupsHolder_invalidLookupsDefinition() {
185         final Properties props = new Properties();
186         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "base64_encoder nope");
187         final Exception exc = assertThrows(IllegalArgumentException.class, () -> new StringLookupFactory.DefaultStringLookupsHolder(props));
188         assertEquals("Invalid default string lookups definition: base64_encoder nope", exc.getMessage());
189     }
190 
191     @Test
192     void testDefaultStringLookupsHolder_lookupsPropertyEmptyAndBlank() {
193         final Properties propsWithNull = new Properties();
194         propsWithNull.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "");
195         checkDefaultStringLookupsHolder(propsWithNull);
196         final Properties propsWithBlank = new Properties();
197         propsWithBlank.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, " ");
198         checkDefaultStringLookupsHolder(propsWithBlank);
199     }
200 
201     @Test
202     void testDefaultStringLookupsHolder_lookupsPropertyNotPresent() {
203         // @formatter:off
204         checkDefaultStringLookupsHolder(new Properties(),
205                 "base64",
206                 StringLookupFactory.KEY_BASE64_DECODER,
207                 StringLookupFactory.KEY_BASE64_ENCODER,
208                 StringLookupFactory.KEY_CONST,
209                 StringLookupFactory.KEY_DATE,
210                 StringLookupFactory.KEY_ENV,
211                 StringLookupFactory.KEY_FILE,
212                 StringLookupFactory.KEY_JAVA,
213                 StringLookupFactory.KEY_LOCALHOST,
214                 StringLookupFactory.KEY_LOOPBACK_ADDRESS,
215                 StringLookupFactory.KEY_PROPERTIES,
216                 StringLookupFactory.KEY_RESOURCE_BUNDLE,
217                 StringLookupFactory.KEY_SYS,
218                 StringLookupFactory.KEY_URL_DECODER,
219                 StringLookupFactory.KEY_URL_ENCODER,
220                 StringLookupFactory.KEY_XML,
221                 StringLookupFactory.KEY_XML_DECODER,
222                 StringLookupFactory.KEY_XML_ENCODER);
223         // @formatter:on
224     }
225 
226     @Test
227     void testDefaultStringLookupsHolder_multipleLookups() {
228         final Properties props = new Properties();
229         props.setProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY, "dns, url script ");
230         // @formatter:off
231         checkDefaultStringLookupsHolder(props,
232                 StringLookupFactory.KEY_DNS,
233                 StringLookupFactory.KEY_URL,
234                 StringLookupFactory.KEY_SCRIPT);
235         // @formatter:on
236     }
237 
238     /**
239      * Tests that we return the singleton.
240      */
241     @Test
242     void testDeprecatedSingletons() {
243         assertSame(StringLookupFactory.INSTANCE_BASE64_DECODER, StringLookupFactory.INSTANCE.base64StringLookup());
244     }
245 
246     /**
247      * Tests that we return the singleton.
248      */
249     @Test
250     void testSingletons() {
251         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
252         assertSame(StringLookupFactory.INSTANCE_BASE64_DECODER, stringLookupFactory.base64DecoderStringLookup());
253         assertSame(StringLookupFactory.INSTANCE_BASE64_ENCODER, stringLookupFactory.base64EncoderStringLookup());
254         assertSame(ConstantStringLookup.INSTANCE, stringLookupFactory.constantStringLookup());
255         assertSame(DateStringLookup.INSTANCE, stringLookupFactory.dateStringLookup());
256         assertSame(DnsStringLookup.INSTANCE, stringLookupFactory.dnsStringLookup());
257         assertSame(StringLookupFactory.INSTANCE_ENVIRONMENT_VARIABLES, stringLookupFactory.environmentVariableStringLookup());
258         assertSame(InterpolatorStringLookup.INSTANCE, stringLookupFactory.interpolatorStringLookup());
259         assertSame(JavaPlatformStringLookup.INSTANCE, stringLookupFactory.javaPlatformStringLookup());
260         assertSame(InetAddressStringLookup.LOCAL_HOST, stringLookupFactory.localHostStringLookup());
261         assertSame(InetAddressStringLookup.LOOPACK_ADDRESS, stringLookupFactory.loopbackAddressStringLookup());
262         assertSame(StringLookupFactory.INSTANCE_NULL, stringLookupFactory.nullStringLookup());
263         assertSame(ResourceBundleStringLookup.INSTANCE, stringLookupFactory.resourceBundleStringLookup());
264         assertSame(ScriptStringLookup.INSTANCE, stringLookupFactory.scriptStringLookup());
265         assertSame(StringLookupFactory.INSTANCE_SYSTEM_PROPERTIES, stringLookupFactory.systemPropertyStringLookup());
266         assertSame(UrlDecoderStringLookup.INSTANCE, stringLookupFactory.urlDecoderStringLookup());
267         assertSame(UrlEncoderStringLookup.INSTANCE, stringLookupFactory.urlEncoderStringLookup());
268         assertSame(UrlStringLookup.INSTANCE, stringLookupFactory.urlStringLookup());
269         assertSame(XmlStringLookup.INSTANCE, stringLookupFactory.xmlStringLookup());
270         assertSame(XmlDecoderStringLookup.INSTANCE, stringLookupFactory.xmlDecoderStringLookup());
271         assertSame(XmlEncoderStringLookup.INSTANCE, stringLookupFactory.xmlEncoderStringLookup());
272     }
273 
274     @Test
275     void testXmlStringLookup() {
276         final StringLookupFactory stringLookupFactory = StringLookupFactory.INSTANCE;
277         final HashMap<String, Boolean> features = new HashMap<>(1);
278         features.put(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
279         XmlStringLookupTest.assertLookup(stringLookupFactory.xmlStringLookup(features));
280         XmlStringLookupTest.assertLookup(stringLookupFactory.xmlStringLookup(new HashMap<>()));
281     }
282 
283     @Test
284     void testXmlStringLookupExternalEntityOff() {
285         assertThrows(IllegalArgumentException.class,
286                 () -> StringLookupFactory.INSTANCE.xmlStringLookup().apply(XmlStringLookupTest.DOC_DIR + "document-entity-ref.xml:/document/content"));
287     }
288 
289     @Test
290     @SetSystemProperty(key = "XmlStringLookup.secure", value = "false")
291     void testXmlStringLookupExternalEntityOn() {
292         final String key = XmlStringLookupTest.DOC_DIR + "document-entity-ref.xml:/document/content";
293         assertEquals(XmlStringLookupTest.DATA, StringLookupFactory.INSTANCE.xmlStringLookup(XmlStringLookupTest.EMPTY_MAP).apply(key).trim());
294     }
295 }