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 }