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