Recursive java.util.Properties references Background

A subclass of java.util.Properties attempts to recursively resolve configured properties:

prop_a=Prop A is ${prop_b} 
prop_b=Prop B is ${prop_c} 
prop_c=Prop C.


After reading the file, prop_a should be Prop A is Prop B is Prop C.

Property Reference Semantics

Nest properties are not allowed, making the following invalid:

${property_name_a ${property_name_b}}


A valid property is:

name=Text for ${property_a}, followed by ${property_b}.


Source Code

Code that handles parsing:

/**
 * This will throw an IllegalArgumentException if there are mismatched
 * braces.
 *
 * @param s - The string with zero or more ${} references to resolve.
 * @return The value of the string, with all substituted values made.
 */
public String resolve( String s ) {
  StringBuffer sb = new StringBuffer( 256 );
  Stack&lt;StringBuffer&gt; stack = new Stack&lt;StringBuffer&gt;();
  int len = s.length();

  for( int i = 0; i &lt; len; i++ ) {
    char c = s.charAt( i );

    switch( c ) {
      case '$': {
        if( i + 1 &lt; len &amp;&amp; s.charAt( i + 1 ) == '{' ) {
          stack.push( sb );
          sb = new StringBuffer( 256 );
          i++;
        }
        break;
      }

      case '}': {
        if( stack.isEmpty() ) {
          throw new IllegalArgumentException( "unexpected '}'" );
        }

        String name = sb.toString();

        sb = stack.pop();
        sb.append( super.getProperty( name, null ) );
        break;
      }

      default: {
        sb.append( c );
        break;
      }
    }
  }

  if( !stack.isEmpty() ) {
    throw new IllegalArgumentException( "missing '}'" );
  }

  return sb.toString();
}


Question

How would you simplify the code and detect/avoid infinite recursion?
You say "recursively resolve", but in fact resolve is not recursive. It returns Prop A is Prop B is ${prop_c} instead of Prop A is Prop B is Prop C, if you call it with Prop A is ${prop_b}. If you call it with prop_a, as advertised, it returns prop_a.
You allow nested ${} in you syntax. What is your semantics of it?
Do you have any test cases for this. The bug and Unclear semantics of nested parens for instance, strongly indicate you should have. 
Since you access the super class through its public interface, namely the the singe call to the super.getProperty you could instead pass the properties as a parameter. It will improve usability. Then you can make the static, if you wish. Since the method does not refer to any instance members otherwise.
StringBuffer and Stack have built in synchronization, whose overhead you can avoid by using StringBuilder and ArrayList.


Here is the test code to save others some effort:

import java.util.Properties;
import java.util.Stack;


public class Main {
    public static String resolve(Properties props,  String s ) {
          StringBuffer sb = new StringBuffer( 256 );
          Stack&lt;StringBuffer&gt; stack = new Stack&lt;StringBuffer&gt;();
          int len = s.length();

          for( int i = 0; i &lt; len; i++ ) {
            char c = s.charAt( i );

            switch( c ) {
              case '$': {
                if( i + 1 &lt; len &amp;&amp; s.charAt( i + 1 ) == '{' ) {
                  stack.push( sb );
                  sb = new StringBuffer( 256 );
                  i++;
                }
                break;
              }

              case '}': {
                if( stack.isEmpty() ) {
                  throw new IllegalArgumentException( "unexpected '}'" );
                }

                String name = sb.toString();

                sb = stack.pop();
                sb.append( props.get(name) );
                break;
              }

              default: {
                sb.append( c );
                break;
              }
            }
          }

          if( !stack.isEmpty() ) {
            throw new IllegalArgumentException( "missing '}'" );
          }

          return sb.toString();
        }

    public static void main(String[] args) {
        Properties props = new Properties();

        props.setProperty("prop_a", "Prop A is ${prop_b}"); 
        props.setProperty("prop_b", "Prop B is ${prop_c}"); 
        props.setProperty("prop_c", "Prop C.");

        System.out.println(resolve(props, "Prop A is ${prop_b}"));
        System.out.println(resolve(props, "prop_a"));
        System.out.println(resolve(props, "${prop_a}"));
        System.out.println(resolve(props, "${${prop_b}}"));
    }
}