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

andysch pushed a commit to branch feature/SLING-7768
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git

commit bdc07c81a082329627a8bb2ba14e0b1fde41531e
Author: Andreas Schaefer <[email protected]>
AuthorDate: Tue Jul 30 09:16:55 2019 -0700

    Migrated the Sling Interplation to Lang3 StrSubtitutor, adjusted the tests 
and fixed some merge problems
---
 README.md                                          |  54 ++++++++
 .../resourceresolver/impl/mapping/MapEntries.java  |  56 +-------
 .../impl/mapping/StringInterpolationProvider.java  |  68 +---------
 .../StringInterpolationProviderConfiguration.java  |  24 ++++
 .../mapping/StringInterpolationProviderImpl.java   | 114 ++++------------
 .../impl/EtcMappingResourceResolverTest.java       |  33 +----
 .../mapping/AbstractMappingMapEntriesTest.java     |  27 +---
 .../impl/mapping/EtcMappingMapEntriesTest.java     |  13 --
 .../impl/mapping/MapEntriesTest.java               |  20 +--
 .../impl/mapping/ResourceMapperImplTest.java       |   3 +-
 .../mapping/StringInterpolationMapEntriesTest.java |   4 +-
 .../StringInterpolationProviderImplTest.java       | 148 +++++++++++++++------
 .../sling/resourceresolver/util/MockTestUtil.java  |  87 ++++--------
 13 files changed, 262 insertions(+), 389 deletions(-)

diff --git a/README.md b/README.md
index 02bf7bc..00d8c1c 100644
--- a/README.md
+++ b/README.md
@@ -7,3 +7,57 @@
 This module is part of the [Apache Sling](https://sling.apache.org) project.
 
 This bundle provides the Resource Resolver and Resource Resolver Factory
+
+## ETC Map String Interpolation
+
+Setting up ETC Mappings (/etc/map) for different instances like dev, stage,
+qa and production was time consuming and error prone due to copy-n-paste
+errors. 
+As a new feature Sling now supports String Interpolation in the /etc/map.
+With it it is possible to create a single set of etc-mapping and then adjust
+the actual values of an instance by an OSGi configuration.
+By default a variable name is enclosed in **${}** with a **$** as escape
+character and no in-variable-substitution. All of that is configurable
+together with the actual value map.
+
+### Setup
+
+The Substitution Configuration can be found in the OSGi Configuration
+as **Apache Sling String Interpolation Provider**. The property **Placeholder
+Values** takes a list of **key=value** entries where each of them map a
+variable with its actual value.
+In our little introduction we add an entry of
+**phv.default.host.name=localhost**. Save the configuration for now.
+Before going on make sure that you know Mapping Location configuration
+in the OSGi configuration of **Apache Sling Resource Resolver Factory**.
+Now to to **composum** and go to that node. If it does not exist then create
+one. The mapping should look like this:
+* etc
+    * map
+        * http
+            * ${phv.fq.host.name}.8080
+            
+Opening the page **http://localhost:8080/starter/index.html** should
+work just fine.
+
+### Testing
+
+Now got back to the String Interpolation configuration and change the value
+to **qa.author.acme.com** and save it.
+
+For local testing open your **hosts** file (/etc/hosts on Unix) and add a
+line like this:
+```
+127.0.0.1 qa.author.acme.com
+```
+save it and test with `ping qa.author.acme.com` to make sure the name
+resolves.
+Now you should be able to open the same page with:
+**http://qa.author.acme.com/starter/index.html**.
+
+Now do the same with **phv.fq.host.name=staging.author.acme.com**.
+
+The String Interpolation works with any part of the etc-map tree.
+ 
+ 
+
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
index 2efa826..b13b25a 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
@@ -163,7 +163,8 @@ public class MapEntries implements
         this.resolveMapsMap = Collections.singletonMap(GLOBAL_LIST_KEY, 
(List<MapEntry>)Collections.EMPTY_LIST);
         this.mapMaps = Collections.<MapEntry> emptyList();
         this.vanityTargets = Collections.<String,List <String>>emptyMap();
-        this.aliasMap = Collections.emptyMap();
+        this.aliasMap = Collections.<String, Map<String, String>>emptyMap();
+        this.stringInterpolationProvider = stringInterpolationProvider;
 
         doInit();
 
@@ -355,7 +356,6 @@ public class MapEntries implements
      * Remove all aliases for the content path
      * @param contentPath The content path
      * @param path Optional sub path of the vanity path
-     * @param refreshed Flag if session needs refresh
      * @return {@code true} if a change happened
      */
     private boolean removeAlias(final String contentPath, final String path, 
final AtomicBoolean resolverRefreshed) {
@@ -713,7 +713,7 @@ public class MapEntries implements
 
     /**
      * Handles the change to any of the node properties relevant for vanity URL
-     * mappings. The {@link #MapEntries(ResourceResolverFactoryImpl, 
BundleContext, EventAdmin)}
+     * mappings. The {@link #MapEntries(MapConfigurationProvider, 
BundleContext, EventAdmin, StringInterpolationProvider)}
      * constructor makes sure the event listener is registered to only get
      * appropriate events.
      */
@@ -968,10 +968,7 @@ public class MapEntries implements
                 trailingSlash = true;
             }
             // Check for placeholders and replace if needed
-            StringInterpolationProvider.Check check = 
stringInterpolationProvider.hasPlaceholder(name);
-            if(check.getStatus() == StringInterpolationProvider.STATUS.found) {
-                name = stringInterpolationProvider.resolve(check);
-            }
+            name = stringInterpolationProvider.substitute(name);
 
             final String childPath = parentPath.concat(name);
 
@@ -1043,58 +1040,15 @@ public class MapEntries implements
      */
     private Map<String, Map<String, String>> loadAliases(final 
ResourceResolver resolver) {
         final Map<String, Map<String, String>> map = new ConcurrentHashMap<>();
-               String queryString = this.factory.isForceNoAliasTraversal() ? 
ALIAS_QUERY_NO_TRAVERSAL : ALIAS_QUERY_DEFAULT;
-               while (true){
-               try {
+        final String queryString = "SELECT sling:alias FROM nt:base WHERE 
sling:alias IS NOT NULL";
                        final Iterator<Resource> i = 
resolver.findResources(queryString, "sql");
                        while (i.hasNext()) {
                            final Resource resource = i.next();
                            loadAlias(resource, map);
                        }
-                       break;
-               } catch (SlingException e) {
-                       Throwable cause = unwrapThrowable(e);
-                       if (cause instanceof IllegalArgumentException && 
ALIAS_QUERY_NO_TRAVERSAL.equals(queryString)) {
-                                       log.debug(
-                                               "Expected index not available 
yet - will retry", e);
-                                       try {
-                                               
TimeUnit.MILLISECONDS.sleep(getTraversalRetryInterval());
-                                       } catch (InterruptedException ex) {
-                                               log.warn("Interrupted while 
sleeping", ex);
-                                       }
-                               } else if (cause instanceof ParseException) {
-                               if 
(ALIAS_QUERY_NO_TRAVERSAL.equals(queryString)) {
-                                               log.warn("Traversal fail option 
set but query not accepted by queryengine, falling back to allowing traversal 
as queryengine might not support option", e);
-                                               queryString = 
ALIAS_QUERY_DEFAULT;
-                                       } else {
-                                               log.error("Queryengine couldn't 
parse query - interrupting loading of aliasmap",e);
-                                               break;
-                                       }
-                                       try {
-                                               
TimeUnit.MILLISECONDS.sleep(getTraversalRetryInterval());
-                                       } catch (InterruptedException ex) {
-                                               log.warn("Interrupted while 
sleeping", ex);
-                                       }
-
-
-                               } else {
-                                       log.error("QueryEngine not able to 
process query {} ", queryString, e);
-                                       break;
-                               }
-               }
-               }
         return map;
     }
 
-    /**
-     * Extract root cause of exception
-     * @param e {@code Throwable} to be checked
-     * @return Root {@code Throwable}
-     */
-    private Throwable unwrapThrowable(Throwable e) {
-               return e.getCause() == null ? e : unwrapThrowable(e.getCause());
-       }
-
        /**
      * Load alias given a resource
      */
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProvider.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProvider.java
index aa398e2..eabf1d2 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProvider.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProvider.java
@@ -31,70 +31,16 @@ import java.util.List;
  */
 public interface StringInterpolationProvider {
 
-    enum STATUS {found, unknown, none};
-
-    public static final String PLACEHOLDER_START_TOKEN = "${";
-    public static final String PLACEHOLDER_END_TOKEN = "}";
-    /**
-     * Checks if the given values contains a placeholder and if that 
placeholder is known
-     * @param value String to check
-     * @return Indicator if the given strings contains a known, unknown or no 
placeholders
-     */
-    Check hasPlaceholder(String value);
+    String DEFAULT_PREFIX = "${";
+    String DEFAULT_SUFFIX = "}";
+    char DEFAULT_ESCAPE_CHARACTER = '$';
+    boolean DEFAULT_IN_VARIABLE_SUBSTITUTION = false;
 
     /**
      * Replaces any placeholders with the replacement value
-     * ATTENTION: it is assumed that the string was checked and STATUS.found 
was returned.
-     * Any known placeholders will be replaced with an empty string
      *
-     * @param check Instance returned by has placeholder method
-     * @return Resolve string
+     * @param text Text to be substituted
+     * @return Substituted string
      */
-    String resolve(Check check);
-
-    public class Check {
-        private STATUS status;
-        private List<PlaceholderContext> placeholderContextList;
-        private String line;
-
-        public Check(STATUS status, String line, List<PlaceholderContext> 
placeholderContextList) {
-            this.status = status;
-            this.line = line;
-            this.placeholderContextList = placeholderContextList;
-        }
-
-        public STATUS getStatus() { return status; }
-
-        public List<PlaceholderContext> getPlaceholderContextList() {
-            return placeholderContextList;
-        }
-
-        public String getLine() {
-            return line;
-        }
-    }
-
-    public class PlaceholderContext {
-        private int start;
-        private int end;
-        private String name;
-
-        public PlaceholderContext(int start, int end, String name) {
-            this.start = start;
-            this.end = end;
-            this.name = name;
-        }
-
-        public int getStart() {
-            return start;
-        }
-
-        public int getEnd() {
-            return end;
-        }
-
-        public String getName() {
-            return name;
-        }
-    }
+    String substitute(String text);
 }
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderConfiguration.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderConfiguration.java
index 664669e..8b5f371 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderConfiguration.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderConfiguration.java
@@ -21,9 +21,33 @@ package org.apache.sling.resourceresolver.impl.mapping;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_ESCAPE_CHARACTER;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_IN_VARIABLE_SUBSTITUTION;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_PREFIX;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_SUFFIX;
+
 @ObjectClassDefinition(name = "Apache Sling String Interpolation Provider",
     description = "Configures the String Interpolation Provider and the 
location of its key/value pairs")
 public @interface StringInterpolationProviderConfiguration {
+
+    // Setup for the String Substitution
+    @AttributeDefinition(
+        name = "Substitution Prefix",
+        description = "The Prefix of the Variable to be replaced (default = 
'${')")
+    String substitution_prefix() default DEFAULT_PREFIX;
+    @AttributeDefinition(
+        name = "Substitution Suffix",
+        description = "The Suffix of the Variable to be replaced (deault = 
'}'")
+    String substitution_suffix() default DEFAULT_SUFFIX;
+    @AttributeDefinition(
+        name = "Substitution Escape Character",
+        description = "The Escape Character for Prefix or Suffix (default = 
'$'")
+    char substitution_escape_character() default DEFAULT_ESCAPE_CHARACTER;
+    @AttributeDefinition(
+        name = "Enable Substitution in Variables",
+        description = "Flag that indicates if substitution is allowed in 
Variables (default = false")
+    boolean substitution_in_variables() default 
DEFAULT_IN_VARIABLE_SUBSTITUTION;
+
     @AttributeDefinition(
         name = "Placeholder Values",
         description = "A list of key / value pairs separated by a equal (=) 
sign. " +
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImpl.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImpl.java
index 5150c83..f5bfab9 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImpl.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImpl.java
@@ -18,7 +18,7 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
-import org.osgi.framework.BundleContext;
+import org.apache.commons.lang3.text.StrSubstitutor;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -30,9 +30,7 @@ import org.slf4j.LoggerFactory;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 @Designate(ocd = StringInterpolationProviderConfiguration.class)
@@ -75,9 +73,9 @@ public class StringInterpolationProviderImpl
         );
     }
 
-    private BundleContext bundleContext;
-    private StringInterpolationProviderConfiguration config = DEFAULT_CONFIG;
+//    private StringInterpolationProviderConfiguration config = DEFAULT_CONFIG;
     private Map<String, String> placeholderEntries = new HashMap<>();
+    private StrSubstitutor substitutor = new StrSubstitutor();
 
     // ---------- SCR Integration ---------------------------------------------
 
@@ -85,10 +83,14 @@ public class StringInterpolationProviderImpl
      * Activates this component (called by SCR before)
      */
     @Activate
-    protected void activate(final BundleContext bundleContext, final 
StringInterpolationProviderConfiguration config) {
-        this.bundleContext = bundleContext;
-        this.config = config;
-        for(String line: this.config.place_holder_key_value_pairs()) {
+    protected void activate(final StringInterpolationProviderConfiguration 
config) {
+        String prefix = config.substitution_prefix();
+        String suffix = config.substitution_suffix();
+        char escapeCharacter = config.substitution_escape_character();
+        boolean substitudeInVariables = config.substitution_in_variables();
+
+        String[] valueMap = config.place_holder_key_value_pairs();
+        for(String line: valueMap) {
             // Ignore no or empty lines
             if(line == null || line.isEmpty()) { continue; }
             // Ignore comments
@@ -104,15 +106,22 @@ public class StringInterpolationProviderImpl
             }
             placeholderEntries.put(line.substring(0, index), 
line.substring(index + 1));
         }
+
+        substitutor = new StrSubstitutor(
+            placeholderEntries,
+            prefix,
+            suffix,
+            escapeCharacter
+        );
+        substitutor.setEnableSubstitutionInVariables(substitudeInVariables);
     }
 
     /**
      * Modifies this component (called by SCR to update this component)
      */
     @Modified
-    protected void modified(final BundleContext bundleContext, final 
StringInterpolationProviderConfiguration config) {
-        this.deactivate();
-        this.activate(bundleContext, config);
+    protected void modified(final StringInterpolationProviderConfiguration 
config) {
+        this.activate(config);
     }
 
     /**
@@ -120,86 +129,11 @@ public class StringInterpolationProviderImpl
      */
     @Deactivate
     protected void deactivate() {
-        this.bundleContext = null;
-        this.config = DEFAULT_CONFIG;
-    }
-
-    @Override
-    public Check hasPlaceholder(String line) {
-        STATUS status = STATUS.none;
-        List<PlaceholderContext> placeholderContextList = parseLine(line);
-        for(PlaceholderContext placeholderContext: placeholderContextList) {
-            String name = placeholderContext.getName();
-            if(!placeholderEntries.containsKey(name)) {
-                logger.warn("Placeholder: '{}' not found in list of 
Placeholders: '{}'", name, placeholderEntries);
-                status = STATUS.unknown;
-            }
-            status = status == STATUS.none ? STATUS.found : status;
-        }
-        return new Check(status, line, placeholderContextList);
+        activate(DEFAULT_CONFIG);
     }
 
     @Override
-    public String resolve(Check check) {
-        if(check.getStatus() == STATUS.unknown) {
-            logger.warn("Line: '{}' contains unknown placeholders -> ignored", 
check.getLine());
-            return check.getLine();
-        }
-        List<PlaceholderContext> placeholderContextList = 
check.getPlaceholderContextList();
-        String line = check.getLine();
-        String answer = "";
-        if(placeholderContextList.isEmpty()) {
-            answer = line;
-        } else {
-            // The carret is the position in the source line. It is used to 
copy regular text
-            int carret = 0;
-            for (PlaceholderContext context : 
check.getPlaceholderContextList()) {
-                int start = context.getStart();
-                if(start > carret) {
-                    // There is text between the current position in the 
source and the next placeholder
-                    // so copy this into the target line
-                    String text = line.substring(carret, start);
-                    answer += text;
-                    carret += text.length();
-                }
-                int end = context.getEnd();
-                String name = context.getName();
-                String value = placeholderEntries.get(name);
-                // Add placeholder value into the target line
-                answer += value;
-                carret = carret + end - start + PLACEHOLDER_END_TOKEN.length();
-            }
-            if(carret < line.length()) {
-                // There is some text left after the last placeholder so copy 
this to the target line
-                answer += line.substring(carret);
-            }
-        }
-        return answer;
-    }
-
-    private List<PlaceholderContext> parseLine(String line) {
-        List<PlaceholderContext> answer = new ArrayList<>();
-        int index = -2;
-        if(line != null && !line.isEmpty()) {
-            while(true) {
-                index = line.indexOf(PLACEHOLDER_START_TOKEN, index + 1);
-                if (index < 0) {
-                    break;
-                }
-                int index2 = line.indexOf(PLACEHOLDER_END_TOKEN, index);
-                if(index2 < 0) {
-                    logger.warn("Given Line: '{}' contains an unclosed 
placeholder -> ignored", line);
-                    continue;
-                }
-                answer.add(
-                    new PlaceholderContext(
-                        index,
-                        index2,
-                        line.substring(index + 
PLACEHOLDER_START_TOKEN.length(), index2)
-                    )
-                );
-            }
-        }
-        return answer;
+    public String substitute(String text) {
+        return substitutor.replace(text);
     }
 }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
index 9ae3f7d..6460ff5 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
@@ -23,11 +23,8 @@ import 
org.apache.sling.api.resource.observation.ResourceChange;
 import org.apache.sling.api.resource.path.Path;
 import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
-<<<<<<< HEAD
 import 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration;
 import 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderImpl;
-=======
->>>>>>> master
 import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
@@ -57,11 +54,9 @@ import static 
org.apache.sling.resourceresolver.util.MockTestUtil.callInaccessib
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.checkInternalResource;
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.checkRedirectResource;
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.createRequestFromUrl;
+import static 
org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.setInaccessibleField;
-<<<<<<< HEAD
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
-=======
->>>>>>> master
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -96,13 +91,9 @@ public class EtcMappingResourceResolverTest {
     @Mock
     ResourceProvider<?> resourceProvider;
 
-<<<<<<< HEAD
-    @Mock
     StringInterpolationProviderConfiguration 
stringInterpolationProviderConfiguration;
 
     StringInterpolationProviderImpl stringInterpolationProvider = new 
StringInterpolationProviderImpl();
-=======
->>>>>>> master
     MapEntries mapEntries;
 
     File vanityBloomFilterFile;
@@ -131,10 +122,8 @@ public class EtcMappingResourceResolverTest {
         setInaccessibleField("resourceProviderTracker", activator, 
resourceProviderTracker);
         setInaccessibleField("resourceAccessSecurityTracker", activator, new 
ResourceAccessSecurityTracker());
         setInaccessibleField("bundleContext", activator, bundleContext);
-<<<<<<< HEAD
+        stringInterpolationProviderConfiguration = 
createStringInterpolationProviderConfiguration();
         setInaccessibleField("stringInterpolationProvider", activator, 
stringInterpolationProvider);
-=======
->>>>>>> master
         setInaccessibleField("mapRoot", activator, "/etc/map");
         setInaccessibleField("mapRootPrefix", activator, "/etc/map");
         setInaccessibleField("observationPaths", activator, new Path[] {new 
Path("/")});
@@ -145,11 +134,7 @@ public class EtcMappingResourceResolverTest {
         
when(bundleContext.getDataFile("vanityBloomFilter.txt")).thenReturn(vanityBloomFilterFile);
         
when(serviceUserMapper.getServiceUserID(any(Bundle.class),anyString())).thenReturn("mapping");
         // Activate method is package private so we use reflection to to call 
it
-<<<<<<< HEAD
-        callInaccessibleMethod("activate", commonFactory, BundleContext.class, 
bundleContext);
-=======
         callInaccessibleMethod("activate", null, commonFactory, 
BundleContext.class, bundleContext);
->>>>>>> master
         final Bundle usingBundle = mock(Bundle.class);
         resourceResolverFactory = new 
ResourceResolverFactoryImpl(commonFactory, usingBundle, null);
         resourceResolver = 
resourceResolverFactory.getAdministrativeResourceResolver(null);
@@ -163,8 +148,6 @@ public class EtcMappingResourceResolverTest {
         return new ArrayList<>();
     }
 
-<<<<<<< HEAD
-=======
     /**
      * Changes to the /etc/map in our tests are not taking effect until there 
is an Change Event issued
      *
@@ -174,7 +157,6 @@ public class EtcMappingResourceResolverTest {
      * @param path Path to the resource root to be refreshed
      * @param isExternal External flag of the ResourceChange event
      */
->>>>>>> master
     void refreshMapEntries(String path, boolean isExternal) {
         ((MapEntries) commonFactory.getMapEntries()).onChange(
             asList(
@@ -300,11 +282,10 @@ public class EtcMappingResourceResolverTest {
         checkInternalResource(resolvedResource, "/anecdotes/stories");
     }
 
-<<<<<<< HEAD
     @Test
     public void simple_node_string_interpolation() throws Exception {
         buildResource("${siv.one}", http, resourceResolver, 
resourceProvider,PROP_REDIRECT_EXTERNAL, "/content/simple-node");
-        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, bundleContext, new String[] 
{"siv.one=test-simple-node.80"});
+        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, new String[] 
{"siv.one=test-simple-node.80"});
 
         refreshMapEntries("/etc/map", true);
 
@@ -325,7 +306,7 @@ public class EtcMappingResourceResolverTest {
             PROP_REG_EXP, "${siv.one}/",
             PROP_REDIRECT_EXTERNAL, "/content/simple-match/"
         );
-        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, bundleContext, new String[] 
{"siv.one=test-simple-match.80"});
+        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, new String[] 
{"siv.one=test-simple-match.80"});
 
         refreshMapEntries("/etc/map", true);
 
@@ -335,11 +316,12 @@ public class EtcMappingResourceResolverTest {
         HttpServletRequest request = 
createRequestFromUrl("http://test-simple-match:80/";);
         Resource resolvedResource = resourceResolver.resolve(request, "/");
         checkRedirectResource(resolvedResource, "/content/simple-match/", 302);
-=======
+    }
+
     /**
      * ATTENTION: this tests showcases an erroneous condition of an endless 
circular mapping in the /etc/map. When
      * this test passes this condition is present. After a fix this test must 
be adjusted.
-     * 
+     *
      * This confirms an issue with the Etc Mapping where a mapping from a node 
to a child node (here / to /content)
      * ends up in a endless circular mapping.
      * The only way to recover from this is to go to the OSGi console and 
change the /etc/map path in the Resource
@@ -368,6 +350,5 @@ public class EtcMappingResourceResolverTest {
 
         resolvedResource = resourceResolver.resolve(request, 
"/content/content/test.html");
         checkRedirectResource(resolvedResource, 
"/content/content/content/test.html", 302);
->>>>>>> master
     }
 }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
index e7129da..44e04a9 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
@@ -46,10 +46,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.Semaphore;
 
-<<<<<<< HEAD
+import static 
org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
 import static 
org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
-=======
->>>>>>> master
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyMap;
@@ -82,13 +80,9 @@ public abstract class AbstractMappingMapEntriesTest {
     @Mock
     ResourceResolver resourceResolver;
 
-<<<<<<< HEAD
-    @Mock
     StringInterpolationProviderConfiguration 
stringInterpolationProviderConfiguration;
 
     StringInterpolationProviderImpl stringInterpolationProvider = new 
StringInterpolationProviderImpl();
-=======
->>>>>>> master
     MapEntries mapEntries;
 
     File vanityBloomFilterFile;
@@ -112,10 +106,6 @@ public abstract class AbstractMappingMapEntriesTest {
         when(resourceResolverFactory.isVanityPathEnabled()).thenReturn(true);
         
when(resourceResolverFactory.getVanityPathConfig()).thenReturn(configs);
         
when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(true);
-<<<<<<< HEAD
-        
when(resourceResolverFactory.isForceNoAliasTraversal()).thenReturn(true);
-=======
->>>>>>> master
         when(resourceResolverFactory.getObservationPaths()).thenReturn(new 
Path[] {new Path("/")});
         
when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT);
         
when(resourceResolverFactory.getMaxCachedVanityPathEntries()).thenReturn(-1L);
@@ -126,12 +116,9 @@ public abstract class AbstractMappingMapEntriesTest {
         map = setupEtcMapResource("/etc", "map");
         http = setupEtcMapResource("http", map);
 
-<<<<<<< HEAD
-        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, bundleContext, new String[] {});
+        stringInterpolationProviderConfiguration = 
createStringInterpolationProviderConfiguration();
+        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, new String[] {});
         mapEntries = new MapEntries(resourceResolverFactory, bundleContext, 
eventAdmin, stringInterpolationProvider);
-=======
-        mapEntries = new MapEntries(resourceResolverFactory, bundleContext, 
eventAdmin);
->>>>>>> master
 
         final Field aliasMapField = 
MapEntries.class.getDeclaredField("aliasMap");
         aliasMapField.setAccessible(true);
@@ -204,11 +191,7 @@ public abstract class AbstractMappingMapEntriesTest {
         return resource;
     }
 
-<<<<<<< HEAD
-    MapEntriesTest.DataFuture createDataFuture(ExecutorService pool, final 
MapEntries mapEntries) {
-=======
     DataFuture createDataFuture(ExecutorService pool, final MapEntries 
mapEntries) {
->>>>>>> master
 
         Future<Iterator<?>> future = pool.submit(new Callable<Iterator<?>>() {
             @Override
@@ -216,11 +199,7 @@ public abstract class AbstractMappingMapEntriesTest {
                 return 
mapEntries.getResolveMapsIterator("http/localhost.8080/target/justVanityPath");
             }
         });
-<<<<<<< HEAD
-        return new MapEntriesTest.DataFuture(future);
-=======
         return new DataFuture(future);
->>>>>>> master
     }
 
     void simulateSomewhatSlowSessionOperation(final Semaphore sessionLock) 
throws InterruptedException {
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
index bfede24..a98ca6e 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
-<<<<<<< HEAD
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.path.Path;
@@ -51,14 +50,6 @@ import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
-=======
-import static 
org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
-import static 
org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
-import org.junit.Test;
->>>>>>> master
 
 /**
  * These tests are for the /etc/map setup of the Map Entries when
@@ -136,7 +127,6 @@ public class EtcMappingMapEntriesTest extends 
AbstractMappingMapEntriesTest {
             .addEtcMapEntry("^http/localhost\\.\\d*/gateway/", true, 
"http://gbiv.com/";)
             .addEtcMapEntry("^http/localhost\\.\\d*/(stories)/", true, 
"/anecdotes/$1/");
         expectedEtcMapping.assertEtcMap("Etc Mapping for nested internal mixed 
mapping", mapEntries.getResolveMaps());
-<<<<<<< HEAD
 
         // Not really an etc-map resource but it is good for now
         final Resource test = setupEtcMapResource("/scripts", "test");
@@ -258,7 +248,4 @@ public class EtcMappingMapEntriesTest extends 
AbstractMappingMapEntriesTest {
 //        Resource mappedResource = resResolver.resolve(request, "/b.html");
 //        String path = mappedResource.getPath();
 //    }
-=======
-    }
->>>>>>> master
 }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
index db072c3..fc6691c 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
@@ -21,11 +21,9 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -33,11 +31,9 @@ import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -45,22 +41,16 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.apache.sling.api.SlingException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.resource.observation.ResourceChange;
 import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
 import org.apache.sling.api.resource.path.Path;
@@ -70,7 +60,6 @@ import 
org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider.V
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -80,7 +69,7 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.event.EventAdmin;
 
-public class MapEntriesTest {
+public class MapEntriesTest extends AbstractMappingMapEntriesTest {
 
     private MapEntries mapEntries;
 
@@ -129,7 +118,6 @@ public class MapEntriesTest {
         when(resourceResolverFactory.isVanityPathEnabled()).thenReturn(true);
         
when(resourceResolverFactory.getVanityPathConfig()).thenReturn(configs);
         
when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(true);
-        
when(resourceResolverFactory.isForceNoAliasTraversal()).thenReturn(true);
         when(resourceResolverFactory.getObservationPaths()).thenReturn(new 
Path[] {new Path("/")});
         
when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT);
         
when(resourceResolverFactory.getMaxCachedVanityPathEntries()).thenReturn(-1L);
@@ -137,7 +125,7 @@ public class MapEntriesTest {
         when(resourceResolver.findResources(anyString(), 
eq("sql"))).thenReturn(
                 Collections.<Resource> emptySet().iterator());
 
-        mapEntries = new MapEntries(resourceResolverFactory, bundleContext, 
eventAdmin);
+        mapEntries = new MapEntries(resourceResolverFactory, bundleContext, 
eventAdmin, stringInterpolationProvider);
         final Field aliasMapField = 
MapEntries.class.getDeclaredField("aliasMap");
         aliasMapField.setAccessible(true);
 
@@ -150,8 +138,8 @@ public class MapEntriesTest {
     }
 
 
-    @Test(timeout = 1000)
-    public void test_simple_alias_support() throws InterruptedException {
+    @Test
+    public void test_simple_alias_support() {
         Resource parent = mock(Resource.class);
         when(parent.getPath()).thenReturn("/parent");
 
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
index 58263b6..479ff0e 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
@@ -81,7 +81,8 @@ public class ResourceMapperImplTest {
 
         ctx.registerInjectActivateService(new ServiceUserMapperImpl());
         ctx.registerInjectActivateService(new ResourceAccessSecurityTracker());
-        
+        ctx.registerInjectActivateService(new 
StringInterpolationProviderImpl());
+
         InMemoryResourceProvider resourceProvider = new 
InMemoryResourceProvider();
         resourceProvider.putResource("/"); // root
         resourceProvider.putResource("/here"); // regular page
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
index 680dd36..9aa5d05 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
@@ -33,7 +33,7 @@ public class StringInterpolationMapEntriesTest extends 
AbstractMappingMapEntries
     public void simple_node_string_interpolation() throws Exception {
         // To avoid side effects the String Interpolation uses its own 
Resource Resolver
         Resource sivOne = setupEtcMapResource("${siv.one}", 
http,PROP_REDIRECT_EXTERNAL, "/content/simple-node");
-        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, bundleContext, new String[] 
{"siv.one=test-simple-node"});
+        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, new String[] 
{"siv.one=test-simple-node"});
 
         mapEntries.doInit();
         ExpectedEtcMapping expectedEtcMapping = new 
ExpectedEtcMapping("^http/test-simple-node/", "/content/simple-node/");
@@ -47,7 +47,7 @@ public class StringInterpolationMapEntriesTest extends 
AbstractMappingMapEntries
             PROP_REG_EXP, "${siv.one}/",
             PROP_REDIRECT_EXTERNAL, "/content/simple-match/"
         );
-        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, bundleContext, new String[] 
{"siv.one=test-simple-match"});
+        setupStringInterpolationProvider(stringInterpolationProvider, 
stringInterpolationProviderConfiguration, new String[] 
{"siv.one=test-simple-match"});
 
         mapEntries.doInit();
         ExpectedEtcMapping expectedEtcMapping = new 
ExpectedEtcMapping("^http/test-simple-match/", "/content/simple-match/");
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImplTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImplTest.java
index 212c928..3c0b461 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImplTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationProviderImplTest.java
@@ -18,16 +18,18 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
+import org.apache.commons.lang3.text.StrSubstitutor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.osgi.framework.BundleContext;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.when;
-import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.PLACEHOLDER_START_TOKEN;
-import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.PLACEHOLDER_END_TOKEN;
 
 public class StringInterpolationProviderImplTest {
 
@@ -41,6 +43,19 @@ public class StringInterpolationProviderImplTest {
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
+        
when(stringInterpolationProviderConfiguration.substitution_prefix()).thenReturn("${");
+        
when(stringInterpolationProviderConfiguration.substitution_suffix()).thenReturn("}");
+        
when(stringInterpolationProviderConfiguration.substitution_escape_character()).thenReturn('$');
+        
when(stringInterpolationProviderConfiguration.substitution_in_variables()).thenReturn(false);
+    }
+
+    @Test
+    public void test_strsubstitutor() {
+        Map<String,String> values = new HashMap<>();
+        values.put("one", "two");
+        StrSubstitutor substitutor = new StrSubstitutor(values, "${", "}", 
'$');
+        String substitude = substitutor.replace("${one}");
+        assertEquals("Wrong Replacement", "two", substitude);
     }
 
     @Test
@@ -50,13 +65,11 @@ public class StringInterpolationProviderImplTest {
         );
 
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = PLACEHOLDER_START_TOKEN + "one" + PLACEHOLDER_END_TOKEN;
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.found, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "two", resolved);
+        String line = "${one}";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "two", substituted);
     }
 
     @Test
@@ -65,13 +78,11 @@ public class StringInterpolationProviderImplTest {
             new String[] { "one=two"}
         );
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = "Here is " + PLACEHOLDER_START_TOKEN + "one" + 
PLACEHOLDER_END_TOKEN + ", too";
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.found, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "Here is two, too", resolved);
+        String line = "Here is ${one}, too";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "Here is two, too", substituted);
     }
 
     @Test
@@ -80,13 +91,11 @@ public class StringInterpolationProviderImplTest {
             new String[] { "one=two", "three=four"}
         );
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = PLACEHOLDER_START_TOKEN + "one" + PLACEHOLDER_END_TOKEN 
+ " with another " + PLACEHOLDER_START_TOKEN + "three" + PLACEHOLDER_END_TOKEN;
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.found, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "two with another four", resolved);
+        String line = "${one} with another ${three}";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "two with another four", 
substituted);
     }
 
     @Test
@@ -96,13 +105,11 @@ public class StringInterpolationProviderImplTest {
         );
 
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = "Here comes " + PLACEHOLDER_START_TOKEN + "one" + 
PLACEHOLDER_END_TOKEN + " with another " + PLACEHOLDER_START_TOKEN + "three" + 
PLACEHOLDER_END_TOKEN + " equals " + PLACEHOLDER_START_TOKEN + "five" + 
PLACEHOLDER_END_TOKEN + ", horray!";
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.found, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "Here comes two with another four 
equals six, horray!", resolved);
+        String line = "Here comes ${one} with another ${three} equals ${five}, 
horray!";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "Here comes two with another four 
equals six, horray!", substituted);
     }
 
     @Test
@@ -112,13 +119,11 @@ public class StringInterpolationProviderImplTest {
         );
 
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
         String line = "Here comes is a text with no placeholders!";
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.none, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "Here comes is a text with no 
placeholders!", resolved);
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "Here comes is a text with no 
placeholders!", substituted);
     }
 
     @Test
@@ -128,13 +133,11 @@ public class StringInterpolationProviderImplTest {
         );
 
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = "Here comes " + PLACEHOLDER_START_TOKEN + "unkown" + 
PLACEHOLDER_END_TOKEN + " placeholders!";
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.unknown, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "Here comes ${unkown} 
placeholders!", resolved);
+        String line = "Here comes ${unkown} placeholders!";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "Here comes ${unkown} 
placeholders!", substituted);
     }
 
     @Test
@@ -144,12 +147,71 @@ public class StringInterpolationProviderImplTest {
         );
 
         StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
-        placeholderProvider.activate(bundleContext, 
stringInterpolationProviderConfiguration);
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
+
+        String line = "${siv.one}/";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "test-value/", substituted);
+    }
+
+    @Test
+    public void test_different_suffix_prefix() {
+        
when(stringInterpolationProviderConfiguration.place_holder_key_value_pairs()).thenReturn(
+            new String[] { "test-me.one=hello"}
+        );
+        
when(stringInterpolationProviderConfiguration.substitution_prefix()).thenReturn("{{");
+        
when(stringInterpolationProviderConfiguration.substitution_suffix()).thenReturn("}}");
+
+        StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
+
+        String line = "a-{{test-me.one}}-a";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "a-hello-a", substituted);
+    }
+
+    @Test
+    public void test_escape_character() {
+        
when(stringInterpolationProviderConfiguration.place_holder_key_value_pairs()).thenReturn(
+            new String[] { "one=two"}
+        );
+        
when(stringInterpolationProviderConfiguration.substitution_escape_character()).thenReturn('\\');
+
+        StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
+
+        String line = "\\${one}=${one}";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "${one}=two", substituted);
+    }
+
+    @Test
+    public void test_in_variables_substitution() {
+        
when(stringInterpolationProviderConfiguration.place_holder_key_value_pairs()).thenReturn(
+            new String[] { "one=two", "two=three"}
+        );
+        
when(stringInterpolationProviderConfiguration.substitution_in_variables()).thenReturn(true);
+
+        StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
+
+        String line = "${${one}}";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "three", substituted);
+    }
+
+    @Test
+    public void test_in_variables_substitution2() {
+        
when(stringInterpolationProviderConfiguration.place_holder_key_value_pairs()).thenReturn(
+            new String[] { "one=two", "onetwo=three"}
+        );
+        
when(stringInterpolationProviderConfiguration.substitution_in_variables()).thenReturn(true);
+
+        StringInterpolationProviderImpl placeholderProvider = new 
StringInterpolationProviderImpl();
+        placeholderProvider.activate(stringInterpolationProviderConfiguration);
 
-        String line = PLACEHOLDER_START_TOKEN + "siv.one" + 
PLACEHOLDER_END_TOKEN + "/";
-        StringInterpolationProvider.Check check = 
placeholderProvider.hasPlaceholder(line);
-        assertEquals("Wrong Check status", 
StringInterpolationProvider.STATUS.found, check.getStatus());
-        String resolved = placeholderProvider.resolve(check);
-        assertEquals("Wrong resolved line", "test-value/", resolved);
+        String line = "${one${one}}";
+        String substituted = placeholderProvider.substitute(line);
+        assertEquals("Wrong resolved line", "three", substituted);
     }
 }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java 
b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
index 3f36327..d40bcf6 100644
--- a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
+++ b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
@@ -26,21 +26,15 @@ import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.resourceresolver.impl.SimpleValueMapImpl;
 import org.apache.sling.resourceresolver.impl.helper.RedirectResource;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
-<<<<<<< HEAD
 import 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider;
 import 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration;
-=======
->>>>>>> master
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
-<<<<<<< HEAD
 import org.osgi.framework.BundleContext;
-=======
->>>>>>> master
 
 import javax.servlet.http.HttpServletRequest;
 import java.lang.reflect.Field;
@@ -48,10 +42,15 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_ESCAPE_CHARACTER;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_IN_VARIABLE_SUBSTITUTION;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_PREFIX;
+import static 
org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider.DEFAULT_SUFFIX;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
@@ -81,12 +80,6 @@ public class MockTestUtil {
     }
 
     public static void checkInternalResource(Resource internal, String path) {
-<<<<<<< HEAD
-//        assertThat("Not a Non Existing Resource", redirect, 
instanceOf(NonExistingResource.class));
-//        NonExistingResource nonExistingResource = (NonExistingResource) 
redirect;
-//        if(path != null) {
-=======
->>>>>>> master
         assertEquals("Wrong Path for Resource", path, internal.getPath());
     }
 
@@ -141,11 +134,7 @@ public class MockTestUtil {
      * @param resourceResolver Resource Resolver of this resource
      * @param provider         Resource Provider Instance
      * @param properties       Key / Value pair for resource properties (the 
number of strings must be even)
-<<<<<<< HEAD
-     * @return
-=======
      * @return Mock Resource able to handle addition of children later on
->>>>>>> master
      */
     @SuppressWarnings("unchecked")
     public static Resource buildResource(String fullPath, Resource parent, 
ResourceResolver resourceResolver, ResourceProvider<?> provider, String... 
properties) {
@@ -201,25 +190,6 @@ public class MockTestUtil {
         return resource;
     }
 
-<<<<<<< HEAD
-    public static Object callInaccessibleMethod(String methodName, Object 
target, Class paramsType, Object param) throws NoSuchMethodException {
-        return callInaccessibleMethod(methodName, target, new Class[] 
{paramsType}, new Object[] {param});
-    }
-
-    public static Object callInaccessibleMethod(String methodName, Object 
target, Class[] paramsTypes, Object[] params) throws NoSuchMethodException {
-        if(paramsTypes != null && params != null) {
-            if(params.length != paramsTypes.length) { throw new 
IllegalArgumentException("Number of Parameter Types and Values were not the 
same"); }
-        } else {
-            paramsTypes = null;
-            params = null;
-        }
-        try {
-            Method method = target.getClass().getDeclaredMethod(methodName, 
paramsTypes);
-            method.setAccessible(true);
-            return method.invoke(target, params);
-        } catch(NoSuchMethodException e) {
-            throw new UnsupportedOperationException("Failed to find method: " 
+ methodName, e);
-=======
     /**
      * Calls a private method that has no parameter like a getter
      *
@@ -271,7 +241,6 @@ public class MockTestUtil {
         }
         try {
             return getInaccessibleMethod(methodName, returnType, target, 
parameterTypes).call(parameters);
->>>>>>> master
         } catch (IllegalAccessException e) {
             throw new UnsupportedOperationException("Failed to access method: 
" + methodName, e);
         } catch (InvocationTargetException e) {
@@ -279,28 +248,6 @@ public class MockTestUtil {
         }
     }
 
-<<<<<<< HEAD
-    public static void setInaccessibleField(String fieldName, Object target, 
Object fieldValue) throws NoSuchMethodException {
-        try {
-            Field field = target.getClass().getDeclaredField(fieldName);
-            field.setAccessible(true);
-            field.set(target, fieldValue);
-        } catch (IllegalAccessException e) {
-            throw new UnsupportedOperationException("Failed to access field: " 
+ fieldName, e);
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public static void setupStringInterpolationProvider(
-        StringInterpolationProvider provider, 
StringInterpolationProviderConfiguration configuration, BundleContext 
bundleContext, final String[] placeholderValues
-    ) throws NoSuchMethodException {
-        
when(configuration.place_holder_key_value_pairs()).thenReturn(placeholderValues);
-        callInaccessibleMethod("activate", provider,
-            new Class[] {BundleContext.class, 
StringInterpolationProviderConfiguration.class},
-            new Object[] {bundleContext, configuration}
-        );
-=======
     public static <T> MethodWrapper<T> getInaccessibleMethod(String 
methodName, Class<T> returnType, Object target, Class...parameterTypes) {
         return new MethodWrapper(methodName, returnType, target, 
parameterTypes);
     }
@@ -363,6 +310,26 @@ public class MockTestUtil {
         }
     }
 
+    public static StringInterpolationProviderConfiguration 
createStringInterpolationProviderConfiguration() {
+        StringInterpolationProviderConfiguration answer = 
mock(StringInterpolationProviderConfiguration.class);
+        when(answer.substitution_prefix()).thenReturn(DEFAULT_PREFIX);
+        when(answer.substitution_suffix()).thenReturn(DEFAULT_SUFFIX);
+        
when(answer.substitution_escape_character()).thenReturn(DEFAULT_ESCAPE_CHARACTER);
+        
when(answer.substitution_in_variables()).thenReturn(DEFAULT_IN_VARIABLE_SUBSTITUTION);
+        when(answer.place_holder_key_value_pairs()).thenReturn(new String[] 
{});
+        return answer;
+    }
+
+    public static void setupStringInterpolationProvider(
+        StringInterpolationProvider provider, 
StringInterpolationProviderConfiguration configuration, final String[] 
placeholderValues
+    ) {
+        
when(configuration.place_holder_key_value_pairs()).thenReturn(placeholderValues);
+        callInaccessibleMethod("activate", Void.TYPE, provider,
+            new Class[] {StringInterpolationProviderConfiguration.class},
+            new Object[] {configuration}
+        );
+    }
+
     public static class FieldWrapper<T> {
         private Field field;
         private Object target;
@@ -380,7 +347,6 @@ public class MockTestUtil {
         public T get() throws IllegalAccessException {
             return (T) field.get(target);
         }
->>>>>>> master
     }
 
     /**
@@ -392,12 +358,9 @@ public class MockTestUtil {
         public List<Resource> getChildrenList();
     }
 
-<<<<<<< HEAD
-=======
     /**
      * Defines the Result of the Etc Mapping for easy testing
      */
->>>>>>> master
     public static class ExpectedEtcMapping {
         List<ExpectedEtcMapEntry> expectedEtcMapEntries = new ArrayList<>();
 

Reply via email to