001package org.eclipse.aether.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.List;
027import java.util.Map;
028
029import org.eclipse.aether.RepositorySystemSession;
030
031import static java.util.stream.Collectors.toList;
032
033/**
034 * A utility class to read configuration properties from a repository system session.
035 * 
036 * @see RepositorySystemSession#getConfigProperties()
037 */
038public final class ConfigUtils
039{
040
041    private ConfigUtils()
042    {
043        // hide constructor
044    }
045
046    /**
047     * Gets the specified configuration property.
048     * 
049     * @param properties The configuration properties to read, must not be {@code null}.
050     * @param defaultValue The default value to return in case none of the property keys are set, may be {@code null}.
051     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
052     *            a valid value is found.
053     * @return The property value or {@code null} if none.
054     */
055    public static Object getObject( Map<?, ?> properties, Object defaultValue, String... keys )
056    {
057        for ( String key : keys )
058        {
059            Object value = properties.get( key );
060
061            if ( value != null )
062            {
063                return value;
064            }
065        }
066
067        return defaultValue;
068    }
069
070    /**
071     * Gets the specified configuration property.
072     * 
073     * @param session The repository system session from which to read the configuration property, must not be
074     *            {@code null}.
075     * @param defaultValue The default value to return in case none of the property keys are set, may be {@code null}.
076     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
077     *            a valid value is found.
078     * @return The property value or {@code null} if none.
079     */
080    public static Object getObject( RepositorySystemSession session, Object defaultValue, String... keys )
081    {
082        return getObject( session.getConfigProperties(), defaultValue, keys );
083    }
084
085    /**
086     * Gets the specified configuration property.
087     * 
088     * @param properties The configuration properties to read, must not be {@code null}.
089     * @param defaultValue The default value to return in case none of the property keys is set to a string, may be
090     *            {@code null}.
091     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
092     *            a string value is found.
093     * @return The property value or {@code null} if none.
094     */
095    public static String getString( Map<?, ?> properties, String defaultValue, String... keys )
096    {
097        for ( String key : keys )
098        {
099            Object value = properties.get( key );
100
101            if ( value instanceof String )
102            {
103                return (String) value;
104            }
105        }
106
107        return defaultValue;
108    }
109
110    /**
111     * Gets the specified configuration property.
112     * 
113     * @param session The repository system session from which to read the configuration property, must not be
114     *            {@code null}.
115     * @param defaultValue The default value to return in case none of the property keys is set to a string, may be
116     *            {@code null}.
117     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
118     *            a string value is found.
119     * @return The property value or {@code null} if none.
120     */
121    public static String getString( RepositorySystemSession session, String defaultValue, String... keys )
122    {
123        return getString( session.getConfigProperties(), defaultValue, keys );
124    }
125
126    /**
127     * Gets the specified configuration property.
128     * 
129     * @param properties The configuration properties to read, must not be {@code null}.
130     * @param defaultValue The default value to return in case none of the property keys is set to a number.
131     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
132     *            a {@link Number} or a string representation of an {@link Integer} is found.
133     * @return The property value.
134     */
135    public static int getInteger( Map<?, ?> properties, int defaultValue, String... keys )
136    {
137        for ( String key : keys )
138        {
139            Object value = properties.get( key );
140
141            if ( value instanceof Number )
142            {
143                return ( (Number) value ).intValue();
144            }
145            else if ( value instanceof String )
146            {
147                try
148                {
149                    return Integer.parseInt( (String) value );
150                }
151                catch ( NumberFormatException e )
152                {
153                    // try next key
154                }
155            }
156        }
157
158        return defaultValue;
159    }
160
161    /**
162     * Gets the specified configuration property.
163     * 
164     * @param session The repository system session from which to read the configuration property, must not be
165     *            {@code null}.
166     * @param defaultValue The default value to return in case none of the property keys is set to a number.
167     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
168     *            a {@link Number} or a string representation of an {@link Integer} is found.
169     * @return The property value.
170     */
171    public static int getInteger( RepositorySystemSession session, int defaultValue, String... keys )
172    {
173        return getInteger( session.getConfigProperties(), defaultValue, keys );
174    }
175
176    /**
177     * Gets the specified configuration property.
178     * 
179     * @param properties The configuration properties to read, must not be {@code null}.
180     * @param defaultValue The default value to return in case none of the property keys is set to a number.
181     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
182     *            a {@link Number} or a string representation of a {@link Long} is found.
183     * @return The property value.
184     */
185    public static long getLong( Map<?, ?> properties, long defaultValue, String... keys )
186    {
187        for ( String key : keys )
188        {
189            Object value = properties.get( key );
190
191            if ( value instanceof Number )
192            {
193                return ( (Number) value ).longValue();
194            }
195            else if ( value instanceof String )
196            {
197                try
198                {
199                    return Long.parseLong( (String) value );
200                }
201                catch ( NumberFormatException e )
202                {
203                    // try next key
204                }
205            }
206        }
207
208        return defaultValue;
209    }
210
211    /**
212     * Gets the specified configuration property.
213     * 
214     * @param session The repository system session from which to read the configuration property, must not be
215     *            {@code null}.
216     * @param defaultValue The default value to return in case none of the property keys is set to a number.
217     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
218     *            a {@link Number} or a string representation of a {@link Long} is found.
219     * @return The property value.
220     */
221    public static long getLong( RepositorySystemSession session, long defaultValue, String... keys )
222    {
223        return getLong( session.getConfigProperties(), defaultValue, keys );
224    }
225
226    /**
227     * Gets the specified configuration property.
228     * 
229     * @param properties The configuration properties to read, must not be {@code null}.
230     * @param defaultValue The default value to return in case none of the property keys is set to a number.
231     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
232     *            a {@link Number} or a string representation of a {@link Float} is found.
233     * @return The property value.
234     */
235    public static float getFloat( Map<?, ?> properties, float defaultValue, String... keys )
236    {
237        for ( String key : keys )
238        {
239            Object value = properties.get( key );
240
241            if ( value instanceof Number )
242            {
243                return ( (Number) value ).floatValue();
244            }
245            else if ( value instanceof String )
246            {
247                try
248                {
249                    return Float.parseFloat( (String) value );
250                }
251                catch ( NumberFormatException e )
252                {
253                    // try next key
254                }
255            }
256        }
257
258        return defaultValue;
259    }
260
261    /**
262     * Gets the specified configuration property.
263     * 
264     * @param session The repository system session from which to read the configuration property, must not be
265     *            {@code null}.
266     * @param defaultValue The default value to return in case none of the property keys is set to a number.
267     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
268     *            a {@link Number} or a string representation of a {@link Float} is found.
269     * @return The property value.
270     */
271    public static float getFloat( RepositorySystemSession session, float defaultValue, String... keys )
272    {
273        return getFloat( session.getConfigProperties(), defaultValue, keys );
274    }
275
276    /**
277     * Gets the specified configuration property.
278     * 
279     * @param properties The configuration properties to read, must not be {@code null}.
280     * @param defaultValue The default value to return in case none of the property keys is set to a boolean.
281     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
282     *            a {@link Boolean} or a string (to be {@link Boolean#parseBoolean(String) parsed as boolean}) is found.
283     * @return The property value.
284     */
285    public static boolean getBoolean( Map<?, ?> properties, boolean defaultValue, String... keys )
286    {
287        for ( String key : keys )
288        {
289            Object value = properties.get( key );
290
291            if ( value instanceof Boolean )
292            {
293                return (Boolean) value;
294            }
295            else if ( value instanceof String )
296            {
297                return Boolean.parseBoolean( (String) value );
298            }
299        }
300
301        return defaultValue;
302    }
303
304    /**
305     * Gets the specified configuration property.
306     * 
307     * @param session The repository system session from which to read the configuration property, must not be
308     *            {@code null}.
309     * @param defaultValue The default value to return in case none of the property keys is set to a boolean.
310     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
311     *            a {@link Boolean} or a string (to be {@link Boolean#parseBoolean(String) parsed as boolean}) is found.
312     * @return The property value.
313     */
314    public static boolean getBoolean( RepositorySystemSession session, boolean defaultValue, String... keys )
315    {
316        return getBoolean( session.getConfigProperties(), defaultValue, keys );
317    }
318
319    /**
320     * Gets the specified configuration property.
321     * 
322     * @param properties The configuration properties to read, must not be {@code null}.
323     * @param defaultValue The default value to return in case none of the property keys is set to a collection.
324     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
325     *            a collection is found.
326     * @return The property value or {@code null} if none.
327     */
328    public static List<?> getList( Map<?, ?> properties, List<?> defaultValue, String... keys )
329    {
330        for ( String key : keys )
331        {
332            Object value = properties.get( key );
333
334            if ( value instanceof List )
335            {
336                return (List<?>) value;
337            }
338            else if ( value instanceof Collection )
339            {
340                return Collections.unmodifiableList( new ArrayList<>( (Collection<?>) value ) );
341            }
342        }
343
344        return defaultValue;
345    }
346
347    /**
348     * Gets the specified configuration property.
349     * 
350     * @param session The repository system session from which to read the configuration property, must not be
351     *            {@code null}.
352     * @param defaultValue The default value to return in case none of the property keys is set to a collection.
353     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
354     *            a collection is found.
355     * @return The property value or {@code null} if none.
356     */
357    public static List<?> getList( RepositorySystemSession session, List<?> defaultValue, String... keys )
358    {
359        return getList( session.getConfigProperties(), defaultValue, keys );
360    }
361
362    /**
363     * Gets the specified configuration property.
364     * 
365     * @param properties The configuration properties to read, must not be {@code null}.
366     * @param defaultValue The default value to return in case none of the property keys is set to a map.
367     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
368     *            a map is found.
369     * @return The property value or {@code null} if none.
370     */
371    public static Map<?, ?> getMap( Map<?, ?> properties, Map<?, ?> defaultValue, String... keys )
372    {
373        for ( String key : keys )
374        {
375            Object value = properties.get( key );
376
377            if ( value instanceof Map )
378            {
379                return (Map<?, ?>) value;
380            }
381        }
382
383        return defaultValue;
384    }
385
386    /**
387     * Gets the specified configuration property.
388     * 
389     * @param session The repository system session from which to read the configuration property, must not be
390     *            {@code null}.
391     * @param defaultValue The default value to return in case none of the property keys is set to a map.
392     * @param keys The property keys to read, must not be {@code null}. The specified keys are read one after one until
393     *            a map is found.
394     * @return The property value or {@code null} if none.
395     */
396    public static Map<?, ?> getMap( RepositorySystemSession session, Map<?, ?> defaultValue, String... keys )
397    {
398        return getMap( session.getConfigProperties(), defaultValue, keys );
399    }
400
401    /**
402     * Utility method to parse configuration string that contains comma separated list of names into
403     * {@link List<String>}, never returns {@code null}.
404     *
405     * @since 1.9.0
406     */
407    public static List<String> parseCommaSeparatedNames( String commaSeparatedNames )
408    {
409        if ( commaSeparatedNames == null || commaSeparatedNames.trim().isEmpty() )
410        {
411            return Collections.emptyList();
412        }
413        return Arrays.stream( commaSeparatedNames.split( "," ) )
414                .filter( s -> s != null && !s.trim().isEmpty() )
415                .collect( toList() );
416    }
417
418    /**
419     * Utility method to parse configuration string that contains comma separated list of names into
420     * {@link List<String>} with unique elements (duplicates, if any, are discarded), never returns {@code null}.
421     *
422     * @since 1.9.0
423     */
424    public static List<String> parseCommaSeparatedUniqueNames( String commaSeparatedNames )
425    {
426        return parseCommaSeparatedNames( commaSeparatedNames ).stream().distinct().collect( toList() );
427    }
428}