This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git


The following commit(s) were added to refs/heads/master by this push:
     new 45c391f  FREEMARKER-195 Improve exposure of DataSources using 
TemplateHashModelEx2 (#36)
45c391f is described below

commit 45c391f98cd91299ab192084bcfb38f779bb6134
Author: Siegfried Goeschl <[email protected]>
AuthorDate: Fri Oct 8 12:30:57 2021 +0200

    FREEMARKER-195 Improve exposure of DataSources using TemplateHashModelEx2 
(#36)
---
 freemarker-generator-base/pom.xml                  |   2 +-
 .../generator/base/datasource/DataSource.java      |   9 +-
 .../base/datasource/DataSourceLoader.java          |   2 +-
 .../generator/base/datasource/DataSources.java     | 101 +++++++++++++----
 .../base/util/NonClosableWriterWrapper.java        |   2 +-
 .../generator/datasource/DataSourcesTest.java      |  48 ++++----
 freemarker-generator-cli/CHANGELOG.md              |   9 +-
 freemarker-generator-cli/pom.xml                   |   2 +-
 .../src/app/examples/data/ftl/nginx/nginx.conf.ftl |   2 +-
 .../templates/accesslog/combined-access.ftl        |   2 +-
 .../templates/csv/csv/gatling-user-credentials.ftl |   2 +-
 .../app/examples/templates/csv/fo/transactions.ftl |   2 +-
 .../app/examples/templates/csv/fo/transform.ftl    |   2 +-
 .../examples/templates/csv/html/transactions.ftl   |   2 +-
 .../src/app/examples/templates/csv/md/filter.ftl   |   2 +-
 .../src/app/examples/templates/csv/shell/curl.ftl  |   2 +-
 .../app/examples/templates/dataframe/example.ftl   |   2 +-
 .../examples/templates/dataframe/html/print.ftl    |   2 +-
 .../src/app/examples/templates/datasources.ftl     | 126 +++++++++++++--------
 .../src/app/examples/templates/demo.ftl            |  10 +-
 .../app/examples/templates/excel/csv/custom.ftl    |   2 +-
 .../templates/excel/dataframe/transform.ftl        |   2 +-
 .../examples/templates/html/csv/dependencies.ftl   |   2 +-
 .../app/examples/templates/html/txt/licence.ftl    |   2 +-
 .../templates/json/csv/swagger-endpoints.ftl       |   2 +-
 .../templates/json/dataframe/github-users.ftl      |   2 +-
 .../examples/templates/json/md/github-users.ftl    |   2 +-
 .../templates/logs/csv/serverlog-to-csv.ftl        |   2 +-
 .../templates/properties/csv/locker-test-users.ftl |   2 +-
 .../app/examples/templates/tsv/fo/transactions.ftl |   2 +-
 .../app/examples/templates/xml/txt/recipients.ftl  |   2 +-
 .../app/examples/templates/yaml/txt/transform.ftl  |   2 +-
 .../src/app/scripts/run-examples.bat               |  15 ++-
 .../src/app/scripts/run-examples.sh                |  15 ++-
 .../src/app/templates/freemarker-generator/cat.ftl |   2 +-
 .../csv/{md => confluence}/transform.ftl           |   7 +-
 .../freemarker-generator/csv/csv/transform.ftl     |   2 +-
 .../freemarker-generator/csv/html/transform.ftl    |   2 +-
 .../freemarker-generator/csv/md/transform.ftl      |   2 +-
 .../freemarker-generator/excel/csv/transform.ftl   |   2 +-
 .../freemarker-generator/excel/html/transform.ftl  |   2 +-
 .../freemarker-generator/excel/md/transform.ftl    |   2 +-
 .../app/templates/freemarker-generator/info.ftl    |   5 +-
 .../freemarker-generator/json/yaml/transform.ftl   |   2 +-
 .../freemarker-generator/yaml/json/transform.ftl   |   2 +-
 .../org/apache/freemarker/generator/cli/Main.java  |   2 +-
 .../cli/config/ConfigurationSupplier.java          |   5 +
 .../generator/cli/task/FreeMarkerTask.java         |   3 +-
 .../generator/cli/wrapper/DataSourcesAdapter.java  | 115 +++++++++++++++++++
 .../wrapper/FreeMarkerGeneratorObjectWrapper.java  |  35 +++---
 .../src/site/markdown/cli/concepts/data-sources.md |  14 +--
 .../site/markdown/cli/usage/parsing-with-grok.md   |   4 +-
 .../site/markdown/cli/usage/running-examples.md    |  30 ++---
 .../site/markdown/cli/usage/using-dataframes.md    |   6 +-
 .../freemarker/generator/cli/ExamplesTest.java     |  14 ++-
 .../freemarker/generator/cli/ManualTest.java       |   6 +-
 .../cli/config/ConfigurationSupplierTest.java      |   3 +-
 .../generator/cli/config/SuppliersTest.java        |   2 -
 .../src/test/templates/echo.ftl                    |   2 +-
 .../src/test/templates/manual.ftl                  |   8 +-
 .../src/test/templates/tools/csv.ftl               |   2 +-
 freemarker-generator-maven-plugin-sample/pom.xml   |   2 +-
 freemarker-generator-maven-plugin/pom.xml          |   2 +-
 freemarker-generator-tools/pom.xml                 |   2 +-
 .../generator/tools/commonscsv/CommonsCSVTool.java |   6 +-
 .../generator/tools/excel/ExcelTool.java           |   2 +-
 .../freemarker/generator/tools/grok/GrokTool.java  |   4 +-
 pom.xml                                            |   2 +-
 68 files changed, 458 insertions(+), 228 deletions(-)

diff --git a/freemarker-generator-base/pom.xml 
b/freemarker-generator-base/pom.xml
index deb18fb..512158d 100644
--- a/freemarker-generator-base/pom.xml
+++ b/freemarker-generator-base/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.freemarker.generator</groupId>
         <artifactId>freemarker-generator</artifactId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>freemarker-generator-base</artifactId>
diff --git 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSource.java
 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSource.java
index 779e955..de2b295 100644
--- 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSource.java
+++ 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSource.java
@@ -410,7 +410,8 @@ public class DataSource implements Closeable, 
javax.activation.DataSource {
     }
 
     /**
-     * Matches a metadata key with a wildcard expression.
+     * Matches a metadata key with a wildcard expression. If the wildcard is 
prefixed
+     * with a "!" than the match will be negated.
      *
      * @param key      metadata key, e.g. "name", "fileName", "baseName", 
"extension", "uri", "group"
      * @param wildcard the wildcard string to match against
@@ -419,7 +420,11 @@ public class DataSource implements Closeable, 
javax.activation.DataSource {
      */
     public boolean match(String key, String wildcard) {
         final String value = getMetadata(key);
-        return FilenameUtils.wildcardMatch(value, wildcard);
+        if (wildcard != null && wildcard.startsWith("!")) {
+            return !FilenameUtils.wildcardMatch(value, wildcard.substring(1));
+        } else {
+            return FilenameUtils.wildcardMatch(value, wildcard);
+        }
     }
 
     /**
diff --git 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
index 4a22137..d2d80c2 100644
--- 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
+++ 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
@@ -22,7 +22,7 @@ public interface DataSourceLoader {
      * Check if the data source can be loaded by this instance.
      *
      * @param source source to be loaded from
-     * @return true if the instance wold be able to load a data source
+     * @return true if the instance would be able to load a data source
      */
     boolean accept(String source);
 
diff --git 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSources.java
 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSources.java
index fcb3432..31a6462 100644
--- 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSources.java
+++ 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSources.java
@@ -23,6 +23,7 @@ import org.apache.freemarker.generator.base.util.Validate;
 import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -39,24 +40,27 @@ public class DataSources implements Closeable {
     /** The underlying list of data sources */
     private final List<DataSource> dataSources;
 
+    /** Map of named data sources */
+    private final Map<String, DataSource> dataSourcesMap;
+
     public DataSources(Collection<DataSource> dataSources) {
-        Validate.notNull(dataSources, "No data sources provided");
-        this.dataSources = new ArrayList<>(dataSources);
+        Validate.notNull(dataSources, "dataSources must not be null");
+
+        this.dataSources = Collections.unmodifiableList(new 
ArrayList<>(dataSources));
+        this.dataSourcesMap = 
Collections.unmodifiableMap(dataSourcesMap(dataSources));
     }
 
     /**
      * Get the names of all data sources.
      *
-     * @return data source names
+     * @return list of data source names
      */
     public List<String> getNames() {
-        return dataSources.stream()
-                .map(DataSource::getName)
-                .collect(Collectors.toList());
+        return new ArrayList<>(dataSourcesMap.keySet());
     }
 
     /**
-     * Get the requested metadata value for all data sources.
+     * Get a list of distinct metadata values for all data sources.
      *
      * @param key key of the metadata part
      * @return list of metadata values
@@ -64,13 +68,15 @@ public class DataSources implements Closeable {
     public List<String> getMetadata(String key) {
         return dataSources.stream()
                 .map(ds -> ds.getMetadata(key))
+                .filter(StringUtils::isNotEmpty)
+                .distinct()
                 .collect(Collectors.toList());
     }
 
     /**
-     * Get a list of unique groups of all data sources.
+     * Get a list of distinct group names of all data sources.
      *
-     * @return list of groups
+     * @return list of group names
      */
     public List<String> getGroups() {
         return dataSources.stream()
@@ -80,27 +86,46 @@ public class DataSources implements Closeable {
                 .collect(Collectors.toList());
     }
 
+    /**
+     * Returns the number of elements in this list.
+     *
+     * @return the number of elements in this list
+     */
     public int size() {
         return dataSources.size();
     }
 
+    /**
+     * Returns <tt>true</tt> if this list contains no elements.
+     *
+     * @return <tt>true</tt> if this list contains no elements
+     */
     public boolean isEmpty() {
         return dataSources.isEmpty();
     }
 
     /**
+     * Get an array representation of the underlying data sources.
+     *
+     * @return array of data sources
+     */
+    public DataSource[] toArray() {
+        return dataSources.toArray(new DataSource[0]);
+    }
+
+    /**
      * Get a list representation of the underlying data sources.
      *
      * @return list of data sources
      */
     public List<DataSource> toList() {
-        return new ArrayList<>(dataSources);
+        return dataSources;
     }
 
     /**
      * Get a map representation of the underlying data sources.
      * In <code>freemarker-cli</code> the map is also used to
-     * iterate over data source so we need to return a
+     * iterate over data source, so we need to return a
      * <code>LinkedHashMap</code>.
      * <p>
      * The implementation also throws as <code>IllegalStateException</code>
@@ -109,15 +134,15 @@ public class DataSources implements Closeable {
      * @return linked hasp map of data sources
      */
     public Map<String, DataSource> toMap() {
-        return dataSources.stream().collect(Collectors.toMap(
-                DataSource::getName,
-                identity(),
-                (ds1, ds2) -> {
-                    throw new IllegalStateException("Duplicate key detected 
when generating map: " + ds1 + ", " + ds2);
-                },
-                LinkedHashMap::new));
+        return dataSourcesMap;
     }
 
+    /**
+     * Returns the element at the specified position in this list.
+     *
+     * @param index index of the element to return
+     * @return the element at the specified position in this list
+     */
     public DataSource get(int index) {
         return dataSources.get(index);
     }
@@ -130,7 +155,7 @@ public class DataSources implements Closeable {
      * @return data source
      */
     public DataSource get(String name) {
-        final List<DataSource> list = find(name);
+        final List<DataSource> list = findByName(name);
 
         if (list.isEmpty()) {
             throw new IllegalArgumentException("Data source not found : " + 
name);
@@ -144,16 +169,14 @@ public class DataSources implements Closeable {
     }
 
     /**
-     * Find data sources based on their name using a wildcard string..
+     * Find data sources based on their name using a wildcard string.
      *
      * @param wildcard the wildcard string to match against
      * @return list of matching data sources
      * @see <a 
href="https://commons.apache.org/proper/commons-io/javadocs/api-2.7/org/apache/commons/io/FilenameUtils.html#wildcardMatch-java.lang.String-java.lang.String-";>Apache
 Commons IO</a>
      */
-    public List<DataSource> find(String wildcard) {
-        return dataSources.stream()
-                .filter(dataSource -> dataSource.match("name", wildcard))
-                .collect(Collectors.toList());
+    public List<DataSource> findByName(String wildcard) {
+        return find(DataSource.METADATA_NAME, wildcard);
     }
 
     /**
@@ -170,6 +193,19 @@ public class DataSources implements Closeable {
                 .collect(Collectors.toList());
     }
 
+    /**
+     * Create a new <code>DataSources</code> instance consisting of
+     * data sources matching the filter.
+     *
+     * @param key      metadata key to match
+     * @param wildcard the wildcard string to match against
+     * @return list of matching data sources
+     * @see <a 
href="https://commons.apache.org/proper/commons-io/javadocs/api-2.7/org/apache/commons/io/FilenameUtils.html#wildcardMatch-java.lang.String-java.lang.String-";>Apache
 Commons IO</a>
+     */
+    public DataSources filter(String key, String wildcard) {
+        return new DataSources(find(key, wildcard));
+    }
+
     @Override
     public void close() {
         dataSources.forEach(ClosableUtils::closeQuietly);
@@ -181,4 +217,21 @@ public class DataSources implements Closeable {
                 "dataSources=" + dataSources +
                 '}';
     }
+
+    private static List<DataSource> getNamedDataSources(Collection<DataSource> 
dataSources) {
+        return dataSources.stream()
+                .filter(dataSource -> 
StringUtils.isNotEmpty(dataSource.getName()))
+                .collect(Collectors.toList());
+    }
+
+    private Map<String, DataSource> dataSourcesMap(Collection<DataSource> 
dataSources) {
+        final List<DataSource> namedDataSources = 
getNamedDataSources(dataSources);
+        return namedDataSources.stream().collect(Collectors.toMap(
+                DataSource::getName,
+                identity(),
+                (ds1, ds2) -> {
+                    throw new IllegalStateException("Duplicate names detected 
when generating data source map: " + ds1 + ", " + ds2);
+                },
+                LinkedHashMap::new));
+    }
 }
diff --git 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/NonClosableWriterWrapper.java
 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/NonClosableWriterWrapper.java
index e2637b7..cd48b5b 100644
--- 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/NonClosableWriterWrapper.java
+++ 
b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/NonClosableWriterWrapper.java
@@ -22,7 +22,7 @@ import java.io.Writer;
 import static java.util.Objects.requireNonNull;
 
 /**
- * Wraps a writer (usually the internal FreeMarker's writer instance
+ * Wraps a writer (usually the internal FreeMarker's writer instance)
  * and avoids closing it since this would crash FreeMarker. E.g. the
  * Commons CSV integration uses the FreeMarker writer directly but
  * some implementation could call "CSVPrinter#close"
diff --git 
a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesTest.java
 
b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesTest.java
index 5dd4360..4bdf193 100644
--- 
a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesTest.java
+++ 
b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesTest.java
@@ -45,28 +45,32 @@ public class DataSourcesTest {
     private static final String GROUP_PART = "group";
 
     @Test
-    public void shouldFindByName() {
+    public void shouldFindDataSourcesByName() {
         try (DataSources dataSources = dataSources()) {
-            assertEquals(0, dataSources.find(null).size());
-            assertEquals(0, dataSources.find("").size());
-            assertEquals(0, dataSources.find("*.bar").size());
-            assertEquals(0, dataSources.find("foo.*").size());
-            assertEquals(0, dataSources.find("foo.bar").size());
-
-            assertEquals(2, dataSources.find("*.*").size());
-            assertEquals(1, dataSources.find("*." + 
ANY_FILE_EXTENSION).size());
-            assertEquals(1, dataSources.find("*.???").size());
-            assertEquals(1, dataSources.find("*om*").size());
-            assertEquals(1, dataSources.find("*o*.xml").size());
-
-            assertEquals(3, dataSources.find("*").size());
+            assertEquals(0, dataSources.findByName(null).size());
+            assertEquals(0, dataSources.findByName("").size());
+            assertEquals(0, dataSources.findByName("*.bar").size());
+            assertEquals(0, dataSources.findByName("foo.*").size());
+            assertEquals(0, dataSources.findByName("foo.bar").size());
+
+            assertEquals(2, dataSources.findByName("*.*").size());
+            assertEquals(1, dataSources.findByName("*." + 
ANY_FILE_EXTENSION).size());
+            assertEquals(1, dataSources.findByName("*.???").size());
+            assertEquals(1, dataSources.findByName("*om*").size());
+            assertEquals(1, dataSources.findByName("*o*.xml").size());
+
+            assertEquals(3, dataSources.findByName("*").size());
+
+            assertEquals(2, dataSources.findByName("!pom.xml").size());
+            assertEquals(3, dataSources.findByName("!").size());
+            assertEquals(0, dataSources.findByName("!*").size());
+            assertEquals(1, dataSources.findByName("!*.*").size());
         }
     }
 
     @Test
-    public void shouldFindByGroupPart() {
+    public void shouldFindDataSourcesByMetadata() {
         try (DataSources dataSources = dataSources()) {
-
             assertEquals(0, dataSources.find(GROUP_PART, null).size());
             assertEquals(0, dataSources.find(GROUP_PART, "").size());
 
@@ -76,6 +80,10 @@ public class DataSourcesTest {
             assertEquals(3, dataSources.find(GROUP_PART, "default").size());
             assertEquals(3, dataSources.find(GROUP_PART, "d*").size());
             assertEquals(3, dataSources.find(GROUP_PART, "d??????").size());
+
+            assertEquals(0, dataSources.find(GROUP_PART, "!*").size());
+            assertEquals(3, dataSources.find(GROUP_PART, "!unknown").size());
+            assertEquals(0, dataSources.find(GROUP_PART, "!default").size());
         }
     }
 
@@ -87,10 +95,10 @@ public class DataSourcesTest {
     @Test
     public void shouldGetAllDataSource() {
         try (DataSources dataSources = dataSources()) {
-
             assertEquals("unknown", dataSources.get(0).getName());
             assertEquals("pom.xml", dataSources.get(1).getName());
             assertEquals("server.invalid?foo=bar", 
dataSources.get(2).getName());
+            assertEquals(3, dataSources.toArray().length);
             assertEquals(3, dataSources.toList().size());
             assertEquals(3, dataSources.toMap().size());
             assertEquals(3, dataSources.size());
@@ -100,9 +108,9 @@ public class DataSourcesTest {
 
     @Test
     public void shouldGetMetadataParts() {
-        assertEquals(asList("", "pom.xml", ""), 
dataSources().getMetadata("fileName"));
-        assertEquals(asList("default", "default", "default"), 
dataSources().getMetadata("group"));
-        assertEquals(asList("", "xml", ""), 
dataSources().getMetadata("extension"));
+        assertEquals(asList("pom.xml"), dataSources().getMetadata("fileName"));
+        assertEquals(asList("default"), dataSources().getMetadata("group"));
+        assertEquals(asList("xml"), dataSources().getMetadata("extension"));
         assertEquals(asList("unknown", "pom.xml", "server.invalid?foo=bar"), 
dataSources().getMetadata("name"));
     }
 
diff --git a/freemarker-generator-cli/CHANGELOG.md 
b/freemarker-generator-cli/CHANGELOG.md
index 8a250ec..d74ca0e 100644
--- a/freemarker-generator-cli/CHANGELOG.md
+++ b/freemarker-generator-cli/CHANGELOG.md
@@ -2,7 +2,12 @@
 
 All notable changes to this project will be documented in this file. We try to 
adhere to https://github.com/olivierlacan/keep-a-changelog.
 
-## 0.1.0-SNAPSHOT
+## 0.2.0-SNAPSHOT
+
+### Changed
+* [FREEMARKER-195] Improve exposure of DataSources using TemplateHashModelEx2
+
+## 0.1.0-SNAPSHOT (unreleased)
 
 ### Added
 * Use `-Xverify:none -XX:TieredStopAtLevel=1` to improve startup time of CLI
@@ -22,7 +27,6 @@ All notable changes to this project will be documented in 
this file. We try to a
 * [FREEMARKER-129] Migrate `freemarker-cli` into `freemarker-generator` 
project (see 
[https://github.com/sgoeschl/freemarker-cli](https://github.com/sgoeschl/freemarker-cli))
 * [FREEMARKER-129] Provide a `toString()` method for all tools
 
-### Changed
 * [FREEMARKER-182] Upgrade to Apache FreeMarker 2.3.31
 * [FREEMARKER-175] Use latest FreeMarker version
 * [FREEMARKER-173] Allow to pass arbitrary key/value pairs to DataSource when 
using NamedURIs
@@ -85,4 +89,5 @@ All notable changes to this project will be documented in 
this file. We try to a
 [FREEMARKER-181]: https://issues.apache.org/jira/browse/FREEMARKER-181
 [FREEMARKER-182]: https://issues.apache.org/jira/browse/FREEMARKER-182
 [FREEMARKER-188]: https://issues.apache.org/jira/browse/FREEMARKER-188
+[FREEMARKER-195]: https://issues.apache.org/jira/browse/FREEMARKER-195
 
diff --git a/freemarker-generator-cli/pom.xml b/freemarker-generator-cli/pom.xml
index 72ffafc..05497dc 100644
--- a/freemarker-generator-cli/pom.xml
+++ b/freemarker-generator-cli/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.freemarker.generator</groupId>
         <artifactId>freemarker-generator</artifactId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>freemarker-generator-cli</artifactId>
diff --git 
a/freemarker-generator-cli/src/app/examples/data/ftl/nginx/nginx.conf.ftl 
b/freemarker-generator-cli/src/app/examples/data/ftl/nginx/nginx.conf.ftl
index 3cbf310..d74b193 100644
--- a/freemarker-generator-cli/src/app/examples/data/ftl/nginx/nginx.conf.ftl
+++ b/freemarker-generator-cli/src/app/examples/data/ftl/nginx/nginx.conf.ftl
@@ -1,4 +1,4 @@
-<#assign env = tools.properties.parse(dataSources?values[0])>
+<#assign env = tools.properties.parse(dataSources[0])>
 
 server {
   listen 80;
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/accesslog/combined-access.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/accesslog/combined-access.ftl
index 944a826..28f7f71 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/accesslog/combined-access.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/accesslog/combined-access.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#assign grok = tools.grok.create("%{COMBINEDAPACHELOG}")>
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign lines = dataSource.getLineIterator()>
 
 <#compress>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/csv/gatling-user-credentials.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/csv/csv/gatling-user-credentials.ftl
index aae83f4..46d505b 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/csv/csv/gatling-user-credentials.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/csv/csv/gatling-user-credentials.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign cvsFormat = tools.csv.formats.DEFAULT.withDelimiter(';')>
 <#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
 <#assign csvRecords = csvParser.records>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/fo/transactions.ftl 
b/freemarker-generator-cli/src/app/examples/templates/csv/fo/transactions.ftl
index 9a513ca..089790f 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/csv/fo/transactions.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/csv/fo/transactions.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign cvsFormat = 
tools.csv.formats.DEFAULT.withDelimiter('\t').withHeader()>
 <#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/fo/transform.ftl 
b/freemarker-generator-cli/src/app/examples/templates/csv/fo/transform.ftl
index 7e77af2..8f49b72 100644
--- a/freemarker-generator-cli/src/app/examples/templates/csv/fo/transform.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/csv/fo/transform.ftl
@@ -17,7 +17,7 @@
 -->
 <#assign csvFormatName = CVS_IN_FORMAT!"DEFAULT">
 <#assign cvsFormat = tools.csv.formats[csvFormatName].withHeader()>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/html/transactions.ftl 
b/freemarker-generator-cli/src/app/examples/templates/csv/html/transactions.ftl
index 030cd82..c2eaa05 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/csv/html/transactions.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/csv/html/transactions.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign cvsFormat = 
tools.csv.formats.DEFAULT.withDelimiter('\t').withHeader()>
 <#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/md/filter.ftl 
b/freemarker-generator-cli/src/app/examples/templates/csv/md/filter.ftl
index 49beb57..acec8b3 100644
--- a/freemarker-generator-cli/src/app/examples/templates/csv/md/filter.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/csv/md/filter.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign parser = parser(dataSource)>
 <#assign headers = parser.getHeaderNames()>
 <#assign column = tools.system.getParameter("column")>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/csv/shell/curl.ftl 
b/freemarker-generator-cli/src/app/examples/templates/csv/shell/curl.ftl
index 114dd4f..4a18ecf 100644
--- a/freemarker-generator-cli/src/app/examples/templates/csv/shell/curl.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/csv/shell/curl.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#assign cvsFormat = tools.csv.formats.DEFAULT.withHeader()>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign records = csvParser.records>
 <#assign csvMap = tools.csv.toMap(records, "disposer")>
 <#--------------------------------------------------------------------------->
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/dataframe/example.ftl 
b/freemarker-generator-cli/src/app/examples/templates/dataframe/example.ftl
index 3b154ea..7cbf309 100644
--- a/freemarker-generator-cli/src/app/examples/templates/dataframe/example.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/dataframe/example.ftl
@@ -14,7 +14,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, tools.csv.formats.DATAFRAME)>
 <#assign users = tools.dataframe.fromCSVParser(csvParser)>
 
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/dataframe/html/print.ftl 
b/freemarker-generator-cli/src/app/examples/templates/dataframe/html/print.ftl
index 3ae73c8..82343b8 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/dataframe/html/print.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/dataframe/html/print.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#assign cvsFormat = tools.csv.formats.DEFAULT.withHeader().withDelimiter(';')>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign dataFrame = tools.dataframe.toDataFrame(csvParser)>
 <#--------------------------------------------------------------------------->
 <!DOCTYPE html>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/datasources.ftl 
b/freemarker-generator-cli/src/app/examples/templates/datasources.ftl
index 4c7d11f..e00e76a 100644
--- a/freemarker-generator-cli/src/app/examples/templates/datasources.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/datasources.ftl
@@ -1,80 +1,112 @@
-<#ftl output_format="plainText">
 <#--
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you 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.
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
 -->
-Support FreeMarker Directives
+Support Of FreeMarker Directives
 ==============================================================================
-has_content: ${dataSources?has_content?c}
-size: ${dataSources?size}
+dataSources?has_content: ${dataSources?has_content?c}
+dataSources?size: ${dataSources?size}
 
-Use FTL Array-style Access
+Iterate Over DataSources Using Array-style Access
 ==============================================================================
-${dataSources?values[0].name}
-${dataSources?values?first.name}
+<#if dataSources?has_content>
+<#list 0..<dataSources?size as i>
+- dataSource[${i}] ==> ${dataSources[i].name}
+</#list>
+<#else>
+No data sources provided ...
+</#if>
 
-Get Document Names As Keys
+Iterate Over DataSources Using Sequence
 ==============================================================================
-<#list dataSources?keys as name>
-    ${name}<#lt>
+<#list dataSources as dataSource>
+- dataSource[${dataSource?index}] => ${dataSource.name}
+<#else>
+No data sources provided ...
 </#list>
 
-Iterate Over Names & DataSources
+Iterate Over DataSources Using Key & Values
 ==============================================================================
 <#list dataSources as name, dataSource>
-    ${name} => ${dataSource.uri}<#lt>
+- dataSource["${name}"] => ${dataSource.name}<#lt>
+<#else>
+No data sources provided ...
 </#list>
 
-<#if dataSources?has_content>
-    <#list dataSources?values as dataSource>
-        <@writeDataSource dataSource/>
-    </#list>
+Iterate Over DataSources Using Values
+==============================================================================
+<#list dataSources?values as dataSource>
+- dataSource[${dataSource?index}] => ${dataSource.name}
 <#else>
-    No data sources found ...
-</#if>
+No data sources provided ...
+</#list>
 
-<#macro writeDataSource dataSource>
+Iterate Over DataSources Using Hash Map Keys
+==============================================================================
+<#list dataSources?keys as key>
+- dataSource["${key}"] => ${dataSources[key].name}
+<#else>
+No data sources provided ...
+</#list>
 
-${dataSource.name}
+Iterate Over DataSources Using Local Lambda Expression
+==============================================================================
+<#list dataSources?filter(ds -> ds.match("group", "default")) as dataSource>
+- Group "default" => ${dataSource.name}
+<#else>
+No data sources provided ...
+</#list>
+
+Iterate Over DataSources Using Wildcard Search
+==============================================================================
+<#list dataSources?api.findByName("*") as dataSource>
+- ${dataSource.name}
+<#else>
+No data sources provided ...
+</#list>
+
+Access Underlying DataSources API
+==============================================================================
+DataSources.getNames(): ${dataSources?api.names?size}
+DataSources.getGroups(): ${dataSources?api.getGroups()?size}
+DataSources.find("name", "*"): ${dataSources?api.find("name", "*")?size}
+DataSources.find("name", "!readme"): ${dataSources?api.find("name", 
"!readme")?size}
+DataSources.find("uri", "*.md"): ${dataSources?api.find("uri", "*.md")?size}
+DataSources.find("extension", "md"): ${dataSources?api.find("extension", 
"md")?size}
+
+<#if dataSources?has_content>
+<#list dataSources as dataSource>
+[#${dataSource?counter}] - ${dataSource.name}
 ==============================================================================
 
 Invoke Arbitrary Methods On DataSource
 ---------------------------------------------------------------------------
-<#assign dataSource=dataSources?values?first>
+<#assign dataSource=dataSources?first>
 Name                : ${dataSource.name}
-Group               : ${dataSource.group}
 Nr of lines         : ${dataSource.lines?size}
-ContentType         : ${dataSource.contentType}
-MimeType            : ${dataSource.mimeType}
+Content Type        : ${dataSource.contentType}
 Charset             : ${dataSource.charset}
 Extension           : ${dataSource.extension}
 Nr of chars         : ${dataSource.text?length}
 Nr of bytes         : ${dataSource.bytes?size}
-File name           : ${dataSource.fileName}
-URI schema          : ${dataSource.uri.scheme}
-Relative File Path  : ${dataSource.relativeFilePath}
 
 Iterating Over Metadata Of A Datasource
 ---------------------------------------------------------------------------
 <#list dataSource.metadata as name, value>
-${name?right_pad(15)} : ${value}
+${name?right_pad(19)} : ${value}
 </#list>
 
-Iterating Over Properties Of A Datasource
----------------------------------------------------------------------------
-<#list dataSource.properties as name, value>
-${name?right_pad(15)} : ${value}
 </#list>
-</#macro>
+</#if>
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/app/examples/templates/demo.ftl 
b/freemarker-generator-cli/src/app/examples/templates/demo.ftl
index 879fb3f..ff4f3e2 100644
--- a/freemarker-generator-cli/src/app/examples/templates/demo.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/demo.ftl
@@ -48,7 +48,7 @@ java.math.RoundingMode#UP: 
${tools.freemarker.enums["java.math.RoundingMode"].UP
 6) Display list of data sources
 ---------------------------------------------------------------------------
 List all data sources:
-<#list dataSources?values as dataSource>
+<#list dataSources as dataSource>
 - Document: name=${dataSource.name} uri=${dataSource.uri} 
length=${dataSource.length} charset=${dataSource.charset}
 </#list>
 
@@ -89,22 +89,22 @@ Get the number of data sources:
 - ${dataSources?size}
 <#if dataSources?has_content>
 Get the first data source:
-- ${dataSources?values[0].name!"No data sources provided"}
+- ${dataSources[0].name!"No data sources provided"}
 </#if>
 Get all documents as map:
 <#list dataSources as name, ds>
 - ${name} => ${ds.mimeType}
 </#list>
 List all data sources containing "test" in the name
-<#list dataSources?values?filter(ds -> ds.match("name", "*test*")) as ds>
+<#list dataSources?filter(ds -> ds.match("name", "*test*")) as ds>
 - ${ds.name}
 </#list>
 List all data sources having "json" extension
-<#list dataSources?values?filter(ds -> ds.match("extension", "json")) as ds>
+<#list dataSources?filter(ds -> ds.match("extension", "json")) as ds>
 - ${ds.name}
 </#list>
 List all data sources having "src/test/data/properties" in their file path
-<#list dataSources?values?filter(ds -> ds.match("filePath", 
"*/src/test/data/properties")) as ds>
+<#list dataSources?filter(ds -> ds.match("filePath", 
"*/src/test/data/properties")) as ds>
 - ${ds.name}
 </#list>
 
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/excel/csv/custom.ftl 
b/freemarker-generator-cli/src/app/examples/templates/excel/csv/custom.ftl
index 1e2bd06..59ca407 100644
--- a/freemarker-generator-cli/src/app/examples/templates/excel/csv/custom.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/excel/csv/custom.ftl
@@ -18,7 +18,7 @@
 <#assign format = CSV_TARGET_FORMAT!"DEFAULT">
 <#assign salt = tools.system.parameters.salt!"salt">
 <#-- Parse the first data source & sheet of the Excel document -->
-<#assign workbook = tools.excel.parse(dataSources?values[0])>
+<#assign workbook = tools.excel.parse(dataSources[0])>
 <#assign sheet = tools.excel.getSheets(workbook)[0]>
 <#assign records = tools.excel.toTable(sheet)>
 <#-- Setup CSVPrinter  -->
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/excel/dataframe/transform.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/excel/dataframe/transform.ftl
index 5b24903..bd5252c 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/excel/dataframe/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/excel/dataframe/transform.ftl
@@ -14,7 +14,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign workbook = tools.excel.parse(dataSource)>
 <#list tools.excel.getSheets(workbook) as sheet>
     <#assign table = tools.excel.toTable(sheet)>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/html/csv/dependencies.ftl 
b/freemarker-generator-cli/src/app/examples/templates/html/csv/dependencies.ftl
index 51fe17e..4c2e2de 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/html/csv/dependencies.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/html/csv/dependencies.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign html = tools.jsoup.parse(dataSource)>
 
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/html/txt/licence.ftl 
b/freemarker-generator-cli/src/app/examples/templates/html/txt/licence.ftl
index 2807493..d503011 100644
--- a/freemarker-generator-cli/src/app/examples/templates/html/txt/licence.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/html/txt/licence.ftl
@@ -17,7 +17,7 @@
 <#--
 FreeMarker template to create a LICENCE file.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign html = tools.jsoup.parse(dataSource)>
 <#assign dataframe = tools.dataframe.create()
     .addStringColumn("GroupId")
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/json/csv/swagger-endpoints.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/json/csv/swagger-endpoints.ftl
index 6ab4ce2..a70286c 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/json/csv/swagger-endpoints.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/json/csv/swagger-endpoints.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign map = tools.gson.parse(dataSources?values[0])>
+<#assign map = tools.gson.parse(dataSources[0])>
 <#assign basePath = map.basePath!"/">
 <#assign paths = map.paths!{}>
 
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/json/dataframe/github-users.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/json/dataframe/github-users.ftl
index 480f59a..49c21c2 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/json/dataframe/github-users.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/json/dataframe/github-users.ftl
@@ -15,6 +15,6 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<#assign json = tools.gson.parse(dataSources?values[0])>
+<#assign json = tools.gson.parse(dataSources[0])>
 <#assign dataframe = tools.dataframe.fromMaps(json)>
 ${tools.dataframe.print(dataframe)}
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/json/md/github-users.ftl 
b/freemarker-generator-cli/src/app/examples/templates/json/md/github-users.ftl
index a83d07b..39b37a2 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/json/md/github-users.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/json/md/github-users.ftl
@@ -15,7 +15,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<#assign json = tools.jsonpath.parse(dataSources?values[0])>
+<#assign json = tools.jsonpath.parse(dataSources[0])>
 <#assign users = json.read("$[*]")>
 <#--------------------------------------------------------------------------->
 # GitHub Users
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/logs/csv/serverlog-to-csv.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/logs/csv/serverlog-to-csv.ftl
index 544975f..d53e24f 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/logs/csv/serverlog-to-csv.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/logs/csv/serverlog-to-csv.ftl
@@ -35,7 +35,7 @@
 <#compress>
     TIMESTAMP;MILLIS
     <#if dataSources?has_content>
-        <#list dataSources?values as dataSource>
+        <#list dataSources as dataSource>
             <#list dataSource.getLineIterator() as line>
                 <#assign parts = grok.match(line).capture()>
                 <#if parts?has_content>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/properties/csv/locker-test-users.ftl
 
b/freemarker-generator-cli/src/app/examples/templates/properties/csv/locker-test-users.ftl
index 8e01dac..32dd5d6 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/properties/csv/locker-test-users.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/properties/csv/locker-test-users.ftl
@@ -17,7 +17,7 @@
 -->
 <#compress>
     TENANT,SITE,USER_ID,DISPOSER_ID,PASSWORD,SMS_OTP,NAME,DESCRIPTION
-    <#list dataSources?values as dataSource>
+    <#list dataSources as dataSource>
         <#assign properties = tools.properties.parse(dataSource)>
         <#assign environments = properties.ENVIRONMENTS!"">
         <#assign tenant = extractTenant(environments)>
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/tsv/fo/transactions.ftl 
b/freemarker-generator-cli/src/app/examples/templates/tsv/fo/transactions.ftl
index 5beefc3..46a89de 100644
--- 
a/freemarker-generator-cli/src/app/examples/templates/tsv/fo/transactions.ftl
+++ 
b/freemarker-generator-cli/src/app/examples/templates/tsv/fo/transactions.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#assign cvsFormat = tools.csv.formats.TDF.withHeader()>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/xml/txt/recipients.ftl 
b/freemarker-generator-cli/src/app/examples/templates/xml/txt/recipients.ftl
index b67e8e7..83bf4e5 100644
--- a/freemarker-generator-cli/src/app/examples/templates/xml/txt/recipients.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/xml/txt/recipients.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign xml = tools.xml.parse(dataSources?values[0])>
+<#assign xml = tools.xml.parse(dataSources[0])>
 <#list xml.recipients.person as recipient>
 To: ${recipient.name}
 ${recipient.address}
diff --git 
a/freemarker-generator-cli/src/app/examples/templates/yaml/txt/transform.ftl 
b/freemarker-generator-cli/src/app/examples/templates/yaml/txt/transform.ftl
index ebcf525..1e13504 100644
--- a/freemarker-generator-cli/src/app/examples/templates/yaml/txt/transform.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/yaml/txt/transform.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign map = tools.yaml.parse(dataSources?values[0])>
+<#assign map = tools.yaml.parse(dataSources[0])>
 <#--------------------------------------------------------------------------->
 <#compress>
 <@print map 1/>
diff --git a/freemarker-generator-cli/src/app/scripts/run-examples.bat 
b/freemarker-generator-cli/src/app/scripts/run-examples.bat
index 7adcb91..551b5c1 100644
--- a/freemarker-generator-cli/src/app/scripts/run-examples.bat
+++ b/freemarker-generator-cli/src/app/scripts/run-examples.bat
@@ -51,17 +51,20 @@ REM 
=========================================================================
 REM Interactive Mode
 REM =========================================================================
 
-%FREEMARKER_CMD% -i 
'${tools.jsonpath.parse(dataSources?values[0]).read("""$.info.title""")}' 
examples\data\json\swagger-spec.json > target\out\interactive-json.txt
-%FREEMARKER_CMD% -i 
'${tools.xml.parse(dataSources?values[0])["""recipients/person[1]/name"""]}' 
examples\data\xml\recipients.xml > target\out\interactive-xml.txt
-%FREEMARKER_CMD% -i 
'${tools.jsoup.parse(dataSources?values[0]).select("""a""")[0]}' 
examples\data\html\dependencies.html > target\out\interactive-html.txt
-%FREEMARKER_CMD% -i 
'${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}' 
examples\data\yaml\swagger-spec.yaml > target\out\interactive-swagger.json
-%FREEMARKER_CMD% -i 
'${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}' 
examples\data\json\swagger-spec.json > target\out\interactive-swagger.yaml
-%FREEMARKER_CMD% -i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))}'
 examples\data\json\github-users.json > target\out\interactive-dataframe.txt
+%FREEMARKER_CMD% -i 
'${tools.jsonpath.parse(dataSources[0]).read("""$.info.title""")}' 
examples\data\json\swagger-spec.json > target\out\interactive-json.txt
+%FREEMARKER_CMD% -i 
'${tools.xml.parse(dataSources[0])["""recipients/person[1]/name"""]}' 
examples\data\xml\recipients.xml > target\out\interactive-xml.txt
+%FREEMARKER_CMD% -i '${tools.jsoup.parse(dataSources[0]).select("""a""")[0]}' 
examples\data\html\dependencies.html > target\out\interactive-html.txt
+%FREEMARKER_CMD% -i '${tools.gson.toJson(tools.yaml.parse(dataSources[0]))}' 
examples\data\yaml\swagger-spec.yaml > target\out\interactive-swagger.json
+%FREEMARKER_CMD% -i '${tools.yaml.toYaml(tools.gson.parse(dataSources[0]))}' 
examples\data\json\swagger-spec.json > target\out\interactive-swagger.yaml
+%FREEMARKER_CMD% -i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources[0])))}'
 examples\data\json\github-users.json > target\out\interactive-dataframe.txt
 
 REM =========================================================================
 REM CSV
 REM =========================================================================
 
+echo "templates\freemarker-generator\csv\confluence\transform.ftl"
+%FREEMARKER_CMD% -t freemarker-generator\csv\confluence\transform.ftl 
examples\data\csv\contract.csv > target\out\contract.txt
+
 echo "templates\freemarker-generator\csv\html\transform.ftl"
 %FREEMARKER_CMD% -t freemarker-generator\csv\html\transform.ftl 
examples\data\csv\contract.csv > target\out\contract.html
 
diff --git a/freemarker-generator-cli/src/app/scripts/run-examples.sh 
b/freemarker-generator-cli/src/app/scripts/run-examples.sh
index 5298eaf..979f0b1 100755
--- a/freemarker-generator-cli/src/app/scripts/run-examples.sh
+++ b/freemarker-generator-cli/src/app/scripts/run-examples.sh
@@ -56,17 +56,20 @@ $FREEMARKER_CMD -t examples/templates/datasources.ftl -s 
https://xkcd.com/info.0
 # Interactive Mode
 #############################################################################
 
-$FREEMARKER_CMD -i 
'${tools.jsonpath.parse(dataSources?values[0]).read("$.info.title")}' 
examples/data/json/swagger-spec.json > target/out/interactive-json.txt || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i 
'${tools.xml.parse(dataSources?values[0])["recipients/person[1]/name"]}' 
examples/data/xml/recipients.xml > target/out/interactive-xml.txt || { echo >&2 
"Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i 
'${tools.jsoup.parse(dataSources?values[0]).select("a")[0]}' 
examples/data/html/dependencies.html > target/out/interactive-html.txt || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i 
'${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}' 
examples/data/yaml/swagger-spec.yaml > target/out/interactive-swagger.json || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i 
'${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}' 
examples/data/json/swagger-spec.json > target/out/interactive-swagger.yaml || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))}'
 examples/data/json/github-users.json > target/out/interactive-dataframe.txt || 
{ echo >&2 "Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i 
'${tools.jsonpath.parse(dataSources[0]).read("$.info.title")}' 
examples/data/json/swagger-spec.json > target/out/interactive-json.txt || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i 
'${tools.xml.parse(dataSources[0])["recipients/person[1]/name"]}' 
examples/data/xml/recipients.xml > target/out/interactive-xml.txt || { echo >&2 
"Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i '${tools.jsoup.parse(dataSources[0]).select("a")[0]}' 
examples/data/html/dependencies.html > target/out/interactive-html.txt || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i '${tools.gson.toJson(tools.yaml.parse(dataSources[0]))}' 
examples/data/yaml/swagger-spec.yaml > target/out/interactive-swagger.json || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i '${tools.yaml.toYaml(tools.gson.parse(dataSources[0]))}' 
examples/data/json/swagger-spec.json > target/out/interactive-swagger.yaml || { 
echo >&2 "Test failed.  Aborting."; exit 1; }
+$FREEMARKER_CMD -i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources[0])))}'
 examples/data/json/github-users.json > target/out/interactive-dataframe.txt || 
{ echo >&2 "Test failed.  Aborting."; exit 1; }
 
 #############################################################################
 # CSV
 #############################################################################
 
+echo "templates/freemarker-generator/csv/confluence/transform.ftl"
+$FREEMARKER_CMD -t freemarker-generator/csv/confluence/transform.ftl 
examples/data/csv/contract.csv > target/out/contract.txt || { echo >&2 "Test 
failed.  Aborting."; exit 1; }
+
 echo "templates/freemarker-generator/csv/html/transform.ftl"
 $FREEMARKER_CMD -t freemarker-generator/csv/html/transform.ftl 
examples/data/csv/contract.csv > target/out/contract.html || { echo >&2 "Test 
failed.  Aborting."; exit 1; }
 
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/cat.ftl 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/cat.ftl
index cdbe0fa..f4f82e8 100644
--- a/freemarker-generator-cli/src/app/templates/freemarker-generator/cat.ftl
+++ b/freemarker-generator-cli/src/app/templates/freemarker-generator/cat.ftl
@@ -14,7 +14,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#list dataSources?values as dataSource>
+<#list dataSources as dataSource>
 <#list dataSource.lineIterator as line>
 ${line}
 </#list>
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/confluence/transform.ftl
similarity index 91%
copy from 
freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
copy to 
freemarker-generator-cli/src/app/templates/freemarker-generator/csv/confluence/transform.ftl
index 6929b31..35857db 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/confluence/transform.ftl
@@ -15,7 +15,7 @@
   under the License.
 -->
 <#import "/freemarker-generator/lib/commons-csv.ftl" as csv />
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign headers = (csvParser.getHeaderMap()!{})?keys>
 <#assign records = csvParser.records>
@@ -27,8 +27,7 @@
 <#--------------------------------------------------------------------------->
 <#macro writeHeaders headers>
     <#if headers?has_content>
-        | ${headers?join(" | ", "")} |
-        <#list headers as header>| --------</#list>|
+        || ${headers?join(" || ", "")} ||
     </#if>
 </#macro>
 <#--------------------------------------------------------------------------->
@@ -38,4 +37,4 @@
             | ${column.iterator()?join(" | ", "")} |
         </#list>
     </#if>
-</#macro>
+</#macro>
\ No newline at end of file
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/csv/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/csv/transform.ftl
index 3941c4c..c999235 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/csv/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/csv/transform.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#import "/freemarker-generator/lib/commons-csv.ftl" as csv />
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign csvTargetFormat = csv.targetFormat()>
 <#assign csvPrinter = tools.csv.printer(csvTargetFormat)>
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/html/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/html/transform.ftl
index d027672..e39a76d 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/html/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/html/transform.ftl
@@ -16,7 +16,7 @@
   under the License.
 -->
 <#import "/freemarker-generator/lib/commons-csv.ftl" as csv />
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign csvHeaders = csvParser.getHeaderNames()>
 <#--------------------------------------------------------------------------->
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
index 6929b31..edd8b3e 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/csv/md/transform.ftl
@@ -15,7 +15,7 @@
   under the License.
 -->
 <#import "/freemarker-generator/lib/commons-csv.ftl" as csv />
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign headers = (csvParser.getHeaderMap()!{})?keys>
 <#assign records = csvParser.records>
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/csv/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/csv/transform.ftl
index 18179f4..22dec59 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/csv/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/csv/transform.ftl
@@ -17,7 +17,7 @@
 -->
 <#-- Parse the first data source & sheet of the Excel document -->
 <#import "/freemarker-generator/lib/commons-csv.ftl" as csv />
-<#assign workbook = tools.excel.parse(dataSources?values[0])>
+<#assign workbook = tools.excel.parse(dataSources[0])>
 <#assign sheet = tools.excel.getSheets(workbook)[0]>
 <#assign records = tools.excel.toTable(sheet)>
 <#-- Setup CSVPrinter  -->
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/html/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/html/transform.ftl
index 7564751..e1e9409 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/html/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/html/transform.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/md/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/md/transform.ftl
index e3fb970..a0e13b7 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/md/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/excel/md/transform.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
index 05eff21..d5725d0 100644
--- a/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
+++ b/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
@@ -1,4 +1,3 @@
-<#ftl output_format="plainText" strip_whitespace=true>
 <#--
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
@@ -23,6 +22,7 @@ Template name          : ${.current_template_name}
 Language               : ${.lang}
 Locale                 : ${.locale}
 Timestamp              : ${.now}
+Time zone              : ${.now?string["z"]}
 Output encoding        : ${.output_encoding}
 Output format          : ${.output_format}
 
@@ -50,7 +50,7 @@ FreeMarker Generator Data Model
 FreeMarker Generator Data Sources
 ==============================================================================
 <#if dataSources?has_content>
-<#list dataSources?values as dataSource>
+<#list dataSources as dataSource>
 
 DataSource #${dataSource?counter}
 ------------------------------------------------------------------------------
@@ -60,6 +60,7 @@ contentType           : ${dataSource.contentType}
 fileName              : ${dataSource.fileName}
 baseName              : ${dataSource.baseName}
 extension             : ${dataSource.extension}
+extension             : ${dataSource.extension}
 relativeFilePath      : ${dataSource.relativeFilePath}
 charset               : ${dataSource.charset}
 mimeType              : ${dataSource.mimeType}
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/json/yaml/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/json/yaml/transform.ftl
index 6e48945..d5b32f9 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/json/yaml/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/json/yaml/transform.ftl
@@ -14,4 +14,4 @@
   specific language governing permissions and limitations
   under the License.
 -->
-${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}
\ No newline at end of file
+${tools.yaml.toYaml(tools.gson.parse(dataSources[0]))}
\ No newline at end of file
diff --git 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/yaml/json/transform.ftl
 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/yaml/json/transform.ftl
index 81a704a..d0f0581 100644
--- 
a/freemarker-generator-cli/src/app/templates/freemarker-generator/yaml/json/transform.ftl
+++ 
b/freemarker-generator-cli/src/app/templates/freemarker-generator/yaml/json/transform.ftl
@@ -14,4 +14,4 @@
   specific language governing permissions and limitations
   under the License.
 -->
-${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}
\ No newline at end of file
+${tools.gson.toJson(tools.yaml.parse(dataSources[0]))}
\ No newline at end of file
diff --git 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
index 363aa5a..7c9c4d9 100644
--- 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
+++ 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
@@ -206,7 +206,7 @@ public class Main implements Callable<Integer> {
 
     /**
      * Data sources can be passed via command line option and/or
-     * positional parameter so we need to merge them.
+     * positional parameter, so we need to merge them.
      *
      * @return List of data sources
      */
diff --git 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
index 593f7f7..a54f98d 100644
--- 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
+++ 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
@@ -20,6 +20,7 @@ import freemarker.cache.TemplateLoader;
 import freemarker.template.Configuration;
 import freemarker.template.Version;
 import org.apache.freemarker.generator.base.util.PropertiesTransformer;
+import 
org.apache.freemarker.generator.cli.wrapper.FreeMarkerGeneratorObjectWrapper;
 
 import java.util.Properties;
 import java.util.function.Supplier;
@@ -50,6 +51,10 @@ public class ConfigurationSupplier implements 
Supplier<Configuration> {
         try {
             final Configuration configuration = new 
Configuration(FREEMARKER_VERSION);
 
+            // support a custom "DataSourcesAdaptor"
+            configuration.setAPIBuiltinEnabled(true);
+            configuration.setObjectWrapper(new 
FreeMarkerGeneratorObjectWrapper(configuration.getIncompatibleImprovements()));
+
             // apply all "freemarker.configuration.setting" values
             configuration.setSettings(freeMarkerConfigurationSettings());
 
diff --git 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
index 914bd79..8e158e4 100644
--- 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
+++ 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
@@ -138,8 +138,7 @@ public class FreeMarkerTask implements Callable<Integer> {
     private static Map<String, Object> toTemplateDataModel(DataSources 
dataSources, Map<String, Object>... maps) {
         final Map<String, Object> result = new HashMap<>();
         Arrays.stream(maps).forEach(result::putAll);
-        // expose only the map and not the "DataSources" instance (see 
FREEMARKER-174)
-        result.put(Model.DATASOURCES, dataSources.toMap());
+        result.put(Model.DATASOURCES, dataSources);
         return result;
     }
 
diff --git 
a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/DataSourcesAdapter.java
 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/DataSourcesAdapter.java
new file mode 100644
index 0000000..de25ed4
--- /dev/null
+++ 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/DataSourcesAdapter.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.freemarker.generator.cli.wrapper;
+
+import freemarker.ext.util.WrapperTemplateModel;
+import freemarker.template.AdapterTemplateModel;
+import freemarker.template.MapKeyValuePairIterator;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.SimpleCollection;
+import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateHashModelEx2;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateModelWithAPISupport;
+import freemarker.template.TemplateSequenceModel;
+import freemarker.template.WrappingTemplateModel;
+import freemarker.template.utility.ObjectWrapperWithAPISupport;
+import org.apache.freemarker.generator.base.datasource.DataSources;
+
+import java.io.Serializable;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Wraps an instance of <code>DataSources</code> into a FreeMarker template 
model
+ * providing sequence and hash type access. If required the 
<code>DataSources</code>
+ * API can be accessed using FreeMarkers "?api" built-in.
+ */
+public class DataSourcesAdapter extends WrappingTemplateModel
+        implements TemplateHashModelEx2, AdapterTemplateModel, 
WrapperTemplateModel, TemplateModelWithAPISupport,
+        TemplateSequenceModel, Serializable {
+
+    /** Wrapped instance */
+    private final DataSources dataSources;
+
+    /**
+     * Factory method for creating new adapter instances.
+     *
+     * @param dataSources The dataSources to adapt; can't be {@code null}.
+     * @param wrapper     The {@link ObjectWrapper} used to wrap the items in 
the array.
+     * @return adapter
+     */
+    public static DataSourcesAdapter create(DataSources dataSources, 
ObjectWrapperWithAPISupport wrapper) {
+        return new DataSourcesAdapter(dataSources, wrapper);
+    }
+
+    private DataSourcesAdapter(DataSources dataSources, ObjectWrapper wrapper) 
{
+        super(requireNonNull(wrapper));
+        this.dataSources = requireNonNull(dataSources);
+    }
+
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        return wrap(dataSources.toMap().get(key));
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return dataSources.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return dataSources.size();
+    }
+
+    @Override
+    public TemplateCollectionModel keys() {
+        return new SimpleCollection(dataSources.toMap().keySet(), 
getObjectWrapper());
+    }
+
+    @Override
+    public TemplateCollectionModel values() {
+        return new SimpleCollection(dataSources.toMap().values(), 
getObjectWrapper());
+    }
+
+    @Override
+    public KeyValuePairIterator keyValuePairIterator() {
+        return new MapKeyValuePairIterator(dataSources.toMap(), 
getObjectWrapper());
+    }
+
+    @Override
+    public TemplateModel get(int index) throws TemplateModelException {
+        return index >= 0 && index < dataSources.size() ? 
wrap(dataSources.get(index)) : null;
+    }
+
+    @Override
+    public Object getAdaptedObject(Class hint) {
+        return dataSources;
+    }
+
+    @Override
+    public Object getWrappedObject() {
+        return dataSources;
+    }
+
+    @Override
+    public TemplateModel getAPI() throws TemplateModelException {
+        return ((ObjectWrapperWithAPISupport) 
getObjectWrapper()).wrapAsAPI(dataSources);
+    }
+}
diff --git 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/FreeMarkerGeneratorObjectWrapper.java
similarity index 50%
copy from 
freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
copy to 
freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/FreeMarkerGeneratorObjectWrapper.java
index 4a22137..102a84e 100644
--- 
a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceLoader.java
+++ 
b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/wrapper/FreeMarkerGeneratorObjectWrapper.java
@@ -14,23 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.freemarker.generator.base.datasource;
+package org.apache.freemarker.generator.cli.wrapper;
 
-public interface DataSourceLoader {
+import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.Version;
+import org.apache.freemarker.generator.base.datasource.DataSources;
 
-    /**
-     * Check if the data source can be loaded by this instance.
-     *
-     * @param source source to be loaded from
-     * @return true if the instance wold be able to load a data source
-     */
-    boolean accept(String source);
+public class FreeMarkerGeneratorObjectWrapper extends DefaultObjectWrapper {
 
-    /**
-     * Load a DataSource.
-     *
-     * @param source source of the data source
-     * @return DataSource
-     */
-    DataSource load(String source);
+    public FreeMarkerGeneratorObjectWrapper(Version incompatibleImprovements) {
+        super(incompatibleImprovements);
+    }
+
+    @Override
+    protected TemplateModel handleUnknownType(final Object obj) throws 
TemplateModelException {
+        if (obj instanceof DataSources) {
+            return DataSourcesAdapter.create((DataSources) obj, this);
+        }
+
+        return super.handleUnknownType(obj);
+    }
 }
diff --git 
a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md 
b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
index 2e0ba54..2a7d01e 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
@@ -98,7 +98,7 @@ URI : system:///stdin
 
 After loading one or more `DataSource` they are accessible as `dataSource` map 
in the FreeMarker model
 
-* `dataSources?values[0]` or `dataSources?values?first` selects the first data 
source
+* `dataSources[0]` or `dataSources?first` selects the first data source
 * `dataSources["user.csv"]` selects the data source with the name "user.csv"
 
 ### Iterating Over DataSources
@@ -122,7 +122,7 @@ ${dataSources?size}
 </#list>
 
 <#-- Iterate over a list of data sources -->
-<#list dataSources?values as dataSource>
+<#list dataSources as dataSource>
 - [#${dataSource?counter}]: name=${dataSource.name}
 </#list>
 ```
@@ -134,22 +134,22 @@ selection of data sources (using Apache Commons IO 
wild-card matching)
 
 ```
 <#-- List all data sources containing "test" in the name -->
-<#list dataSources?values?filter(ds -> ds.match("name", "*test*")) as ds>
+<#list dataSources?filter(ds -> ds.match("name", "*test*")) as ds>
 - ${ds.name}
 </#list>
 
 <#-- List all data sources having "json" extension -->
-<#list dataSources?values?filter(ds -> ds.match("extension", "json")) as ds>
+<#list dataSources?filter(ds -> ds.match("extension", "json")) as ds>
 - ${ds.name}
 </#list>
 
 <#-- List all data sources having "src/test/data/properties" in their file 
path -->
-<#list dataSources?values?filter(ds -> ds.match("filePath", 
"*/src/test/data/properties")) as ds>
+<#list dataSources?filter(ds -> ds.match("filePath", 
"*/src/test/data/properties")) as ds>
 - ${ds.name}
 </#list>
 
 <#-- List all data sources of a group -->
-<#list dataSources?values?filter(ds -> ds.match("group", "default")) as ds>
+<#list dataSources?filter(ds -> ds.match("group", "default")) as ds>
 - ${ds.name}
 </#list>
 
@@ -163,7 +163,7 @@ In most cases the data source will be passed to a tool, but 
there are some usefu
 Invoke Arbitrary Methods On DataSource
 ---------------------------------------------------------------------------
 <#if dataSources?has_content>
-<#assign dataSource=dataSources?values?first>
+<#assign dataSource=dataSources?first>
 Name            : ${dataSource.name}
 Nr of lines     : ${dataSource.lines?size}
 Content Type    : ${dataSource.contentType}
diff --git 
a/freemarker-generator-cli/src/site/markdown/cli/usage/parsing-with-grok.md 
b/freemarker-generator-cli/src/site/markdown/cli/usage/parsing-with-grok.md
index 5cbadda..7f4404c 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/parsing-with-grok.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/parsing-with-grok.md
@@ -33,7 +33,7 @@ using the following FreeMarker template
 ```text
 <#ftl output_format="plainText" strip_whitespace=true>
 <#assign grok = tools.grok.create("%{COMBINEDAPACHELOG}")>
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign lines = dataSource.getLineIterator()>
 
 <#compress>
@@ -93,7 +93,7 @@ In technical terms the FTL
 <#compress>
     TIMESTAMP;MILLIS
     <#if dataSources?has_content>
-        <#list dataSources?values as dataSource>
+        <#list dataSources as dataSource>
             <#list dataSource.getLineIterator() as line>
                 <#assign parts = grok.match(line).capture()>
                 <#if parts?has_content>
diff --git 
a/freemarker-generator-cli/src/site/markdown/cli/usage/running-examples.md 
b/freemarker-generator-cli/src/site/markdown/cli/usage/running-examples.md
index e4cc875..9b686a2 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/running-examples.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/running-examples.md
@@ -92,7 +92,7 @@ Below you see the Apache FreeMarker Template
 
 ```text
 <#ftl output_format="plainText" >
-<#assign json = tools.jsonpath.parse(dataSources?values[0])>
+<#assign json = tools.jsonpath.parse(dataSources[0])>
 <#assign users = json.read("$[*]")>
 <#--------------------------------------------------------------------------->
 # GitHub Users
@@ -130,7 +130,7 @@ The FreeMarker template is shown below
 ```text
 <#ftl output_format="plainText">
 <#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader()>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
@@ -167,7 +167,7 @@ using the following template
 
 ```text
 <#ftl output_format="plainText" >
-<#assign xml = tools.xml.parse(dataSources?values[0])>
+<#assign xml = tools.xml.parse(dataSources[0])>
 <#list xml.recipients.person as recipient>
 To: ${recipient.name}
 ${recipient.address}
@@ -214,7 +214,7 @@ One day I was asked a to prepare a CSV files containing 
REST endpoints described
 
 ```text
 <#ftl output_format="plainText" strip_text="true">
-<#assign json = tools.jsonpath.parse(dataSources?values[0])>
+<#assign json = tools.jsonpath.parse(dataSources[0])>
 <#assign basePath = json.read("$.basePath")>
 <#assign paths = json.read("$.paths")>
 
@@ -276,7 +276,7 @@ The provided FTL transforms an Excel into a HTML document 
supporting multiple Ex
 
 ```text
 <#ftl output_format="HTML" >
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
@@ -402,7 +402,7 @@ For a POC (proof of concept) I created a sample 
transformation from CSV to XML-F
 
 ```text
 <#ftl output_format="XML" >
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign name = dataSource.name>
 <#assign cvsFormat = 
tools.csv.formats.DEFAULT.withDelimiter('\t').withHeader()>
 <#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
@@ -523,7 +523,7 @@ Recently I got the rather unusual question how to determine 
the list of dependen
 
 ```text
 <#ftl output_format="plainText" strip_text="true">
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign html = tools.jsoup.parse(dataSource)>
 
 <#compress>
@@ -598,7 +598,7 @@ and the final FTL is found below
 ```text
 <#ftl output_format="plainText">
 <#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader()>
-<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign csvParser = tools.csv.parse(dataSources[0], cvsFormat)>
 <#assign records = csvParser.records>
 <#assign csvMap = tools.csv.toMap(records, "disposer")>
 <#--------------------------------------------------------------------------->
@@ -713,16 +713,16 @@ Sometime you need to apply a CSS, JSON or XPath query in 
ad ad-hoc way without i
 > bin/freemarker-generator -i 'Hello ${tools.system.envs["USER"]}'; echo
 Hello sgoeschl
 
-> bin/freemarker-generator -i 
'${tools.jsonpath.parse(dataSources?values[0]).read("$.info.title")}' 
examples/data/json/swagger-spec.json; echo
+> bin/freemarker-generator -i 
'${tools.jsonpath.parse(dataSources[0]).read("$.info.title")}' 
examples/data/json/swagger-spec.json; echo
 Swagger Petstore
 
-> bin/freemarker-generator -i 'Post Title : 
${tools.jsonpath.parse(dataSources?values[0]).read("$.title")}' 
https://jsonplaceholder.typicode.com/posts/2; echo
+> bin/freemarker-generator -i 'Post Title : 
${tools.jsonpath.parse(dataSources[0]).read("$.title")}' 
https://jsonplaceholder.typicode.com/posts/2; echo
 Post Title : qui est esse
 
-> bin/freemarker-generator -i 
'${tools.xml.parse(dataSources?values[0])["recipients/person[1]/name"]}' 
examples/data/xml/recipients.xml; echo
+> bin/freemarker-generator -i 
'${tools.xml.parse(dataSources[0])["recipients/person[1]/name"]}' 
examples/data/xml/recipients.xml; echo
 John Smith
 
-> bin/freemarker-generator -i 
'${tools.jsoup.parse(dataSources?values[0]).select("a")[0]}' 
examples/data/html/dependencies.html; echo
+> bin/freemarker-generator -i 
'${tools.jsoup.parse(dataSources[0]).select("a")[0]}' 
examples/data/html/dependencies.html; echo
 <a href="${project.url}" title="FreeMarker Generator">FreeMarker Generator</a>
 
 > freemarker-generator -i '<#list tools.system.envs as name,value>${name} ==> 
 > ${value}${"\n"}</#list>'
@@ -749,7 +749,7 @@ and Apache FreeMarker template
 
 ```text
 <#ftl output_format="plainText" strip_text="true">
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign parser = parser(dataSource)>
 <#assign headers = parser.getHeaderNames()>
 <#assign column = tools.system.getParameter("column")>
@@ -835,11 +835,11 @@ Sometimes we simply need to transform a JSON into an 
equivalent YAML or the othe
 
 ```
 > freemarker-generator -t freemarker-generator/yaml/json/transform.ftl 
 > examples/data/yaml/swagger-spec.yaml 
-> freemarker-generator -i 
'${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}' 
examples/data/yaml/swagger-spec.yaml
+> freemarker-generator -i 
'${tools.gson.toJson(tools.yaml.parse(dataSources[0]))}' 
examples/data/yaml/swagger-spec.yaml
 > freemarker-generator -i '${tools.gson.toJson(yaml)}' -m 
 > yaml=examples/data/yaml/swagger-spec.yaml
 
 > freemarker-generator -t freemarker-generator/json/yaml/transform.ftl 
 > examples/data/json/swagger-spec.json
-> freemarker-generator -i 
'${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}' 
examples/data/json/swagger-spec.json
+> freemarker-generator -i 
'${tools.yaml.toYaml(tools.gson.parse(dataSources[0]))}' 
examples/data/json/swagger-spec.json
 > freemarker-generator -i '${tools.yaml.toYaml(json)}' -m 
 > json=examples/data/json/swagger-spec.json
 ```
 
diff --git 
a/freemarker-generator-cli/src/site/markdown/cli/usage/using-dataframes.md 
b/freemarker-generator-cli/src/site/markdown/cli/usage/using-dataframes.md
index 9fbf9d2..8866983 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/using-dataframes.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/using-dataframes.md
@@ -30,7 +30,7 @@ Meier;30;Germany
 and create a `DateFrame` using the following code snippet
 
 ```
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign csvParser = tools.csv.parse(dataSource, 
tools.csv.formats["DATAFRAME"])>
 <#assign users = tools.dataframe.fromCSVParser(csvParser)>
 ```
@@ -162,7 +162,7 @@ it is a list of maps hence we invoke 
`tools.dataframe.fromMaps()
 
 ```
 freemarker-generator \
--i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))}'
 \
+-i 
'${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources[0])))}'
 \
 examples/data/json/github-users.json
 
 
┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
@@ -189,7 +189,7 @@ examples/data/json/github-users.json
 Let's transform an Excel Sheet to a `DataFrame` being printed using the 
following template
 
 ```
-<#assign dataSource = dataSources?values[0]>
+<#assign dataSource = dataSources[0]>
 <#assign workbook = tools.excel.parse(dataSource)>
 <#list tools.excel.getSheets(workbook) as sheet>
     <#assign table = tools.excel.toTable(sheet)>
diff --git 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
index a5192e4..0aa2399 100644
--- 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
+++ 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
@@ -49,11 +49,13 @@ public class ExamplesTest extends AbstractMainTest {
 
     @Test
     public void shouldRunDataSourceExamples() throws IOException {
+        assertValid(execute("-t src/app/examples/templates/datasources.ftl"));
         assertValid(execute("-t src/app/examples/templates/datasources.ftl -s 
:csv=src/app/examples/data/csv"));
     }
 
     @Test
     public void shouldRunCsvExamples() throws IOException {
+        assertValid(execute("-t 
freemarker-generator/csv/confluence/transform.ftl 
src/app/examples/data/csv/contract.csv"));
         assertValid(execute("-t freemarker-generator/csv/html/transform.ftl 
src/app/examples/data/csv/contract.csv"));
         assertValid(execute("-t freemarker-generator/csv/md/transform.ftl 
src/app/examples/data/csv/contract.csv"));
         assertValid(execute("-t src/app/examples/templates/csv/shell/curl.ftl 
src/app/examples/data/csv/user.csv"));
@@ -125,14 +127,14 @@ public class ExamplesTest extends AbstractMainTest {
 
     @Test
     public void shouldRunInteractiveTemplateExamples() throws IOException {
-        assertValid(execute("-i 
${tools.jsonpath.parse(dataSources?values[0]).read(\"$.info.title\")} 
src/app/examples/data/json/swagger-spec.json"));
-        assertValid(execute("-i 
${tools.xml.parse(dataSources?values[0])[\"recipients/person[1]/name\"]} 
src/app/examples/data/xml/recipients.xml"));
-        assertValid(execute("-i 
${tools.jsoup.parse(dataSources?values[0]).select(\"a\")[0]} 
src/app/examples/data/html/dependencies.html"));
-        assertValid(execute("-i 
${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))} 
src/app/examples/data/yaml/swagger-spec.yaml"));
+        assertValid(execute("-i 
${tools.jsonpath.parse(dataSources[0]).read(\"$.info.title\")} 
src/app/examples/data/json/swagger-spec.json"));
+        assertValid(execute("-i 
${tools.xml.parse(dataSources[0])[\"recipients/person[1]/name\"]} 
src/app/examples/data/xml/recipients.xml"));
+        assertValid(execute("-i 
${tools.jsoup.parse(dataSources[0]).select(\"a\")[0]} 
src/app/examples/data/html/dependencies.html"));
+        assertValid(execute("-i 
${tools.gson.toJson(tools.yaml.parse(dataSources[0]))} 
src/app/examples/data/yaml/swagger-spec.yaml"));
         assertValid(execute("-i ${tools.gson.toJson(yaml)} -m 
yaml=src/app/examples/data/yaml/swagger-spec.yaml"));
-        assertValid(execute("-i 
${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))} 
src/app/examples/data/json/swagger-spec.json"));
+        assertValid(execute("-i 
${tools.yaml.toYaml(tools.gson.parse(dataSources[0]))} 
src/app/examples/data/json/swagger-spec.json"));
         assertValid(execute("-i ${tools.yaml.toYaml(json)} -m 
json=src/app/examples/data/json/swagger-spec.json"));
-        assertValid(execute("-i 
${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))}
 src/app/examples/data/json/github-users.json"));
+        assertValid(execute("-i 
${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources[0])))}
 src/app/examples/data/json/github-users.json"));
     }
 
     @Test
diff --git 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ManualTest.java
 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ManualTest.java
index 64705a0..2f1596b 100644
--- 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ManualTest.java
+++ 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ManualTest.java
@@ -25,10 +25,8 @@ public class ManualTest extends AbstractMainTest {
 
     // private static final String CMD = "-V";
     private static final String CMD =
-            "-M generate " +
-                    "-o target/out " +
-                    "-t freemarker-generator/csv/html/transform.ftl " +
-                    "src/app/examples/data/csv";
+            "-t src/app/examples/templates/datasources.ftl 
readme:documentation=README.md src/main/assembly";
+            // "-t src/app/examples/templates/datasources.ftl";
 
     @Override
     public String execute(String commandLine) throws IOException {
diff --git 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
index ae9fa68..b2daff2 100644
--- 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
+++ 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
@@ -41,11 +41,12 @@ public class ConfigurationSupplierTest {
         assertTrue(configuration.isOutputEncodingSet());
 
         assertFalse(configuration.isCacheStorageExplicitlySet());
-        assertFalse(configuration.isObjectWrapperExplicitlySet());
+        assertTrue(configuration.isObjectWrapperExplicitlySet());
         assertFalse(configuration.isOutputFormatExplicitlySet());
         assertFalse(configuration.isTemplateExceptionHandlerExplicitlySet());
         assertFalse(configuration.isTimeZoneExplicitlySet());
         assertFalse(configuration.isWrapUncheckedExceptionsExplicitlySet());
+        assertTrue(configuration.isAPIBuiltinEnabled());
     }
 
     private ConfigurationSupplier configurationSupplier(Settings settings) {
diff --git 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
index a772bb2..fa9b47e 100644
--- 
a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
+++ 
b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
@@ -39,7 +39,6 @@ import java.util.Properties;
 
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -95,7 +94,6 @@ public class SuppliersTest {
 
         assertNotNull(configuration.getSharedVariable(Model.TOOLS));
         assertTrue(configuration.isTemplateLoaderExplicitlySet());
-        assertFalse(configuration.isObjectWrapperExplicitlySet());
     }
 
     @Test
diff --git a/freemarker-generator-cli/src/test/templates/echo.ftl 
b/freemarker-generator-cli/src/test/templates/echo.ftl
index 38627f4..56c0b56 100644
--- a/freemarker-generator-cli/src/test/templates/echo.ftl
+++ b/freemarker-generator-cli/src/test/templates/echo.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#list dataSources?values as ds>
+<#list dataSources as ds>
 ${ds.name}, ${ds.uri}
 =============================================================================
 ${ds.text}
diff --git a/freemarker-generator-cli/src/test/templates/manual.ftl 
b/freemarker-generator-cli/src/test/templates/manual.ftl
index c1112be..1e6f5ac 100644
--- a/freemarker-generator-cli/src/test/templates/manual.ftl
+++ b/freemarker-generator-cli/src/test/templates/manual.ftl
@@ -22,8 +22,8 @@ Nr. of Documents: ${dataSources?size}
 
 Use FTL Array-style Access
 ==============================================================================
-${dataSources?values[0].toString()}
-${dataSources?values?first.toString()}
+${dataSources[0].toString()}
+${dataSources?first.toString()}
 
 Get Document Names As Keys
 ==============================================================================
@@ -38,13 +38,13 @@ Iterate Over Names & DataSources
 </#list>
 
 <#if dataSources?has_content>
-    <#list dataSources?values as dataSource>
+    <#list dataSources as dataSource>
 [#${dataSource?counter}] - ${dataSource.name}
 ==============================================================================
 
 Invoke Arbitrary Methods On DataSource
 ---------------------------------------------------------------------------
-<#assign dataSource=dataSources?values?first>
+<#assign dataSource=dataSources?first>
 Name            : ${dataSource.name}
 Nr of lines     : ${dataSource.lines?size}
 Content Type    : ${dataSource.contentType}
diff --git a/freemarker-generator-cli/src/test/templates/tools/csv.ftl 
b/freemarker-generator-cli/src/test/templates/tools/csv.ftl
index 25dfb3b..07c4f43 100644
--- a/freemarker-generator-cli/src/test/templates/tools/csv.ftl
+++ b/freemarker-generator-cli/src/test/templates/tools/csv.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign records = tools.csv.parse(dataSources?values[0], 
CSVFormat.DEFAULT.withHeader()).records>
+<#assign records = tools.csv.parse(dataSources[0], 
CSVFormat.DEFAULT.withHeader()).records>
 
 tools.csv.toMap(name)
 =============================================================================
diff --git a/freemarker-generator-maven-plugin-sample/pom.xml 
b/freemarker-generator-maven-plugin-sample/pom.xml
index cbd84a1..1ce4a10 100644
--- a/freemarker-generator-maven-plugin-sample/pom.xml
+++ b/freemarker-generator-maven-plugin-sample/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <groupId>org.apache.freemarker.generator</groupId>
         <artifactId>freemarker-generator</artifactId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
     <artifactId>freemarker-generator-maven-plugin-sample</artifactId>
     <name>Apache FreeMarker Generator: Maven Plugin Sample</name>
diff --git a/freemarker-generator-maven-plugin/pom.xml 
b/freemarker-generator-maven-plugin/pom.xml
index d1f9515..c8b014a 100644
--- a/freemarker-generator-maven-plugin/pom.xml
+++ b/freemarker-generator-maven-plugin/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.freemarker.generator</groupId>
         <artifactId>freemarker-generator</artifactId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>freemarker-generator-maven-plugin</artifactId>
diff --git a/freemarker-generator-tools/pom.xml 
b/freemarker-generator-tools/pom.xml
index e532237..590246f 100644
--- a/freemarker-generator-tools/pom.xml
+++ b/freemarker-generator-tools/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.freemarker.generator</groupId>
         <artifactId>freemarker-generator</artifactId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.2.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>freemarker-generator-tools</artifactId>
diff --git 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
index 3490ee8..12c757d 100644
--- 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
+++ 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
@@ -110,7 +110,7 @@ public class CommonsCSVTool {
     }
 
     /**
-     * Extract the list of unique values (keys) of the column with the given 
index..
+     * Extract the list of unique values (keys) of the column with the given 
index.
      *
      * @param records records to process
      * @param index   column index to map
@@ -123,7 +123,7 @@ public class CommonsCSVTool {
     /**
      * Map the given value of the CVS record into (key to record). If 
duplicates
      * are encountered return the first occurrence of the CVS record. The map
-     * retains the insertion order of they keys.
+     * retains the insertion order of their keys.
      *
      * @param records records to process
      * @param name    column name to map
@@ -136,7 +136,7 @@ public class CommonsCSVTool {
     /**
      * Map the given value of the CVS record into (key to record). If 
duplicates
      * are encountered return the first occurrence of the CVS record. The map
-     * retains the insertion order of they keys.
+     * retains the insertion order of their keys.
      *
      * @param records records to process
      * @param index   column index to map
diff --git 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/excel/ExcelTool.java
 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/excel/ExcelTool.java
index eff89ea..300cf44 100644
--- 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/excel/ExcelTool.java
+++ 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/excel/ExcelTool.java
@@ -80,7 +80,7 @@ public class ExcelTool {
     }
 
     /**
-     * Transform the Excel sheet into a table. Please not that locales are 
mostly
+     * Transform the Excel sheet into a table. Please note that locales are 
mostly
      * ignored by Apache POI (see 
https://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/DataFormatter.html)
      *
      * @param sheet Excel sheet
diff --git 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/grok/GrokTool.java
 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/grok/GrokTool.java
index 5f3a485..34af4bd 100644
--- 
a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/grok/GrokTool.java
+++ 
b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/grok/GrokTool.java
@@ -28,7 +28,7 @@ public class GrokTool {
     private static final String DEFAULT_PATTERN_FILE = "/patterns/patterns";
 
     /**
-     * Create a default Grok instance using the the default pattern files 
loaded
+     * Create a default Grok instance using the default pattern files loaded
      * from the classpath.
      *
      * @param pattern Grok pattern to compile
@@ -39,7 +39,7 @@ public class GrokTool {
     }
 
     /**
-     * Get a default Grok instance using the the default pattern files loaded
+     * Get a default Grok instance using the default pattern files loaded
      * from the classpath.
      *
      * @param pattern            Grok pattern to compile
diff --git a/pom.xml b/pom.xml
index 6d71dfb..5a0c552 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,7 @@
     <groupId>org.apache.freemarker.generator</groupId>
     <artifactId>freemarker-generator</artifactId>
     <packaging>pom</packaging>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.2.0-SNAPSHOT</version>
     <name>Apache FreeMarker Generator</name>
     <url>https://freemarker-generator.apache.org/</url>
 

Reply via email to