/** {@inheritDoc} */
 @Override
 protected void toTextInternal(Appendable writer) throws IOException {
   writer.append(getArithmeticSign());
   if (stateObject != null) {
     writer.append(SPACE);
     stateObject.toString(writer);
   }
 }
  /** {@inheritDoc} */
  @Override
  public void parse(String jpqlFragment) {

    StringBuilder sb = new StringBuilder();
    sb.append(TRIM);
    sb.append(LEFT_PARENTHESIS);
    sb.append(jpqlFragment);
    sb.append(RIGHT_PARENTHESIS);

    JPQLExpression jpqlExpression =
        new JPQLExpression(sb, getGrammar(), FunctionsReturningStringsBNF.ID, true);

    TrimExpression trimExpression = (TrimExpression) jpqlExpression.getQueryStatement();
    setSpecification(trimExpression.getSpecification());
    parseTrimCharacter(trimExpression.getTrimCharacter().toParsedText());
    super.parse(trimExpression.getExpression().toParsedText());

    // The trim character is actually the string primary
    if (!hasStateObject() && hasTrimCharacter()) {
      setStateObject(new StringLiteralStateObject(this, trimCharacter.toString()));
      trimCharacter = null;
    }
  }
  /** {@inheritDoc} */
  @Override
  protected void toTextEncapsulatedExpression(Appendable writer) throws IOException {

    // Specification
    if (specification != Specification.DEFAULT) {
      writer.append(specification.name());
      writer.append(SPACE);
    }

    // Trim character
    if (hasTrimCharacter()) {
      trimCharacter.toString(writer);
      writer.append(SPACE);
    }

    // FROM
    if ((specification != Specification.DEFAULT) || hasTrimCharacter()) {
      writer.append(FROM);
      writer.append(SPACE);
    }

    // String primary
    super.toTextEncapsulatedExpression(writer);
  }