code: package com.taxonic.carml.engine.template;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.stream.IntStream;
  
  import org.apache.commons.lang3.mutable.MutableBoolean;
  import org.apache.commons.lang3.mutable.MutableInt;
  import org.apache.commons.lang3.mutable.MutableObject;
  
  public class TemplateParser {
  
  	private String escapableChars;
  	
  	private TemplateParser(
  		String escapableChars
  	) {
  		this.escapableChars = escapableChars;
  	}
  
  	public static TemplateParser build() {
  		return new TemplateParser("{}\\");
  	}
  	
  	private abstract static class Segment {
  		
  		StringBuilder str = new StringBuilder();
  
  		void append(String c) {
  			str.append(c);
  		}
  		
  		String value() {
  			return str.toString();
  		}
  	}
  	
  	private static class Text extends Segment {}
  	
  	private static class Expression extends Segment {}
  	
  	// TODO: PM: reduce cognitive complexity by splitting up in sub-methods
  	public Template parse(String template) {
  
  		MutableBoolean escaping = new MutableBoolean(false);
  		MutableObject<Segment> segmentContainer = new MutableObject<Segment>(new Text());
  		
  		List<CarmlTemplate.Segment> segments = new ArrayList<>();
  		
  		MutableInt nextId = new MutableInt();
  		
  		Runnable close = () -> {
  			Segment segment = segmentContainer.getValue();
  			if (segment == null) return;
  			String value = segment.value();
  			if (value.isEmpty()) return;
  			if (segment instanceof Text)
  				segments.add(new CarmlTemplate.Text(value));
  			else if (segment instanceof Expression)
  				segments.add(new CarmlTemplate.ExpressionSegment(nextId.getAndIncrement(), value));
  			// (assuming no other segment types)
  		};
  		
  		IntStream.range(0, template.length())
  			.mapToObj(i -> template.substring(i, i + 1))
  			.forEach(c -> {
  			
  				Segment segment = segmentContainer.getValue();
  				
  				if (escaping.booleanValue()) {
  					if (!escapableChars.contains(c))
  						throw new RuntimeException("invalid escape sequence in template [" + template + "] - escaping char [" + c + "]");
  					segment.append(c);
  					escaping.setFalse();
  				}
  				
  				else if (c.equals("\\"))
  					escaping.setTrue();
  				
  				else if (c.equals("{")) {
  					if (segment instanceof Expression)
  						throw new RuntimeException("encountered unescaped nested { character in template [" + template + "]");
  					// (assuming segment is Text)
  					close.run();
  					segmentContainer.setValue(new Expression());
  				}
  				
  				else if (c.equals("}")) {
  					if (segment instanceof Expression) {
  						close.run();
  						segmentContainer.setValue(new Text());
  					}
  					// allow adding } outside expressions to text segment
  					else segment.append(c);
  					// (assuming no other segment types)
  				}
  				
  				else segment.append(c);
  			
  			});
  		
  		Segment segment = segmentContainer.getValue();
  		if (segment instanceof Expression)
  			throw new RuntimeException("unclosed expression in template [" + template + "]");
  		close.run();
  		
  		return CarmlTemplate.build(segments);
  	}
  	
  }
  