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.configuration2.convert;
18
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.IdentityHashMap;
22 import java.util.List;
23
24 /**
25 * <p>
26 * Definition of an interface that controls the handling of list delimiters in configuration properties.
27 * </p>
28 * <p>
29 * {@link org.apache.commons.configuration2.AbstractConfiguration AbstractConfiguration} supports list delimiters in
30 * property values. If such a delimiter is found, the value actually contains multiple values and has to be split. This
31 * is useful for instance for {@link org.apache.commons.configuration2.PropertiesConfiguration PropertiesConfiguration}:
32 * properties files that have to be compatible with the {@link java.util.Properties} class cannot have multiple
33 * occurrences of a single property key, therefore a different storage scheme for multi-valued properties is needed. A
34 * possible storage scheme could look as follows:
35 * </p>
36 *
37 * <pre>
38 * myProperty=value1,value2,value3
39 * </pre>
40 *
41 * <p>
42 * Here a comma is used as list delimiter. When parsing this property (and using a corresponding
43 * {@code ListDelimiterHandler} implementation) the string value is split, and three values are added for the property
44 * key.
45 * </p>
46 * <p>
47 * A {@code ListDelimiterHandler} knows how to parse and to escape property values. It is called by concrete
48 * {@code Configuration} implementations when they have to deal with properties with multiple values.
49 * </p>
50 *
51 * @since 2.0
52 */
53 public interface ListDelimiterHandler {
54
55 /**
56 * A specialized {@code ValueTransformer} implementation which does no transformation. The {@code transformValue()}
57 * method just returns the passed in object without changes. This instance can be used by configurations which do not
58 * require additional encoding.
59 */
60 ValueTransformer NOOP_TRANSFORMER = value -> value;
61
62 /**
63 * Escapes the specified single value object. This method is called for properties containing only a single value. So
64 * this method can rely on the fact that the passed in object is not a list. An implementation has to check whether the
65 * value contains list delimiter characters and - if so - escape them accordingly.
66 *
67 * @param value the value to be escaped
68 * @param transformer a {@code ValueTransformer} for an additional encoding (must not be <strong>null</strong>)
69 * @return the escaped value
70 */
71 Object escape(Object value, ValueTransformer transformer);
72
73 /**
74 * Escapes all values in the given list and concatenates them to a single string. This operation is required by
75 * configurations that have to represent properties with multiple values in a single line in their external
76 * configuration representation. This may require an advanced escaping in some cases.
77 *
78 * @param values the list with all the values to be converted to a single value
79 * @param transformer a {@code ValueTransformer} for an additional encoding (must not be <strong>null</strong>)
80 * @return the resulting escaped value
81 */
82 Object escapeList(List<?> values, ValueTransformer transformer);
83
84 /**
85 * Extracts all values contained in the specified object up to the given limit. The passed in object is evaluated (if
86 * necessary in a recursive way). If it is a complex object (for example a collection or an array), all its elements are
87 * processed recursively and added to a target collection. The process stops if the limit is reached, but depending on
88 * the input object, it might be exceeded. (The limit is just an indicator to stop the process to avoid unnecessary work
89 * if the caller is only interested in a few values.)
90 *
91 * @param value the value to be processed
92 * @param limit the limit for aborting the processing
93 * @return a "flat" collection containing all primitive values of the passed in object
94 * @since 2.9.0
95 */
96 default Collection<?> flatten(final Object value, final int limit) {
97 return AbstractListDelimiterHandler.flatten(this, value, limit, Collections.newSetFromMap(new IdentityHashMap<>()));
98 }
99
100 /**
101 * Parses the specified value for list delimiters and splits it if necessary. The passed in object can be either a
102 * single value or a complex one, for example a collection, an array, or an {@code Iterable}. It is the responsibility of this
103 * method to return an {@code Iterable} which contains all extracted values.
104 *
105 * @param value the value to be parsed
106 * @return an {@code Iterable} allowing access to all extracted values
107 */
108 Iterable<?> parse(Object value);
109
110 /**
111 * Splits the specified string at the list delimiter and returns a collection with all extracted components. A concrete
112 * implementation also has to deal with escape characters which might mask a list delimiter character at certain
113 * positions. The boolean {@code trim} flag determines whether each extracted component should be trimmed. This
114 * typically makes sense as the list delimiter may be surrounded by whitespace. However, there may be specific use cases
115 * in which automatic trimming is not desired.
116 *
117 * @param s the string to be split
118 * @param trim a flag whether each component of the string is to be trimmed
119 * @return a collection with all components extracted from the string
120 */
121 Collection<String> split(String s, boolean trim);
122
123 }