You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2018/02/10 19:39:44 UTC

[text] [TEXT-113] Add an interpolator string lookup.

Repository: commons-text
Updated Branches:
  refs/heads/master 03cacd843 -> 59984cafa


[TEXT-113] Add an interpolator string lookup.

Project: http://git-wip-us.apache.org/repos/asf/commons-text/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-text/commit/59984caf
Tree: http://git-wip-us.apache.org/repos/asf/commons-text/tree/59984caf
Diff: http://git-wip-us.apache.org/repos/asf/commons-text/diff/59984caf

Branch: refs/heads/master
Commit: 59984cafab8d600627e4358186e145defa26d141
Parents: 03cacd8
Author: Gary Gregory <ga...@gmail.com>
Authored: Sat Feb 10 12:39:40 2018 -0700
Committer: Gary Gregory <ga...@gmail.com>
Committed: Sat Feb 10 12:39:40 2018 -0700

----------------------------------------------------------------------
 src/changes/changes.xml                         |   1 +
 .../java/org/apache/commons/text/StrLookup.java | 440 +++++++++----------
 .../text/lookup/AbstractStringLookup.java       |  39 ++
 .../commons/text/lookup/DateStringLookup.java   |  66 +++
 .../lookup/EnvironmentVariableStringLookup.java |  50 +++
 .../text/lookup/IllegalArgumentExceptions.java  |  41 ++
 .../text/lookup/InterpolatorStringLookup.java   | 116 +++++
 .../text/lookup/JavaPlatformStringLookup.java   | 124 ++++++
 .../commons/text/lookup/MapStringLookup.java    |  80 ++++
 .../commons/text/lookup/NullStringLookup.java   |  30 ++
 .../text/lookup/ResourceBundleStringLookup.java |  72 +++
 .../commons/text/lookup/StringLookup.java       |  66 +++
 .../text/lookup/SystemPropertyStringLookup.java |  54 +++
 .../text/lookup/DateStringLookupTest.java       |  50 +++
 .../EnvironmentVariableStringLookupTest.java    |  37 ++
 .../lookup/InterpolatorStringLookupTest.java    | 101 +++++
 .../lookup/JavaPlatformStringLookupTest.java    |  31 ++
 .../text/lookup/MapStringLookupTest.java        |  37 ++
 .../text/lookup/NullStringLookupTest.java       |  30 ++
 .../lookup/ResourceBundleStringLookupTest.java  |  35 ++
 .../StrSubstitutorWithInterpolatorTest.java     |  47 ++
 .../lookup/SystemPropertyStringLookupTest.java  |  31 ++
 22 files changed, 1347 insertions(+), 231 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index abf1337..eedd7ac 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -48,6 +48,7 @@ The <action> type attribute can be add,update,fix,remove.
   <release version="1.3" date="YYYY-MM-DD" description="Release 1.3">
     <action issue="TEXT-110" type="add" dev="pschumacher">Add Automatic-Module-Name MANIFEST entry for Java 9 compatibility</action>
     <action issue="TEXT-70" type="fix" dev="pschumacher">Build failure with java 9-ea+159</action>
+    <action issue="TEXT-113" type="add" dev="ggregory">Add an interpolator string lookup</action>
   </release>
 
   <release version="1.2" date="2017-12-12" description="Release 1.2">

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/StrLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/StrLookup.java b/src/main/java/org/apache/commons/text/StrLookup.java
index 5eeed88..87caf25 100644
--- a/src/main/java/org/apache/commons/text/StrLookup.java
+++ b/src/main/java/org/apache/commons/text/StrLookup.java
@@ -1,231 +1,209 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.text;
-
-import java.util.Map;
-import java.util.ResourceBundle;
-
-/**
- * Lookup a String key to a String value.
- * <p>
- * This class represents the simplest form of a string to string map. It has a benefit over a map in that it can create
- * the result on demand based on the key.
- * <p>
- * This class comes complete with various factory methods. If these do not suffice, you can subclass and implement your
- * own matcher.
- * <p>
- * For example, it would be possible to implement a lookup that used the key as a primary key, and looked up the value
- * on demand from the database
- *
- * @param <V> the type of the values supported by the lookup
- * @since 1.0
- */
-public abstract class StrLookup<V> {
-
-    /**
-     * Lookup that always returns null.
-     */
-    private static final StrLookup<String> NONE_LOOKUP = new MapStrLookup<>(null);
-
-    /**
-     * Lookup based on system properties.
-     */
-    private static final StrLookup<String> SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup();
-
-    // -----------------------------------------------------------------------
-    /**
-     * Returns a lookup which always returns null.
-     *
-     * @return a lookup that always returns null, not null
-     */
-    public static StrLookup<?> noneLookup() {
-        return NONE_LOOKUP;
-    }
-
-    /**
-     * Returns a new lookup which uses a copy of the current {@link System#getProperties() System properties}.
-     * <p>
-     * If a security manager blocked access to system properties, then null will be returned from every lookup.
-     * <p>
-     * If a null key is used, this lookup will throw a NullPointerException.
-     *
-     * @return a lookup using system properties, not null
-     */
-    public static StrLookup<String> systemPropertiesLookup() {
-        return SYSTEM_PROPERTIES_LOOKUP;
-    }
-
-    /**
-     * Returns a lookup which looks up values using a map.
-     * <p>
-     * If the map is null, then null will be returned from every lookup. The map result object is converted to a string
-     * using toString().
-     *
-     * @param <V> the type of the values supported by the lookup
-     * @param map the map of keys to values, may be null
-     * @return a lookup using the map, not null
-     */
-    public static <V> StrLookup<V> mapLookup(final Map<String, V> map) {
-        return new MapStrLookup<>(map);
-    }
-
-    /**
-     * Returns a lookup which looks up values using a ResourceBundle.
-     * <p>
-     * If the ResourceBundle is null, then null will be returned from every lookup. The map result object is converted
-     * to a string using toString().
-     *
-     * @param resourceBundle the map of keys to values, may be null
-     * @return a lookup using the map, not null
-     */
-    public static StrLookup<String> resourceBundleLookup(final ResourceBundle resourceBundle) {
-        return new ResourceBundleLookup(resourceBundle);
-    }
-
-    // -----------------------------------------------------------------------
-    /**
-     * Constructor.
-     */
-    protected StrLookup() {
-        super();
-    }
-
-    /**
-     * Looks up a String key to a String value.
-     * <p>
-     * The internal implementation may use any mechanism to return the value. The simplest implementation is to use a
-     * Map. However, virtually any implementation is possible.
-     * <p>
-     * For example, it would be possible to implement a lookup that used the key as a primary key, and looked up the
-     * value on demand from the database Or, a numeric based implementation could be created that treats the key as an
-     * integer, increments the value and return the result as a string - converting 1 to 2, 15 to 16 etc.
-     * <p>
-     * The {@link #lookup(String)} method always returns a String, regardless of the underlying data, by converting it
-     * as necessary. For example:
-     *
-     * <pre>
-     * Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
-     * map.put("number", Integer.valueOf(2));
-     * assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
-     * </pre>
-     *
-     * @param key the key to be looked up, may be null
-     * @return the matching value, null if no match
-     */
-    public abstract String lookup(String key);
-
-    // -----------------------------------------------------------------------
-    /**
-     * Lookup implementation that uses a Map.
-     *
-     * @param <V> the type of the values supported by the lookup
-     */
-    static class MapStrLookup<V> extends StrLookup<V> {
-
-        /** Map keys are variable names and value. */
-        private final Map<String, V> map;
-
-        /**
-         * Creates a new instance backed by a Map.
-         *
-         * @param map the map of keys to values, may be null
-         */
-        MapStrLookup(final Map<String, V> map) {
-            this.map = map;
-        }
-
-        /**
-         * Looks up a String key to a String value using the map.
-         * <p>
-         * If the map is null, then null is returned. The map result object is converted to a string using toString().
-         *
-         * @param key the key to be looked up, may be null
-         * @return the matching value, null if no match
-         */
-        @Override
-        public String lookup(final String key) {
-            if (map == null) {
-                return null;
-            }
-            final Object obj = map.get(key);
-            if (obj == null) {
-                return null;
-            }
-            return obj.toString();
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + " [map=" + map + "]";
-        }
-    }
-
-    // -----------------------------------------------------------------------
-    /**
-     * Lookup implementation based on a ResourceBundle.
-     */
-    private static final class ResourceBundleLookup extends StrLookup<String> {
-
-        /** ResourceBundle keys are variable names and value. */
-        private final ResourceBundle resourceBundle;
-
-        /**
-         * Creates a new instance backed by a ResourceBundle.
-         *
-         * @param resourceBundle the ResourceBundle of keys to values, may be null
-         */
-        private ResourceBundleLookup(final ResourceBundle resourceBundle) {
-            this.resourceBundle = resourceBundle;
-        }
-
-        @Override
-        public String lookup(final String key) {
-            if (resourceBundle == null || key == null || !resourceBundle.containsKey(key)) {
-                return null;
-            }
-            return resourceBundle.getString(key);
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + " [resourceBundle=" + resourceBundle + "]";
-        }
-
-    }
-
-    // -----------------------------------------------------------------------
-    /**
-     * Lookup implementation based on system properties.
-     */
-    private static final class SystemPropertiesStrLookup extends StrLookup<String> {
-        /**
-         * {@inheritDoc} This implementation directly accesses system properties.
-         */
-        @Override
-        public String lookup(final String key) {
-            if (key.length() > 0) {
-                try {
-                    return System.getProperty(key);
-                } catch (final SecurityException scex) {
-                    // Squelched. All lookup(String) will return null.
-                    return null;
-                }
-            }
-            return null;
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.text;
+
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.apache.commons.text.lookup.StringLookup;
+
+/**
+ * Lookup a String key to a String value.
+ * <p>
+ * This class represents the simplest form of a string to string map. It has a benefit over a map in that it can create
+ * the result on demand based on the key.
+ * <p>
+ * This class comes complete with various factory methods. If these do not suffice, you can subclass and implement your
+ * own matcher.
+ * <p>
+ * For example, it would be possible to implement a lookup that used the key as a primary key, and looked up the value
+ * on demand from the database
+ *
+ * @param <V> the type of the values supported by the lookup
+ * @since 1.0
+ */
+public abstract class StrLookup<V> implements StringLookup {
+
+    /**
+     * Lookup that always returns null.
+     */
+    private static final StrLookup<String> NONE_LOOKUP = new MapStrLookup<>(null);
+
+    /**
+     * Lookup based on system properties.
+     */
+    private static final StrLookup<String> SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup();
+
+    // -----------------------------------------------------------------------
+    /**
+     * Returns a lookup which always returns null.
+     *
+     * @return a lookup that always returns null, not null
+     */
+    public static StrLookup<?> noneLookup() {
+        return NONE_LOOKUP;
+    }
+
+    /**
+     * Returns a new lookup which uses a copy of the current {@link System#getProperties() System properties}.
+     * <p>
+     * If a security manager blocked access to system properties, then null will be returned from every lookup.
+     * <p>
+     * If a null key is used, this lookup will throw a NullPointerException.
+     *
+     * @return a lookup using system properties, not null
+     */
+    public static StrLookup<String> systemPropertiesLookup() {
+        return SYSTEM_PROPERTIES_LOOKUP;
+    }
+
+    /**
+     * Returns a lookup which looks up values using a map.
+     * <p>
+     * If the map is null, then null will be returned from every lookup. The map result object is converted to a string
+     * using toString().
+     *
+     * @param <V> the type of the values supported by the lookup
+     * @param map the map of keys to values, may be null
+     * @return a lookup using the map, not null
+     */
+    public static <V> StrLookup<V> mapLookup(final Map<String, V> map) {
+        return new MapStrLookup<>(map);
+    }
+
+    /**
+     * Returns a lookup which looks up values using a ResourceBundle.
+     * <p>
+     * If the ResourceBundle is null, then null will be returned from every lookup. The map result object is converted
+     * to a string using toString().
+     *
+     * @param resourceBundle the map of keys to values, may be null
+     * @return a lookup using the map, not null
+     */
+    public static StrLookup<String> resourceBundleLookup(final ResourceBundle resourceBundle) {
+        return new ResourceBundleLookup(resourceBundle);
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Constructor.
+     */
+    protected StrLookup() {
+        super();
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Lookup implementation that uses a Map.
+     *
+     * @param <V> the type of the values supported by the lookup
+     */
+    static class MapStrLookup<V> extends StrLookup<V> {
+
+        /** Map keys are variable names and value. */
+        private final Map<String, V> map;
+
+        /**
+         * Creates a new instance backed by a Map.
+         *
+         * @param map the map of keys to values, may be null
+         */
+        MapStrLookup(final Map<String, V> map) {
+            this.map = map;
+        }
+
+        /**
+         * Looks up a String key to a String value using the map.
+         * <p>
+         * If the map is null, then null is returned. The map result object is converted to a string using toString().
+         *
+         * @param key the key to be looked up, may be null
+         * @return the matching value, null if no match
+         */
+        @Override
+        public String lookup(final String key) {
+            if (map == null) {
+                return null;
+            }
+            final Object obj = map.get(key);
+            if (obj == null) {
+                return null;
+            }
+            return obj.toString();
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + " [map=" + map + "]";
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Lookup implementation based on a ResourceBundle.
+     */
+    private static final class ResourceBundleLookup extends StrLookup<String> {
+
+        /** ResourceBundle keys are variable names and value. */
+        private final ResourceBundle resourceBundle;
+
+        /**
+         * Creates a new instance backed by a ResourceBundle.
+         *
+         * @param resourceBundle the ResourceBundle of keys to values, may be null
+         */
+        private ResourceBundleLookup(final ResourceBundle resourceBundle) {
+            this.resourceBundle = resourceBundle;
+        }
+
+        @Override
+        public String lookup(final String key) {
+            if (resourceBundle == null || key == null || !resourceBundle.containsKey(key)) {
+                return null;
+            }
+            return resourceBundle.getString(key);
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + " [resourceBundle=" + resourceBundle + "]";
+        }
+
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Lookup implementation based on system properties.
+     */
+    private static final class SystemPropertiesStrLookup extends StrLookup<String> {
+        /**
+         * {@inheritDoc} This implementation directly accesses system properties.
+         */
+        @Override
+        public String lookup(final String key) {
+            if (key.length() > 0) {
+                try {
+                    return System.getProperty(key);
+                } catch (final SecurityException scex) {
+                    // Squelched. All lookup(String) will return null.
+                    return null;
+                }
+            }
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/AbstractStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/AbstractStringLookup.java b/src/main/java/org/apache/commons/text/lookup/AbstractStringLookup.java
new file mode 100644
index 0000000..fb43349
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/AbstractStringLookup.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import org.apache.commons.text.StrLookup;
+
+/**
+ * A default lookup for others to extend in this package.
+ * <p>
+ * Unfortunately, the type {@link org.apache.commons.text.StrLookup} was defined as class and not an interface, which is
+ * why this package introduces the interface {@link StringLookup}. In time, some deprecation strategy should be created.
+ * </p>
+ * 
+ * @since 1.3
+ */
+public abstract class AbstractStringLookup extends StrLookup<String> {
+
+    protected static final String EMPTY = "";
+
+    protected boolean isEmpty(final String value) {
+        return value == null ? true : value.isEmpty();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/DateStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/DateStringLookup.java b/src/main/java/org/apache/commons/text/lookup/DateStringLookup.java
new file mode 100644
index 0000000..ee2a89d
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/DateStringLookup.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Formats the current date or the date in the LogEvent. The "key" is used as the format String.
+ */
+public final class DateStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final DateStringLookup INSTANCE = new DateStringLookup();
+
+    /**
+     * No need to build instances for now.
+     */
+    private DateStringLookup() {
+        // empty
+    }
+
+    private String formatDate(final long date, final String format) {
+        DateFormat dateFormat = null;
+        if (format != null) {
+            try {
+                dateFormat = new SimpleDateFormat(format);
+            } catch (final Exception ex) {
+                throw IllegalArgumentExceptions.format(ex, "Invalid date format: [%s], using default", format);
+            }
+        }
+        if (dateFormat == null) {
+            dateFormat = DateFormat.getInstance();
+        }
+        return dateFormat.format(new Date(date));
+    }
+
+    /**
+     * Looks up the value of the environment variable.
+     * 
+     * @param key
+     *            the format to use. If null, the default DateFormat will be used.
+     * @return The value of the environment variable.
+     */
+    @Override
+    public String lookup(final String key) {
+        return formatDate(System.currentTimeMillis(), key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookup.java b/src/main/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookup.java
new file mode 100644
index 0000000..31034ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookup.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+/**
+ * Looks up keys from environment variables.
+ * 
+ * @since 1.3
+ */
+public final class EnvironmentVariableStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final EnvironmentVariableStringLookup INSTANCE = new EnvironmentVariableStringLookup();
+
+    /**
+     * No need to build instances for now.
+     */
+    private EnvironmentVariableStringLookup() {
+        // empty
+    }
+
+    /**
+     * Looks up the value of the given environment variable.
+     * 
+     * @param key
+     *            the key to be looked up, may be null
+     * @return The value of the environment variable.
+     */
+    @Override
+    public String lookup(final String key) {
+        // getenv throws NullPointerException if <code>name</code> is <code>null</code>
+        return key != null ? System.getenv(key) : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/IllegalArgumentExceptions.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/IllegalArgumentExceptions.java b/src/main/java/org/apache/commons/text/lookup/IllegalArgumentExceptions.java
new file mode 100644
index 0000000..307202d
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/IllegalArgumentExceptions.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+/**
+ * Shorthands creating {@link IllegalArgumentException} instances using formatted strings.
+ * 
+ * @since 1.3
+ */
+final class IllegalArgumentExceptions {
+
+    static IllegalArgumentException format(final String format, final Object... args) {
+        return new IllegalArgumentException(String.format(format, args));
+    }
+
+    static IllegalArgumentException format(final Throwable t, final String format, final Object... args) {
+        return new IllegalArgumentException(String.format(format, args), t);
+    }
+
+    /**
+     * No need to build instances.
+     */
+    private IllegalArgumentExceptions() {
+        // empty
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java b/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
new file mode 100644
index 0000000..1e6faf2
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/InterpolatorStringLookup.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Proxies other {@link StringLookup}s using a keys within ${} markers using the format "${StringLookup:Key}".
+ */
+public class InterpolatorStringLookup extends AbstractStringLookup {
+
+    /** Constant for the prefix separator. */
+    private static final char PREFIX_SEPARATOR = ':';
+
+    private final StringLookup defaultStringLookup;
+
+    private final Map<String, StringLookup> stringLookupMap = new HashMap<>();
+
+    /**
+     * Create the default Interpolator using only Lookups that work without an event.
+     */
+    public InterpolatorStringLookup() {
+        this((Map<String, String>) null);
+    }
+
+    /**
+     * Creates the Interpolator using only Lookups that work without an event and initial properties.
+     * <p>
+     * The following lookups are installed:
+     * </p>
+     * <ul>
+     * <li>"sys" for the SystemPropertyStringLookup.</li>
+     * <li>"env" for the EnvironmentVariableStringLookup.</li>
+     * <li>"java" for the JavaPlatformStringLookup.</li>
+     * <li>"date" for the DateStringLookup.</li>
+     * </ul>
+     * 
+     * @param defaultMap
+     *            the default map for string lookups.
+     */
+    public InterpolatorStringLookup(final Map<String, String> defaultMap) {
+        this(new MapStringLookup<>(defaultMap == null ? new HashMap<String, String>() : defaultMap));
+        // TODO: Use a service loader
+        stringLookupMap.put("sys", SystemPropertyStringLookup.INSTANCE);
+        stringLookupMap.put("env", EnvironmentVariableStringLookup.INSTANCE);
+        stringLookupMap.put("java", JavaPlatformStringLookup.INSTANCE);
+        stringLookupMap.put("date", DateStringLookup.INSTANCE);
+    }
+
+    public InterpolatorStringLookup(final StringLookup defaultStringLookup) {
+        this.defaultStringLookup = defaultStringLookup;
+    }
+
+    public Map<String, StringLookup> getStrLookupMap() {
+        return stringLookupMap;
+    }
+
+    /**
+     * Resolves the specified variable. This implementation will try to extract a variable prefix from the given
+     * variable name (the first colon (':') is used as prefix separator). It then passes the name of the variable with
+     * the prefix stripped to the lookup object registered for this prefix. If no prefix can be found or if the
+     * associated lookup object cannot resolve this variable, the default lookup object will be used.
+     *
+     * @param var
+     *            the name of the variable whose value is to be looked up
+     * @return the value of this variable or <b>null</b> if it cannot be resolved
+     */
+    @Override
+    public String lookup(String var) {
+        if (var == null) {
+            return null;
+        }
+
+        final int prefixPos = var.indexOf(PREFIX_SEPARATOR);
+        if (prefixPos >= 0) {
+            final String prefix = var.substring(0, prefixPos).toLowerCase(Locale.US);
+            final String name = var.substring(prefixPos + 1);
+            final StringLookup lookup = stringLookupMap.get(prefix);
+            String value = null;
+            if (lookup != null) {
+                value = lookup.lookup(name);
+            }
+
+            if (value != null) {
+                return value;
+            }
+            var = var.substring(prefixPos + 1);
+        }
+        if (defaultStringLookup != null) {
+            return defaultStringLookup.lookup(var);
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + " [stringLookupMap=" + stringLookupMap + ", defaultStringLookup="
+                + defaultStringLookup + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/JavaPlatformStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/JavaPlatformStringLookup.java b/src/main/java/org/apache/commons/text/lookup/JavaPlatformStringLookup.java
new file mode 100644
index 0000000..f7ecee1
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/JavaPlatformStringLookup.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+import java.util.Locale;
+
+/**
+ * Looks up keys related to Java: Java version, JRE version, VM version, and so on.
+ * 
+ * @since 1.3
+ */
+public final class JavaPlatformStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final JavaPlatformStringLookup INSTANCE = new JavaPlatformStringLookup();
+
+    /**
+     * No need to build instances for now.
+     */
+    private JavaPlatformStringLookup() {
+        // empty
+    }
+
+    /**
+     * Accessible through the Lookup key {@code hardware}.
+     * @return hardware processor information.
+     */
+    public String getHardware() {
+        return "processors: " + Runtime.getRuntime().availableProcessors() + ", architecture: "
+                + getSystemProperty("os.arch") + this.getSystemProperty("-", "sun.arch.data.model")
+                + this.getSystemProperty(", instruction sets: ", "sun.cpu.isalist");
+    }
+
+    /**
+     * Accessible through the Lookup key {@code locale}.
+     * @return system locale and file encoding information.
+     */
+    public String getLocale() {
+        return "default locale: " + Locale.getDefault() + ", platform encoding: " + getSystemProperty("file.encoding");
+    }
+
+    /**
+     * Accessible through the Lookup key {@code os}.
+     * @return operating system information.
+     */
+    public String getOperatingSystem() {
+        return getSystemProperty("os.name") + " " + getSystemProperty("os.version")
+                + getSystemProperty(" ", "sun.os.patch.level") + ", architecture: " + getSystemProperty("os.arch")
+                + getSystemProperty("-", "sun.arch.data.model");
+    }
+
+    /**
+     * Accessible through the Lookup key {@code runtime}.
+     * @return Java Runtime Environment information.
+     */
+    public String getRuntime() {
+        return getSystemProperty("java.runtime.name") + " (build " + getSystemProperty("java.runtime.version")
+                + ") from " + getSystemProperty("java.vendor");
+    }
+
+    private String getSystemProperty(final String name) {
+        return SystemPropertyStringLookup.INSTANCE.lookup(name);
+    }
+
+    private String getSystemProperty(final String prefix, final String name) {
+        final String value = getSystemProperty(name);
+        if (isEmpty(value)) {
+            return EMPTY;
+        }
+        return prefix + value;
+    }
+
+    /**
+     * Accessible through the Lookup key {@code vm}.
+     * @return Java Virtual Machine information.
+     */
+    public String getVirtualMachine() {
+        return getSystemProperty("java.vm.name") + " (build " + getSystemProperty("java.vm.version") + ", "
+                + getSystemProperty("java.vm.info") + ")";
+    }
+
+    /**
+     * Looks up the value of the Java platform key.
+     *
+     * @param key
+     *        the key to be looked up, may be null
+     * @return The value of the environment variable.
+     */
+    @Override
+    public String lookup(final String key) {
+        switch (key) {
+        case "version":
+            return "Java version " + getSystemProperty("java.version");
+        case "runtime":
+            return getRuntime();
+        case "vm":
+            return getVirtualMachine();
+        case "os":
+            return getOperatingSystem();
+        case "hardware":
+            return getHardware();
+        case "locale":
+            return getLocale();
+        default:
+            throw new IllegalArgumentException(key);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/MapStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/MapStringLookup.java b/src/main/java/org/apache/commons/text/lookup/MapStringLookup.java
new file mode 100644
index 0000000..51a9c5b
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/MapStringLookup.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+import java.util.Map;
+
+/**
+ * A map-based lookup.
+ * 
+ * @param <V>
+ *            A map's value type
+ * 
+ * @since 1.3
+ */
+public final class MapStringLookup<V> implements StringLookup {
+
+    /**
+     * Map keys are variable names and value.
+     */
+    private final Map<String, V> map;
+
+    /**
+     * Creates a new instance backed by a Map. Used by the default lookup.
+     *
+     * @param map
+     *            the map of keys to values, may be null
+     */
+    public MapStringLookup(final Map<String, V> map) {
+        this.map = map;
+    }
+
+    protected Map<String, V> getMap() {
+        return map;
+    }
+
+    /**
+     * Looks up a String key to a String value using the map.
+     * <p>
+     * If the map is null, then null is returned. The map result object is converted to a string using toString().
+     * </p>
+     *
+     * @param key
+     *            the key to be looked up, may be null
+     * @return the matching value, null if no match
+     */
+    @Override
+    public String lookup(final String key) {
+        if (map == null) {
+            return null;
+        }
+        final V obj;
+        try {
+            obj = map.get(key);
+        } catch (final NullPointerException e) {
+            // Could be a ConcurrentHashMap and a null key request
+            return null;
+        }
+        return obj != null ? obj.toString() : null;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + " [map=" + map + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/NullStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/NullStringLookup.java b/src/main/java/org/apache/commons/text/lookup/NullStringLookup.java
new file mode 100644
index 0000000..befd3bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/NullStringLookup.java
@@ -0,0 +1,30 @@
+package org.apache.commons.text.lookup;
+
+/**
+ * Always returns null.
+ * 
+ * @since 1.3
+ */
+public final class NullStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final NullStringLookup INSTANCE = new NullStringLookup();
+
+    /**
+     * No need to build instances for now.
+     */
+    private NullStringLookup() {
+        // empty
+    }
+
+    /**
+     * Always returns null.
+     */
+    @Override
+    public String lookup(final String key) {
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/ResourceBundleStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/ResourceBundleStringLookup.java b/src/main/java/org/apache/commons/text/lookup/ResourceBundleStringLookup.java
new file mode 100644
index 0000000..1d7b718
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/ResourceBundleStringLookup.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import java.util.ResourceBundle;
+
+/**
+ * Looks up keys from resource bundles.
+ * 
+ * @since 1.3
+ */
+public final class ResourceBundleStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final ResourceBundleStringLookup INSTANCE = new ResourceBundleStringLookup();
+    
+    /**
+     * No need to build instances for now.
+     */
+    private ResourceBundleStringLookup() {
+        // empty
+    }
+
+    /**
+     * Looks up the value for the key in the format "BundleName:BundleKey".
+     *
+     * For example: "com.domain.messages:MyKey".
+     *
+     * @param key
+     *            the key to be looked up, may be null
+     * @return The value associated with the key.
+     */
+    @Override
+    public String lookup(final String key) {
+        if (key == null) {
+            return null;
+        }
+        final String[] keys = key.split(":");
+        final int keyLen = keys.length;
+        if (keyLen != 2) {
+            throw IllegalArgumentExceptions
+                    .format("Bad ResourceBundle key format [%s]. Expected format is BundleName:KeyName.", key);
+        }
+        final String bundleName = keys[0];
+        final String bundleKey = keys[1];
+        try {
+            // The ResourceBundle class caches bundles, no need to cache here.
+            return ResourceBundle.getBundle(bundleName).getString(bundleKey);
+        } catch (final Exception e) {
+            throw IllegalArgumentExceptions.format(e, "Error looking up ResourceBundle [%s] and key [%s].", bundleName,
+                    bundleKey);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/StringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/StringLookup.java b/src/main/java/org/apache/commons/text/lookup/StringLookup.java
new file mode 100644
index 0000000..4fd29b8
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/StringLookup.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+/**
+ * Lookup a String key to a String value.
+ * <p>
+ * This class represents the simplest form of a string to string map. It has a benefit over a map in that it can create
+ * the result on demand based on the key.
+ * </p>
+ * <p>
+ * For example, it would be possible to implement a lookup that used the key as a primary key, and looked up the value
+ * on demand from the database
+ * </p>
+ * <p>
+ * Unfortunately, the type {@link org.apache.commons.text.StrLookup} was defined as class and not an interface, which is
+ * why this package introduces the interface {@link StringLookup}. In time, some deprecation strategy should be created.
+ * </p>
+ * 
+ * @since 1.3
+ */
+public interface StringLookup {
+
+    /**
+     * Looks up a String key to a String value.
+     * <p>
+     * The internal implementation may use any mechanism to return the value. The simplest implementation is to use a
+     * Map. However, virtually any implementation is possible.
+     * </p>
+     * <p>
+     * For example, it would be possible to implement a lookup that used the key as a primary key, and looked up the
+     * value on demand from the database Or, a numeric based implementation could be created that treats the key as an
+     * integer, increments the value and return the result as a string - converting 1 to 2, 15 to 16 etc.
+     * </p>
+     * <p>
+     * This method always returns a String, regardless of the underlying data, by converting it as necessary. For
+     * example:
+     * </p>
+     * 
+     * <pre>
+     * Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
+     * map.put("number", new Integer(2));
+     * assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
+     * </pre>
+     * 
+     * @param key
+     *            the key to be looked up, may be null
+     * @return the matching value, null if no match
+     */
+    String lookup(String key);
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/main/java/org/apache/commons/text/lookup/SystemPropertyStringLookup.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/text/lookup/SystemPropertyStringLookup.java b/src/main/java/org/apache/commons/text/lookup/SystemPropertyStringLookup.java
new file mode 100644
index 0000000..fa52869
--- /dev/null
+++ b/src/main/java/org/apache/commons/text/lookup/SystemPropertyStringLookup.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+/**
+ * Looks up keys from system properties.
+ * 
+ * @since 1.3
+ */
+public final class SystemPropertyStringLookup extends AbstractStringLookup {
+
+    /**
+     * Defines the singleton for this class.
+     */
+    public static final SystemPropertyStringLookup INSTANCE = new SystemPropertyStringLookup();
+    
+    /**
+     * No need to build instances for now.
+     */
+    private SystemPropertyStringLookup() {
+        // empty
+    }
+
+    /**
+     * Looks up the value for the key from system properties.
+     * 
+     * @param key
+     *            the key to be looked up, may be null
+     * @return The value associated with the key.
+     */
+    @Override
+    public String lookup(final String key) {
+        try {
+            return System.getProperty(key);
+        } catch (final SecurityException | NullPointerException | IllegalArgumentException e) {
+            // Squelched. All lookup(String) will return null.
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/DateStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/DateStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/DateStringLookupTest.java
new file mode 100644
index 0000000..229a1e8
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/DateStringLookupTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Test;
+
+public class DateStringLookupTest {
+
+    @Test
+    public void testDefault() throws ParseException {
+        final String formatted = DateStringLookup.INSTANCE.lookup(null);
+        DateFormat.getInstance().parse(formatted); // throws ParseException
+
+    }
+
+    @Test
+    public void testFormat() {
+        final String fomat = "yyyy-MM-dd";
+        final String value = DateStringLookup.INSTANCE.lookup(fomat);
+        assertNotNull("No Date", value);
+        final SimpleDateFormat format = new SimpleDateFormat(fomat);
+        final String today = format.format(new Date());
+        assertEquals(value, today);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookupTest.java
new file mode 100644
index 0000000..643e3d7
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/EnvironmentVariableStringLookupTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class EnvironmentVariableStringLookupTest {
+
+    @Test
+    public void testOne() {
+        if (SystemUtils.IS_OS_WINDOWS) {
+            final String key = "PATH";
+            Assert.assertEquals(System.getenv(key), EnvironmentVariableStringLookup.INSTANCE.lookup(key));
+        } else if (SystemUtils.IS_OS_LINUX) {
+            final String key = "USER";
+            Assert.assertEquals(System.getenv(key), EnvironmentVariableStringLookup.INSTANCE.lookup(key));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/InterpolatorStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/InterpolatorStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/InterpolatorStringLookupTest.java
new file mode 100644
index 0000000..f88bc9c
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/InterpolatorStringLookupTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.commons.text.lookup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.RuleChain;
+
+/**
+ *
+ */
+public class InterpolatorStringLookupTest {
+
+    @ClassRule
+    public static RuleChain rules = RuleChain.outerRule(new ExternalResource() {
+        @Override
+        protected void after() {
+            System.clearProperty(TESTKEY);
+            System.clearProperty(TESTKEY2);
+        }
+
+        @Override
+        protected void before() throws Throwable {
+            System.setProperty(TESTKEY, TESTVAL);
+            System.setProperty(TESTKEY2, TESTVAL);
+        }
+    });
+    private static final String TESTKEY = "TestKey";
+    private static final String TESTKEY2 = "TestKey2";
+
+    private static final String TESTVAL = "TestValue";
+
+    private void assertLookupNotEmpty(final StringLookup lookup, final String key) {
+        final String value = lookup.lookup(key);
+        assertNotNull(value);
+        assertFalse(value.isEmpty());
+        System.out.println(key + " = " + value);
+    }
+
+    @Test
+    public void testLookup() {
+        final Map<String, String> map = new HashMap<>();
+        map.put(TESTKEY, TESTVAL);
+        final StringLookup lookup = new InterpolatorStringLookup(new MapStringLookup<>(map));
+        String value = lookup.lookup(TESTKEY);
+        assertEquals(TESTVAL, value);
+        value = lookup.lookup("ctx:" + TESTKEY);
+        assertEquals(TESTVAL, value);
+        value = lookup.lookup("sys:" + TESTKEY);
+        assertEquals(TESTVAL, value);
+        value = lookup.lookup("BadKey");
+        assertNull(value);
+        value = lookup.lookup("ctx:" + TESTKEY);
+        assertEquals(TESTVAL, value);
+    }
+
+    @Test
+    public void testLookupWithDefaultInterpolator() {
+        final StringLookup lookup = new InterpolatorStringLookup();
+        String value = lookup.lookup("sys:" + TESTKEY);
+        assertEquals(TESTVAL, value);
+        value = lookup.lookup("env:PATH");
+        assertNotNull(value);
+        value = lookup.lookup("date:yyyy-MM-dd");
+        assertNotNull("No Date", value);
+        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+        final String today = format.format(new Date());
+        assertEquals(value, today);
+        assertLookupNotEmpty(lookup, "java:version");
+        assertLookupNotEmpty(lookup, "java:runtime");
+        assertLookupNotEmpty(lookup, "java:vm");
+        assertLookupNotEmpty(lookup, "java:os");
+        assertLookupNotEmpty(lookup, "java:locale");
+        assertLookupNotEmpty(lookup, "java:hardware");
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/JavaPlatformStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/JavaPlatformStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/JavaPlatformStringLookupTest.java
new file mode 100644
index 0000000..9a8941b
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/JavaPlatformStringLookupTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class JavaPlatformStringLookupTest {
+
+    @Test
+    public void testVm() {
+        final String key = "vm";
+        Assert.assertTrue(JavaPlatformStringLookup.INSTANCE.lookup(key).contains(System.getProperty("java.vm.name")));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/MapStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/MapStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/MapStringLookupTest.java
new file mode 100644
index 0000000..664078b
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/MapStringLookupTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MapStringLookupTest {
+
+    @Test
+    public void testOne() {
+        final String key = "key";
+        final String value = "value";
+        final Map<String, String> map = new HashMap<>();
+        map.put(key, value);
+        Assert.assertEquals(value, new MapStringLookup<>(map).lookup(key));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/NullStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/NullStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/NullStringLookupTest.java
new file mode 100644
index 0000000..2efde17
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/NullStringLookupTest.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NullStringLookupTest {
+
+    @Test
+    public void test() {
+        Assert.assertEquals(null, NullStringLookup.INSTANCE.lookup("EverythingIsNull"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
new file mode 100644
index 0000000..a9357da
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/ResourceBundleStringLookupTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import java.util.ResourceBundle;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ResourceBundleStringLookupTest {
+
+    @Test
+    public void testOne() {
+        final String bundleName = "testResourceBundleLookup";
+        final String bundleKey = "key";
+        Assert.assertEquals(ResourceBundle.getBundle(bundleName).getString(bundleKey),
+                ResourceBundleStringLookup.INSTANCE.lookup(bundleName + ":" + bundleKey));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/StrSubstitutorWithInterpolatorTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/StrSubstitutorWithInterpolatorTest.java b/src/test/java/org/apache/commons/text/lookup/StrSubstitutorWithInterpolatorTest.java
new file mode 100644
index 0000000..893eb5f
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/StrSubstitutorWithInterpolatorTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.text.lookup;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.text.StrSubstitutor;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StrSubstitutorWithInterpolatorTest {
+
+    @Test
+    public void testMapAndSystemProperty() {
+        final String key = "key";
+        final String value = "value";
+        final Map<String, String> map = new HashMap<>();
+        map.put(key, value);
+        final StrSubstitutor strSubst = new StrSubstitutor(new InterpolatorStringLookup(map));
+        final String spKey = "user.name";
+        Assert.assertEquals(System.getProperty(spKey), strSubst.replace("${sys:" + spKey + "}"));
+        Assert.assertEquals(value, strSubst.replace("${" + key + "}"));
+    }
+
+    @Test
+    public void testSystemProperty() {
+        final StrSubstitutor strSubst = new StrSubstitutor(new InterpolatorStringLookup());
+        final String spKey = "user.name";
+        Assert.assertEquals(System.getProperty(spKey), strSubst.replace("${sys:" + spKey + "}"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-text/blob/59984caf/src/test/java/org/apache/commons/text/lookup/SystemPropertyStringLookupTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/text/lookup/SystemPropertyStringLookupTest.java b/src/test/java/org/apache/commons/text/lookup/SystemPropertyStringLookupTest.java
new file mode 100644
index 0000000..4bfd9c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/text/lookup/SystemPropertyStringLookupTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.commons.text.lookup;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SystemPropertyStringLookupTest {
+
+    @Test
+    public void testUserName() {
+        final String key = "user.name";
+        Assert.assertEquals(System.getProperty(key), SystemPropertyStringLookup.INSTANCE.lookup(key));
+    }
+
+}