+
*
* @param the target configuration type
*
@@ -55,6 +66,7 @@ public class ConfigurationBuilder implements ConfigurationMetadataRetriever source;
private Mapper mapper;
private boolean optional;
@@ -145,12 +157,16 @@ public ConfigurationBuilder source(Source source)
*/
public ConfigurationBuilder source(String path)
{
+ this.path = path;
this.source = SourceFactory.dynamicSource(path);
return this;
}
/**
* Defines the {@link Mapper} of the new {@code Configuration}.
+ *
+ * Note: Since 2.6.0, if not specified, a default mapper will be defined
+ * dynamically, based on the source file extension, if present.
*
* @param mapper the {@link Mapper} to be set; not null
* @return a reference to this same {@code ConfigurationBuilder} for chained calls
@@ -237,21 +253,42 @@ public ConfigurationBuilder bean(T bean)
* Builds the target {@code Configuration}.
*
* @return a new {@link Configuration} object
- * @throws NullPointerException if either the {@code Source} or {@code Mapper}
- * configuration parameters are missing
+ * @throws IllegalStateException if the {@code Source} parameter is missing; or the
+ * {@code Mapper} is missing; or the {@code Mapper}
+ * could not be inferred
* @throws ConfigurationSourceException in the event of a failure loading the
* configuration source
*/
public Configuration build()
{
- Objects.requireNonNull(source, "The configuration source must not be null");
- Objects.requireNonNull(mapper, "The configuration mapper must not be null");
+ requireNonNullForBuild(source, "The configuration source must not be null");
+ evaluateMapper();
- namespace = StringUtils.defaultString(namespace);
+ namespace = defaultString(namespace);
return new Configuration<>(this);
}
+ /**
+ * Secure the existence of a {@link Mapper}. Try to infer one if not specified.
+ *
+ * @throws IllegalStateException if the {@link Mapper} is null and could not be inferred
+ * @since 2.6.0
+ */
+ private void evaluateMapper()
+ {
+ if (mapper == null)
+ {
+ // Try to infer based on the file extension if present
+ String extension = substringAfterLast(path, ".");
+ if (isEmpty(extension))
+ {
+ throw new IllegalStateException("The mapper could not be inferred. Please specify a concrete mapper.");
+ }
+ mapper = (Mapper) new DynamicMapper(extension);
+ }
+ }
+
@Override
public String getNamespace()
{
@@ -293,4 +330,13 @@ public T getBean()
return bean;
}
+ private static T requireNonNullForBuild(T object, String message)
+ {
+ if (object == null)
+ {
+ throw new IllegalStateException(message);
+ }
+ return object;
+ }
+
}
diff --git a/confectory-core/src/main/java/net/obvj/confectory/mapper/DynamicMapper.java b/confectory-core/src/main/java/net/obvj/confectory/mapper/DynamicMapper.java
new file mode 100644
index 00000000..863aee02
--- /dev/null
+++ b/confectory-core/src/main/java/net/obvj/confectory/mapper/DynamicMapper.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2024 obvj.net
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.obvj.confectory.mapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.obvj.confectory.internal.helper.ConfigurationHelper;
+import net.obvj.confectory.util.Exceptions;
+
+/**
+ * A dynamic {@link Mapper} which automatically assigns a concrete mapping class
+ * (from the default core mappers) depending on a given file extension:
+ *
+ *
"ini" assigns the {@link INIToJSONObjectMapper}
+ *
"json" assigns the {@link JSONObjectMapper}
+ *
"properties" assigns the {@link PropertiesMapper}
+ *
"txt" assigns the {@link StringMapper}
+ *
"xml" assigns the {@link DocumentMapper}
+ *
+ *
+ * @author oswaldo.bapvic.jr (Oswaldo Junior)
+ * @since 2.6.0
+ */
+public final class DynamicMapper extends AbstractBeanMapper