protected boolean isTemplateLine(Line line) {
   ProcessedRichString string = line.getRichString();
   if (string.getLines().size() == 1) {
     return false;
   }
   boolean firstOrLast = string.getLines().get(0) == line;
   if (!firstOrLast) {
     if (string.getLines().get(string.getLines().size() - 1) == line) {
       if (!(line.getParts().get(line.getParts().size() - 1) instanceof LineBreak))
         firstOrLast = true;
     }
   }
   boolean onlyLiterals = true;
   for (LinePart part : line.getParts()) {
     if (part instanceof PrintedExpression) return false;
     if (part instanceof Literal) {
       Literal literal = (Literal) part;
       if (literal instanceof LineBreak) {
         if (firstOrLast) return onlyLiterals;
         return !onlyLiterals;
       }
       if (!(new TextLine(
               literal.getLiteral().getValue(), literal.getOffset(), literal.getLength(), 0)
           .containsOnlyWhitespace())) return false;
     } else if (firstOrLast) {
       return false;
     } else {
       onlyLiterals = false;
     }
   }
   if (firstOrLast) return onlyLiterals;
   return !onlyLiterals;
 }
 protected void addToCurrentLine(LinePart part) {
   if (currentLine == null) {
     currentLine = factory.createLine();
     if (!(part instanceof Literal) && !result.getLines().isEmpty()) {
       Line prevLine = result.getLines().get(result.getLines().size() - 1);
       LineBreak lineBreak = (LineBreak) prevLine.getParts().get(prevLine.getParts().size() - 1);
       Literal literal = factory.createLiteral();
       literal.setLength(0);
       literal.setOffset(lineBreak.getLiteral().getValue().length());
       literal.setLiteral(lineBreak.getLiteral());
       currentLine.getParts().add(literal);
     }
     result.getLines().add(currentLine);
   }
   if (part != null) currentLine.getParts().add(part);
 }
 @Override
 public Boolean caseProcessedRichString(ProcessedRichString object) {
   String indentation = computeInitialIndentation(object.getRichString());
   pushTemplateIndentation(indentation);
   List<Line> lines = object.getLines();
   if (!lines.isEmpty()) {
     int i = 0;
     nextPart = null;
     Line currentLine = lines.get(i);
     while (currentLine.getParts().isEmpty() && i < lines.size()) {
       currentLine = lines.get(i);
     }
     if (!currentLine.getParts().isEmpty()) {
       nextPart = currentLine.getParts().get(0);
     }
     while (nextPart != null) {
       doSwitch(nextPart);
     }
   }
   popIndentation();
   return Boolean.TRUE;
 }
  private static List<String> update(List<String> lines, Line line) {
    String text = line.getText();

    int index = lines.indexOf(text);
    if (index < 0) {
      throw new IllegalArgumentException();
    }

    String updatedText = Line.join(line.getParts());

    lines.set(index, updatedText);

    return lines;
  }
 protected void computeNextPart(LinePart part) {
   Line line = part.getLine();
   int index = line.getParts().indexOf(part);
   if (index == line.getParts().size() - 1) {
     ProcessedRichString richString = line.getRichString();
     index = richString.getLines().indexOf(line);
     if (index == richString.getLines().size() - 1) nextPart = null;
     else {
       int i = index + 1;
       nextPart = null;
       line = richString.getLines().get(i);
       while (line.getParts().isEmpty() && i < richString.getLines().size()) {
         line = richString.getLines().get(i);
       }
       if (!line.getParts().isEmpty()) {
         nextPart = line.getParts().get(0);
       }
     }
   } else {
     nextPart = line.getParts().get(index + 1);
   }
 }
 @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;
 }