All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.carml.engine.template.TemplateParser Maven / Gradle / Ivy

There is a newer version: 0.4.11
Show newest version
package io.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 final 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 {
  }

  public Template parse(String template) {

    MutableBoolean escaping = new MutableBoolean(false);
    MutableObject segmentContainer = new MutableObject<>(new Text());

    List segments = new ArrayList<>();

    MutableInt nextId = new MutableInt();

    Runnable storeSegment = () -> {
      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(character -> parseCharToSegments(template, character, segmentContainer, escaping, storeSegment));

    Segment segment = segmentContainer.getValue();
    if (segment instanceof Expression) {
      throw new TemplateException(String.format("unclosed expression in template [%s]", template));
    }
    storeSegment.run();

    return CarmlTemplate.build(segments);
  }

  private void parseCharToSegments(String template, String character, MutableObject segmentContainer,
      MutableBoolean escaping, Runnable storeSegment) {
    Segment segment = segmentContainer.getValue();

    if (escaping.booleanValue()) {
      if (!escapableChars.contains(character)) {
        throw new TemplateException(
            String.format("invalid escape sequence in template [%s] - escaping char [%s]", template, character));
      }
      segment.append(character);
      escaping.setFalse();
    } else if (character.equals("\\")) {
      escaping.setTrue();
    } else if (character.equals("{")) {
      if (segment instanceof Expression) {
        throw new TemplateException(
            String.format("encountered unescaped nested { character in template [%s]", template));
      }
      // (assuming segment is Text)
      storeSegment.run();
      segmentContainer.setValue(new Expression());
    } else if (character.equals("}")) {
      if (segment instanceof Expression) {
        storeSegment.run();
        segmentContainer.setValue(new Text());
      } else { // allow adding } outside expressions to text segment
        segment.append(character);
      }
      // (assuming no other segment types)
    } else {
      segment.append(character);
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy