docs: private String readLiteral(int flags) throws SAXException, IOException {
          char delim, c;
          int startLine = line;
          boolean saved = expandPE;
          boolean savedReport = doReport;
  
          // Find the first delimiter.
          delim = readCh();
          if ((delim != '"') && (delim != '\'')) {
              fatal("expected '\"' or \"'\"", delim, null);
              return null;
          }
          inLiteral = true;
          if ((flags & LIT_DISABLE_PE) != 0) {
              expandPE = false;
          }
          doReport = false;
  
          // Each level of input source has its own buffer; remember
          // ours, so we won't read the ending delimiter from any
          // other input source, regardless of entity processing.
          char[] ourBuf = readBuffer;
  
          // Read the literal.
          try {
              c = readCh();
              loop: while (!((c == delim) && (readBuffer == ourBuf))) {
                  switch (c) {
                      // attributes and public ids are normalized
                      // in almost the same ways
                      case '\n':
                      case '\r':
                          if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0) {
                              c = ' ';
                          }
                          break;
                      case '\t':
                          if ((flags & LIT_ATTRIBUTE) != 0) {
                              c = ' ';
                          }
                          break;
                      case '&':
                          c = readCh();
                          // Char refs are expanded immediately, except for
                          // all the cases where it's deferred.
                          if (c == '#') {
                              if ((flags & LIT_DISABLE_CREF) != 0) {
                                  dataBufferAppend('&');
                                  break;
                              }
                              parseCharRef(false /* Do not do flushDataBuffer */);
  
                              // exotic WFness risk: this is an entity literal,
                              // dataBuffer [dataBufferPos - 1] == '&', and
                              // following chars are a _partial_ entity/char ref
  
                              // It looks like an entity ref ...
                          } else {
                              unread(c);
                              // Expand it?
                              if ((flags & LIT_ENTITY_REF) > 0) {
                                  parseEntityRef(false);
                                  // Is it just data?
                              } else if ((flags & LIT_DISABLE_EREF) != 0) {
                                  dataBufferAppend('&');
  
                                  // OK, it will be an entity ref -- expanded
                                  // later.
                              } else {
                                  String name = readNmtoken(true);
                                  require(';');
                                  dataBufferAppend('&');
                                  dataBufferAppend(name);
                                  dataBufferAppend(';');
                              }
                          }
                          c = readCh();
                          continue loop;
  
                      case '<':
                          // and why? Perhaps so "&foo;" expands the same
                          // inside and outside an attribute?
                          if ((flags & LIT_ATTRIBUTE) != 0) {
                              fatal("attribute values may not contain '<'");
                          }
                          break;
  
                      // We don't worry about case '%' and PE refs, readCh does.
  
                      default:
                          break;
                  }
                  dataBufferAppend(c);
                  c = readCh();
              }
          } catch (EOFException e) {
              fatal("end of input while looking for delimiter (started on line "
                      + startLine + ')', null, Character.valueOf(delim).toString());
          }
          inLiteral = false;
          expandPE = saved;
          doReport = savedReport;
  
          // Normalise whitespace if necessary.
          if ((flags & LIT_NORMALIZE) > 0) {
              dataBufferNormalize();
          }
  
          // Return the value.
          return dataBufferToString();
      }