void consumeEntity()

in lib/src/tokenizer.dart [258:358]


  void consumeEntity({String? allowedChar, bool fromAttribute = false}) {
    // Initialise to the default output for when no entity is matched
    String? output = '&';

    final charStack = [stream.char()];
    if (isWhitespace(charStack[0]) ||
        charStack[0] == '<' ||
        charStack[0] == '&' ||
        charStack[0] == eof ||
        allowedChar == charStack[0]) {
      stream.unget(charStack[0]);
    } else if (charStack[0] == '#') {
      // Read the next character to see if it's hex or decimal
      var hex = false;
      charStack.add(stream.char());
      if (charStack.last == 'x' || charStack.last == 'X') {
        hex = true;
        charStack.add(stream.char());
      }

      // charStack.last should be the first digit
      if (hex && isHexDigit(charStack.last) ||
          (!hex && isDigit(charStack.last))) {
        // At least one digit found, so consume the whole number
        stream.unget(charStack.last);
        output = consumeNumberEntity(hex);
      } else {
        // No digits found
        _addToken(ParseErrorToken('expected-numeric-entity'));
        stream.unget(charStack.removeLast());
        output = '&${charStack.join()}';
      }
    } else {
      // At this point in the process might have named entity. Entities
      // are stored in the global variable "entities".
      //
      // Consume characters and compare to these to a substring of the
      // entity names in the list until the substring no longer matches.
      var filteredEntityList = entitiesByFirstChar[charStack[0]!] ?? const [];

      while (charStack.last != eof) {
        final name = charStack.join();
        filteredEntityList =
            filteredEntityList.where((e) => e.startsWith(name)).toList();

        if (filteredEntityList.isEmpty) {
          break;
        }
        charStack.add(stream.char());
      }

      // At this point we have a string that starts with some characters
      // that may match an entity
      String? entityName;

      // Try to find the longest entity the string will match to take care
      // of &noti for instance.

      int entityLen;
      for (entityLen = charStack.length - 1; entityLen > 1; entityLen--) {
        final possibleEntityName = charStack.sublist(0, entityLen).join();
        if (entities.containsKey(possibleEntityName)) {
          entityName = possibleEntityName;
          break;
        }
      }

      if (entityName != null) {
        final lastChar = entityName[entityName.length - 1];
        if (lastChar != ';') {
          _addToken(ParseErrorToken('named-entity-without-semicolon'));
        }
        if (lastChar != ';' &&
            fromAttribute &&
            (isLetterOrDigit(charStack[entityLen]) ||
                charStack[entityLen] == '=')) {
          stream.unget(charStack.removeLast());
          output = '&${charStack.join()}';
        } else {
          output = entities[entityName];
          stream.unget(charStack.removeLast());
          output = '$output${slice(charStack, entityLen).join()}';
        }
      } else {
        _addToken(ParseErrorToken('expected-named-entity'));
        stream.unget(charStack.removeLast());
        output = '&${charStack.join()}';
      }
    }
    if (fromAttribute) {
      _attributeValue.write(output);
    } else {
      Token token;
      if (isWhitespace(output)) {
        token = SpaceCharactersToken(output);
      } else {
        token = CharactersToken(output);
      }
      _addToken(token);
    }
  }