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 <strong>null</strong> 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 }