paragraphs property

Iterable<LicenseParagraph> paragraphs
override

The paragraphs of the license, each as a LicenseParagraph consisting of a string and some formatting information. Paragraphs can include newline characters, but this is discouraged as it results in ugliness.

Implementation

@override
Iterable<LicenseParagraph> get paragraphs sync* {
  int lineStart = 0;
  int currentPosition = 0;
  int lastLineIndent = 0;
  int currentLineIndent = 0;
  int currentParagraphIndentation;
  _LicenseEntryWithLineBreaksParserState state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
  final List<String> lines = <String>[];

  void addLine() {
    assert(lineStart < currentPosition);
    lines.add(text.substring(lineStart, currentPosition));
  }

  LicenseParagraph getParagraph() {
    assert(lines.isNotEmpty);
    assert(currentParagraphIndentation != null);
    final LicenseParagraph result = LicenseParagraph(lines.join(' '), currentParagraphIndentation);
    assert(result.text.trimLeft() == result.text);
    assert(result.text.isNotEmpty);
    lines.clear();
    return result;
  }

  while (currentPosition < text.length) {
    switch (state) {
      case _LicenseEntryWithLineBreaksParserState.beforeParagraph:
        assert(lineStart == currentPosition);
        switch (text[currentPosition]) {
          case ' ':
            lineStart = currentPosition + 1;
            currentLineIndent += 1;
            state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
            break;
          case '\t':
            lineStart = currentPosition + 1;
            currentLineIndent += 8;
            state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
            break;
          case '\r':
          case '\n':
          case '\f':
            if (lines.isNotEmpty) {
              yield getParagraph();
            }
            if (text[currentPosition] == '\r' && currentPosition < text.length - 1
                && text[currentPosition + 1] == '\n') {
              currentPosition += 1;
            }
            lastLineIndent = 0;
            currentLineIndent = 0;
            currentParagraphIndentation = null;
            lineStart = currentPosition + 1;
            state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
            break;
          case '[':
            // This is a bit of a hack for the LGPL 2.1, which does something like this:
            //
            //   [this is a
            //    single paragraph]
            //
            // ...near the top.
            currentLineIndent += 1;
            continue startParagraph;
          startParagraph:
          default:
            if (lines.isNotEmpty && currentLineIndent > lastLineIndent) {
              yield getParagraph();
              currentParagraphIndentation = null;
            }
            // The following is a wild heuristic for guessing the indentation level.
            // It happens to work for common variants of the BSD and LGPL licenses.
            if (currentParagraphIndentation == null) {
              if (currentLineIndent > 10)
                currentParagraphIndentation = LicenseParagraph.centeredIndent;
              else
                currentParagraphIndentation = currentLineIndent ~/ 3;
            }
            state = _LicenseEntryWithLineBreaksParserState.inParagraph;
        }
        break;
      case _LicenseEntryWithLineBreaksParserState.inParagraph:
        switch (text[currentPosition]) {
          case '\n':
            addLine();
            lastLineIndent = currentLineIndent;
            currentLineIndent = 0;
            lineStart = currentPosition + 1;
            state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
            break;
          case '\f':
            addLine();
            yield getParagraph();
            lastLineIndent = 0;
            currentLineIndent = 0;
            currentParagraphIndentation = null;
            lineStart = currentPosition + 1;
            state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
            break;
          default:
            state = _LicenseEntryWithLineBreaksParserState.inParagraph;
        }
        break;
    }
    currentPosition += 1;
  }
  switch (state) {
    case _LicenseEntryWithLineBreaksParserState.beforeParagraph:
      if (lines.isNotEmpty) {
        yield getParagraph();
      }
      break;
    case _LicenseEntryWithLineBreaksParserState.inParagraph:
      addLine();
      yield getParagraph();
      break;
  }
}