@Override
 public Boolean caseForLoopStart(ForLoopStart object) {
   RichStringForLoop loop = object.getLoop();
   acceptor.acceptForLoop(loop.getDeclaredParam(), loop.getForExpression());
   pushTemplateIndentationTwice(
       computeInitialIndentation((RichString) loop.getEachExpression()));
   boolean hasNext =
       acceptor.forLoopHasNext(
           loop.getBefore(),
           loop.getSeparator(),
           indentationHandler.getTotalSemanticIndentation());
   if (hasNext) {
     while (hasNext) {
       computeNextPart(object);
       while (nextPart != object.getEnd()) {
         doSwitch(nextPart);
       }
       hasNext =
           acceptor.forLoopHasNext(
               loop.getBefore(),
               loop.getSeparator(),
               indentationHandler.getTotalSemanticIndentation());
     }
   } else {
     nextPart = object.getEnd();
   }
   return Boolean.TRUE;
 }
 @Override
 public Boolean casePrintedExpression(PrintedExpression object) {
   acceptor.acceptExpression(
       object.getExpression(), indentationHandler.getTotalSemanticIndentation());
   computeNextPart(object);
   return Boolean.TRUE;
 }
 @Override
 public Boolean caseEndIf(EndIf object) {
   popIndentationTwice();
   acceptor.acceptEndIf();
   computeNextPart(object);
   return Boolean.TRUE;
 }
 @Override
 public Boolean caseIfConditionStart(IfConditionStart object) {
   RichStringIf richStringIf = object.getRichStringIf();
   acceptor.acceptIfCondition(richStringIf.getIf());
   computeNextPart(object);
   pushTemplateIndentationTwice(computeInitialIndentation((RichString) richStringIf.getThen()));
   return Boolean.TRUE;
 }
 @Override
 public Boolean caseForLoopEnd(ForLoopEnd object) {
   popIndentationTwice();
   acceptor.acceptEndFor(
       object.getStart().getLoop().getAfter(), indentationHandler.getTotalSemanticIndentation());
   computeNextPart(object);
   return Boolean.TRUE;
 }
 @Override
 public Boolean caseElseIfCondition(ElseIfCondition object) {
   popIndentationTwice();
   RichStringElseIf elseIf = object.getRichStringElseIf();
   acceptor.acceptElseIfCondition(elseIf.getIf());
   computeNextPart(object);
   pushTemplateIndentationTwice(computeInitialIndentation((RichString) elseIf.getThen()));
   return Boolean.TRUE;
 }
 @Override
 public Boolean caseElseStart(ElseStart object) {
   popIndentationTwice();
   acceptor.acceptElse();
   computeNextPart(object);
   pushTemplateIndentationTwice(
       computeInitialIndentation(
           (RichString) object.getIfConditionStart().getRichStringIf().getElse()));
   return Boolean.TRUE;
 }
 public void announceSemanticLinebreak(LineBreak lineBreak) {
   boolean controlStructureSeen = false;
   for (LinePart part : lineBreak.getLine().getParts()) {
     if (!(part instanceof Literal)) {
       controlStructureSeen = true;
       break;
     }
   }
   acceptor.acceptSemanticLineBreak(
       lineBreak.getLength(), lineBreak.getLiteral(), controlStructureSeen);
 }
 protected void announceTemplateLinebreak(LineBreak lineBreak) {
   acceptor.acceptTemplateLineBreak(lineBreak.getLength(), lineBreak.getLiteral());
 }
 public void announceTemplateText(CharSequence text, RichStringLiteral origin) {
   acceptor.acceptTemplateText(text, origin);
 }
 protected void announceSemanticText(CharSequence text, RichStringLiteral origin) {
   acceptor.acceptSemanticText(text, origin);
 }
 @Override
 public Boolean caseLiteral(Literal object) {
   if (announced == null || announced != object.getLiteral()) {
     acceptor.announceNextLiteral(object.getLiteral());
     announced = object.getLiteral();
   }
   Line line = object.getLine();
   TextLine textLine =
       new TextLine(
           Strings.emptyIfNull(object.getLiteral().getValue()),
           object.getOffset(),
           object.getLength(),
           0);
   CharSequence ws = textLine.getLeadingWhiteSpace();
   ProcessedRichString string = line.getRichString();
   boolean firstOrLast =
       string.getLines().get(0) == line
           || string.getLines().get(string.getLines().size() - 1) == line;
   if (isTemplateLine(line)) {
     if (line.getParts().get(0) == object) {
       if (!firstOrLast) {
         boolean followedByOpening = false;
         if (line.getParts().size() >= 2) {
           LinePart next = line.getParts().get(1);
           if (next instanceof ForLoopStart || next instanceof IfConditionStart) {
             followedByOpening = true;
           }
         }
         if (!followedByOpening) {
           pushSemanticIndentation(indentationHandler.getTotalIndentation());
         } else {
           pushSemanticIndentation(ws);
         }
       }
     }
     announceTemplateText(textLine, object.getLiteral());
   } else {
     if (skipCount <= 1) {
       firstOrLast = false;
       if (skipCount == 0 && line.getParts().get(0) == object) {
         if (textLine.length() == ws.length()) {
           for (int i = 1; i < line.getParts().size(); i++) {
             if (line.getParts().get(i) instanceof Literal
                 && !(line.getParts().get(i) instanceof LineBreak)) {
               Literal nextLiteralInSameLine = (Literal) line.getParts().get(i);
               TextLine nextLiteralLine =
                   new TextLine(
                       nextLiteralInSameLine.getLiteral().getValue(),
                       nextLiteralInSameLine.getOffset(),
                       nextLiteralInSameLine.getLength(),
                       0);
               CharSequence nextLeading = nextLiteralLine.getLeadingWhiteSpace();
               if (nextLeading.length() > 0) {
                 ws = ws.toString() + nextLeading;
               }
               skipCount++;
               if (nextLeading.length() != nextLiteralLine.length()) {
                 break;
               }
             } else {
               break;
             }
           }
           if (skipCount != 0) {
             pushSemanticIndentation(ws);
           } else {
             pushSemanticIndentation(ws);
             announceIndentation();
             announceSemanticText(
                 textLine.subSequence(ws.length(), textLine.length()), object.getLiteral());
           }
         } else {
           pushSemanticIndentation(ws);
           announceIndentation();
           announceSemanticText(
               textLine.subSequence(ws.length(), textLine.length()), object.getLiteral());
         }
       } else {
         if (skipCount == 1) {
           skipCount--;
           announceIndentation();
           announceSemanticText(
               textLine.subSequence(ws.length(), textLine.length()), object.getLiteral());
         } else {
           announceSemanticText(textLine, object.getLiteral());
         }
       }
     } else {
       skipCount--;
     }
   }
   if (!firstOrLast && line.getParts().get(line.getParts().size() - 1) == object) {
     popIndentation();
   }
   computeNextPart(object);
   return Boolean.TRUE;
 }