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.builder.combined;
18
19 import java.util.Collection;
20
21 import org.apache.commons.configuration2.builder.BuilderParameters;
22 import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
23 import org.apache.commons.configuration2.ex.ConfigurationException;
24
25 /**
26 * <p>
27 * A specialized implementation of {@link ConfigurationBuilderProvider} which determines the name of the result
28 * configuration class based on the extension of the file to load.
29 * </p>
30 * <p>
31 * This class works analogously to its base class {@link BaseConfigurationBuilderProvider}; especially, the resulting
32 * builder is created based on reflection. It extends the super class's functionality by a specific mechanism for
33 * determining the resulting configuration class: At construction time two configuration class names and a file
34 * extension are passed in. If a file name is provided in the builder's initialization parameters and this file name has
35 * the specified extension, then the first configuration class name is used; otherwise the default configuration class
36 * name is selected.
37 * </p>
38 * <p>
39 * There are some tags for {@code CombinedConfigurationProvider} which can produce different results depending on the
40 * configuration files they have to load. This class can be used to implement this feature in a generic way.
41 * </p>
42 *
43 * @since 2.0
44 */
45 public class FileExtensionConfigurationBuilderProvider extends BaseConfigurationBuilderProvider {
46
47 /** Constant for the file extension separator. */
48 private static final char EXT_SEPARATOR = '.';
49
50 /**
51 * Extracts the extension from the given file name. The name can be <strong>null</strong>.
52 *
53 * @param fileName the file name
54 * @return the extension (<strong>null</strong> if there is none)
55 */
56 private static String extractExtension(final String fileName) {
57 if (fileName == null) {
58 return null;
59 }
60
61 final int pos = fileName.lastIndexOf(EXT_SEPARATOR);
62 return pos < 0 ? null : fileName.substring(pos + 1);
63 }
64
65 /**
66 * Tries to obtain the current file name from the given list of parameter objects.
67 *
68 * @param params the parameter objects
69 * @return the file name or <strong>null</strong> if unspecified
70 */
71 private static String fetchCurrentFileName(final Collection<BuilderParameters> params) {
72 for (final BuilderParameters p : params) {
73 if (p instanceof FileBasedBuilderParametersImpl) {
74 final FileBasedBuilderParametersImpl fp = (FileBasedBuilderParametersImpl) p;
75 return fp.getFileHandler().getFileName();
76 }
77 }
78 return null;
79 }
80
81 /** The matching configuration class. */
82 private final String matchingConfigurationClass;
83
84 /** The file extension. */
85 private final String extension;
86
87 /**
88 * Creates a new instance of {@code FileExtensionConfigurationBuilderProvider}.
89 *
90 * @param bldrCls the name of the builder class
91 * @param reloadBldrCls the name of a builder class to be used if reloading support is required (<strong>null</strong> if
92 * reloading is not supported)
93 * @param matchingConfigCls the name of the configuration class to be used if the provided file extension matches (must
94 * not be <strong>null</strong>)
95 * @param defConfigClass the name of the configuration class to be used if the provided file extension does not match
96 * (must not be <strong>null</strong>)
97 * @param ext the file extension to select the configuration class (must not be <strong>null</strong>)
98 * @param paramCls a collection with the names of parameters classes; an instance of a parameters object with basic
99 * properties is created automatically and does not need to be contained in this list; the collection can be
100 * <strong>null</strong> if no additional parameter objects are needed
101 * @throws IllegalArgumentException if a required parameter is missing
102 */
103 public FileExtensionConfigurationBuilderProvider(final String bldrCls, final String reloadBldrCls, final String matchingConfigCls,
104 final String defConfigClass, final String ext, final Collection<String> paramCls) {
105 super(bldrCls, reloadBldrCls, defConfigClass, paramCls);
106 if (matchingConfigCls == null) {
107 throw new IllegalArgumentException("Matching configuration class must not be null.");
108 }
109 if (ext == null) {
110 throw new IllegalArgumentException("File extension must not be null.");
111 }
112
113 matchingConfigurationClass = matchingConfigCls;
114 extension = ext;
115 }
116
117 /**
118 * {@inheritDoc} This implementation tries to find a {@link FileBasedBuilderParametersImpl} object in the parameter
119 * objects. If one is found, the extension of the file name is obtained and compared against the stored file extension.
120 * In case of a match, the matching configuration class is selected, otherwise the default one.
121 */
122 @Override
123 protected String determineConfigurationClass(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params)
124 throws ConfigurationException {
125 final String currentExt = extractExtension(fetchCurrentFileName(params));
126 return getExtension().equalsIgnoreCase(currentExt) ? getMatchingConfigurationClass() : getConfigurationClass();
127 }
128
129 /**
130 * Gets the file extension of this provider.
131 *
132 * @return the file extension to match
133 */
134 public String getExtension() {
135 return extension;
136 }
137
138 /**
139 * Gets the name of the matching configuration class. This class is used if the file extension matches the extension
140 * of this provider.
141 *
142 * @return the matching configuration class
143 */
144 public String getMatchingConfigurationClass() {
145 return matchingConfigurationClass;
146 }
147 }