/**
   * Deal with formatting characters.
   *
   * <p>Parsing is as follows: - Treat all contiguous strings of formatting characters as one block.
   * (This method processes one block.) - Only a single instance of a particular format character
   * within a block is used to determine whether to turn on/off that type of formatting; other
   * instances simply print the character itself. - If the format is to be turned on, we use the
   * _first_ instance; if it is to be turned off, we use the _last_ instance (by appending the
   * format.)
   *
   * <p>Example: **string** turns into <b>*string*</b>
   */
  private boolean parseFormatting() {
    if (!parseFormatting) {
      return false;
    }
    int endChar = nextChar;
    while ((endChar < text.length()) && isFormatChar(text.charAt(endChar))) {
      endChar += 1;
    }

    if ((endChar == nextChar) || !isWordBreak(endChar)) {
      return false;
    }

    // Keeps track of whether we've seen a character (in map if we've seen it)
    // and whether we should append a closing format token (if value in
    // map is TRUE).  Linked hashmap for consistent ordering.
    LinkedHashMap<Character, Boolean> seenCharacters = new LinkedHashMap<Character, Boolean>();

    for (int index = nextChar; index < endChar; ++index) {
      char ch = text.charAt(index);
      Character key = Character.valueOf(ch);
      if (seenCharacters.containsKey(key)) {
        // Already seen this character, just append an unmatched token, which
        // will print plaintext character
        addToken(new Format(ch, false));
      } else {
        Format start = formatStart.get(key);
        if (start != null) {
          // Match the start token, and ask an end token to be appended
          start.setMatched(true);
          formatStart.remove(key);
          seenCharacters.put(key, Boolean.TRUE);
        } else {
          // Append start token
          start = new Format(ch, true);
          formatStart.put(key, start);
          addToken(start);
          seenCharacters.put(key, Boolean.FALSE);
        }
      }
    }

    // Append any necessary end tokens
    for (Character key : seenCharacters.keySet()) {
      if (seenCharacters.get(key) == Boolean.TRUE) {
        Format end = new Format(key.charValue(), false);
        end.setMatched(true);
        addToken(end);
      }
    }

    nextChar = endChar;
    return true;
  }