docs: private String substitute(String exp,
                                List<DSLMappingEntry> entries,
                                int line,
                                Map<String, Integer> use,
                                boolean showSingleSteps) {
          if ( entries.size() == 0 ) {
              if ( line > 0 ) {
                  this.addError( new ExpanderException( "No mapping entries for expanding: " + exp,
                                                        line ) );
              }
              return exp;
          }
          if ( showSingleSteps ) {
              logger.info( "to expand: |" + exp + "|" );
          }
          Map<String, String> key2value = new HashMap<String, String>();
          for ( final DSLMappingEntry entry : entries ) {
              Map<String, Integer> vars = entry.getVariables();
              String mappingKey = entry.getMappingKey();
              String vp = entry.getValuePattern();
              Pattern kp = entry.getKeyPattern();
              Matcher m = kp.matcher( exp );
              int startPos = 0;
              boolean match;
              Integer count;
              if ( showUsage ) {
                  count = use.get( mappingKey );
                  if ( count == null ) use.put( mappingKey, 0 );
              }
              while ( startPos < exp.length() && m.find( startPos ) ) {
                  match = true;
                  if ( showSingleSteps ) {
                      logger.info( "  matches: " + kp.toString() );
                  }
                  if ( showUsage ) {
                      use.put( mappingKey,
                               use.get( mappingKey ) + 1 );
                  }
                  // Replace the range of group 0.  
                  String target = m.group( 0 );
                  if ( !vars.keySet().isEmpty() ) {
                      // Build a pattern matching any variable enclosed in braces.
                      StringBuilder sb = new StringBuilder();
                      String del = "\\{(";
                      for ( String key : vars.keySet() ) {
                          sb.append( del ).append( Pattern.quote( key ) );
                          del = "|";
                      }
                      sb.append( ")" ).append( funcPatStr ).append( "\\}" );
                      Pattern allkeyPat = Pattern.compile( sb.toString() );
                      vp = entry.getValuePattern();
                      Matcher allkeyMat = allkeyPat.matcher( vp );
  
                      // While the pattern matches, get the actual key and replace by '$' + index
                      while ( allkeyMat.find() ) {
                          String theKey = allkeyMat.group( 1 );
                          String theFunc = allkeyMat.group( 2 );
                          String foundValue = m.group( vars.get( theKey ) );
                          String theValue = applyFunc( theFunc,
                                                       foundValue );
                          String newVp = vp.substring( 0,
                                                       allkeyMat.start() ) + theValue + vp.substring( allkeyMat.end() );
                          allkeyMat.reset( newVp );
                          key2value.put( theKey,
                                         foundValue );
                          if (newVp.equals(vp)) {
                              break;
                          } else {
                              vp = newVp;
                          }
                      }
                  }
  
                  // Try to find any matches from previous lines.
                  Matcher varRefMat = varRefPat.matcher( vp );
                  while ( varRefMat.find() ) {
                      String theKey = varRefMat.group( 1 );
                      String theFunc = varRefMat.group( 2 );
                      for ( int ientry = substitutions.size() - 1; ientry >= 0; ientry-- ) {
                          String foundValue = substitutions.get( ientry ).get( theKey );
                          if ( foundValue != null ) {
                              String theValue = applyFunc( theFunc,
                                                           foundValue );
                              // replace it
                              vp = vp.substring( 0,
                                                 varRefMat.start() ) + theValue + vp.substring( varRefMat.end() );
                              varRefMat.reset( vp );
                              break;
                          }
                      }
                  }
  
                  // add the new set of substitutions
                  if ( key2value.size() > 0 ) {
                      substitutions.add( key2value );
                  }
  
                  // now replace the target
                  exp = exp.substring( 0,
                                       m.start() ) + vp + exp.substring( m.end() );
                  if ( match && showSingleSteps ) {
                      logger.info( "   result: |" + exp + "|" );
                  }
                  startPos = m.start() + vp.length();
                  m.reset( exp );
              }
          }
          return exp;
      }