001package org.apache.commons.digester3.substitution;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Map;
023import java.util.ArrayList;
024
025/**
026 * <p>
027 * Expands variable references from multiple sources.
028 * </p>
029 * 
030 * @since 1.6
031 */
032public class MultiVariableExpander
033    implements VariableExpander
034{
035
036    private int nEntries = 0;
037
038    private final ArrayList<String> markers = new ArrayList<String>( 2 );
039
040    private final ArrayList<Map<String, Object>> sources = new ArrayList<Map<String, Object>>( 2 );
041
042    /**
043     * Add a new variables source, identified by the input marker
044     *
045     * @param marker The input variables marker
046     * @param source The variables source
047     */
048    public void addSource( String marker, Map<String, Object> source )
049    {
050        ++nEntries;
051        markers.add( marker );
052        sources.add( source );
053    }
054
055    /**
056     * {@inheritDoc}
057     */
058    public String expand( String param )
059    {
060        for ( int i = 0; i < nEntries; ++i )
061        {
062            param = expand( param, markers.get( i ), sources.get( i ) );
063        }
064        return param;
065    }
066
067    /**
068     * Replace any occurrences within the string of the form "marker{key}" with the value from source[key].
069     * <p>
070     * Commonly, the variable marker is "$", in which case variables are indicated by ${key} in the string.
071     * <p>
072     * Returns the string after performing all substitutions.
073     * <p>
074     * If no substitutions were made, the input string object is returned (not a copy).
075     *
076     * @param str The input string containing placeholders
077     * @param marker The input variables marker
078     * @param source The variables source
079     * @return The input string where variables have been expanded by replacing values found in source
080     */
081    public String expand( String str, String marker, Map<String, Object> source )
082    {
083        String startMark = marker + "{";
084        int markLen = startMark.length();
085
086        int index = 0;
087        for ( ;; )
088        {
089            index = str.indexOf( startMark, index );
090            if ( index == -1 )
091            {
092                return str;
093            }
094
095            int startIndex = index + markLen;
096            if ( startIndex > str.length() )
097            {
098                throw new IllegalArgumentException( "var expression starts at end of string" );
099            }
100
101            int endIndex = str.indexOf( "}", index + markLen );
102            if ( endIndex == -1 )
103            {
104                throw new IllegalArgumentException( "var expression starts but does not end" );
105            }
106
107            String key = str.substring( index + markLen, endIndex );
108            Object value = source.get( key );
109            if ( value == null )
110            {
111                throw new IllegalArgumentException( "parameter [" + key + "] is not defined." );
112            }
113            String varValue = value.toString();
114
115            str = str.substring( 0, index ) + varValue + str.substring( endIndex + 1 );
116            index += varValue.length();
117        }
118    }
119
120}