docs: public ImmutableList<EscapingMode> getEscapingModes(
        SoyNode node, List<PrintDirectiveNode> printDirectives) {
      EscapingMode escapingMode = state.getEscapingMode();
  
      // Short circuit on the error case first.
      if (escapingMode == null) {
        throw SoyAutoescapeException.createWithNode(state.getErrorMessage(), node);
      }
  
      // Any additional mode that allows the primary escaping mode's output language to be
      // embedded in the specific quoting context in which it appears.
      EscapingMode extraEscapingMode = null;
  
      // Make sure we're using the right part for a URI context.
      switch (uriPart) {
        case QUERY:
          escapingMode = EscapingMode.ESCAPE_URI;
          break;
        case START:
          // We need to filter substitutions at the start of a URL since they can switch the
          // protocol to a code loading protocol like javascript:. We don't want these filters to
          // happen when the URL in question is TrustedResourceUrl as we are sure it is not in
          // attacker control.
          if (escapingMode != EscapingMode.NORMALIZE_URI) {
            extraEscapingMode = escapingMode;
          }
          switch (uriType) {
            case MEDIA:
              escapingMode = EscapingMode.FILTER_NORMALIZE_MEDIA_URI;
              break;
            case REFRESH:
              escapingMode = EscapingMode.FILTER_NORMALIZE_REFRESH_URI;
              break;
            case TRUSTED_RESOURCE:
              if (hasBlessStringAsTrustedResourceUrlForLegacyDirective(printDirectives)) {
                escapingMode = EscapingMode.NORMALIZE_URI;
              } else {
                escapingMode = EscapingMode.FILTER_TRUSTED_RESOURCE_URI;
              }
              break;
            case NONE:
            case NORMAL:
              escapingMode = EscapingMode.FILTER_NORMALIZE_URI;
              break;
          }
          break;
  
        case AUTHORITY_OR_PATH:
        case FRAGMENT:
          if (uriType == UriType.TRUSTED_RESOURCE) {
            escapingMode = EscapingMode.ESCAPE_URI;
          }
          break;
        case UNKNOWN:
        case UNKNOWN_PRE_FRAGMENT:
          // We can't choose an appropriate escaping convention if we're in a URI but don't know which
          // part.  E.g. in
          //   <a href="
          //     {if ...}
          //       ?foo=
          //     {else}
          //       /bar/
          //     {/else}
          //     {$baz}">
          // Is {$baz} part of a query or part of a path?
          // TODO(gboyer): In these unknown states, it might be interesting to indicate what the two
          // contexts were.
          throw SoyAutoescapeException.createWithNode(
              "Cannot determine which part of the URL this dynamic value is in. Most likely, a"
                  + " preceding conditional block began a ?query or #fragment, "
                  + "but only on one branch.",
              node);
        case MAYBE_VARIABLE_SCHEME:
          // Is $y in the scheme, path, query, or fragment below?
          //   <a href="{$x}{$y}">
          throw SoyAutoescapeException.createWithNode(
              "Soy can't prove this URI concatenation has a safe scheme at compile time."
                  + " Either combine adjacent print statements (e.g. {$x + $y} instead of {$x}{$y}),"
                  + " or introduce disambiguating characters"
                  + " (e.g. {$x}/{$y}, {$x}?y={$y}, {$x}&y={$y}, {$x}#{$y})",
              node);
        case MAYBE_SCHEME:
          // Could $x cause a bad scheme, e.g. if it's "script:deleteMyAccount()"?
          //   <a href="java{$x}">
          throw SoyAutoescapeException.createWithNode(
              "Soy can't prove this URI has a safe scheme at compile time. Either make sure one of"
                  + " ':', '/', '?', or '#' comes before the dynamic value (e.g. foo/{$bar}), or"
                  + " move the print statement to the start of the URI to enable runtime validation"
                  + " (e.g. href=\"{'foo' + $bar}\" instead of href=\"foo{$bar}\").",
              node);
        case DANGEROUS_SCHEME:
          // After javascript: or other dangerous schemes.
          throw SoyAutoescapeException.createWithNode(
              "Soy can't properly escape for this URI scheme. For image sources, you can print full"
                  + " data and blob URIs directly (e.g. src=\"{$someDataUri}\")."
                  + " Otherwise, hardcode the full URI in the template or pass a complete"
                  + " SanitizedContent or SafeUrl object.",
              node);
        case NONE:
        case TRUSTED_RESOURCE_URI_END:
          break;
      }
  
      // Check the quote embedding mode.
      switch (delimType) {
        case SPACE_OR_TAG_END:
          // Also escape any spaces that could prematurely end the attribute value.
          // E.g. when the value of $s is "was checked" in
          //     <input value={$s}>
          // then we want to emit
          //     <input name=was&#32;checked>
          // instead of
          //     <input name=was checked>
          if (escapingMode == EscapingMode.ESCAPE_HTML_ATTRIBUTE
              || escapingMode == EscapingMode.NORMALIZE_URI) {
            escapingMode = EscapingMode.ESCAPE_HTML_ATTRIBUTE_NOSPACE;
          } else {
            extraEscapingMode = EscapingMode.ESCAPE_HTML_ATTRIBUTE_NOSPACE;
          }
          break;
        case SINGLE_QUOTE:
        case DOUBLE_QUOTE:
          if (escapingMode == EscapingMode.NORMALIZE_URI) {
            // URI's should still be HTML-escaped to escape ampersands, quotes, and other characters.
            // Normalizing a URI (which mostly percent-encodes quotes) is unnecessary if it's going
            // to be escaped as an HTML attribute, so as a performance optimization, we simply
            // replace the escaper.
            escapingMode = EscapingMode.ESCAPE_HTML_ATTRIBUTE;
          } else if (!escapingMode.isHtmlEmbeddable) {
            // Some modes, like JS and CSS value modes, might insert quotes to make
            // a quoted string, so make sure to escape those as HTML.
            // E.g. when the value of $s is “' onmouseover=evil() foo='”, in
            //    <a onclick='alert({$s})'>
            // we want to produce
            //    <a onclick='alert(&#39; onmouseover=evil() foo=&#39;)'>
            // instead of
            //    <a onclick='alert(' onmouseover=evil() foo=')'>
            extraEscapingMode = EscapingMode.ESCAPE_HTML_ATTRIBUTE;
          }
          break;
        case NONE:
          break;
      }
      // Return and immutable list of (escapingMode, extraEscapingMode)
      ImmutableList.Builder<EscapingMode> escapingListBuilder = new ImmutableList.Builder<>();
      escapingListBuilder.add(escapingMode);
      if (extraEscapingMode != null) {
        escapingListBuilder.add(extraEscapingMode);
      }
  
      return escapingListBuilder.build();
    }