1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.cling.props;
20
21 import java.io.IOException;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.util.Enumeration;
25 import java.util.Map;
26 import java.util.StringTokenizer;
27 import java.util.function.Function;
28
29 import org.apache.maven.internal.impl.model.DefaultInterpolator;
30
31 public class MavenPropertiesLoader {
32
33 public static final String INCLUDES_PROPERTY = "${includes}";
34
35 public static final String OVERRIDE_PREFIX =
36 "maven.override.";
37
38 public static void loadProperties(
39 java.util.Properties properties, Path path, Function<String, String> callback, boolean escape)
40 throws IOException {
41 MavenProperties sp = new MavenProperties(false);
42 if (Files.exists(path)) {
43 sp.load(path);
44 }
45 properties.forEach(
46 (k, v) -> sp.put(k.toString(), escape ? DefaultInterpolator.escape(v.toString()) : v.toString()));
47 loadIncludes(path, sp, callback);
48 substitute(sp, callback);
49 sp.forEach(properties::setProperty);
50 }
51
52 public static void substitute(MavenProperties props, Function<String, String> callback) {
53 for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements(); ) {
54 String name = (String) e.nextElement();
55 String value = props.getProperty(name);
56 if (value == null) {
57 value = callback.apply(name);
58 }
59 if (name.startsWith(OVERRIDE_PREFIX)) {
60 String overrideName = name.substring(OVERRIDE_PREFIX.length());
61 props.put(overrideName, substVars(value, name, props, callback));
62 } else {
63 props.put(name, substVars(value, name, props, callback));
64 }
65 }
66 props.keySet().removeIf(k -> k.startsWith(OVERRIDE_PREFIX));
67 }
68
69 private static MavenProperties loadPropertiesFile(
70 Path path, boolean failIfNotFound, Function<String, String> callback) throws IOException {
71 MavenProperties configProps = new MavenProperties(null, false);
72 if (Files.exists(path) || failIfNotFound) {
73 configProps.load(path);
74 loadIncludes(path, configProps, callback);
75 trimValues(configProps);
76 }
77 return configProps;
78 }
79
80 private static void loadIncludes(Path configProp, MavenProperties configProps, Function<String, String> callback)
81 throws IOException {
82 String includes = configProps.get(INCLUDES_PROPERTY);
83 if (includes != null) {
84 includes = substVars(includes, INCLUDES_PROPERTY, configProps, callback);
85 StringTokenizer st = new StringTokenizer(includes, "?\",", true);
86 if (st.countTokens() > 0) {
87 String location;
88 do {
89 location = nextLocation(st);
90 if (location != null) {
91 boolean mandatory = true;
92 if (location.startsWith("?")) {
93 mandatory = false;
94 location = location.substring(1);
95 }
96 Path path = configProp.resolveSibling(location);
97 MavenProperties props = loadPropertiesFile(path, mandatory, s -> {
98 var v = callback.apply(s);
99 return v != null ? v : configProps.getProperty(s);
100 });
101 configProps.putAll(props);
102 }
103 } while (location != null);
104 }
105 }
106 configProps.remove(INCLUDES_PROPERTY);
107 }
108
109 private static void trimValues(MavenProperties configProps) {
110 configProps.replaceAll((k, v) -> v.trim());
111 }
112
113 private static String nextLocation(StringTokenizer st) {
114 boolean optional = false;
115 String retVal = null;
116
117 if (st.countTokens() > 0) {
118 String tokenList = "?\",";
119 StringBuilder tokBuf = new StringBuilder(10);
120 String tok;
121 boolean inQuote = false;
122 boolean tokStarted = false;
123 boolean exit = false;
124 while ((st.hasMoreTokens()) && (!exit)) {
125 tok = st.nextToken(tokenList);
126 switch (tok) {
127 case "\"":
128 inQuote = !inQuote;
129 if (inQuote) {
130 tokenList = "\"";
131 } else {
132 tokenList = "\" ";
133 }
134 break;
135 case ",":
136 if (tokStarted) {
137 retVal = tokBuf.toString();
138 tokStarted = false;
139 tokBuf = new StringBuilder(10);
140 exit = true;
141 }
142 break;
143 case "?":
144 optional = true;
145 break;
146 default:
147 tokStarted = true;
148 tokBuf.append(tok.trim());
149 break;
150 }
151 }
152
153
154
155 if ((!exit) && (tokStarted)) {
156 retVal = tokBuf.toString();
157 }
158 }
159
160 return optional ? "?" + retVal : retVal;
161 }
162
163 public static String substVars(
164 String value, String name, Map<String, String> props, Function<String, String> callback) {
165 return DefaultInterpolator.substVars(value, name, null, props, callback, null, false);
166 }
167 }