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 java.util.Collections;
20  import java.util.Map;
21  import java.util.Map.Entry;
22  import java.util.stream.Collectors;
23  
24  /**
25   * Proxies other {@link StringLookup}s using a keys within ${} markers using the format "${StringLookup:Key}".
26   * <p>
27   * Uses the {@link StringLookupFactory default lookups}.
28   * </p>
29   * <p>
30   * Public access is through {@link StringLookupFactory}.
31   * </p>
32   *
33   * @see StringLookupFactory
34   */
35  final class InterpolatorStringLookup extends AbstractStringLookup {
36  
37      /**
38       * Defines the singleton for this class.
39       *
40       * @since 1.6
41       */
42      static final AbstractStringLookup INSTANCE = new InterpolatorStringLookup();
43  
44      /** Constant for the prefix separator. */
45      private static final char PREFIX_SEPARATOR = ':';
46  
47      /** The default string lookup. */
48      private final StringLookup defaultStringLookup;
49  
50      /** The map of String lookups keyed by prefix. */
51      private final Map<String, StringLookup> stringLookupMap;
52  
53      /**
54       * Constructs an instance using only lookups that work without initial properties and are stateless.
55       * <p>
56       * Uses the {@link StringLookupFactory default lookups}.
57       * </p>
58       */
59      InterpolatorStringLookup() {
60          this((Map<String, String>) null);
61      }
62  
63      /**
64       * Constructs a fully customized instance.
65       *
66       * @param stringLookupMap the map of string lookups.
67       * @param defaultStringLookup the default string lookup.
68       * @param addDefaultLookups whether the default lookups should be used.
69       */
70      InterpolatorStringLookup(final Map<String, StringLookup> stringLookupMap, final StringLookup defaultStringLookup,
71              final boolean addDefaultLookups) {
72          this.defaultStringLookup = defaultStringLookup;
73          this.stringLookupMap = stringLookupMap.entrySet().stream().collect(Collectors.toMap(e -> StringLookupFactory.toKey(e.getKey()), Entry::getValue));
74          if (addDefaultLookups) {
75              StringLookupFactory.INSTANCE.addDefaultStringLookups(this.stringLookupMap);
76          }
77      }
78  
79      /**
80       * Constructs an instance using only lookups that work without initial properties and are stateless.
81       * <p>
82       * Uses the {@link StringLookupFactory default lookups}.
83       * </p>
84       *
85       * @param <V> the map's value type.
86       * @param defaultMap the default map for string lookups.
87       */
88      <V> InterpolatorStringLookup(final Map<String, V> defaultMap) {
89          this(StringLookupFactory.INSTANCE.mapStringLookup(defaultMap));
90      }
91  
92      /**
93       * Constructs an instance with the given lookup.
94       *
95       * @param defaultStringLookup the default lookup.
96       */
97      InterpolatorStringLookup(final StringLookup defaultStringLookup) {
98          this(Collections.emptyMap(), defaultStringLookup, true);
99      }
100 
101     /**
102      * Gets the lookup map.
103      *
104      * @return The lookup map.
105      */
106     public Map<String, StringLookup> getStringLookupMap() {
107         return stringLookupMap;
108     }
109 
110     /**
111      * Resolves the specified variable. This implementation will try to extract a variable prefix from the given
112      * variable name (the first colon (':') is used as prefix separator). It then passes the name of the variable with
113      * the prefix stripped to the lookup object registered for this prefix. If no prefix can be found or if the
114      * associated lookup object cannot resolve this variable, the default lookup object will be used.
115      *
116      * @param key the name of the variable whose value is to be looked up.
117      * @return The value of this variable or <strong>null</strong> if it cannot be resolved.
118      */
119     @Override
120     public String lookup(String key) {
121         if (key == null) {
122             return null;
123         }
124 
125         final int prefixPos = key.indexOf(PREFIX_SEPARATOR);
126         if (prefixPos >= 0) {
127             final String prefix = StringLookupFactory.toKey(key.substring(0, prefixPos));
128             final String name = key.substring(prefixPos + 1);
129             final StringLookup lookup = stringLookupMap.get(prefix);
130             String value = null;
131             if (lookup != null) {
132                 value = lookup.apply(name);
133             }
134 
135             if (value != null) {
136                 return value;
137             }
138             key = key.substring(prefixPos + 1);
139         }
140         if (defaultStringLookup != null) {
141             return defaultStringLookup.apply(key);
142         }
143         return null;
144     }
145 
146     @Override
147     public String toString() {
148         return super.toString() + " [stringLookupMap=" + stringLookupMap + ", defaultStringLookup="
149             + defaultStringLookup + "]";
150     }
151 }