View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cli.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  @Deprecated
32  public class MavenPropertiesLoader {
33  
34      public static final String INCLUDES_PROPERTY = "${includes}"; // includes
35  
36      public static final String OVERRIDE_PREFIX =
37              "maven.override."; // prefix that marks that system property should override defaults.
38  
39      public static void loadProperties(
40              java.util.Properties properties, Path path, Function<String, String> callback, boolean escape)
41              throws IOException {
42          MavenProperties sp = new MavenProperties(false);
43          if (Files.exists(path)) {
44              sp.load(path);
45          }
46          properties.forEach(
47                  (k, v) -> sp.put(k.toString(), escape ? DefaultInterpolator.escape(v.toString()) : v.toString()));
48          loadIncludes(path, sp, callback);
49          substitute(sp, callback);
50          sp.forEach(properties::setProperty);
51      }
52  
53      public static void substitute(MavenProperties props, Function<String, String> callback) {
54          for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements(); ) {
55              String name = (String) e.nextElement();
56              String value = props.getProperty(name);
57              if (value == null) {
58                  value = callback.apply(name);
59              }
60              if (name.startsWith(OVERRIDE_PREFIX)) {
61                  String overrideName = name.substring(OVERRIDE_PREFIX.length());
62                  props.put(overrideName, substVars(value, name, props, callback));
63              } else {
64                  props.put(name, substVars(value, name, props, callback));
65              }
66          }
67          props.keySet().removeIf(k -> k.startsWith(OVERRIDE_PREFIX));
68      }
69  
70      private static MavenProperties loadPropertiesFile(
71              Path path, boolean failIfNotFound, Function<String, String> callback) throws IOException {
72          MavenProperties configProps = new MavenProperties(null, false);
73          if (Files.exists(path) || failIfNotFound) {
74              configProps.load(path);
75              loadIncludes(path, configProps, callback);
76              trimValues(configProps);
77          }
78          return configProps;
79      }
80  
81      private static void loadIncludes(Path configProp, MavenProperties configProps, Function<String, String> callback)
82              throws IOException {
83          String includes = configProps.get(INCLUDES_PROPERTY);
84          if (includes != null) {
85              includes = substVars(includes, INCLUDES_PROPERTY, configProps, callback);
86              StringTokenizer st = new StringTokenizer(includes, "?\",", true);
87              if (st.countTokens() > 0) {
88                  String location;
89                  do {
90                      location = nextLocation(st);
91                      if (location != null) {
92                          boolean mandatory = true;
93                          if (location.startsWith("?")) {
94                              mandatory = false;
95                              location = location.substring(1);
96                          }
97                          Path path = configProp.resolveSibling(location);
98                          MavenProperties props = loadPropertiesFile(path, mandatory, s -> {
99                              var v = callback.apply(s);
100                             return v != null ? v : configProps.getProperty(s);
101                         });
102                         configProps.putAll(props);
103                     }
104                 } while (location != null);
105             }
106         }
107         configProps.remove(INCLUDES_PROPERTY);
108     }
109 
110     private static void trimValues(MavenProperties configProps) {
111         configProps.replaceAll((k, v) -> v.trim());
112     }
113 
114     private static String nextLocation(StringTokenizer st) {
115         boolean optional = false;
116         String retVal = null;
117 
118         if (st.countTokens() > 0) {
119             String tokenList = "?\",";
120             StringBuilder tokBuf = new StringBuilder(10);
121             String tok;
122             boolean inQuote = false;
123             boolean tokStarted = false;
124             boolean exit = false;
125             while ((st.hasMoreTokens()) && (!exit)) {
126                 tok = st.nextToken(tokenList);
127                 switch (tok) {
128                     case "\"":
129                         inQuote = !inQuote;
130                         if (inQuote) {
131                             tokenList = "\"";
132                         } else {
133                             tokenList = "\" ";
134                         }
135                         break;
136                     case ",":
137                         if (tokStarted) {
138                             retVal = tokBuf.toString();
139                             tokStarted = false;
140                             tokBuf = new StringBuilder(10);
141                             exit = true;
142                         }
143                         break;
144                     case "?":
145                         optional = true;
146                         break;
147                     default:
148                         tokStarted = true;
149                         tokBuf.append(tok.trim());
150                         break;
151                 }
152             }
153 
154             // Handle case where end of token stream and
155             // still got data
156             if ((!exit) && (tokStarted)) {
157                 retVal = tokBuf.toString();
158             }
159         }
160 
161         return optional ? "?" + retVal : retVal;
162     }
163 
164     public static String substVars(
165             String value, String name, Map<String, String> props, Function<String, String> callback) {
166         return DefaultInterpolator.substVars(value, name, null, props, callback, null, false);
167     }
168 }