class JsonReferenceReplacer {

    protected JsonNode getParameterValue(Map<String, JsonNode> parameterMap, String parameter, String jsonPtrExpr,
        ReferenceReplacementResult report)
    {
        if(!parameterMap.containsKey(parameter))
        {
            // this is a missing reference
            report.addMissingReference(generateReference(parameter, jsonPtrExpr));
            return null;
        }

        JsonNode parameterValue = parameterMap.get(parameter);
        // at method does not appear to support the json pointer path '/'
        if(StringUtils.isBlank(jsonPtrExpr) || StringUtils.equals(jsonPtrExpr, JACKSON_PATH_SEPARATOR))
        {
            return parameterValue;
        }

        // NOTE / TODO: If we opt to create more special types I recommend individual processors and multiple characters (don't overload '?' for example)
        String[] splitPtr = StringUtils.split(jsonPtrExpr, CONTEXT_FALLBACK_VALUE_SEPARATOR, 2);
        String jsonPointer = splitPtr[0];

        JsonNode atNode = parameterValue.at(StringUtils.prependIfMissing(jsonPointer, JACKSON_PATH_SEPARATOR));
        if(atNode.isMissingNode())
        {
            if(splitPtr.length == 1
                && jsonPtrExpr.endsWith(CONTEXT_FALLBACK_VALUE_SEPARATOR))
            {
                return NullNode.getInstance();
            }
            else if(splitPtr.length > 1)
            {
                return new TextNode(splitPtr[1]);
            }
            else
            {
                // this is an invalid reference
                report.addInvalidReference(generateReference(parameter, jsonPointer));
                return null;
            }
        }
        return atNode;
    }

    public  JsonReferenceReplacer();
    public  JsonReferenceReplacer(String referencePrefix, String referenceSeparator, String referenceSuffix, String fallbackValueSeparator);

}