You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by sg...@apache.org on 2020/07/11 20:32:56 UTC

[freemarker-generator] branch master updated: FREEMARKER-148 Make usage of "DataSources" more "Freemarker" like (#18)

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 6974296  FREEMARKER-148 Make usage of "DataSources" more "Freemarker" like (#18)
6974296 is described below

commit 6974296060ac47c6a3a0e4dac845a2bb9d9d8694
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Sat Jul 11 22:32:48 2020 +0200

    FREEMARKER-148 Make usage of "DataSources" more "Freemarker" like (#18)
---
 README.md                                          |   2 +-
 freemarker-generator-base/README.md                |   2 +-
 .../generator/base/FreeMarkerConstants.java        |   5 +-
 .../generator/base/datasource/DataSource.java      | 128 +++++++++++--------
 .../base/datasource/DataSourceFactory.java         |  34 ++----
 .../generator/base/datasource/DataSources.java     |  57 +++++++--
 .../base/datasource/DataSourcesSupplier.java       |   3 +-
 .../{MimetypeParser.java => MimeTypeParser.java}   |   6 +-
 .../generator/base/parameter/Parameter.java        |   2 +-
 .../generator/base/template/TemplateSource.java    |   2 +-
 .../base/template/TemplateTransformations.java     |   2 +-
 .../freemarker/generator/base/uri/NamedUri.java    |   2 +-
 .../generator/base/uri/NamedUriStringParser.java   |   6 +-
 .../generator/base/util/MapFlattener.java          |   2 +-
 .../generator/base/util/StringUtils.java           |   1 -
 .../freemarker/generator/base/util/UriUtils.java   |  31 +++--
 .../datasource/DataSourceFactoryTest.java          |   8 +-
 .../generator/datasource/DataSourceTest.java       |   9 +-
 .../datasource/DataSourcesSupplierTest.java        |  12 +-
 .../generator/datasource/DataSourcesTest.java      |  24 ++--
 .../generator/mime/MimetypeParserTest.java         |  32 ++---
 .../generator/uri/NamedUriStringParserTest.java    |   8 +-
 freemarker-generator-cli/CHANGELOG.md              |   4 +-
 .../examples/data/ftl/nginx/nginx.conf.ftl         |   2 +-
 .../templates/accesslog/combined-access.ftl        |   4 +-
 .../templates/csv/csv/gatling-user-credentials.ftl |   6 +-
 .../examples/templates/csv/fo/transactions.ftl     |   6 +-
 .../examples/templates/csv/fo/transform.ftl        |   4 +-
 .../examples/templates/csv/html/transactions.ftl   |   6 +-
 .../examples/templates/csv/md/filter.ftl           |  12 +-
 .../examples/templates/csv/shell/curl.ftl          |   6 +-
 .../examples/templates/dataframe/example.ftl       |  20 +--
 .../examples/templates/dataframe/html/print.ftl    |   6 +-
 .../examples/templates/demo.ftl                    |  92 +++++++-------
 .../examples/templates/excel/csv/custom.ftl        |  16 +--
 .../templates/excel/dataframe/transform.ftl        |  12 +-
 .../examples/templates/html/csv/dependencies.ftl   |   4 +-
 .../templates/json/csv/swagger-endpoints.ftl       |   2 +-
 .../templates/json/dataframe/github-users.ftl      |   6 +-
 .../examples/templates/json/md/github-users.ftl    |   2 +-
 .../templates/properties/csv/locker-test-users.ftl |   4 +-
 .../examples/templates/tsv/fo/transactions.ftl     |   4 +-
 .../examples/templates/xml/txt/recipients.ftl      |   2 +-
 .../examples/templates/yaml/txt/transform.ftl      |   2 +-
 freemarker-generator-cli/run-examples.bat          |  12 +-
 freemarker-generator-cli/run-examples.sh           |  12 +-
 .../src/main/config/freemarker-cli.properties      |  28 ++---
 .../cli/config/ConfigurationSupplier.java          |   4 +
 .../generator/cli/config/ToolsSupplier.java        |  13 ++
 .../generator/cli/model/DataSourcesModel.java      |  40 ++++++
 .../cli/model/GeneratorObjectWrapper.java          |  25 ++++
 .../generator/cli/task/FreeMarkerTask.java         |   2 +-
 .../src/main/resources/freemarker-cli.properties   |  28 ++---
 .../src/main/scripts/run-examples.bat              |  12 +-
 .../src/main/scripts/run-examples.sh               |  12 +-
 .../markdown/cli/advanced/cli-configuration.md     |  28 ++---
 .../src/site/markdown/cli/concepts/data-models.md  |   8 +-
 .../src/site/markdown/cli/concepts/data-sources.md |  35 +++---
 .../src/site/markdown/cli/concepts/named-uris.md   |   8 +-
 .../site/markdown/cli/concepts/template-loading.md |   4 +-
 .../site/markdown/cli/usage/running-examples.md    | 135 ++++++++++-----------
 .../site/markdown/cli/usage/transforming-csv.md    |   6 +-
 .../markdown/cli/usage/transforming-directories.md |   8 +-
 .../site/markdown/cli/usage/using-dataframes.md    |  32 ++---
 .../freemarker/generator/cli/ExamplesTest.java     |  47 +++++--
 .../freemarker/generator/cli/ManualTest.java       |   3 +-
 .../cli/config/ConfigurationSupplierTest.java      |   2 +-
 .../src/test/templates/echo.ftl                    |   6 +-
 .../src/test/templates/manual.ftl                  |  49 +++++++-
 .../src/test/templates/tools/csv.ftl               |  18 +--
 freemarker-generator-cli/templates/cat.ftl         |   2 +-
 .../templates/csv/csv/transform.ftl                |   6 +-
 .../templates/csv/html/transform.ftl               |   4 +-
 .../templates/csv/md/transform.ftl                 |   4 +-
 .../templates/excel/csv/transform.ftl              |  12 +-
 .../templates/excel/html/transform.ftl             |   8 +-
 .../templates/excel/md/transform.ftl               |   8 +-
 freemarker-generator-cli/templates/info.ftl        |  28 ++---
 .../templates/json/yaml/transform.ftl              |   2 +-
 .../templates/lib/commons-csv.ftl                  |   8 +-
 .../templates/yaml/json/transform.ftl              |   2 +-
 freemarker-generator-maven-plugin/README.md        |   4 +-
 .../src/site/markdown/index.md                     |   4 +-
 .../generator/tools/dataframe/DataFrameTool.java   |   1 +
 .../tools/dataframe/impl/ListConverter.java        |   2 +-
 src/site/markdown/index.md                         |   2 +-
 travis.sh                                          |   2 +-
 87 files changed, 743 insertions(+), 533 deletions(-)

diff --git a/README.md b/README.md
index 1db8027..d0c6e3d 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ Currently it can be invoked as a
 Building Apache FreeMarker Generator
 -----------------------------------------------------------------------------
 
-To create the artefacts locally run
+To create the artifacts locally run
 
 > mvn clean install
 
diff --git a/freemarker-generator-base/README.md b/freemarker-generator-base/README.md
index 4a5c119..1b8a0c4 100644
--- a/freemarker-generator-base/README.md
+++ b/freemarker-generator-base/README.md
@@ -4,7 +4,7 @@ Apache FreeMarker Generator Base
 This module provides common functionality for `freemarker-generator-cli` and `freemarker-generator-maven-plugin` such as
 
 * Various implementation of `javax.activation.DataSources`
-* Implementation of `DataSource` and utitity methods
+* Implementation of `DataSource` and utility methods
 * Creating `DataSources` 
 
 The code actually does not depend on Apache FreeMarker since it useful for other command line tools as well.
\ No newline at end of file
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/FreeMarkerConstants.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/FreeMarkerConstants.java
index 584fcbd..239c80e 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/FreeMarkerConstants.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/FreeMarkerConstants.java
@@ -41,7 +41,7 @@ public class FreeMarkerConstants {
 
     /* Default group name for data sources */
     public static final String DEFAULT_GROUP = "default";
-    
+
     public static class Configuration {
 
         private Configuration() {
@@ -77,7 +77,8 @@ public class FreeMarkerConstants {
         private Model() {
         }
 
-        public static final String DATASOURCES = "DataSources";
+        public static final String DATASOURCES = "dataSources";
+        public static final String TOOLS = "tools";
 
         public static final String FREEMARKER_CLI_ARGS = "freemarker.cli.args";
         public static final String FREEMARKER_LOCALE = "freemarker.locale";
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 0b82014..be9934c 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
@@ -21,7 +21,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.LineIterator;
 import org.apache.freemarker.generator.base.activation.ByteArrayDataSource;
 import org.apache.freemarker.generator.base.activation.StringDataSource;
-import org.apache.freemarker.generator.base.mime.MimetypeParser;
+import org.apache.freemarker.generator.base.mime.MimeTypeParser;
 import org.apache.freemarker.generator.base.util.CloseableReaper;
 import org.apache.freemarker.generator.base.util.StringUtils;
 import org.apache.freemarker.generator.base.util.Validate;
@@ -49,9 +49,21 @@ import static org.apache.freemarker.generator.base.mime.Mimetypes.MIME_APPLICATI
  * There is also special support of <code>UrlDataSource</code> since
  * the content type &amp; charset might be determined using a network
  * call.
+ * <br>
+ * The implementation makes no assumption if the underlying input
+ * stream can be consumed more than once.
  */
 public class DataSource implements Closeable, javax.activation.DataSource {
 
+    public static final String METADATA_BASE_NAME = "baseName";
+    public static final String METADATA_EXTENSION = "extension";
+    public static final String METADATA_FILE_NAME = "fileName";
+    public static final String METADATA_FILE_PATH = "filePath";
+    public static final String METADATA_GROUP = "group";
+    public static final String METADATA_NAME = "name";
+    public static final String METADATA_URI = "uri";
+    public static final String METADATA_URI_PATH = "uriPath";
+
     /** Human-readable name of the data source */
     private final String name;
 
@@ -64,14 +76,14 @@ public class DataSource implements Closeable, javax.activation.DataSource {
     /** The underlying "javax.activation.DataSource" */
     private final javax.activation.DataSource dataSource;
 
-    /** Optional content type */
+    /** Content type of data source either provided by the user or fetched directly from the data source */
     private final String contentType;
 
-    /** Optional charset for directly accessing text-based content */
+    /** Charset for directly accessing text-based content */
     private final Charset charset;
 
-    /** Collect all closables handed out to the caller to be closed when the data source is closed itself */
-    private final CloseableReaper closables;
+    /** Collect all closeables handed out to the caller to be closed when the data source is closed itself */
+    private final CloseableReaper closeables;
 
     public DataSource(
             String name,
@@ -86,7 +98,7 @@ public class DataSource implements Closeable, javax.activation.DataSource {
         this.dataSource = requireNonNull(dataSource);
         this.contentType = contentType;
         this.charset = charset;
-        this.closables = new CloseableReaper();
+        this.closeables = new CloseableReaper();
     }
 
     @Override
@@ -111,7 +123,7 @@ public class DataSource implements Closeable, javax.activation.DataSource {
      */
     @Override
     public InputStream getInputStream() {
-        return closables.add(getUnsafeInputStream());
+        return closeables.add(getUnsafeInputStream());
     }
 
     @Override
@@ -121,32 +133,41 @@ public class DataSource implements Closeable, javax.activation.DataSource {
 
     @Override
     public void close() {
-        closables.close();
+        closeables.close();
     }
 
     public String getGroup() {
         return group;
     }
 
+    public String getFileName() {
+        return FilenameUtils.getName(name);
+    }
+
     public String getBaseName() {
-        return FilenameUtils.getBaseName(name);
+        return FilenameUtils.getBaseName(getFileName());
     }
 
     public String getExtension() {
-        return FilenameUtils.getExtension(name);
+        return FilenameUtils.getExtension(getFileName());
     }
 
+    /**
+     * Get the charset. If no charset can be detected UTF-8 is assumed.
+     *
+     * @return charset
+     */
     public Charset getCharset() {
-        return charset != null ? charset : MimetypeParser.getCharset(contentType(), UTF_8);
+        return charset != null ? charset : MimeTypeParser.getCharset(contentType(), UTF_8);
     }
 
     /**
-     * Get the mimetype , i.e. content type without additional charset parameter.
+     * Get the mime type , i.e. content type without additional charset parameter.
      *
-     * @return mimetype
+     * @return mime type
      */
-    public String getMimetype() {
-        return MimetypeParser.getMimetype(contentType());
+    public String getMimeType() {
+        return MimeTypeParser.getMimeType(contentType());
     }
 
     public URI getUri() {
@@ -226,8 +247,8 @@ public class DataSource implements Closeable, javax.activation.DataSource {
 
     /**
      * Returns an Iterator for the lines in an <code>InputStream</code>, using
-     * the default character encoding specified. The caller is responsible to close
-     * the line iterator.
+     * the default character encoding specified. The exposed iterator is closed
+     * by the <code>DataSource</code>.
      *
      * @return line iterator
      */
@@ -237,7 +258,8 @@ public class DataSource implements Closeable, javax.activation.DataSource {
 
     /**
      * Returns an Iterator for the lines in an <code>InputStream</code>, using
-     * the character encoding specified.
+     * the character encoding specified. The exposed iterator is closed
+     * by the <code>DataSource</code>.
      *
      * @param charsetName The name of the requested charset
      * @return line iterator
@@ -245,7 +267,7 @@ public class DataSource implements Closeable, javax.activation.DataSource {
     public LineIterator getLineIterator(String charsetName) {
         Validate.notEmpty(charsetName, "No charset name provided");
         try {
-            return closables.add(IOUtils.lineIterator(getUnsafeInputStream(), Charset.forName(charsetName)));
+            return closeables.add(IOUtils.lineIterator(getUnsafeInputStream(), Charset.forName(charsetName)));
         } catch (IOException e) {
             throw new RuntimeException("Failed to create line iterator: " + toString(), e);
         }
@@ -260,15 +282,45 @@ public class DataSource implements Closeable, javax.activation.DataSource {
     }
 
     /**
-     * Matches a metadata entry with a wildcard expression.
+     * Expose various parts of the metadata as simple strings to cater for filtering in  a script.
+     *
+     * @param key key part key
+     * @return value
+     */
+    public String getMetadata(String key) {
+        Validate.notEmpty(key, "No key provided");
+        switch (key) {
+            case METADATA_BASE_NAME:
+                return getBaseName();
+            case METADATA_EXTENSION:
+                return getExtension();
+            case METADATA_FILE_NAME:
+                return getFileName();
+            case METADATA_FILE_PATH:
+                return FilenameUtils.getFullPathNoEndSeparator(uri.getPath());
+            case METADATA_GROUP:
+                return getGroup();
+            case METADATA_NAME:
+                return getName();
+            case METADATA_URI_PATH:
+                return uri.getPath();
+            case METADATA_URI:
+                return uri.toString();
+            default:
+                throw new IllegalArgumentException("Unknown key: " + key);
+        }
+    }
+
+    /**
+     * Matches a metadata key with a wildcard expression.
      *
-     * @param part     part, e.g. "name", "basename", "extension", "uri", "group"
+     * @param key      metadata key, e.g. "name", "fileName", "baseName", "extension", "uri", "group"
      * @param wildcard the wildcard string to match against
      * @return true if the wildcard expression matches
      * @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 boolean match(String part, String wildcard) {
-        final String value = getPart(part);
+    public boolean match(String key, String wildcard) {
+        final String value = getMetadata(key);
         return FilenameUtils.wildcardMatch(value, wildcard);
     }
 
@@ -282,7 +334,7 @@ public class DataSource implements Closeable, javax.activation.DataSource {
      * @return Closable
      */
     public <T extends Closeable> T addClosable(T closeable) {
-        return closables.add(closeable);
+        return closeables.add(closeable);
     }
 
     @Override
@@ -296,7 +348,7 @@ public class DataSource implements Closeable, javax.activation.DataSource {
 
     /**
      * If there is no content type we ask the underlying data source. E.g. for
-     * an URL data source this information is fetched from the server.
+     * an URL data source this information is fetched from the remote server.
      *
      * @return content type
      */
@@ -307,30 +359,4 @@ public class DataSource implements Closeable, javax.activation.DataSource {
             return StringUtils.firstNonEmpty(dataSource.getContentType(), MIME_APPLICATION_OCTET_STREAM);
         }
     }
-
-    private String getPart(String part) {
-        Validate.notEmpty(part, "No metadata part provided");
-        switch (part.toLowerCase()) {
-            case "basename":
-                return getBaseName();
-            case "contenttype":
-                return getContentType();
-            case "extension":
-                return getExtension();
-            case "group":
-                return getGroup();
-            case "mimetype":
-                return getContentType();
-            case "name":
-                return getName();
-            case "path":
-                return uri.getPath();
-            case "scheme":
-                return uri.getScheme();
-            case "uri":
-                return uri.toASCIIString();
-            default:
-                throw new IllegalArgumentException("Unknown part: " + part);
-        }
-    }
 }
\ No newline at end of file
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceFactory.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceFactory.java
index ed69fff..0c9055c 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceFactory.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourceFactory.java
@@ -75,13 +75,13 @@ public abstract class DataSourceFactory {
         final Charset charset = getCharsetOrElse(namedUri, NO_CHARSET);
         final String mimeType = getMimeTypeOrElse(namedUri, NO_MIME_TYPE);
 
-        if (UriUtils.isHttpURI(uri)) {
-            final URL url = toURL(uri);
-            final String name = namedUri.getNameOrElse(url.getHost());
+        if (UriUtils.isHttpUri(uri)) {
+            final URL url = toUrl(uri);
+            final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(uri));
             return fromUrl(name, group, url, mimeType, charset);
         } else if (UriUtils.isFileUri(uri)) {
             final File file = namedUri.getFile();
-            final String name = namedUri.getNameOrElse(file.getName());
+            final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(file.toURI()));
             return fromFile(name, group, file, charset);
         } else if (UriUtils.isEnvUri(uri)) {
             // environment variables come with a leading "/" to be removed
@@ -96,39 +96,31 @@ public abstract class DataSourceFactory {
         } else {
             // handle things such as "foo=some.file"
             final File file = namedUri.getFile();
-            final String name = namedUri.getNameOrElse(file.getName());
+            final String name = namedUri.getNameOrElse(UriUtils.toStringWithoutFragment(file.toURI()));
             return fromFile(name, group, file, charset);
         }
     }
 
     // == URL ===============================================================
 
-    public static DataSource fromUrl(String name, String group, URL url, Charset charset) {
-        return fromUrl(name, group, url, NO_MIME_TYPE, charset);
-    }
-
     public static DataSource fromUrl(String name, String group, URL url, String contentType, Charset charset) {
         final URLDataSource dataSource = new CachingUrlDataSource(url);
-        final URI uri = UriUtils.toURI(url);
+        final URI uri = UriUtils.toUri(url);
         return create(name, group, uri, dataSource, contentType, charset);
     }
 
     // == String ============================================================
 
-    public static DataSource fromString(String content, String contentType) {
-        return fromString(Location.STRING, DEFAULT_GROUP, content, contentType);
-    }
-
     public static DataSource fromString(String name, String group, String content, String contentType) {
         final StringDataSource dataSource = new StringDataSource(name, content, contentType, UTF_8);
-        final URI uri = UriUtils.toURI(Location.STRING, UUID.randomUUID().toString());
+        final URI uri = UriUtils.toUri(Location.STRING, UUID.randomUUID().toString());
         return create(name, group, uri, dataSource, contentType, UTF_8);
     }
 
     // == File ==============================================================
 
     public static DataSource fromFile(File file, Charset charset) {
-        return fromFile(file.getName(), DEFAULT_GROUP, file, charset);
+        return fromFile(UriUtils.toStringWithoutFragment(file.toURI()), DEFAULT_GROUP, file, charset);
     }
 
     public static DataSource fromFile(String name, String group, File file, Charset charset) {
@@ -144,7 +136,7 @@ public abstract class DataSourceFactory {
 
     public static DataSource fromBytes(String name, String group, byte[] content, String contentType) {
         final ByteArrayDataSource dataSource = new ByteArrayDataSource(name, content);
-        final URI uri = UriUtils.toURI(Location.BYTES + ":///");
+        final URI uri = UriUtils.toUri(Location.BYTES + ":///");
         return create(name, group, uri, dataSource, contentType, UTF_8);
     }
 
@@ -152,7 +144,7 @@ public abstract class DataSourceFactory {
 
     public static DataSource fromInputStream(String name, String group, InputStream is, String contentType, Charset charset) {
         final InputStreamDataSource dataSource = new InputStreamDataSource(name, is);
-        final URI uri = UriUtils.toURI(Location.INPUTSTREAM + ":///");
+        final URI uri = UriUtils.toUri(Location.INPUTSTREAM + ":///");
         return create(name, group, uri, dataSource, contentType, charset);
     }
 
@@ -169,7 +161,7 @@ public abstract class DataSourceFactory {
             final StringWriter writer = new StringWriter();
             properties.store(writer, null);
             final StringDataSource dataSource = new StringDataSource(name, writer.toString(), contentType, UTF_8);
-            final URI uri = UriUtils.toURI(Location.ENVIRONMENT, "");
+            final URI uri = UriUtils.toUri(Location.ENVIRONMENT, "");
             return create(name, group, uri, dataSource, contentType, UTF_8);
         } catch (IOException e) {
             throw new RuntimeException(e);
@@ -180,7 +172,7 @@ public abstract class DataSourceFactory {
         Validate.notEmpty(System.getenv(key), "Environment variable not found: " + key);
 
         final StringDataSource dataSource = new StringDataSource(name, System.getenv(key), contentType, UTF_8);
-        final URI uri = UriUtils.toURI(Location.ENVIRONMENT, key);
+        final URI uri = UriUtils.toUri(Location.ENVIRONMENT, key);
         return create(name, group, uri, dataSource, contentType, UTF_8);
     }
 
@@ -225,7 +217,7 @@ public abstract class DataSourceFactory {
         return StringUtils.isEmpty(charsetName) ? def : Charset.forName(charsetName);
     }
 
-    private static URL toURL(URI uri) {
+    private static URL toUrl(URI uri) {
         try {
             return uri.toURL();
         } catch (MalformedURLException e) {
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 1981b74..5af23bf 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,9 +23,12 @@ import org.apache.freemarker.generator.base.util.Validate;
 import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
-import static java.util.stream.Collectors.toList;
+import static java.util.function.Function.identity;
 
 /**
  * Container for data sources with a couple of convenience functions to select
@@ -49,12 +52,23 @@ public class DataSources implements Closeable {
     public List<String> getNames() {
         return dataSources.stream()
                 .map(DataSource::getName)
-                .filter(StringUtils::isNotEmpty)
-                .collect(toList());
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Get the given metadata value for all data sources.
+     *
+     * @param key key of the metadata part
+     * @return list of metadata values
+     */
+    public List<String> getMetadata(String key) {
+        return dataSources.stream()
+                .map(ds -> ds.getMetadata(key))
+                .collect(Collectors.toList());
     }
 
     /**
-     * Get the groups of all data sources.
+     * Get the unique groups of all data sources.
      *
      * @return data source names
      */
@@ -64,7 +78,7 @@ public class DataSources implements Closeable {
                 .filter(StringUtils::isNotEmpty)
                 .sorted()
                 .distinct()
-                .collect(toList());
+                .collect(Collectors.toList());
     }
 
     public int size() {
@@ -75,10 +89,27 @@ public class DataSources implements Closeable {
         return dataSources.isEmpty();
     }
 
-    public List<DataSource> getList() {
+    /**
+     * Get a list representation of the underlying data sources.
+     *
+     * @return list of data sources
+     */
+    public List<DataSource> toList() {
         return new ArrayList<>(dataSources);
     }
 
+    /**
+     * Get a map representation of the underlying data sources.
+     *
+     * @return map of data sources
+     */
+    public Map<String, DataSource> toMap() {
+        return dataSources.stream().collect(Collectors.toMap(DataSource::getName,
+                identity(),
+                (v1, v2) -> v1,
+                LinkedHashMap::new));
+    }
+
     public DataSource get(int index) {
         return dataSources.get(index);
     }
@@ -105,7 +136,7 @@ public class DataSources implements Closeable {
     }
 
     /**
-     * Find data sources based on their name and a wildcard.
+     * 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
@@ -114,21 +145,21 @@ public class DataSources implements Closeable {
     public List<DataSource> find(String wildcard) {
         return dataSources.stream()
                 .filter(dataSource -> dataSource.match("name", wildcard))
-                .collect(toList());
+                .collect(Collectors.toList());
     }
 
     /**
-     * Find data sources based on their metadata part and wildcard.
+     * Find data sources based on their metadata key and wildcard string.
      *
-     * @param part part of metadata to match
+     * @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 List<DataSource> find(String part, String wildcard) {
+    public List<DataSource> find(String key, String wildcard) {
         return dataSources.stream()
-                .filter(dataSource -> dataSource.match(part, wildcard))
-                .collect(toList());
+                .filter(dataSource -> dataSource.match(key, wildcard))
+                .collect(Collectors.toList());
     }
 
     @Override
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourcesSupplier.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourcesSupplier.java
index 6e741e3..cbf651b 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourcesSupplier.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/datasource/DataSourcesSupplier.java
@@ -19,6 +19,7 @@ package org.apache.freemarker.generator.base.datasource;
 import org.apache.freemarker.generator.base.file.RecursiveFileSupplier;
 import org.apache.freemarker.generator.base.uri.NamedUri;
 import org.apache.freemarker.generator.base.uri.NamedUriStringParser;
+import org.apache.freemarker.generator.base.util.UriUtils;
 import org.apache.freemarker.generator.base.util.Validate;
 
 import java.io.File;
@@ -135,7 +136,7 @@ public class DataSourcesSupplier implements Supplier<List<DataSource>> {
         if (namedUri.hasName()) {
             return namedUri.getName();
         } else {
-            return file.getName();
+            return UriUtils.toStringWithoutFragment(file.toURI());
         }
     }
 }
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimetypeParser.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimeTypeParser.java
similarity index 94%
rename from freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimetypeParser.java
rename to freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimeTypeParser.java
index 46b82a6..5d41cd0 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimetypeParser.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/mime/MimeTypeParser.java
@@ -21,7 +21,7 @@ import java.nio.charset.Charset;
 import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty;
 
 /**
- * Parse a mimetype.
+ * Parse a mime type.
  * <p>
  * Some examples
  * <ul>
@@ -29,9 +29,9 @@ import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty;
  *     <li>text/html;charset=windows-1252</li>
  * </ul>
  */
-public class MimetypeParser {
+public class MimeTypeParser {
 
-    public static String getMimetype(String raw) {
+    public static String getMimeType(String raw) {
         if (isEmpty(raw)) {
             return null;
         }
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/parameter/Parameter.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/parameter/Parameter.java
index 707a038..da3e1d8 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/parameter/Parameter.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/parameter/Parameter.java
@@ -21,7 +21,7 @@ import static java.util.Objects.requireNonNull;
 import static org.apache.freemarker.generator.base.util.StringUtils.isNotEmpty;
 
 /**
- * Caputeres the information of a user-supplied parameter.
+ * Captures the information of a user-supplied parameter.
  */
 public class Parameter {
 
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
index c12d35a..fb50b2a 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
@@ -78,7 +78,7 @@ public class TemplateSource {
     /**
      * Template will be loaded from path using a file-base template loader.
      *
-     * @param path template path
+     * @param path     template path
      * @param encoding character encoding og template
      * @return file-based template source
      */
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java
index 6c09b60..e41162d 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java
@@ -24,7 +24,7 @@ import static java.util.Objects.requireNonNull;
 
 /**
  * Keeps track of all transformations being executed.
- */ 
+ */
 public class TemplateTransformations {
 
     private final List<TemplateTransformation> templateTransformations;
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUri.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUri.java
index 1b1c63e..08b7253 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUri.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUri.java
@@ -30,7 +30,7 @@ import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty;
 public class NamedUri {
 
     public static final String CHARSET = "charset";
-    public static final String MIMETYPE = "mimetype";
+    public static final String MIMETYPE = "mimeType";
 
     /** User-supplied name */
     private final String name;
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUriStringParser.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUriStringParser.java
index 6aabbd0..79e8a94 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUriStringParser.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/uri/NamedUriStringParser.java
@@ -33,7 +33,7 @@ import static java.util.regex.Pattern.compile;
  *     <li>file:///users.csv</li>
  *     <li>users=file:///users.csv</li>
  *     <li>users:admin=file:///users.csv</li>
- *     <li>users=file:///users.csv#charset=UTF-16&amp;mimetype=text/csv</li>
+ *     <li>users=file:///users.csv#charset=UTF-16&amp;mimeType=text/csv</li>
  * </ul>
  */
 public class NamedUriStringParser {
@@ -52,10 +52,10 @@ public class NamedUriStringParser {
         if (matcher.matches()) {
             final String name = matcher.group(NAME);
             final String group = matcher.group(GROUP);
-            final URI uri = UriUtils.toURI(matcher.group(URI));
+            final URI uri = UriUtils.toUri(matcher.group(URI));
             return new NamedUri(name, group, uri, parameters(uri));
         } else {
-            final URI uri = UriUtils.toURI(value);
+            final URI uri = UriUtils.toUri(value);
             return new NamedUri(uri, parameters(uri));
         }
     }
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java
index 80f5dd3..c1ad75a 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java
@@ -108,7 +108,7 @@ public abstract class MapFlattener {
         }
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     private static void flattenElement(String propertyPrefix, Object source,
                                        Map<String, ?> resultMap, Function<Object, Object> valueTransformer) {
 
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/StringUtils.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/StringUtils.java
index 3e480fa..c3eabf1 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/StringUtils.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/StringUtils.java
@@ -51,5 +51,4 @@ public class StringUtils {
         }
         return count;
     }
-
 }
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
index 679f299..fab27e0 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
@@ -16,17 +16,16 @@
  */
 package org.apache.freemarker.generator.base.util;
 
-import org.apache.commons.io.FilenameUtils;
-
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 
+import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
 import static org.apache.freemarker.generator.base.util.StringUtils.isNotEmpty;
 
 public class UriUtils {
 
-    public static URI toURI(String str) {
+    public static URI toUri(String str) {
         try {
             return new URI(separatorsToUnix(str));
         } catch (URISyntaxException e) {
@@ -34,19 +33,31 @@ public class UriUtils {
         }
     }
 
-    public static URI toURI(String scheme, String path) {
-        return toURI(scheme + ":///" + path);
+    public static URI toUri(String scheme, String path) {
+        return toUri(scheme + ":///" + path);
+    }
+
+    public static URI toUri(URL url) {
+        return toUri(url.toString());
     }
 
-    public static URI toURI(URL url) {
-        return toURI(url.toString());
+    /**
+     * Return the URI string representation without fragment part.
+     *
+     * @param uri uri
+     * @return string representation of URI without fragment part
+     */
+    public static String toStringWithoutFragment(URI uri) {
+        final String str = uri.toString();
+        final int index = str.indexOf('#');
+        return (index > 0) ? str.substring(0, index) : str;
     }
 
     public static boolean isUri(String str) {
         return isNotEmpty(str) && str.contains("://");
     }
 
-    public static boolean isHttpURI(URI uri) {
+    public static boolean isHttpUri(URI uri) {
         if (uri == null) {
             return false;
         }
@@ -66,8 +77,4 @@ public class UriUtils {
         }
         return "env".equalsIgnoreCase(uri.getScheme());
     }
-
-    private static String separatorsToUnix(String str) {
-        return FilenameUtils.separatorsToUnix(str);
-    }
 }
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceFactoryTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceFactoryTest.java
index ce1ed61..0610e2d 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceFactoryTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceFactoryTest.java
@@ -53,7 +53,7 @@ public class DataSourceFactoryTest {
     public void shouldCreateDataSourceFromFile() {
         final DataSource dataSource = DataSourceFactory.fromFile(ANY_FILE, ANY_CHAR_SET);
 
-        assertEquals(ANY_FILE_NAME, dataSource.getName());
+        assertEquals(ANY_FILE_NAME, dataSource.getFileName());
         assertEquals(UTF_8, dataSource.getCharset());
         assertEquals(MIME_APPLICATION_XML, dataSource.getContentType());
         assertEquals(ANY_FILE.toURI(), dataSource.getUri());
@@ -64,7 +64,7 @@ public class DataSourceFactoryTest {
     public void shouldCreateDataSourceFromFileUri() {
         final DataSource dataSource = DataSourceFactory.create(ANY_FILE_URI);
 
-        assertEquals(ANY_FILE_NAME, dataSource.getName());
+        assertEquals(ANY_FILE_NAME, dataSource.getFileName());
         assertEquals(UTF_8, dataSource.getCharset());
         assertEquals(MIME_APPLICATION_XML, dataSource.getContentType());
         assertEquals(ANY_FILE.toURI(), dataSource.getUri());
@@ -133,7 +133,7 @@ public class DataSourceFactoryTest {
     }
 
     @Test
-    public void shouldCreateDataSourceFromEnviroment() {
+    public void shouldCreateDataSourceFromEnvironment() {
         final NamedUri namedUri = NamedUriStringParser.parse("env:///");
         final DataSource dataSource = DataSourceFactory.fromNamedUri(namedUri);
 
@@ -145,7 +145,7 @@ public class DataSourceFactoryTest {
     }
 
     @Test
-    public void shouldCreateDataSourceFromNamedEnviroment() {
+    public void shouldCreateDataSourceFromNamedEnvironment() {
         final NamedUri namedUri = NamedUriStringParser.parse("config=env:///");
         final DataSource dataSource = DataSourceFactory.fromNamedUri(namedUri);
 
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceTest.java
index 1992c09..155b778 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourceTest.java
@@ -64,7 +64,7 @@ public class DataSourceTest {
     @Test
     public void shouldSupportFileDataSource() {
         try (DataSource dataSource = DataSourceFactory.fromFile(ANY_FILE, ANY_CHAR_SET)) {
-            assertEquals(ANY_FILE_NAME, dataSource.getName());
+            assertEquals(ANY_FILE_NAME, dataSource.getFileName());
             assertEquals(DEFAULT_GROUP, dataSource.getGroup());
             assertEquals("pom", dataSource.getBaseName());
             assertEquals("xml", dataSource.getExtension());
@@ -73,11 +73,10 @@ public class DataSourceTest {
             assertEquals("application/xml", dataSource.getContentType());
             assertTrue(dataSource.getLength() > 0);
             assertFalse(dataSource.getText().isEmpty());
-            assertTrue(dataSource.match("name", ANY_FILE_NAME));
+            assertTrue(dataSource.match("name", "*" + ANY_FILE_NAME));
             assertTrue(dataSource.match("uri", "file:/*/pom.xml"));
             assertTrue(dataSource.match("extension", "xml"));
-            assertTrue(dataSource.match("basename", "pom"));
-            assertTrue(dataSource.match("path", "*/pom.xml"));
+            assertTrue(dataSource.match("baseName", "pom"));
         }
     }
 
@@ -90,7 +89,7 @@ public class DataSourceTest {
             assertEquals("www.google", dataSource.getBaseName());
             assertEquals("com", dataSource.getExtension());
             assertEquals("https://www.google.com/?foo=bar", dataSource.getUri().toString());
-            assertEquals(MIME_TEXT_HTML, dataSource.getMimetype());
+            assertEquals(MIME_TEXT_HTML, dataSource.getMimeType());
             assertEquals("ISO-8859-1", dataSource.getCharset().name());
             assertEquals(-1, dataSource.getLength());
             assertFalse(dataSource.getText().isEmpty());
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesSupplierTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesSupplierTest.java
index 9944681..fddc597 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesSupplierTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/datasource/DataSourcesSupplierTest.java
@@ -43,9 +43,9 @@ public class DataSourcesSupplierTest {
         assertEquals(1, supplier("./pom.xml", "*", NO_EXCLUDE).get().size());
         assertEquals(1, supplier("pom=pom.xml", "*", NO_EXCLUDE).get().size());
         assertEquals(1, supplier("pom=./pom.xml", "*", NO_EXCLUDE).get().size());
-        assertEquals(1, supplier("pom=./pom.xml#mimetype=application/xml", "*", NO_EXCLUDE).get().size());
+        assertEquals(1, supplier("pom=./pom.xml#mimeType=application/xml", "*", NO_EXCLUDE).get().size());
         assertEquals(1, supplier("pom=" + PWD + "/pom.xml", "*", NO_EXCLUDE).get().size());
-        assertEquals(1, supplier("pom=file:///" + PWD + "/pom.xml#mimetype=application/xml", "*", NO_EXCLUDE).get()
+        assertEquals(1, supplier("pom=file:///" + PWD + "/pom.xml#mimeType=application/xml", "*", NO_EXCLUDE).get()
                 .size());
     }
 
@@ -101,7 +101,7 @@ public class DataSourcesSupplierTest {
         final DataSource dataSource = dataSources.get(0);
 
         assertEquals(1, dataSources.size());
-        assertEquals("test.properties", dataSource.getName());
+        assertEquals("test.properties", dataSource.getFileName());
         assertTrue(dataSource.getUri().getPath().contains("src/test/data/properties/test.properties"));
     }
 
@@ -130,9 +130,9 @@ public class DataSourcesSupplierTest {
 
     @Test
     public void shouldNormalizeDataSourceNameBasedOnFilePath() {
-        assertEquals("pom.xml", supplier("pom.xml", "*", NO_EXCLUDE).get().get(0).getName());
-        assertEquals("pom.xml", supplier("./pom.xml", "*", NO_EXCLUDE).get().get(0).getName());
-        assertEquals("pom.xml", supplier("file:///" + PWD + "/pom.xml", "*", NO_EXCLUDE).get().get(0).getName());
+        assertEquals("pom.xml", supplier("pom.xml", "*", NO_EXCLUDE).get().get(0).getFileName());
+        assertEquals("pom.xml", supplier("./pom.xml", "*", NO_EXCLUDE).get().get(0).getFileName());
+        assertEquals("pom.xml", supplier("file:///" + PWD + "/pom.xml", "*", NO_EXCLUDE).get().get(0).getFileName());
     }
 
     private static DataSourcesSupplier supplier(String directory, String include, String exclude) {
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 5639adf..d3cdf45 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
@@ -53,13 +53,11 @@ public class DataSourcesTest {
 
         assertEquals(2, dataSources.find("*.*").size());
         assertEquals(1, dataSources.find("*." + ANY_FILE_EXTENSION).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(1, dataSources.find(ANY_FILE_NAME).size());
-        assertEquals(1, dataSources.find(ANY_FILE_NAME.charAt(0) + "*").size());
-
         assertEquals(3, dataSources.find("*").size());
     }
 
@@ -81,24 +79,30 @@ public class DataSourcesTest {
 
     @Test
     public void shouldGetDataSource() {
-        assertNotNull(dataSources().get(ANY_FILE_NAME));
+        assertNotNull(dataSources().get("*/" + ANY_FILE_NAME));
     }
 
     @Test
     public void shouldGetAllDataSource() {
         final DataSources dataSources = dataSources();
 
-        assertEquals("unknown", dataSources().get(0).getName());
-        assertEquals("pom.xml", dataSources().get(1).getName());
-        assertEquals("server.invalid", dataSources().get(2).getName());
-        assertEquals(3, dataSources.getList().size());
+        assertEquals("unknown", dataSources.get(0).getFileName());
+        assertEquals("pom.xml", dataSources.get(1).getFileName());
+        assertEquals("server.invalid?foo=bar", dataSources.get(2).getFileName());
+        assertEquals(3, dataSources.toList().size());
+        assertEquals(3, dataSources.toMap().size());
         assertEquals(3, dataSources.size());
         assertFalse(dataSources.isEmpty());
     }
 
     @Test
-    public void shouldGetNames() {
-        assertEquals(asList("unknown", "pom.xml", "server.invalid"), dataSources().getNames());
+    public void shouldGetParts() {
+        assertEquals(3, dataSources().getMetadata("name").size());
+    }
+
+    @Test
+    public void shouldGetFileNamePart() {
+        assertEquals(asList("unknown", "pom.xml", "server.invalid?foo=bar"), dataSources().getMetadata("fileName"));
     }
 
     @Test
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/mime/MimetypeParserTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/mime/MimetypeParserTest.java
index f682e13..a05a636 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/mime/MimetypeParserTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/mime/MimetypeParserTest.java
@@ -16,7 +16,7 @@
  */
 package org.apache.freemarker.generator.mime;
 
-import org.apache.freemarker.generator.base.mime.MimetypeParser;
+import org.apache.freemarker.generator.base.mime.MimeTypeParser;
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
@@ -28,32 +28,32 @@ public class MimetypeParserTest {
 
     @Test
     public void shouldHandleMissingMimeType() {
-        assertNull(MimetypeParser.getMimetype(null));
-        assertNull(MimetypeParser.getMimetype(""));
-        assertNull(MimetypeParser.getMimetype(" "));
+        assertNull(MimeTypeParser.getMimeType(null));
+        assertNull(MimeTypeParser.getMimeType(""));
+        assertNull(MimeTypeParser.getMimeType(" "));
     }
 
     @Test
     public void shouldGetMimetype() {
-        assertEquals("text/html", MimetypeParser.getMimetype("text/html"));
-        assertEquals("text/html", MimetypeParser.getMimetype("text/html;charset=utf-8"));
+        assertEquals("text/html", MimeTypeParser.getMimeType("text/html"));
+        assertEquals("text/html", MimeTypeParser.getMimeType("text/html;charset=utf-8"));
     }
 
     @Test
     public void shouldHandleMissingContentType() {
-        assertNull(MimetypeParser.getCharset(null));
-        assertNull(MimetypeParser.getCharset(""));
-        assertNull(MimetypeParser.getCharset(" "));
-        assertNull(MimetypeParser.getCharset("text/html"));
-        assertNull(MimetypeParser.getCharset("text/html;something=utf-8"));
+        assertNull(MimeTypeParser.getCharset(null));
+        assertNull(MimeTypeParser.getCharset(""));
+        assertNull(MimeTypeParser.getCharset(" "));
+        assertNull(MimeTypeParser.getCharset("text/html"));
+        assertNull(MimeTypeParser.getCharset("text/html;something=utf-8"));
     }
 
     @Test
     public void shouldGetCharset() {
-        assertEquals(StandardCharsets.UTF_8, MimetypeParser.getCharset("text/html;charset=utf-8"));
-        assertEquals(StandardCharsets.UTF_8, MimetypeParser.getCharset("text/html;charset=UTF-8"));
-        assertEquals(StandardCharsets.UTF_8, MimetypeParser.getCharset("text/html; charset=utf-8"));
-        assertEquals(StandardCharsets.UTF_8, MimetypeParser.getCharset("text/html; charset=UTF-8"));
-        assertEquals(StandardCharsets.UTF_8, MimetypeParser.getCharset("text/html;Charset=UTF-8"));
+        assertEquals(StandardCharsets.UTF_8, MimeTypeParser.getCharset("text/html;charset=utf-8"));
+        assertEquals(StandardCharsets.UTF_8, MimeTypeParser.getCharset("text/html;charset=UTF-8"));
+        assertEquals(StandardCharsets.UTF_8, MimeTypeParser.getCharset("text/html; charset=utf-8"));
+        assertEquals(StandardCharsets.UTF_8, MimeTypeParser.getCharset("text/html; charset=UTF-8"));
+        assertEquals(StandardCharsets.UTF_8, MimeTypeParser.getCharset("text/html;Charset=UTF-8"));
     }
 }
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/uri/NamedUriStringParserTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/uri/NamedUriStringParserTest.java
index 2d0bb4a..7954f50 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/uri/NamedUriStringParserTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/uri/NamedUriStringParserTest.java
@@ -74,7 +74,7 @@ public class NamedUriStringParserTest {
     }
 
     @Test
-    public void shouldParseAbsoluteWindosFileName() {
+    public void shouldParseAbsoluteWindowsFileName() {
         final NamedUri namedURI = parse("\\data\\users.csv");
 
         assertNull(namedURI.getName());
@@ -178,14 +178,14 @@ public class NamedUriStringParserTest {
 
     @Test
     public void shouldParseNamedFileUriWithFragment() {
-        final NamedUri namedURI = parse("users=file:///users.csv#charset=UTF-16&mimetype=text/csv");
+        final NamedUri namedURI = parse("users=file:///users.csv#charset=UTF-16&mimeType=text/csv");
 
         assertEquals("users", namedURI.getName());
         assertNull(namedURI.getGroup());
-        assertEquals("file:///users.csv#charset=UTF-16&mimetype=text/csv", namedURI.getUri().toString());
+        assertEquals("file:///users.csv#charset=UTF-16&mimeType=text/csv", namedURI.getUri().toString());
         assertEquals(2, namedURI.getParameters().size());
         assertEquals("UTF-16", namedURI.getParameters().get("charset"));
-        assertEquals("text/csv", namedURI.getParameters().get("mimetype"));
+        assertEquals("text/csv", namedURI.getParameters().get("mimeType"));
     }
 
     @Test
diff --git a/freemarker-generator-cli/CHANGELOG.md b/freemarker-generator-cli/CHANGELOG.md
index f5c636e..e0165ed 100644
--- a/freemarker-generator-cli/CHANGELOG.md
+++ b/freemarker-generator-cli/CHANGELOG.md
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. We try to a
 ## 0.1.0-SNAPSHOT
 
 ### Added
+* [FREEMARKER-148] Make usage of "DataSources" more "Freemarker" like
 * [FREEMARKER-149] Support multiple template transformations on the command line
 * [FREEMARKER-144] Proof Of Concept for providing DataFrames
 * [FREEMARKER-142] Support Transformation Of Directories
@@ -16,7 +17,7 @@ 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))
 
 ### Changed
-* Removing `DataSources.first` and use `DataSources.get(0)` instead
+* Removing `DataSources.first` and use `dataSources.get(0)` instead
 * [FREEMARKER-146] Cleanly separate example templates and data from user-supplied content
 * `DataSource` use `uri` instead of `location`
 * [FREEMARKER-138] freemarker-generator: Rename `Datasource` to `DataSource`
@@ -43,5 +44,6 @@ All notable changes to this project will be documented in this file. We try to a
 [FREEMARKER-144]: https://issues.apache.org/jira/browse/FREEMARKER-144
 [FREEMARKER-146]: https://issues.apache.org/jira/browse/FREEMARKER-146
 [FREEMARKER-147]: https://issues.apache.org/jira/browse/FREEMARKER-147
+[FREEMARKER-148]: https://issues.apache.org/jira/browse/FREEMARKER-148
 [FREEMARKER-149]: https://issues.apache.org/jira/browse/FREEMARKER-149
 [FREEMARKER-151]: https://issues.apache.org/jira/browse/FREEMARKER-151
\ No newline at end of file
diff --git a/freemarker-generator-cli/examples/data/ftl/nginx/nginx.conf.ftl b/freemarker-generator-cli/examples/data/ftl/nginx/nginx.conf.ftl
index 41105cb..3cbf310 100644
--- a/freemarker-generator-cli/examples/data/ftl/nginx/nginx.conf.ftl
+++ b/freemarker-generator-cli/examples/data/ftl/nginx/nginx.conf.ftl
@@ -1,4 +1,4 @@
-<#assign env = PropertiesTool.parse(DataSources.get(0))>
+<#assign env = tools.properties.parse(dataSources?values[0])>
 
 server {
   listen 80;
diff --git a/freemarker-generator-cli/examples/templates/accesslog/combined-access.ftl b/freemarker-generator-cli/examples/templates/accesslog/combined-access.ftl
index ea92b68..56d7f21 100644
--- a/freemarker-generator-cli/examples/templates/accesslog/combined-access.ftl
+++ b/freemarker-generator-cli/examples/templates/accesslog/combined-access.ftl
@@ -15,8 +15,8 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign grok = GrokTool.compile("%{COMBINEDAPACHELOG}")>
-<#assign dataSource = DataSources.get(0)>
+<#assign grok = tools.grok.compile("%{COMBINEDAPACHELOG}")>
+<#assign dataSource = dataSources?values[0]>
 <#assign lines = dataSource.getLineIterator()>
 
 <#compress>
diff --git a/freemarker-generator-cli/examples/templates/csv/csv/gatling-user-credentials.ftl b/freemarker-generator-cli/examples/templates/csv/csv/gatling-user-credentials.ftl
index 6186b62..3bf2f68 100644
--- a/freemarker-generator-cli/examples/templates/csv/csv/gatling-user-credentials.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/csv/gatling-user-credentials.ftl
@@ -15,9 +15,9 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withDelimiter(';')>
-<#assign csvParser = CSVTool.parse(dataSource, cvsFormat)>
+<#assign dataSource = dataSources?values[0]>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withDelimiter(';')>
+<#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
 <#assign csvRecords = csvParser.records>
 
 <#compress>
diff --git a/freemarker-generator-cli/examples/templates/csv/fo/transactions.ftl b/freemarker-generator-cli/examples/templates/csv/fo/transactions.ftl
index 48a6416..9a513ca 100644
--- a/freemarker-generator-cli/examples/templates/csv/fo/transactions.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/fo/transactions.ftl
@@ -15,10 +15,10 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign cvsFormat = CSVTool.formats.DEFAULT.withDelimiter('\t').withHeader()>
-<#assign csvParser = CSVTool.parse(dataSource, cvsFormat)>
+<#assign cvsFormat = tools.csv.formats.DEFAULT.withDelimiter('\t').withHeader()>
+<#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git a/freemarker-generator-cli/examples/templates/csv/fo/transform.ftl b/freemarker-generator-cli/examples/templates/csv/fo/transform.ftl
index 4efa596..7e77af2 100644
--- a/freemarker-generator-cli/examples/templates/csv/fo/transform.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/fo/transform.ftl
@@ -16,8 +16,8 @@
   under the License.
 -->
 <#assign csvFormatName = CVS_IN_FORMAT!"DEFAULT">
-<#assign cvsFormat = CSVTool.formats[csvFormatName].withHeader()>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
+<#assign cvsFormat = tools.csv.formats[csvFormatName].withHeader()>
+<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git a/freemarker-generator-cli/examples/templates/csv/html/transactions.ftl b/freemarker-generator-cli/examples/templates/csv/html/transactions.ftl
index d9b32ca..ef2dc5c 100644
--- a/freemarker-generator-cli/examples/templates/csv/html/transactions.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/html/transactions.ftl
@@ -15,10 +15,10 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withDelimiter('\t').withHeader()>
-<#assign csvParser = CSVTool.parse(dataSource, cvsFormat)>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withDelimiter('\t').withHeader()>
+<#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git a/freemarker-generator-cli/examples/templates/csv/md/filter.ftl b/freemarker-generator-cli/examples/templates/csv/md/filter.ftl
index 57f990d..49beb57 100644
--- a/freemarker-generator-cli/examples/templates/csv/md/filter.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/md/filter.ftl
@@ -15,11 +15,11 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign parser = parser(dataSource)>
 <#assign headers = parser.getHeaderNames()>
-<#assign column = SystemTool.getParameter("column")>
-<#assign values = SystemTool.getParameter("values")?split(",")>
+<#assign column = tools.system.getParameter("column")>
+<#assign values = tools.system.getParameter("values")?split(",")>
 
 <#compress>
     <@writePageHeader dataSource/>
@@ -32,9 +32,9 @@
 </#compress>
 
 <#function parser dataSource>
-    <#assign format = CSVTool.formats[SystemTool.getParameter("format", "DEFAULT")]>
-    <#assign delimiter = CSVTool.toDelimiter(SystemTool.getParameter("delimiter", format.getDelimiter()))>
-    <#return CSVTool.parse(dataSource, format.withFirstRecordAsHeader().withDelimiter(delimiter))>
+    <#assign format = tools.csv.formats[tools.system.getParameter("format", "DEFAULT")]>
+    <#assign delimiter = tools.csv.toDelimiter(tools.system.getParameter("delimiter", format.getDelimiter()))>
+    <#return tools.csv.parse(dataSource, format.withFirstRecordAsHeader().withDelimiter(delimiter))>
 </#function>
 
 <#function filter record>
diff --git a/freemarker-generator-cli/examples/templates/csv/shell/curl.ftl b/freemarker-generator-cli/examples/templates/csv/shell/curl.ftl
index b8fa109..aaae7a6 100644
--- a/freemarker-generator-cli/examples/templates/csv/shell/curl.ftl
+++ b/freemarker-generator-cli/examples/templates/csv/shell/curl.ftl
@@ -15,10 +15,10 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withHeader()>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader()>
+<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
 <#assign records = csvParser.records>
-<#assign csvMap = CSVTool.toMap(records, "disposer")>
+<#assign csvMap = tools.csv.toMap(records, "disposer")>
 <#--------------------------------------------------------------------------->
 #!/bin/sh
 
diff --git a/freemarker-generator-cli/examples/templates/dataframe/example.ftl b/freemarker-generator-cli/examples/templates/dataframe/example.ftl
index 9929d21..da26004 100644
--- a/freemarker-generator-cli/examples/templates/dataframe/example.ftl
+++ b/freemarker-generator-cli/examples/templates/dataframe/example.ftl
@@ -14,33 +14,33 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
-<#assign csvParser = CSVTool.parse(dataSource, CSVTool.formats["DATAFRAME"])>
-<#assign users = DataFrameTool.fromCSVParser(csvParser)>
+<#assign dataSource = dataSources?values[0]>
+<#assign csvParser = tools.csv.parse(dataSource, tools.csv.formats["DATAFRAME"])>
+<#assign users = tools.dataframe.fromCSVParser(csvParser)>
 
 Original Data
 =============================================================================
-${DataFrameTool.print(users)}
+${tools.dataframe.print(users)}
 
 Select By Age
 =============================================================================
-${DataFrameTool.print(users.select("(age > 40)"))}
+${tools.dataframe.print(users.select("(age > 40)"))}
 
 Select By Name & Country
 =============================================================================
 <#assign country = "Germany">
-${DataFrameTool.print(users
+${tools.dataframe.print(users
 .select("(name == 'Schmitt' || name == 'Meier') && country == '${country}'")
-.sort("name", DataFrameTool.sortOrder["ASCENDING"]))}
+.sort("name", tools.dataframe.sortOrder["ASCENDING"]))}
 
 Head of Users
 =============================================================================
-${DataFrameTool.print(users.head(2))}
+${tools.dataframe.print(users.head(2))}
 
 Count Column Values
 =============================================================================
-${DataFrameTool.print(users.getColumn("country").transform(DataFrameTool.transformer["COUNT"]))}
+${tools.dataframe.print(users.getColumn("country").transform(tools.dataframe.transformer["COUNT"]))}
 
 Group By Age & Country
 =============================================================================
-${DataFrameTool.print(users.groupBy("country", "age").sort("country"))}
+${tools.dataframe.print(users.groupBy("country", "age").sort("country"))}
diff --git a/freemarker-generator-cli/examples/templates/dataframe/html/print.ftl b/freemarker-generator-cli/examples/templates/dataframe/html/print.ftl
index e17d3a9..e542b1c 100644
--- a/freemarker-generator-cli/examples/templates/dataframe/html/print.ftl
+++ b/freemarker-generator-cli/examples/templates/dataframe/html/print.ftl
@@ -15,9 +15,9 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withHeader().withDelimiter(';')>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
-<#assign dataFrame = DataFrameTool.toDataFrame(csvParser)>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader().withDelimiter(';')>
+<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
+<#assign dataFrame = tools.dataframe.toDataFrame(csvParser)>
 <#--------------------------------------------------------------------------->
 <!DOCTYPE html>
 <html>
diff --git a/freemarker-generator-cli/examples/templates/demo.ftl b/freemarker-generator-cli/examples/templates/demo.ftl
index 0ed952e..da3a61d 100644
--- a/freemarker-generator-cli/examples/templates/demo.ftl
+++ b/freemarker-generator-cli/examples/templates/demo.ftl
@@ -27,101 +27,103 @@ Output format          : ${.output_format}
 
 2) Invoke a constructor of a Java class
 ---------------------------------------------------------------------------
-<#assign date = FreeMarkerTool.objectConstructor("java.util.Date", 1000 * 3600 * 24)>
+<#assign date = tools.freemarker.objectConstructor("java.util.Date", 1000 * 3600 * 24)>
 new java.utilDate(1000 * 3600 * 24): ${date?datetime}
 
 3) Invoke a static method of an non-constructor class
 ---------------------------------------------------------------------------
-Random UUID              : ${FreeMarkerTool.statics["java.util.UUID"].randomUUID()}
-System.currentTimeMillis : ${FreeMarkerTool.statics["java.lang.System"].currentTimeMillis()}
+Random UUID              : ${tools.freemarker.statics["java.util.UUID"].randomUUID()}
+System.currentTimeMillis : ${tools.freemarker.statics["java.lang.System"].currentTimeMillis()}
 
 4) Access an Enumeration
 ---------------------------------------------------------------------------
-java.math.RoundingMode#UP: ${FreeMarkerTool.enums["java.math.RoundingMode"].UP}
+java.math.RoundingMode#UP: ${tools.freemarker.enums["java.math.RoundingMode"].UP}
 
 5) Loop Over The Values Of An Enumeration
 ---------------------------------------------------------------------------
-<#list FreeMarkerTool.enums["java.math.RoundingMode"]?values as roundingMode>
+<#list tools.freemarker.enums["java.math.RoundingMode"]?values as roundingMode>
 - java.math.RoundingMode.${roundingMode}<#lt>
 </#list>
 
 6) Display list of data sources
 ---------------------------------------------------------------------------
 List all data sources:
-<#list DataSources.list as dataSource>
-- Document: name=${dataSource.name} uri=${dataSource.uri} length=${dataSource.length} encoding=${dataSource.encoding!""}
+<#list dataSources?values as dataSource>
+- Document: name=${dataSource.name} uri=${dataSource.uri} length=${dataSource.length} charset=${dataSource.charset}
 </#list>
 
 7) SystemTool
 ---------------------------------------------------------------------------
-Host name       : ${SystemTool.getHostName()}
-Command line    : ${SystemTool.getCommandLineArgs()?join(", ")}
-System property : ${SystemTool.getSystemProperty("user.name", "N.A.")}
-Timestamp       : ${SystemTool.currentTimeMillis?c}
-Environment var : ${SystemTool.envs["USER"]!"N.A."}
+Host name       : ${tools.system.getHostName()}
+Command line    : ${tools.system.getCommandLineArgs()?join(", ")}
+System property : ${tools.system.getSystemProperty("user.name", "N.A.")}
+Timestamp       : ${tools.system.currentTimeMillis?c}
+Environment     : ${tools.system.envs["USER"]!"N.A."}
 
 8) Access System Properties
 ---------------------------------------------------------------------------
-app.dir      : ${SystemTool.systemProperties["app.dir"]!""}
-app.home     : ${SystemTool.systemProperties["app.home"]!""}
-app.pid      : ${SystemTool.systemProperties["app.pid"]!""}
-basedir      : ${SystemTool.systemProperties["basedir"]!""}
-java.version : ${SystemTool.systemProperties["java.version"]!""}
-user.name    : ${SystemTool.systemProperties["user.name"]!""}
-user.dir     : ${SystemTool.systemProperties["user.dir"]!""}
-user.home    : ${SystemTool.systemProperties["user.home"]!""}
+app.dir      : ${tools.system.systemProperties["app.dir"]!""}
+app.home     : ${tools.system.systemProperties["app.home"]!""}
+app.pid      : ${tools.system.systemProperties["app.pid"]!""}
+basedir      : ${tools.system.systemProperties["basedir"]!""}
+java.version : ${tools.system.systemProperties["java.version"]!""}
+user.name    : ${tools.system.systemProperties["user.name"]!""}
+user.dir     : ${tools.system.systemProperties["user.dir"]!""}
+user.home    : ${tools.system.systemProperties["user.home"]!""}
 
 9) List Environment Variables
 ---------------------------------------------------------------------------
-<#list SystemTool.envs as name,value>
+<#list tools.system.envs as name,value>
 - ${name} ==> ${value}<#lt>
 </#list>
 
 10) List System Properties
 ---------------------------------------------------------------------------
-<#list SystemTool.systemProperties as name,value>
+<#list tools.system.systemProperties as name,value>
 - ${name} ==> ${value}<#lt>
 </#list>
 
 11) Access DataSources
 ---------------------------------------------------------------------------
-Get the number of documents:
-- ${DataSources.size()}
-<#if !DataSources.isEmpty()>
-Get the first document
-- ${DataSources.get(0)!"NA"}
+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"}
 </#if>
-List all files containing "README" in the name
-<#list DataSources.find("*README*") as dataSource>
-- ${dataSource.name}
+Get all documents as map:
+<#list dataSources as name, ds>
+- ${name} => ${ds.mimeType}
 </#list>
-List all files having "md" extension
-<#list DataSources.find("*.md") as dataSource>
-- ${dataSource.name}
+List all data sources containing "test" in the name
+<#list dataSources?values?filter(ds -> ds.match("name", "*test*")) as ds>
+- ${ds.name}
 </#list>
-Get all documents
-<#list DataSources.list as dataSource>
-- ${dataSource.name} => ${dataSource.uri}
+List all data sources having "json" extension
+<#list dataSources?values?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>
+- ${ds.name}
 </#list>
 
-12) FreeMarker CLI Tools
+12) Document Data Model
 ---------------------------------------------------------------------------
 <#list .data_model?keys?sort as key>
-<#if key?ends_with("Tool")>
-- ${key?right_pad(20)} : ${.data_model[key]}
-</#if>
+- ${key}<#lt>
 </#list>
 
-13) Document Data Model
+13) FreeMarker CLI Tools
 ---------------------------------------------------------------------------
-<#list .data_model?keys?sort as key>
-- ${key}<#lt>
+<#list .data_model.tools?keys?sort as key>
+- ${key?right_pad(20)} : ${.data_model.tools[key]}
 </#list>
 
 14) Create a UUID
 ---------------------------------------------------------------------------
-UUIDTool Random UUID  : ${UUIDTool.randomUUID()}
-UUIDTool Named UUID   : ${UUIDTool.namedUUID("value and salt")}
+UUIDTool Random UUID  : ${tools.uuid.randomUUID()}
+UUIDTool Named UUID   : ${tools.uuid.namedUUID("value and salt")}
 
 15) Printing Special Characters
 ---------------------------------------------------------------------------
diff --git a/freemarker-generator-cli/examples/templates/excel/csv/custom.ftl b/freemarker-generator-cli/examples/templates/excel/csv/custom.ftl
index ac4b4be..feda79a 100644
--- a/freemarker-generator-cli/examples/templates/excel/csv/custom.ftl
+++ b/freemarker-generator-cli/examples/templates/excel/csv/custom.ftl
@@ -16,14 +16,14 @@
   under the License.
 -->
 <#assign format = CSV_TARGET_FORMAT!"DEFAULT">
-<#assign salt = SystemTool.parameters["salt"]!"salt">
+<#assign salt = tools.system.parameters["salt"]!"salt">
 <#-- Parse the first data source & sheet of the Excel document -->
-<#assign workbook = ExcelTool.parse(DataSources.get(0))>
-<#assign sheet = ExcelTool.getSheets(workbook)[0]>
-<#assign records = ExcelTool.toTable(sheet)>
+<#assign workbook = tools.excel.parse(dataSources?values[0])>
+<#assign sheet = tools.excel.getSheets(workbook)[0]>
+<#assign records = tools.excel.toTable(sheet)>
 <#-- Setup CSVPrinter  -->
-<#assign cvsFormat = CSVTool.formats[format]>
-<#assign csvPrinter = CSVTool.printer(cvsFormat)>
+<#assign cvsFormat = tools.csv.formats[format]>
+<#assign csvPrinter = tools.csv.printer(cvsFormat)>
 <#--
     Transform an arbitrary Excel file and add addition columns using Commons CSV
     We are using an instance of CSVPrinter directly have proper quoting of the output
@@ -50,7 +50,7 @@
 <#--------------------------------------------------------------------------->
 <#function uuidFromValueAndSalt value salt>
     <#assign uuidSource = value + salt>
-    <#assign buffer = FreeMarkerTool.statics["java.nio.charset.Charset"].forName("UTF-8").encode(uuidSource).rewind()>
+    <#assign buffer = tools.freemarker.statics["java.nio.charset.Charset"].forName("UTF-8").encode(uuidSource).rewind()>
     <#assign bytes = buffer.array()[0..<buffer.limit()]>
-    <#return FreeMarkerTool.statics["java.util.UUID"].nameUUIDFromBytes(bytes)>
+    <#return tools.freemarker.statics["java.util.UUID"].nameUUIDFromBytes(bytes)>
 </#function>
\ No newline at end of file
diff --git a/freemarker-generator-cli/examples/templates/excel/dataframe/transform.ftl b/freemarker-generator-cli/examples/templates/excel/dataframe/transform.ftl
index d172bec..5b24903 100644
--- a/freemarker-generator-cli/examples/templates/excel/dataframe/transform.ftl
+++ b/freemarker-generator-cli/examples/templates/excel/dataframe/transform.ftl
@@ -14,10 +14,10 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
-<#assign workbook = ExcelTool.parse(dataSource)>
-<#list ExcelTool.getSheets(workbook) as sheet>
-    <#assign table = ExcelTool.toTable(sheet)>
-    <#assign df = DataFrameTool.fromRows(table, true)>
-    ${DataFrameTool.print(df)}<#t>
+<#assign dataSource = dataSources?values[0]>
+<#assign workbook = tools.excel.parse(dataSource)>
+<#list tools.excel.getSheets(workbook) as sheet>
+    <#assign table = tools.excel.toTable(sheet)>
+    <#assign df = tools.dataframe.fromRows(table, true)>
+    ${tools.dataframe.print(df)}<#t>
 </#list>
diff --git a/freemarker-generator-cli/examples/templates/html/csv/dependencies.ftl b/freemarker-generator-cli/examples/templates/html/csv/dependencies.ftl
index 8664eae..51fe17e 100644
--- a/freemarker-generator-cli/examples/templates/html/csv/dependencies.ftl
+++ b/freemarker-generator-cli/examples/templates/html/csv/dependencies.ftl
@@ -15,9 +15,9 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign html = JsoupTool.parse(dataSource)>
+<#assign html = tools.jsoup.parse(dataSource)>
 
 <#compress>
     <@writeHeader/>
diff --git a/freemarker-generator-cli/examples/templates/json/csv/swagger-endpoints.ftl b/freemarker-generator-cli/examples/templates/json/csv/swagger-endpoints.ftl
index 5f9d6a0..474fd5a 100644
--- a/freemarker-generator-cli/examples/templates/json/csv/swagger-endpoints.ftl
+++ b/freemarker-generator-cli/examples/templates/json/csv/swagger-endpoints.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign map = GsonTool.parse(DataSources.get(0))>
+<#assign map = tools.gson.parse(dataSources?values[0])>
 <#assign basePath = map.basePath!"/">
 <#assign paths = map.paths!{}>
 
diff --git a/freemarker-generator-cli/examples/templates/json/dataframe/github-users.ftl b/freemarker-generator-cli/examples/templates/json/dataframe/github-users.ftl
index a6547fc..480f59a 100644
--- a/freemarker-generator-cli/examples/templates/json/dataframe/github-users.ftl
+++ b/freemarker-generator-cli/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 = GsonTool.parse(DataSources.get(0))>
-<#assign dataframe = DataFrameTool.fromMaps(json)>
-${DataFrameTool.print(dataframe)}
+<#assign json = tools.gson.parse(dataSources?values[0])>
+<#assign dataframe = tools.dataframe.fromMaps(json)>
+${tools.dataframe.print(dataframe)}
diff --git a/freemarker-generator-cli/examples/templates/json/md/github-users.ftl b/freemarker-generator-cli/examples/templates/json/md/github-users.ftl
index 29efbc3..a83d07b 100644
--- a/freemarker-generator-cli/examples/templates/json/md/github-users.ftl
+++ b/freemarker-generator-cli/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 = JsonPathTool.parse(DataSources.get(0))>
+<#assign json = tools.jsonpath.parse(dataSources?values[0])>
 <#assign users = json.read("$[*]")>
 <#--------------------------------------------------------------------------->
 # GitHub Users
diff --git a/freemarker-generator-cli/examples/templates/properties/csv/locker-test-users.ftl b/freemarker-generator-cli/examples/templates/properties/csv/locker-test-users.ftl
index 52ac65f..1d2d21f 100644
--- a/freemarker-generator-cli/examples/templates/properties/csv/locker-test-users.ftl
+++ b/freemarker-generator-cli/examples/templates/properties/csv/locker-test-users.ftl
@@ -17,8 +17,8 @@
 -->
 <#compress>
     TENANT,SITE,USER_ID,DISPOSER_ID,PASSWORD,SMS_OTP,NAME,DESCRIPTION
-    <#list DataSources.list as dataSource>
-        <#assign properties = PropertiesTool.parse(dataSource)>
+    <#list dataSources?values as dataSource>
+        <#assign properties = tools.properties.parse(dataSource)>
         <#assign environments = properties["ENVIRONMENTS"]!"">
         <#assign tenant = extractTenant(environments)>
         <#assign site = extractSite(environments)>
diff --git a/freemarker-generator-cli/examples/templates/tsv/fo/transactions.ftl b/freemarker-generator-cli/examples/templates/tsv/fo/transactions.ftl
index e1840d1..51c2be4 100644
--- a/freemarker-generator-cli/examples/templates/tsv/fo/transactions.ftl
+++ b/freemarker-generator-cli/examples/templates/tsv/fo/transactions.ftl
@@ -15,8 +15,8 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign cvsFormat = CSVTool.formats["TDF"].withHeader()>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
+<#assign cvsFormat = tools.csv.formats["TDF"].withHeader()>
+<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git a/freemarker-generator-cli/examples/templates/xml/txt/recipients.ftl b/freemarker-generator-cli/examples/templates/xml/txt/recipients.ftl
index 43b51b4..b67e8e7 100644
--- a/freemarker-generator-cli/examples/templates/xml/txt/recipients.ftl
+++ b/freemarker-generator-cli/examples/templates/xml/txt/recipients.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign xml = XmlTool.parse(DataSources.get(0))>
+<#assign xml = tools.xml.parse(dataSources?values[0])>
 <#list xml.recipients.person as recipient>
 To: ${recipient.name}
 ${recipient.address}
diff --git a/freemarker-generator-cli/examples/templates/yaml/txt/transform.ftl b/freemarker-generator-cli/examples/templates/yaml/txt/transform.ftl
index fdba743..ebcf525 100644
--- a/freemarker-generator-cli/examples/templates/yaml/txt/transform.ftl
+++ b/freemarker-generator-cli/examples/templates/yaml/txt/transform.ftl
@@ -15,7 +15,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign map = YamlTool.parse(DataSources.get(0))>
+<#assign map = tools.yaml.parse(dataSources?values[0])>
 <#--------------------------------------------------------------------------->
 <#compress>
 <@print map 1/>
diff --git a/freemarker-generator-cli/run-examples.bat b/freemarker-generator-cli/run-examples.bat
index c4864b1..95bbfd9 100644
--- a/freemarker-generator-cli/run-examples.bat
+++ b/freemarker-generator-cli/run-examples.bat
@@ -41,12 +41,12 @@ REM =========================================================================
 REM Interactive Mode
 REM =========================================================================
 
-%FREEMARKER_CMD% -i '${JsonPathTool.parse(DataSources.get(0)).read("""$.info.title""")}' examples\data\json\swagger-spec.json > target\out\interactive-json.txt
-%FREEMARKER_CMD% -i '${XmlTool.parse(DataSources.get(0))["""recipients/person[1]/name"""]}' examples\data\xml\recipients.xml > target\out\interactive-xml.txt
-%FREEMARKER_CMD% -i '${JsoupTool.parse(DataSources.get(0)).select("""a""")[0]}' examples\data\html\dependencies.html > target\out\interactive-html.txt
-%FREEMARKER_CMD% -i '${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}' examples\data\yaml\swagger-spec.yaml > target\out\interactive-swagger.json
-%FREEMARKER_CMD% -i '${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}' examples\data\json\swagger-spec.json > target\out\interactive-swagger.yaml
-%FREEMARKER_CMD% -i '${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(0))))}' examples\data\json\github-users.json > target\out\interactive-dataframe.txt
+%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
 
 REM =========================================================================
 REM CSV
diff --git a/freemarker-generator-cli/run-examples.sh b/freemarker-generator-cli/run-examples.sh
index 2c3d152..719c199 100755
--- a/freemarker-generator-cli/run-examples.sh
+++ b/freemarker-generator-cli/run-examples.sh
@@ -46,12 +46,12 @@ $FREEMARKER_CMD -t examples/templates/demo.ftl README.md > target/out/demo.txt |
 # Interactive Mode
 #############################################################################
 
-$FREEMARKER_CMD -i '${JsonPathTool.parse(DataSources.get(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 '${XmlTool.parse(DataSources.get(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 '${JsoupTool.parse(DataSources.get(0)).select("a")[0]}' examples/data/html/dependencies.html > target/out/interactive-html.txt || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}' examples/data/yaml/swagger-spec.yaml > target/out/interactive-swagger.json || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}' examples/data/json/swagger-spec.json > target/out/interactive-swagger.yaml || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(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?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; }
 
 #############################################################################
 # CSV
diff --git a/freemarker-generator-cli/src/main/config/freemarker-cli.properties b/freemarker-generator-cli/src/main/config/freemarker-cli.properties
index bd9c917..fd4a8f9 100644
--- a/freemarker-generator-cli/src/main/config/freemarker-cli.properties
+++ b/freemarker-generator-cli/src/main/config/freemarker-cli.properties
@@ -24,17 +24,17 @@
 #############################################################################
 # Configure FreeMarker Tools (name -> implementation class)
 #############################################################################
-freemarker.tools.CSVTool=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
-freemarker.tools.DataFrameTool=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
-freemarker.tools.ExcelTool=org.apache.freemarker.generator.tools.excel.ExcelTool
-freemarker.tools.ExecTool=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
-freemarker.tools.FreeMarkerTool=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
-freemarker.tools.GrokTool=org.apache.freemarker.generator.tools.grok.GrokTool
-freemarker.tools.GsonTool=org.apache.freemarker.generator.tools.gson.GsonTool
-freemarker.tools.JsonPathTool=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
-freemarker.tools.JsoupTool=org.apache.freemarker.generator.tools.jsoup.JsoupTool
-freemarker.tools.PropertiesTool=org.apache.freemarker.generator.tools.properties.PropertiesTool
-freemarker.tools.SystemTool=org.apache.freemarker.generator.tools.system.SystemTool
-freemarker.tools.UUIDTool=org.apache.freemarker.generator.tools.uuid.UUIDTool
-freemarker.tools.XmlTool=org.apache.freemarker.generator.tools.xml.XmlTool
-freemarker.tools.YamlTool=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
+freemarker.tools.csv=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
+freemarker.tools.dataframe=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
+freemarker.tools.excel=org.apache.freemarker.generator.tools.excel.ExcelTool
+freemarker.tools.exec=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
+freemarker.tools.freemarker=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
+freemarker.tools.grok=org.apache.freemarker.generator.tools.grok.GrokTool
+freemarker.tools.gson=org.apache.freemarker.generator.tools.gson.GsonTool
+freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
+freemarker.tools.jsoup=org.apache.freemarker.generator.tools.jsoup.JsoupTool
+freemarker.tools.properties=org.apache.freemarker.generator.tools.properties.PropertiesTool
+freemarker.tools.system=org.apache.freemarker.generator.tools.system.SystemTool
+freemarker.tools.uuid=org.apache.freemarker.generator.tools.uuid.UUIDTool
+freemarker.tools.xml=org.apache.freemarker.generator.tools.xml.XmlTool
+freemarker.tools.yaml=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
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 c7d4ea7..bf77a69 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
@@ -19,6 +19,7 @@ package org.apache.freemarker.generator.cli.config;
 import freemarker.cache.TemplateLoader;
 import freemarker.template.Configuration;
 import freemarker.template.Version;
+import org.apache.freemarker.generator.cli.model.GeneratorObjectWrapper;
 
 import java.util.Properties;
 import java.util.function.Supplier;
@@ -58,6 +59,9 @@ public class ConfigurationSupplier implements Supplier<Configuration> {
             // apply all "freemarker.configuration.setting" values
             configuration.setSettings(freeMarkerConfigurationSettings());
 
+            // provide custom models for "DataSources"
+            configuration.setObjectWrapper(new GeneratorObjectWrapper(FREEMARKER_VERSION));
+
             // override current configuration with caller-provided settings
             configuration.setDefaultEncoding(settings.getTemplateEncoding().name());
             configuration.setLocale(settings.getLocale());
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
index e775eb1..34efb5f 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
@@ -16,8 +16,10 @@
  */
 package org.apache.freemarker.generator.cli.config;
 
+import org.apache.freemarker.generator.base.FreeMarkerConstants.Model;
 import org.apache.freemarker.generator.base.tools.ToolsFactory;
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 import java.util.function.Supplier;
@@ -50,6 +52,17 @@ public class ToolsSupplier implements Supplier<Map<String, Object>> {
 
     @Override
     public Map<String, Object> get() {
+        final Map<String, Object> result = new HashMap<>();
+        result.put(Model.TOOLS, tools());
+        return result;
+    }
+
+    /**
+     * Create a map of tools.
+     *
+     * @return tools
+     */
+    private Map<String, Object> tools() {
         final Properties properties = toolsProperties();
         return properties.stringPropertyNames().stream()
                 .filter(key -> toolExists(properties.getProperty(key)))
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java
new file mode 100644
index 0000000..45fa479
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java
@@ -0,0 +1,40 @@
+package org.apache.freemarker.generator.cli.model;
+
+import freemarker.ext.beans.BeanModel;
+import freemarker.ext.beans.BeansWrapper;
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.datasource.DataSources;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Wraps an instance of <code>DataSources</code> into a more user-friendly <code>BeanModel</code>
+ * so the user can use FreeMarker directives and features instead of using the exposed methods.
+ */
+public class DataSourcesModel extends BeanModel {
+
+    public DataSourcesModel(DataSources dataSources, BeansWrapper objectWrapper) {
+        super(new SimpleDataSourcesAdapter(dataSources), requireNonNull(objectWrapper));
+    }
+
+    private static final class SimpleDataSourcesAdapter {
+
+        private final DataSources dataSources;
+
+        public SimpleDataSourcesAdapter(DataSources dataSources) {
+            this.dataSources = dataSources;
+        }
+
+        public DataSource get(int index) {
+            return dataSources.get(index);
+        }
+
+        public DataSource get(String name) {
+            return dataSources.get(name);
+        }
+
+        public int size() {
+            return dataSources.size();
+        }
+    }
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java
new file mode 100644
index 0000000..57b9867
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java
@@ -0,0 +1,25 @@
+package org.apache.freemarker.generator.cli.model;
+
+import freemarker.template.DefaultMapAdapter;
+import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.Version;
+import org.apache.freemarker.generator.base.datasource.DataSources;
+
+public class GeneratorObjectWrapper extends DefaultObjectWrapper {
+
+    public GeneratorObjectWrapper(Version incompatibleImprovements) {
+        super(incompatibleImprovements);
+    }
+
+    @Override
+    protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
+        if (obj instanceof DataSources) {
+            final DataSources dataSources = (DataSources) obj;
+            return DefaultMapAdapter.adapt((dataSources).toMap(), this);
+        }
+
+        return super.handleUnknownType(obj);
+    }
+}
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 7813a4e..3842508 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
@@ -133,7 +133,7 @@ public class FreeMarkerTask implements Callable<Integer> {
         // Add optional data source from STDIN at the start of the list since
         // this allows easy sequence slicing in FreeMarker.
         if (settings.isReadFromStdin()) {
-            final URI uri = UriUtils.toURI(Location.SYSTEM, "in");
+            final URI uri = UriUtils.toUri(Location.SYSTEM, "in");
             dataSources.add(0, DataSourceFactory.fromInputStream(STDIN, DEFAULT_GROUP, uri, System.in, MIME_TEXT_PLAIN, UTF_8));
         }
 
diff --git a/freemarker-generator-cli/src/main/resources/freemarker-cli.properties b/freemarker-generator-cli/src/main/resources/freemarker-cli.properties
index bd9c917..fd4a8f9 100644
--- a/freemarker-generator-cli/src/main/resources/freemarker-cli.properties
+++ b/freemarker-generator-cli/src/main/resources/freemarker-cli.properties
@@ -24,17 +24,17 @@
 #############################################################################
 # Configure FreeMarker Tools (name -> implementation class)
 #############################################################################
-freemarker.tools.CSVTool=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
-freemarker.tools.DataFrameTool=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
-freemarker.tools.ExcelTool=org.apache.freemarker.generator.tools.excel.ExcelTool
-freemarker.tools.ExecTool=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
-freemarker.tools.FreeMarkerTool=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
-freemarker.tools.GrokTool=org.apache.freemarker.generator.tools.grok.GrokTool
-freemarker.tools.GsonTool=org.apache.freemarker.generator.tools.gson.GsonTool
-freemarker.tools.JsonPathTool=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
-freemarker.tools.JsoupTool=org.apache.freemarker.generator.tools.jsoup.JsoupTool
-freemarker.tools.PropertiesTool=org.apache.freemarker.generator.tools.properties.PropertiesTool
-freemarker.tools.SystemTool=org.apache.freemarker.generator.tools.system.SystemTool
-freemarker.tools.UUIDTool=org.apache.freemarker.generator.tools.uuid.UUIDTool
-freemarker.tools.XmlTool=org.apache.freemarker.generator.tools.xml.XmlTool
-freemarker.tools.YamlTool=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
+freemarker.tools.csv=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
+freemarker.tools.dataframe=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
+freemarker.tools.excel=org.apache.freemarker.generator.tools.excel.ExcelTool
+freemarker.tools.exec=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
+freemarker.tools.freemarker=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
+freemarker.tools.grok=org.apache.freemarker.generator.tools.grok.GrokTool
+freemarker.tools.gson=org.apache.freemarker.generator.tools.gson.GsonTool
+freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
+freemarker.tools.jsoup=org.apache.freemarker.generator.tools.jsoup.JsoupTool
+freemarker.tools.properties=org.apache.freemarker.generator.tools.properties.PropertiesTool
+freemarker.tools.system=org.apache.freemarker.generator.tools.system.SystemTool
+freemarker.tools.uuid=org.apache.freemarker.generator.tools.uuid.UUIDTool
+freemarker.tools.xml=org.apache.freemarker.generator.tools.xml.XmlTool
+freemarker.tools.yaml=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
diff --git a/freemarker-generator-cli/src/main/scripts/run-examples.bat b/freemarker-generator-cli/src/main/scripts/run-examples.bat
index c5e25be..98d6654 100644
--- a/freemarker-generator-cli/src/main/scripts/run-examples.bat
+++ b/freemarker-generator-cli/src/main/scripts/run-examples.bat
@@ -41,12 +41,12 @@ REM =========================================================================
 REM Interactive Mode
 REM =========================================================================
 
-%FREEMARKER_CMD% -i '${JsonPathTool.parse(DataSources.get(0)).read("""$.info.title""")}' examples\data\json\swagger-spec.json > target\out\interactive-json.txt
-%FREEMARKER_CMD% -i '${XmlTool.parse(DataSources.get(0))["""recipients/person[1]/name"""]}' examples\data\xml\recipients.xml > target\out\interactive-xml.txt
-%FREEMARKER_CMD% -i '${JsoupTool.parse(DataSources.get(0)).select("""a""")[0]}' examples\data\html\dependencies.html > target\out\interactive-html.txt
-%FREEMARKER_CMD% -i '${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}' examples\data\yaml\swagger-spec.yaml > target\out\interactive-swagger.json
-%FREEMARKER_CMD% -i '${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}' examples\data\json\swagger-spec.json > target\out\interactive-swagger.yaml
-%FREEMARKER_CMD% -i '${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(0))))}' examples\data\json\github-users.json > target\out\interactive-dataframe.txt
+%FREEMARKER_CMD% -i '${tools.jsonpath.parse(dataSources.get(0)).read("""$.info.title""")}' examples\data\json\swagger-spec.json > target\out\interactive-json.txt
+%FREEMARKER_CMD% -i '${tools.xml.parse(dataSources.get(0))["""recipients/person[1]/name"""]}' examples\data\xml\recipients.xml > target\out\interactive-xml.txt
+%FREEMARKER_CMD% -i '${tools.jsoup.parse(dataSources.get(0)).select("""a""")[0]}' examples\data\html\dependencies.html > target\out\interactive-html.txt
+%FREEMARKER_CMD% -i '${tools.gson.toJson(tools.yaml.parse(dataSources.get(0)))}' examples\data\yaml\swagger-spec.yaml > target\out\interactive-swagger.json
+%FREEMARKER_CMD% -i '${tools.yaml.toYaml(tools.gson.parse(dataSources.get(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.get(0))))}' examples\data\json\github-users.json > target\out\interactive-dataframe.txt
 
 REM =========================================================================
 REM CSV
diff --git a/freemarker-generator-cli/src/main/scripts/run-examples.sh b/freemarker-generator-cli/src/main/scripts/run-examples.sh
index 91f750e..f18c9e2 100755
--- a/freemarker-generator-cli/src/main/scripts/run-examples.sh
+++ b/freemarker-generator-cli/src/main/scripts/run-examples.sh
@@ -46,12 +46,12 @@ $FREEMARKER_CMD -t examples/templates/demo.ftl README.md > target/out/demo.txt |
 # Interactive Mode
 #############################################################################
 
-$FREEMARKER_CMD -i '${JsonPathTool.parse(DataSources.get(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 '${XmlTool.parse(DataSources.get(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 '${JsoupTool.parse(DataSources.get(0)).select("a")[0]}' examples/data/html/dependencies.html > target/out/interactive-html.txt || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}' examples/data/yaml/swagger-spec.yaml > target/out/interactive-swagger.json || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}' examples/data/json/swagger-spec.json > target/out/interactive-swagger.yaml || { echo >&2 "Test failed.  Aborting."; exit 1; }
-$FREEMARKER_CMD -i '${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(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.get(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.get(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.get(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.get(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.get(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.get(0))))}' examples/data/json/github-users.json > target/out/interactive-dataframe.txt || { echo >&2 "Test failed.  Aborting."; exit 1; }
 
 #############################################################################
 # CSV
diff --git a/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md b/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
index 0c3e842..96353fd 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
@@ -20,20 +20,20 @@ The `Apache FreeMarker CLI` configuration can be tweaked to
 #############################################################################
 # Configure FreeMarker Tools (name -> implementation class)
 #############################################################################
-freemarker.tools.CSVTool=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
-freemarker.tools.DataFrameTool=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
-freemarker.tools.ExcelTool=org.apache.freemarker.generator.tools.excel.ExcelTool
-freemarker.tools.ExecTool=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
-freemarker.tools.FreeMarkerTool=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
-freemarker.tools.GrokTool=org.apache.freemarker.generator.tools.grok.GrokTool
-freemarker.tools.GsonTool=org.apache.freemarker.generator.tools.gson.GsonTool
-freemarker.tools.JsonPathTool=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
-freemarker.tools.JsoupTool=org.apache.freemarker.generator.tools.jsoup.JsoupTool
-freemarker.tools.PropertiesTool=org.apache.freemarker.generator.tools.properties.PropertiesTool
-freemarker.tools.SystemTool=org.apache.freemarker.generator.tools.system.SystemTool
-freemarker.tools.UUIDTool=org.apache.freemarker.generator.tools.uuid.UUIDTool
-freemarker.tools.XmlTool=org.apache.freemarker.generator.tools.xml.XmlTool
-freemarker.tools.YamlTool=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
+freemarker.tools.csv=org.apache.freemarker.generator.tools.commonscsv.CommonsCSVTool
+freemarker.tools.dataframe=org.apache.freemarker.generator.tools.dataframe.DataFrameTool
+freemarker.tools.excel=org.apache.freemarker.generator.tools.excel.ExcelTool
+freemarker.tools.exec=org.apache.freemarker.generator.tools.commonsexec.CommonsExecTool
+freemarker.tools.freemarker=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
+freemarker.tools.grok=org.apache.freemarker.generator.tools.grok.GrokTool
+freemarker.tools.gson=org.apache.freemarker.generator.tools.gson.GsonTool
+freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.json.JsonPathTool
+freemarker.tools.jsoup=org.apache.freemarker.generator.tools.jsoup.JsoupTool
+freemarker.tools.properties=org.apache.freemarker.generator.tools.properties.PropertiesTool
+freemarker.tools.system=org.apache.freemarker.generator.tools.system.SystemTool
+freemarker.tools.uuid=org.apache.freemarker.generator.tools.uuid.UUIDTool
+freemarker.tools.xml=org.apache.freemarker.generator.tools.xml.XmlTool
+freemarker.tools.yaml=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
 ```
 
 Changing this file allows to tweak the underlying `Apache FreeMarker Configuration` and add custom tools.
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-models.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-models.md
index 25bb3f9..65aac2a 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-models.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-models.md
@@ -4,7 +4,7 @@ A `DataModel` is an eagerly loaded `DataSource` available in Apache FreeMarker's
 
 * The content of the `DataSource` is parsed and a `Map` generated
 * The `Map` is either stored as variable in the model or all entries are copied into the FreeMarker model
-* The parsing is supported for  `JSON`, `YAML`, `Properties` and enviroment variables  
+* The parsing is supported for  `JSON`, `YAML`, `Properties` and environment variables  
 
 Expose the fields of the JSON data source in FreeMarker's model 
 
@@ -51,7 +51,7 @@ HOME=/Users/sgoeschl
 USER=sgoeschl
 ```
 
-Expose a single envionment variable in theFreeMarker model
+Expose a single environment variable in theFreeMarker model
 
 ```
 > freemarker-cli --data-model NAME=env:///USER -i 'Hello ${NAME}'; echo
@@ -68,13 +68,13 @@ Hello sgoeschl!
 The following snippet shows a more advanced example
 
 * The environment variable `DB_CONFIG` holds JSON data
-* Use the `config=env:///DB_CONFIG#mimetype=application/json` to parse JSON payload from `DB_CONFIG` into the data model `config`
+* Use the `config=env:///DB_CONFIG#mimetType=application/json` to parse JSON payload from `DB_CONFIG` into the data model `config`
 
 ```
 > export DB_CONFIG='{"db_default_user":"scott","db_default_password":"tiger"}'
 > echo $DB_CONFIG 
 {"db_default_user":"scott","db_default_password":"tiger"}
-> freemarker-cli -m config=env:///DB_CONFIG#mimetype=application/json  -i '<#list config as name,value>${name}=${value}${"\n"}</#list>'
+> freemarker-cli -m config=env:///DB_CONFIG#mimeType=application/json  -i '<#list config as name,value>${name}=${value}${"\n"}</#list>'
 db_default_user=scott
 db_default_password=tiger
 ```
\ No newline at end of file
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 1a921fc..6d048ab 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
@@ -36,7 +36,7 @@ or from an environment variable, e.g. `NGINX_CONF` having a JSON payload
 
 ```
 export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}'
-freemarker-cli -t templates/info.ftl -s conf=env:///NGINX_CONF#mimetype=application/json
+freemarker-cli -t templates/info.ftl -s conf=env:///NGINX_CONF#mimeType=application/json
 
 FreeMarker CLI DataSources
 ------------------------------------------------------------------------------
@@ -86,26 +86,27 @@ FreeMarker CLI DataSources
 
 ### Selecting A DataSource
 
-After loading one or more `DataSource` it needs to be selected for template processing - the `DataSources` instance 
-exposed in the data model provides
+After loading one or more `DataSource` they are accessible as `dataSource` map in the FreeMarker model
 
-* Selecting by index  
-* Selecting by name
-* Filter by the globbing pattern (see [Apache Commons IO](https://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/filefilter/WildcardFileFilter.html))
+* `dataSources?values[0]` selects the first data source
+* `dataSources["user.csv"]` selects the data source with the name "user.csv"
 
-A few FTL examples
+Combining FreeMarker's `filter` built-in  with the `DataSource#match` methods allows more advanced 
+selection of data sources (using Apache Commons IO wildcard matching)
 
 ```
-<#assign dataSource = DataSources.get(0)>
-
-<#assign dataSource = DataSources.get("user.csv)>
-
-<#list DataSources.find("*.md") as dataSource>
-- ${dataSource.name}
+<#-- List all data sources containing "test" in the name -->
+<#list dataSources?values?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>
+- ${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>
+- ${ds.name}
+</#list>
+```
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/named-uris.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/named-uris.md
index 58f9faa..0e833d1 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/named-uris.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/named-uris.md
@@ -16,15 +16,15 @@ As a refresher, a URI is made up of the following components (inspired by https:
 scheme     authority               path        query   fragment
 ```
 
-For our purposes, the scheme and the path components are especially important, though the other components are used by certain datasources for particular purposes.
+For our purposes, the scheme and the path components are especially important, though the other components are used by certain data sources for particular purposes.
 
 | Component | Purpose                                                                                                   |
 |-----------|-----------------------------------------------------------------------------------------------------------|
-| scheme	| All datasources require a scheme (except for file when using relative paths)                              |
-| authority	| Used only by remote datasources, and can be omitted in some of those cases.                               |
+| scheme	| All data sources require a scheme (except for file when using relative paths)                              |
+| authority	| Used only by remote data sources, and can be omitted in some of those cases.                               |
 | path	    | Can be omitted, but usually used as the basis of the locator for the datasource.                          |
 | query	    | Used mainly for HTTP and HTTPS URLs                                                                       |
-| fragment	| Used rarely for providing additional attributes, e.g. `mimetype` of `charset`                             |
+| fragment	| Used rarely for providing additional attributes, e.g. `mimeType` of `charset`                             |
 
 The following Named URI loads a "user.csv" and the data source is available as `my_users` 
 
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
index c512d5c..30e9404 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
@@ -40,7 +40,7 @@ and [Template Includes](https://freemarker.apache.org/docs/ref_directive_include
 
 ### Free-Style Template Loading
 
-The previosly described `Template Loaders` do not support absolute template files or arbitrary URLS - this behaviour 
+The previously described `Template Loaders` do not support absolute template files or arbitrary URLS - this behaviour 
 stems from security aspects when running `Apache FreeMarker` on the server side. For a command-line tool this is mostly
 irrelevant therefore any template file outside of the template loader directories can be loaded 
 
@@ -55,6 +55,6 @@ freemarker-cli -t https://raw.githubusercontent.com/apache/freemarker-generator/
 The template can be defined directly on the command line in case of trivial transformations
 
 ```
-freemarker-cli -i '${GsonTool.toJson(yaml)}' -m yaml=examples/data/yaml/swagger-spec.yaml
+freemarker-cli -i '${tools.gson.toJson(yaml)}' -m yaml=examples/data/yaml/swagger-spec.yaml
 ```
 
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 26b220c..ec19b06 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 = JsonPathTool.parse(DataSources.get(0))>
+<#assign json = tools.jsonpath.parse(dataSources?values[0])>
 <#assign users = json.read("$[*]")>
 <#--------------------------------------------------------------------------->
 # GitHub Users
@@ -129,8 +129,8 @@ The FreeMarker template is shown below
 
 ```text
 <#ftl output_format="plainText">
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withHeader()>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader()>
+<#assign csvParser = tools.csv.parse(dataSources?values[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 = XmlTool.parse(DataSources.get(0))>
+<#assign xml = tools.xml.parse(dataSources?values[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 = JsonPathTool.parse(DataSources.get(0))>
+<#assign json = tools.jsonpath.parse(dataSources?values[0])>
 <#assign basePath = json.read("$.basePath")>
 <#assign paths = json.read("$.paths")>
 
@@ -276,9 +276,9 @@ The provided FTL transforms an Excel into a HTML document supporting multiple Ex
 
 ```text
 <#ftl output_format="HTML" >
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign workbook = ExcelTool.parse(dataSource)>
+<#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
 <#--------------------------------------------------------------------------->
 <!DOCTYPE html>
@@ -303,7 +303,7 @@ The provided FTL transforms an Excel into a HTML document supporting multiple Ex
 <#-- writeSheets                                                           -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheets workbook>
-    <#assign sheets = ExcelTool.getSheets(workbook)>
+    <#assign sheets = tools.excel.getSheets(workbook)>
     <#list sheets as sheet>
         <@writeSheet sheet/>
     </#list>
@@ -313,7 +313,7 @@ The provided FTL transforms an Excel into a HTML document supporting multiple Ex
 <#-- writeSheet                                                            -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheet sheet>
-    <#assign rows = ExcelTool.toTable(sheet)>
+    <#assign rows = tools.excel.toTable(sheet)>
     <h2>${sheet.getSheetName()}</h2>
     <@writeRows rows/>
 </#macro>
@@ -372,8 +372,8 @@ The FTL uses a couple of interesting features
 <#ftl output_format="plainText" strip_text="true">
 <#compress>
     TENANT,SITE,USER_ID,DISPOSER_ID,PASSWORD,SMS_OTP,NAME,DESCRIPTION
-    <#list DataSources.list as dataSource>
-        <#assign properties = PropertiesTool.parse(dataSource)>
+    <#list dataSources.list as dataSource>
+        <#assign properties = tools.properties.parse(dataSource)>
         <#assign environments = properties["ENVIRONMENTS"]!"">
         <#assign tenant = extractTenant(environments)>
         <#assign site = extractSite(environments)>
@@ -402,10 +402,10 @@ For a POC (proof of concept) I created a sample transformation from CSV to XML-F
 
 ```text
 <#ftl output_format="XML" >
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign cvsFormat = CSVTool.formats.DEFAULT.withDelimiter('\t').withHeader()>
-<#assign csvParser = CSVTool.parse(dataSource, cvsFormat)>
+<#assign cvsFormat = tools.csv.formats.DEFAULT.withDelimiter('\t').withHeader()>
+<#assign csvParser = tools.csv.parse(dataSource, cvsFormat)>
 <#assign csvHeaders = csvParser.getHeaderMap()?keys>
 <#assign csvRecords = csvParser.records>
 <#--------------------------------------------------------------------------->
@@ -523,8 +523,8 @@ 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.get(0)>
-<#assign html = JsoupTool.parse(dataSource)>
+<#assign dataSource = dataSources?values[0]>
+<#assign html = tools.jsoup.parse(dataSource)>
 
 <#compress>
     <@writeHeader/>
@@ -597,10 +597,10 @@ and the final FTL is found below
 
 ```text
 <#ftl output_format="plainText">
-<#assign cvsFormat = CSVTool.formats["DEFAULT"].withHeader()>
-<#assign csvParser = CSVTool.parse(DataSources.get(0), cvsFormat)>
+<#assign cvsFormat = tools.csv.formats["DEFAULT"].withHeader()>
+<#assign csvParser = tools.csv.parse(dataSources?values[0], cvsFormat)>
 <#assign records = csvParser.records>
-<#assign csvMap = CSVTool.toMap(records, "disposer")>
+<#assign csvMap = tools.csv.toMap(records, "disposer")>
 <#--------------------------------------------------------------------------->
 #!/bin/sh
 
@@ -657,7 +657,7 @@ Think of `Grok` as modular regular expressions with a pre-defined functionality
 QUOTEDSTRING (?>(?<!\\)(?>"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))
 ```
 
-And with `Grok` the `QUOTEDSTRING` is just a building block for an even more complex regular expession such as `COMBINEDAPACHELOG`
+And with `Grok` the `QUOTEDSTRING` is just a building block for an even more complex regular expression such as `COMBINEDAPACHELOG`
 
 > bin/freemarker-cli -t examples/templates/accesslog/combined-access.ftl examples/data/accesslog/combined-access.log 
 
@@ -681,8 +681,8 @@ using the following FreeMarker template
 
 ```text
 <#ftl output_format="plainText" strip_whitespace=true>
-<#assign grok = GrokTool.compile("%{COMBINEDAPACHELOG}")>
-<#assign dataSource = DataSources.get(0)>
+<#assign grok = tools.grok.compile("%{COMBINEDAPACHELOG}")>
+<#assign dataSource = dataSources?values[0]>
 <#assign lines = dataSource.getLineIterator()>
 
 <#compress>
@@ -700,7 +700,7 @@ using the following FreeMarker template
 
 While this looks small and tidy there are some nifty features
 
-* `GrokTool.compile("%{COMBINEDAPACHELOG}")` builds the `Grok` instance to parse access logs in `Combined Format`
+* `tools.grok.compile("%{COMBINEDAPACHELOG}")` builds the `Grok` instance to parse access logs in `Combined Format`
 * The data source is streamed line by line and not loaded into memory in one piece
 * This also works for using `stdin` so are able to parse GB of access log or other files
 
@@ -712,7 +712,7 @@ A few snippets to illustrate the points
 
 ```text
 <#ftl output_format="plainText" strip_whitespace="true">
-<#assign profile = SystemTool.getProperty("profile", "default")>
+<#assign profile = tools.system.getProperty("profile", "default")>
 <#assign ec2Instances = ec2Instances()/>
 
 h3. AWS EC2 Instance
@@ -725,8 +725,8 @@ h3. AWS EC2 Instance
 </#function>
 
 <#function awsCliToJson line>
-    <#local output = ExecTool.execute(line)>
-    <#return JsonPathTool.parse(output)>
+    <#local output = tools.exec.execute(line)>
+    <#return tools.jsonpath.parse(output)>
 </#function>
 
 <#function getAwsEc2InstanceTag tags name>
@@ -765,22 +765,22 @@ h3. AWS EC2 Instance
 Sometime you need to apply a CSS, JSON or XPath query in ad ad-hoc way without installing `xmllint`, `jq` or `pup` - in this case you can pass a FreeMarker template in an interactive fashion
 
 ```text
-> bin/freemarker-cli -i 'Hello ${SystemTool.envs["USER"]}'; echo
+> bin/freemarker-cli -i 'Hello ${tools.system.envs["USER"]}'; echo
 Hello sgoeschl
 
-> bin/freemarker-cli -i '${JsonPathTool.parse(DataSources.get(0)).read("$.info.title")}' examples/data/json/swagger-spec.json; echo
+> bin/freemarker-cli -i '${tools.jsonpath.parse(dataSources?values[0]).read("$.info.title")}' examples/data/json/swagger-spec.json; echo
 Swagger Petstore
 
-> bin/freemarker-cli -i 'Post Title : ${JsonPathTool.parse(DataSources.get(0)).read("$.title")}' https://jsonplaceholder.typicode.com/posts/2; echo
+> bin/freemarker-cli -i 'Post Title : ${tools.jsonpath.parse(dataSources?values[0]).read("$.title")}' https://jsonplaceholder.typicode.com/posts/2; echo
 Post Title : qui est esse
 
-> bin/freemarker-cli -i '${XmlTool.parse(DataSources.get(0))["recipients/person[1]/name"]}' examples/data/xml/recipients.xml; echo
+> bin/freemarker-cli -i '${tools.xml.parse(dataSources?values[0])["recipients/person[1]/name"]}' examples/data/xml/recipients.xml; echo
 John Smith
 
-> bin/freemarker-cli -i '${JsoupTool.parse(DataSources.get(0)).select("a")[0]}' examples/data/html/dependencies.html; echo
+> bin/freemarker-cli -i '${tools.jsoup.parse(dataSources?values[0]).select("a")[0]}' examples/data/html/dependencies.html; echo
 <a href="${project.url}" title="FreeMarker CLI">FreeMarker CLI</a>
 
-> freemarker-cli -i '<#list SystemTool.envs as name,value>${name} ==> ${value}${"\n"}</#list>'
+> freemarker-cli -i '<#list tools.system.envs as name,value>${name} ==> ${value}${"\n"}</#list>'
 TERM ==> xterm-256color
 LANG ==> en_US
 DISPLAY ==> :0.0
@@ -804,11 +804,11 @@ and Apache FreeMarker template
 
 ```text
 <#ftl output_format="plainText" strip_text="true">
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign parser = parser(dataSource)>
 <#assign headers = parser.getHeaderNames()>
-<#assign column = SystemTool.getParameter("column")>
-<#assign values = SystemTool.getParameter("values")?split(",")>
+<#assign column = tools.system.getParameter("column")>
+<#assign values = tools.system.getParameter("values")?split(",")>
 
 <#compress>
     <@writePageHeader dataSource/>
@@ -821,9 +821,9 @@ and Apache FreeMarker template
 </#compress>
 
 <#function parser dataSource>
-    <#assign format = CSVTool.formats[SystemTool.getParameter("format", "DEFAULT")]>
-    <#assign delimiter = CSVTool.toDelimiter(SystemTool.getParameter("delimiter", format.getDelimiter()))>
-    <#return CSVTool.parse(dataSource, format.withFirstRecordAsHeader().withDelimiter(delimiter))>
+    <#assign format = tools.csv.formats[tools.system.getParameter("format", "DEFAULT")]>
+    <#assign delimiter = tools.csv.toDelimiter(tools.system.getParameter("delimiter", format.getDelimiter()))>
+    <#return tools.csv.parse(dataSource, format.withFirstRecordAsHeader().withDelimiter(delimiter))>
 </#function>
 
 <#function filter record>
@@ -890,12 +890,12 @@ Sometimes we simply need to transform a JSON into an equivalent YAML or the othe
 
 ```
 > freemarker-cli -t templates/yaml/json/transform.ftl examples/data/yaml/swagger-spec.yaml 
-> freemarker-cli -i '${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}' examples/data/yaml/swagger-spec.yaml
-> freemarker-cli -i '${GsonTool.toJson(yaml)}' -m yaml=examples/data/yaml/swagger-spec.yaml
+> freemarker-cli -i '${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}' examples/data/yaml/swagger-spec.yaml
+> freemarker-cli -i '${tools.gson.toJson(yaml)}' -m yaml=examples/data/yaml/swagger-spec.yaml
 
 > freemarker-cli -t templates/json/yaml/transform.ftl examples/data/json/swagger-spec.json
-> freemarker-cli -i '${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}' examples/data/json/swagger-spec.json
-> freemarker-cli -i '${YamlTool.toYaml(json)}' -m json=examples/data/json/swagger-spec.json
+> freemarker-cli -i '${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}' examples/data/json/swagger-spec.json
+> freemarker-cli -i '${tools.yaml.toYaml(json)}' -m json=examples/data/json/swagger-spec.json
 ```
 
 ### 15. Using Advanced FreeMarker Features
@@ -1033,40 +1033,27 @@ List all files containing "README" in the name
 List all files having "md" extension
 Get all documents
 
-12) FreeMarker CLI Tools
+12) Document Data Model
 ---------------------------------------------------------------------------
-- CSVTool              : Process CSV files using Apache Commons CSV (see https://commons.apache.org/proper/commons-csv/)
-- DataFrameTool        : Bridge to nRo/DataFrame (see https://github.com/nRo/DataFrame)
-- ExcelTool            : Process Excels files (XLS, XLSX) using Apache POI (see https://poi.apache.org)
-- ExecTool             : Execute command line tools using Apache Commons Exec (see https://commons.apache.org/proper/commons-exec/)
-- FreeMarkerTool       : Expose useful Apache FreeMarker classes
-- GrokTool             : Process text files using Grok expressions (see https://github.com/thekrakken/java-grok)
-- GsonTool             : Process JSON files using GSON (see https://github.com/google/gson)
-- JsonPathTool         : Process JSON files using Java JSON Path (see https://github.com/json-path/JsonPath)
-- JsoupTool            : Process  HTML files using Jsoup (see https://jsoup.org)
-- PropertiesTool       : Process JDK properties files
-- SystemTool           : Expose System-related utility methods
-- UUIDTool             : Create UUIDs
-- XmlTool              : Process XML files using Apache FreeMarker (see https://freemarker.apache.org/docs/xgui.html)
-- YamlTool             : Process YAML files using SnakeYAML(see https://bitbucket.org/asomov/snakeyaml/wiki/Home)
-
-13) Document Data Model
+- dataSources
+- tools
+
+13) FreeMarker CLI Tools
 ---------------------------------------------------------------------------
-- CSVTool
-- DataFrameTool
-- DataSources
-- ExcelTool
-- ExecTool
-- FreeMarkerTool
-- GrokTool
-- GsonTool
-- JsonPathTool
-- JsoupTool
-- PropertiesTool
-- SystemTool
-- UUIDTool
-- XmlTool
-- YamlTool
+- csv                  : Process CSV files using Apache Commons CSV (see https://commons.apache.org/proper/commons-csv/)
+- dataframe            : Bridge to [nRo/DataFrame](https://github.com/nRo/DataFrame)
+- excel                : Process Excels files (XLS, XLSX) using Apache POI (see https://poi.apache.org)
+- exec                 : Execute command line tools using Apache Commons Exec (see https://commons.apache.org/proper/commons-exec/)
+- freemarker           : Expose advanced Apache FreeMarker classes
+- grok                 : Process text files using Grok expressions (see https://github.com/thekrakken/java-grok)
+- gson                 : Process JSON files using GSON (see https://github.com/google/gson)
+- jsonpath             : Process JSON files using Java JSON Path (see https://github.com/json-path/JsonPath)
+- jsoup                : Process  HTML files using Jsoup (see https://jsoup.org)
+- properties           : Process JDK properties files
+- system               : Expose System-related utility methods
+- uuid                 : Create UUIDs
+- xml                  : Process XML files using Apache FreeMarker (see https://freemarker.apache.org/docs/xgui.html)
+- yaml                 : Process YAML files using SnakeYAML(see https://bitbucket.org/asomov/snakeyaml/wiki/Home)
 
 14) Create a UUID
 ---------------------------------------------------------------------------
diff --git a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-csv.md b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-csv.md
index e6c056d..a891c38 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-csv.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-csv.md
@@ -26,8 +26,8 @@ freemarker-cli \
 The command line invocation seems a bit complex at first so let's look at it more closely
 
 * `CSV_SOURCE_FORMAT` defines the CSV source format for reading the CSV
-* `CSV_TARGET_FORMAT` defines the CSV tagrte format for writing the CSV
-* `CSV_TARGET_DELIMITER` explicitely sets the delimiter of the target CSV to a semicolon since this expected by Excel for my current locale
+* `CSV_TARGET_FORMAT` defines the CSV target format for writing the CSV
+* `CSV_TARGET_DELIMITER` explicitly sets the delimiter of the target CSV to a semicolon since this expected by Excel for my current locale
 
 ### Convert CSV To Markdown
 
@@ -65,7 +65,7 @@ The following options can be passed to template (as user-supplied parameters)
 | CSV_SOURCE_FORMAT         | DEFAULT           | Source CSV format                                         |
 | CSV_SOURCE_DELIMITER      | COMMA             | Symbolic name of delimiter, e.g. "COLON" or "SEMICOLON"   |
 | CSV_SOURCE_WITH_HEADER    | true              | Whether the first rows are headers                        |
-| CSV_TAGRGET_FORMAT        | DEFAULT           | Target CSV format                                         |
+| CSV_TARGET_FORMAT         | DEFAULT           | Target CSV format                                         |
 | CSV_TARGET_DELIMITER      | COMMA             | Symbolic name of delimiter, e.g. "COLON" or "SEMICOLON"   |
 | CSV_TARGET_WITH_HEADER    | true              | Whether the first rows are headers                        |
                                                             
diff --git a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
index ad694fb..073a7e9 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
@@ -176,11 +176,11 @@ server {
 In the cloud it is common to pass JSON configuration as environment variable
 
 * `env:///NGINX_CONF` selects the `NGINX_CONF` environment variable
-* `#mimetype=application/json` defines that JSON content is parsed
+* `#mimeType=application/json` defines that JSON content is parsed
 
 ```
 export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}'
-freemarker-cli -t examples/data/template/ -m env:///NGINX_CONF#mimetype=application/json
+freemarker-cli -t examples/data/template/ -m env:///NGINX_CONF#mimeType=application/json
 # == application.properties ==================================================
 server.name=localhost
 server.logs=/var/log/nginx
@@ -200,7 +200,7 @@ For testing purpose it is useful to override certain settings
 
 ```
 export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}'
-freemarker-cli -t examples/data/template/ -PNGINX_HOSTNAME=www.mydomain.com -m env:///NGINX_CONF#mimetype=application/json
+freemarker-cli -t examples/data/template/ -PNGINX_HOSTNAME=www.mydomain.com -m env:///NGINX_CONF#mimeType=application/json
 # == application.properties ==================================================
 server.name=www.mydomain.com
 server.logs=/var/log/nginx
@@ -214,4 +214,4 @@ server {
 }
 ```
 
-Please note that this only works for "top-level" variables, i.e. mimicking enviroment variables or property files. 
\ No newline at end of file
+Please note that this only works for "top-level" variables, i.e. mimicking environment variables or property files. 
\ No newline at end of file
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 25c0c0e..ff19878 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,9 +30,9 @@ Meier;30;Germany
 and create a `DateFrame` using the following code snippet
 
 ```
-<#assign dataSource = DataSources.get(0)>
-<#assign csvParser = CSVTool.parse(dataSource, CSVTool.formats["DATAFRAME"])>
-<#assign users = DataFrameTool.fromCSVParser(csvParser)>
+<#assign dataSource = dataSources?values[0]>
+<#assign csvParser = tools.csv.parse(dataSource, tools.csv.formats["DATAFRAME"])>
+<#assign users = tools.dataframe.fromCSVParser(csvParser)>
 ```
 
 The example can be executed by running
@@ -50,7 +50,7 @@ bin/freemarker-cli -PCSV_SOURCE_FORMAT=DATAFRAME -t examples/templates/dataframe
 #### Select By Age
 
 ```
-${DataFrameTool.print(users.select("(age > 40)"))}
+${tools.dataframe.print(users.select("(age > 40)"))}
 ```
 
 which shows 
@@ -71,9 +71,9 @@ Now we want to create a new `DataFrame` by selecting `name` and `country`
 
 ```
 <#assign country = "Germany">
-${DataFrameTool.print(users
+${tools.dataframe.print(users
     .select("(name == 'Schmitt' || name == 'Meier') && country == '${country}'")
-    .sort("name", DataFrameTool.sortOrder["ASCENDING"]))}
+    .sort("name", tools.dataframe.sortOrder["ASCENDING"]))}
 ```
 
 which shows
@@ -99,7 +99,7 @@ which shows
 Let's assume we want to count the records for each `country`
 
 ```
-${DataFrameTool.print(users.getColumn("country").transform(DataFrameTool.transformer["COUNT"]))}
+${tools.dataframe.print(users.getColumn("country").transform(tools.dataframe.transformer["COUNT"]))}
 ```
 
 returns the following `DataFrame`
@@ -123,7 +123,7 @@ returns the following `DataFrame`
 Let's assume that we want to group the `DataFrame` by `age` and `country`
 
 ```
-${DataFrameTool.print(users.groupBy("age", "country").sort("age"))}
+${tools.dataframe.print(users.groupBy("age", "country").sort("age"))}
 ``` 
 
 which results in 
@@ -152,11 +152,11 @@ which results in
 
 Here we load a `examples/data/json/github-users.json` which represents a tabular 
 data being parsed as a list of maps and print the JSON as dataframe. Technically
-it is a list of maps hence we invoke `DataFrameTool.fromMaps()
+it is a list of maps hence we invoke `tools.dataframe.fromMaps()
 
 ```
 freemarker-cli \
-  -i '${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(0))))}' \
+  -i '${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))}' \
   examples/data/json/github-users.json
 
 ┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
@@ -183,12 +183,12 @@ freemarker-cli \
 Let's transform an Excel Sheet to a `DataFrame` being printed using the following template
 
 ```
-<#assign dataSource = DataSources.get(0)>
-<#assign workbook = ExcelTool.parse(dataSource)>
-<#list ExcelTool.getSheets(workbook) as sheet>
-    <#assign table = ExcelTool.toTable(sheet)>
-    <#assign df = DataFrameTool.fromRows(table, true)>
-    ${DataFrameTool.print(df)}<#t>
+<#assign dataSource = dataSources?values[0]>
+<#assign workbook = tools.excel.parse(dataSource)>
+<#list tools.excel.getSheets(workbook) as sheet>
+    <#assign table = tools.excel.toTable(sheet)>
+    <#assign df = tools.dataframe.fromRows(table, true)>
+    ${tools.dataframe.print(df)}<#t>
 </#list>
 ```
 
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 9ddd261..3548b78 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
@@ -21,6 +21,7 @@ import org.junit.Test;
 
 import java.io.IOException;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -110,14 +111,14 @@ public class ExamplesTest extends AbstractMainTest {
 
     @Test
     public void shouldRunInteractiveTemplateExamples() throws IOException {
-        assertValid(execute("-i ${JsonPathTool.parse(DataSources.get(0)).read(\"$.info.title\")} examples/data/json/swagger-spec.json"));
-        assertValid(execute("-i ${XmlTool.parse(DataSources.get(0))[\"recipients/person[1]/name\"]} examples/data/xml/recipients.xml"));
-        assertValid(execute("-i ${JsoupTool.parse(DataSources.get(0)).select(\"a\")[0]} examples/data/html/dependencies.html"));
-        assertValid(execute("-i ${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))} examples/data/yaml/swagger-spec.yaml"));
-        assertValid(execute("-i ${GsonTool.toJson(yaml)} -m yaml=examples/data/yaml/swagger-spec.yaml"));
-        assertValid(execute("-i ${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))} examples/data/json/swagger-spec.json"));
-        assertValid(execute("-i ${YamlTool.toYaml(json)} -m json=examples/data/json/swagger-spec.json"));
-        assertValid(execute("-i ${DataFrameTool.print(DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(0))))} examples/data/json/github-users.json"));
+        assertValid(execute("-i ${tools.jsonpath.parse(dataSources?values[0]).read(\"$.info.title\")} examples/data/json/swagger-spec.json"));
+        assertValid(execute("-i ${tools.xml.parse(dataSources?values[0])[\"recipients/person[1]/name\"]} examples/data/xml/recipients.xml"));
+        assertValid(execute("-i ${tools.jsoup.parse(dataSources?values[0]).select(\"a\")[0]} examples/data/html/dependencies.html"));
+        assertValid(execute("-i ${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))} examples/data/yaml/swagger-spec.yaml"));
+        assertValid(execute("-i ${tools.gson.toJson(yaml)} -m yaml=examples/data/yaml/swagger-spec.yaml"));
+        assertValid(execute("-i ${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))} examples/data/json/swagger-spec.json"));
+        assertValid(execute("-i ${tools.yaml.toYaml(json)} -m json=examples/data/json/swagger-spec.json"));
+        assertValid(execute("-i ${tools.dataframe.print(tools.dataframe.fromMaps(tools.gson.parse(dataSources?values[0])))} examples/data/json/github-users.json"));
     }
 
     @Test
@@ -139,6 +140,36 @@ public class ExamplesTest extends AbstractMainTest {
     }
 
     @Test
+    public void shouldSupportDataSourcesAccessInFTL() throws IOException {
+        final String args = "users=examples/data/json/github-users.json contract=examples/data/csv/contract.csv";
+
+        // check FreeMarker directives
+        assertEquals("true", execute(args + " -i ${dataSources?has_content?c}"));
+        assertEquals("2", execute(args + " -i ${dataSources?size}"));
+
+        // check FTL map-style access
+        assertEquals("users", execute(args + " -i ${dataSources.users.name}"));
+        assertEquals("users", execute(args + " -i ${dataSources[\"users\"].name}"));
+        assertEquals("application/json", execute(args + " -i ${dataSources[\"users\"].mimeType}"));
+    }
+
+    /**
+     * @Test public void shouldNotShadowDataSourcesInFTL() throws IOException {
+     * final String args = "empty=examples/data/json/github-users.json";
+     * <p>
+     * // check shadowing of "isEmpty"
+     * assertEquals("false", execute("empty=examples/data/json/github-users.json -i ${dataSources.empty?c}"));
+     * // DataSources#isEmpty shadows the data source "empty"
+     * // assertEquals("false", execute("empty=examples/data/json/github-users.json -i ${DataSources[\"empty\"]}"));
+     * assertEquals("empty", execute("empty=examples/data/json/github-users.json -i ${dataSources.get(\"empty\").name}"));
+     * <p>
+     * // check shadowing of "find"
+     * // assertEquals("find", execute("find=examples/data/json/github-users.json -i ${dataSources.find.name}"));
+     * // assertEquals("find", execute("find=examples/data/json/github-users.json -i ${DataSources[\"find\"].name}"));
+     * }
+     */
+
+    @Test
     @Ignore("Manual test to check memory consumption and resource handling")
     public void shouldCloseAllResources() throws IOException {
         for (int i = 0; i < 500; i++) {
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 ab10101..06f8714 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
@@ -28,7 +28,8 @@ public class ManualTest {
     // private static final String CMD = "-PCSV_SOURCE_FORMAT=DATAFRAME -t examples/templates/dataframe/example.ftl https://raw.githubusercontent.com/nRo/DataFrame/master/src/test/resources/users.csv";
     // private static final String CMD = "-PCSV_SOURCE_WITH_HEADER=false -PCSV_SOURCE_FORMAT=DEFAULT -PCSV_TARGET_FORMAT=EXCEL -PCSV_TARGET_WITH_HEADER=true -t templates/csv/csv/transform.ftl examples/data/csv/contract.csv";
     // private static final String CMD = "-t examples/templates/json/dataframe/github-users.ftl examples/data/json/github-users.json";
-    private static final String CMD = "-t templates/csv/md/transform.ftl -o target/contract.md -t templates/csv/html/transform.ftl examples/data/csv/contract.csv";
+    // private static final String CMD = "-t templates/csv/md/transform.ftl -o target/contract.md -t templates/csv/html/transform.ftl examples/data/csv/contract.csv";
+    private static final String CMD = "-t examples/templates/demo.ftl src/test/data";
 
 
     public static void main(String[] args) {
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 baae451..93ddfa2 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
@@ -43,7 +43,7 @@ public class ConfigurationSupplierTest {
         assertTrue(configuration.isOutputEncodingSet());
 
         assertFalse(configuration.isCacheStorageExplicitlySet());
-        assertFalse(configuration.isObjectWrapperExplicitlySet());
+        assertTrue(configuration.isObjectWrapperExplicitlySet());
         assertFalse(configuration.isOutputFormatExplicitlySet());
         assertFalse(configuration.isTemplateExceptionHandlerExplicitlySet());
         assertFalse(configuration.isTimeZoneExplicitlySet());
diff --git a/freemarker-generator-cli/src/test/templates/echo.ftl b/freemarker-generator-cli/src/test/templates/echo.ftl
index 2614f49..38627f4 100644
--- a/freemarker-generator-cli/src/test/templates/echo.ftl
+++ b/freemarker-generator-cli/src/test/templates/echo.ftl
@@ -15,8 +15,8 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#list DataSources.list as dataSource>
-${dataSource.name}, ${dataSource.uri}
+<#list dataSources?values as ds>
+${ds.name}, ${ds.uri}
 =============================================================================
-${dataSource.text}
+${ds.text}
 </#list>
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/test/templates/manual.ftl b/freemarker-generator-cli/src/test/templates/manual.ftl
index b381b05..128274e 100644
--- a/freemarker-generator-cli/src/test/templates/manual.ftl
+++ b/freemarker-generator-cli/src/test/templates/manual.ftl
@@ -15,6 +15,51 @@
   specific language governing permissions and limitations
   under the License.
 -->
-Manual Test
+Support FreeMarker Directives
 ---------------------------------------------------------------------------
-<#assign df=DataFrameTool.fromMaps(GsonTool.parse(DataSources.get(0)))>${DataFrameTool.print(df)}
\ No newline at end of file
+Has Content: ${dataSources?has_content?c}
+Nr. of Documents: ${dataSources?size}
+
+Use FTL Array-style Access
+---------------------------------------------------------------------------
+${dataSources[0].toString()}
+
+Use FTL Map-style access
+---------------------------------------------------------------------------
+${DataSources["github-users.json"].toString()}
+${DataSources["github-users.json"].name}
+
+Get Document Names As Keys
+---------------------------------------------------------------------------
+<#list DataSources?keys as name>
+    ${name}<#lt>
+</#list>
+
+Iterate Over Names & DataSources
+---------------------------------------------------------------------------
+<#list DataSources as name, dataSource>
+    ${name} => ${dataSource}<#lt>
+</#list>
+
+Find DataSources By Group
+---------------------------------------------------------------------------
+<#list dataSources.findByGroup("default") as dataSource>
+    ${dataSource}<#lt>
+</#list>
+
+Find DataSources By Wildcard
+---------------------------------------------------------------------------
+<#list dataSources.find("*.csv") as dataSource>
+    ${dataSource}<#lt>
+</#list>
+
+Java Array-style access
+---------------------------------------------------------------------------
+${dataSources?values[0].toString()}
+
+Invoke Arbitrary Methods On DataSources
+---------------------------------------------------------------------------
+empty       : ${dataSources.empty?c}
+isEmpty()   : ${dataSources.isEmpty()?c}
+size()      : ${dataSources.size()}
+close()     : ${dataSources.close()}worx
diff --git a/freemarker-generator-cli/src/test/templates/tools/csv.ftl b/freemarker-generator-cli/src/test/templates/tools/csv.ftl
index f34cb6a..25dfb3b 100644
--- a/freemarker-generator-cli/src/test/templates/tools/csv.ftl
+++ b/freemarker-generator-cli/src/test/templates/tools/csv.ftl
@@ -15,32 +15,32 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign records = CSVTool.parse(DataSources.get(0), CSVFormat.DEFAULT.withHeader()).records>
+<#assign records = tools.csv.parse(dataSources?values[0], CSVFormat.DEFAULT.withHeader()).records>
 
-CSVTool.toMap(name)
+tools.csv.toMap(name)
 =============================================================================
-<#assign map = CSVTool.toMap(records, "contract_id")>
+<#assign map = tools.csv.toMap(records, "contract_id")>
 <#list map as key, record>
 ${key} ==> ${record.get("customer_company_name")}
 </#list>
 
-CSVTool.toMap(index)
+tools.csv.toMap(index)
 =============================================================================
-<#assign map = CSVTool.toMap(records, 0)>
+<#assign map = tools.csv.toMap(records, 0)>
 <#list map as key, record>
 ${key} ==> ${record.get("customer_company_name")}
 </#list>
 
-CSVTool.toMultiMap(name)
+tools.csv.toMultiMap(name)
 =============================================================================
-<#assign map = CSVTool.toMultiMap(records, "contract_id")>
+<#assign map = tools.csv.toMultiMap(records, "contract_id")>
 <#list map as key, records>
 ${key} ==> Found ${records?size} records
 </#list>
 
-CSVTool.toMultiMap(index)
+tools.csv.toMultiMap(index)
 =============================================================================
-<#assign map = CSVTool.toMultiMap(records, 0)>
+<#assign map = tools.csv.toMultiMap(records, 0)>
 <#list map as key, records>
 ${key} ==> Found ${records?size} records
 </#list>
diff --git a/freemarker-generator-cli/templates/cat.ftl b/freemarker-generator-cli/templates/cat.ftl
index c912c12..cdbe0fa 100644
--- a/freemarker-generator-cli/templates/cat.ftl
+++ b/freemarker-generator-cli/templates/cat.ftl
@@ -14,7 +14,7 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#list DataSources.list as dataSource>
+<#list dataSources?values as dataSource>
 <#list dataSource.lineIterator as line>
 ${line}
 </#list>
diff --git a/freemarker-generator-cli/templates/csv/csv/transform.ftl b/freemarker-generator-cli/templates/csv/csv/transform.ftl
index ef8cf26..b02082d 100644
--- a/freemarker-generator-cli/templates/csv/csv/transform.ftl
+++ b/freemarker-generator-cli/templates/csv/csv/transform.ftl
@@ -16,10 +16,10 @@
   under the License.
 -->
 <#import "/templates/lib/commons-csv.ftl" as csv />
-<#assign dataSource = DataSources.get(0)>
-<#assign csvParser = CSVTool.parse(dataSource, csv.sourceFormat())>
+<#assign dataSource = dataSources?values[0]>
+<#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign csvTargetFormat = csv.targetFormat()>
-<#assign csvPrinter = CSVTool.printer(csvTargetFormat)>
+<#assign csvPrinter = tools.csv.printer(csvTargetFormat)>
 <#assign csvHeaders = (csvParser.getHeaderMap()!{})?keys>
 <#if csvHeaders?has_content && csvTargetFormat.getSkipHeaderRecord()>
     ${csvPrinter.printRecord(csvHeaders)}<#t>
diff --git a/freemarker-generator-cli/templates/csv/html/transform.ftl b/freemarker-generator-cli/templates/csv/html/transform.ftl
index cda4cd6..7c52f73 100644
--- a/freemarker-generator-cli/templates/csv/html/transform.ftl
+++ b/freemarker-generator-cli/templates/csv/html/transform.ftl
@@ -16,8 +16,8 @@
   under the License.
 -->
 <#import "/templates/lib/commons-csv.ftl" as csv />
-<#assign dataSource = DataSources.get(0)>
-<#assign csvParser = CSVTool.parse(dataSource, csv.sourceFormat())>
+<#assign dataSource = dataSources?values[0]>
+<#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign csvHeaders = csvParser.getHeaderNames()>
 <#--------------------------------------------------------------------------->
 <!DOCTYPE html>
diff --git a/freemarker-generator-cli/templates/csv/md/transform.ftl b/freemarker-generator-cli/templates/csv/md/transform.ftl
index 2766125..ec50a98 100644
--- a/freemarker-generator-cli/templates/csv/md/transform.ftl
+++ b/freemarker-generator-cli/templates/csv/md/transform.ftl
@@ -15,8 +15,8 @@
   under the License.
 -->
 <#import "/templates/lib/commons-csv.ftl" as csv />
-<#assign dataSource = DataSources.get(0)>
-<#assign csvParser = CSVTool.parse(dataSource, csv.sourceFormat())>
+<#assign dataSource = dataSources?values[0]>
+<#assign csvParser = tools.csv.parse(dataSource, csv.sourceFormat())>
 <#assign headers = (csvParser.getHeaderMap()!{})?keys>
 <#assign records = csvParser.records>
 <#--------------------------------------------------------------------------->
diff --git a/freemarker-generator-cli/templates/excel/csv/transform.ftl b/freemarker-generator-cli/templates/excel/csv/transform.ftl
index 794e942..0138bf5 100644
--- a/freemarker-generator-cli/templates/excel/csv/transform.ftl
+++ b/freemarker-generator-cli/templates/excel/csv/transform.ftl
@@ -17,11 +17,11 @@
 -->
 <#-- Parse the first data source & sheet of the Excel document -->
 <#import "/templates/lib/commons-csv.ftl" as csv />
-<#assign workbook = ExcelTool.parse(DataSources.get(0))>
-<#assign sheet = ExcelTool.getSheets(workbook)[0]>
-<#assign records = ExcelTool.toTable(sheet)>
+<#assign workbook = tools.excel.parse(dataSources?values[0])>
+<#assign sheet = tools.excel.getSheets(workbook)[0]>
+<#assign records = tools.excel.toTable(sheet)>
 <#-- Setup CSVPrinter  -->
-<#assign csvPrinter = CSVTool.printer(csv.targetFormat())>
+<#assign csvPrinter = tools.csv.printer(csv.targetFormat())>
 <#-- Print each line of the Excel as CSV record -->
 <#compress>
     <#list records as record>
@@ -30,8 +30,8 @@
 </#compress>
 <#--------------------------------------------------------------------------->
 <#function csvOutFormat>
-    <#assign format = CSVTool.formats[CSV_TARGET_FORMAT!"DEFAULT"]>
-    <#assign delimiter = CSVTool.toDelimiter(CSV_TARGET_DELIMITER!format.getDelimiter())>
+    <#assign format = tools.csv.formats[CSV_TARGET_FORMAT!"DEFAULT"]>
+    <#assign delimiter = tools.csv.toDelimiter(CSV_TARGET_DELIMITER!format.getDelimiter())>
     <#assign withHeader = CSV_TARGET_WITH_HEADER!"false">
     <#assign format = format.withDelimiter(delimiter)>
     <#if withHeader?boolean>
diff --git a/freemarker-generator-cli/templates/excel/html/transform.ftl b/freemarker-generator-cli/templates/excel/html/transform.ftl
index 58ace89..7564751 100644
--- a/freemarker-generator-cli/templates/excel/html/transform.ftl
+++ b/freemarker-generator-cli/templates/excel/html/transform.ftl
@@ -15,9 +15,9 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign workbook = ExcelTool.parse(dataSource)>
+<#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
 <#--------------------------------------------------------------------------->
 <!DOCTYPE html>
@@ -42,7 +42,7 @@
 <#-- writeSheets                                                           -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheets workbook>
-    <#assign sheets = ExcelTool.getSheets(workbook)>
+    <#assign sheets = tools.excel.getSheets(workbook)>
     <#list sheets as sheet>
         <@writeSheet sheet/>
     </#list>
@@ -52,7 +52,7 @@
 <#-- writeSheet                                                            -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheet sheet>
-    <#assign rows = ExcelTool.toTable(sheet)>
+    <#assign rows = tools.excel.toTable(sheet)>
     <h2>${sheet.getSheetName()}</h2>
     <@writeRows rows/>
 </#macro>
diff --git a/freemarker-generator-cli/templates/excel/md/transform.ftl b/freemarker-generator-cli/templates/excel/md/transform.ftl
index 8f84165..e3fb970 100644
--- a/freemarker-generator-cli/templates/excel/md/transform.ftl
+++ b/freemarker-generator-cli/templates/excel/md/transform.ftl
@@ -15,9 +15,9 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<#assign dataSource = DataSources.get(0)>
+<#assign dataSource = dataSources?values[0]>
 <#assign name = dataSource.name>
-<#assign workbook = ExcelTool.parse(dataSource)>
+<#assign workbook = tools.excel.parse(dataSource)>
 <#assign date = .now?iso_utc>
 
 <#compress>
@@ -30,7 +30,7 @@ ${'\n'}
 <#-- writeSheets                                                           -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheets workbook>
-    <#assign sheets = ExcelTool.getSheets(workbook)>
+    <#assign sheets = tools.excel.getSheets(workbook)>
     <#list sheets as sheet>
         <@writeSheet sheet/>
     </#list>
@@ -40,7 +40,7 @@ ${'\n'}
 <#-- writeSheet                                                            -->
 <#--------------------------------------------------------------------------->
 <#macro writeSheet sheet>
-    <#assign rows = ExcelTool.toTable(sheet)>
+    <#assign rows = tools.excel.toTable(sheet)>
     ## ${sheet.getSheetName()}
     ${'\n'}
     <@writeRows rows/>
diff --git a/freemarker-generator-cli/templates/info.ftl b/freemarker-generator-cli/templates/info.ftl
index d12e68d..10b692c 100644
--- a/freemarker-generator-cli/templates/info.ftl
+++ b/freemarker-generator-cli/templates/info.ftl
@@ -27,37 +27,35 @@ Output format          : ${.output_format}
 
 FreeMarker CLI Template Loader Directories
 ------------------------------------------------------------------------------
-<#list SystemTool.getTemplateDirectories() as directory>
+<#list tools.system.getTemplateDirectories() as directory>
 [#${directory?counter}] ${directory}
 </#list>
 
-FreeMarker CLI Tools
-------------------------------------------------------------------------------
-<#list .data_model?keys?sort as key>
-<#if key?ends_with("Tool")>
-- ${key?right_pad(20)} : ${.data_model[key]}
-</#if>
-</#list>
-
 FreeMarker CLI Data Model
 ---------------------------------------------------------------------------
 <#list .data_model?keys?sort as key>
 - ${key}<#lt>
 </#list>
 
-<#if DataSources.list?has_content>
+FreeMarker CLI Tools
+------------------------------------------------------------------------------
+<#list tools?keys?sort as name>
+- ${name?right_pad(20)} : ${tools[name]}
+</#list>
+
+<#if dataSources?has_content>
 FreeMarker CLI DataSources
 ------------------------------------------------------------------------------
-<#list DataSources.list as dataSource>
-[#${dataSource?counter}], name=${dataSource.name}, group=${dataSource.group}, mimeType=${dataSource.mimetype}, charset=${dataSource.charset}, length=${dataSource.length} Bytes
-URI : ${dataSource.uri}
+<#list dataSources?values as ds>
+[#${ds?counter}]: name=${ds.name}, group=${ds.group}, fileName=${ds.fileName} mimeType=${ds.mimeType}, charset=${ds.charset}, length=${ds.length} Bytes
+URI : ${ds.uri}
 </#list>
 </#if>
 
-<#if SystemTool.parameters?has_content>
+<#if tools.system.parameters?has_content>
 FreeMarker CLI Parameters
 ------------------------------------------------------------------------------
-<#list SystemTool.parameters as key,value>
+<#list tools.system.parameters as key,value>
 <#if value?is_hash>
 - ${key} ==> { <#list value as name,value>${name}=${value} </#list>}
 <#else>
diff --git a/freemarker-generator-cli/templates/json/yaml/transform.ftl b/freemarker-generator-cli/templates/json/yaml/transform.ftl
index 80c8d2f..6e48945 100644
--- a/freemarker-generator-cli/templates/json/yaml/transform.ftl
+++ b/freemarker-generator-cli/templates/json/yaml/transform.ftl
@@ -14,4 +14,4 @@
   specific language governing permissions and limitations
   under the License.
 -->
-${YamlTool.toYaml(GsonTool.parse(DataSources.get(0)))}
\ No newline at end of file
+${tools.yaml.toYaml(tools.gson.parse(dataSources?values[0]))}
\ No newline at end of file
diff --git a/freemarker-generator-cli/templates/lib/commons-csv.ftl b/freemarker-generator-cli/templates/lib/commons-csv.ftl
index 4e3c771..e8b0538 100644
--- a/freemarker-generator-cli/templates/lib/commons-csv.ftl
+++ b/freemarker-generator-cli/templates/lib/commons-csv.ftl
@@ -25,8 +25,8 @@
     * CSV_SOURCE_WITH_HEADER - whether the first rows are headers
 -->
 <#function sourceFormat>
-    <#assign format = CSVTool.formats[CSV_SOURCE_FORMAT!"DEFAULT"]>
-    <#assign delimiter = CSVTool.toDelimiter(CSV_SOURCE_DELIMITER!format.getDelimiter())>
+    <#assign format = tools.csv.formats[CSV_SOURCE_FORMAT!"DEFAULT"]>
+    <#assign delimiter = tools.csv.toDelimiter(CSV_SOURCE_DELIMITER!format.getDelimiter())>
     <#assign withHeader = CSV_SOURCE_WITH_HEADER!"true">
     <#assign format = format.withDelimiter(delimiter)>
     <#if withHeader?boolean>
@@ -44,8 +44,8 @@
     * CSV_TARGET_WITH_HEADER - whether the first rows are headers
 -->
 <#function targetFormat>
-    <#assign format = CSVTool.formats[CSV_TARGET_FORMAT!"DEFAULT"]>
-    <#assign delimiter = CSVTool.toDelimiter(CSV_TARGET_DELIMITER!format.getDelimiter())>
+    <#assign format = tools.csv.formats[CSV_TARGET_FORMAT!"DEFAULT"]>
+    <#assign delimiter = tools.csv.toDelimiter(CSV_TARGET_DELIMITER!format.getDelimiter())>
     <#assign withHeader = CSV_TARGET_WITH_HEADER!"true">
     <#assign format = format.withDelimiter(delimiter)>
     <#if withHeader?boolean>
diff --git a/freemarker-generator-cli/templates/yaml/json/transform.ftl b/freemarker-generator-cli/templates/yaml/json/transform.ftl
index e5e4d90..81a704a 100644
--- a/freemarker-generator-cli/templates/yaml/json/transform.ftl
+++ b/freemarker-generator-cli/templates/yaml/json/transform.ftl
@@ -14,4 +14,4 @@
   specific language governing permissions and limitations
   under the License.
 -->
-${GsonTool.toJson(YamlTool.parse(DataSources.get(0)))}
\ No newline at end of file
+${tools.gson.toJson(tools.yaml.parse(dataSources?values[0]))}
\ No newline at end of file
diff --git a/freemarker-generator-maven-plugin/README.md b/freemarker-generator-maven-plugin/README.md
index f6e186d..631f02b 100644
--- a/freemarker-generator-maven-plugin/README.md
+++ b/freemarker-generator-maven-plugin/README.md
@@ -53,9 +53,9 @@ Add the following snippet within the `<plugins>` tag of your pom.xml:
                     <configuration>
                         <!-- Optional, defaults to src/main/freemarker/generator -->
                         <sourceDirectory>src/main/freemarker/generator</sourceDirectory>
-                        <!-- Optional, defaults to src/main/freemarker/generator/templatee -->
+                        <!-- Optional, defaults to src/main/freemarker/generator/template -->
                         <templateDirectory>src/main/freemarker/generator/template</templateDirectory>
-                        <!-- Optional, defaults to src/main/freemarker/generator/generatorr -->
+                        <!-- Optional, defaults to src/main/freemarker/generator/generator -->
                         <generatorDirectory>src/main/freemarker/generator/generator</generatorDirectory>
                         <!-- Optional, defaults to target/generated-sources/freemarker/generator -->
                         <outputDirectory>target/generated-sources/freemarker/generator</outputDirectory>
diff --git a/freemarker-generator-maven-plugin/src/site/markdown/index.md b/freemarker-generator-maven-plugin/src/site/markdown/index.md
index ca8308f..f79b273 100644
--- a/freemarker-generator-maven-plugin/src/site/markdown/index.md
+++ b/freemarker-generator-maven-plugin/src/site/markdown/index.md
@@ -40,9 +40,9 @@ Add the following snippet within the `<plugins>` tag of your pom.xml:
                     <configuration>
                         <!-- Optional, defaults to src/main/freemarker/generator -->
                         <sourceDirectory>src/main/freemarker/generator</sourceDirectory>
-                        <!-- Optional, defaults to src/main/freemarker/generator/templatee -->
+                        <!-- Optional, defaults to src/main/freemarker/generator/template -->
                         <templateDirectory>src/main/freemarker/generator/template</templateDirectory>
-                        <!-- Optional, defaults to src/main/freemarker/generator/generatorr -->
+                        <!-- Optional, defaults to src/main/freemarker/generator/generator -->
                         <generatorDirectory>src/main/freemarker/generator/generator</generatorDirectory>
                         <!-- Optional, defaults to target/generated-sources/freemarker/generator -->
                         <outputDirectory>target/generated-sources/freemarker/generator</outputDirectory>
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/DataFrameTool.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/DataFrameTool.java
index 640d798..46b2220 100644
--- a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/DataFrameTool.java
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/DataFrameTool.java
@@ -99,6 +99,7 @@ public class DataFrameTool {
      *
      * @return available transformers
      */
+    @SuppressWarnings("rawtypes")
     public Map<String, ColumnDataFrameTransform> getTransformer() {
         final Map<String, ColumnDataFrameTransform> result = new HashMap<>();
         result.put("COUNT", countTransformer(false));
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/impl/ListConverter.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/impl/ListConverter.java
index 4f83d04..bb9cfe0 100644
--- a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/impl/ListConverter.java
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/dataframe/impl/ListConverter.java
@@ -27,7 +27,7 @@ public class ListConverter {
      * Create a data frame from a list of rows. It is assumed
      * that the rows represent tabular data.
      *
-     * @param rows rows to build the data frame
+     * @param rows                      rows to build the data frame
      * @param withFirstRowAsColumnNames column names as first row?
      * @return <code>DataFrame</code>
      */
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index bf0072c..bae1e61 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -1,4 +1,4 @@
-The Apache FreeMarker Generator projects provides additional tools to generate textual ouptut using [Apache FreeMarker](https://freemarker.apache.org).
+The Apache FreeMarker Generator projects provides additional tools to generate textual output using [Apache FreeMarker](https://freemarker.apache.org).
 
 | Name                                                          | Description                                                       |
 | ------------------------------------------------------------- | ----------------------------------------------------------------- |
diff --git a/travis.sh b/travis.sh
index 42dbf88..cba6671 100755
--- a/travis.sh
+++ b/travis.sh
@@ -22,4 +22,4 @@ cd ./freemarker-generator-cli
 sh ./run-examples.sh
 cd ../freemarker-generator-maven-plugin-sample
 mvn clean package
-cd ..
\ No newline at end of file
+cd ..