static Optional<Context> union(Context a, Context b) {
    // NOTE: Avoid the temptation to return early; instead, rely on the equals() check at the end
    // to ensure all properties match. Checking equals() at the end ensures that when new
    // properties are added, they get checked automatically.

    // Try to reconcile each property one-by-one.
    if (a.slashType != b.slashType) {
      a = a.derive(JsFollowingSlash.UNKNOWN);
      b = b.derive(JsFollowingSlash.UNKNOWN);
    }

    if (a.uriPart != b.uriPart) {
      UriPart unionedUriPart = unionUriParts(a.uriPart, b.uriPart);
      a = a.derive(unionedUriPart);
      b = b.derive(unionedUriPart);
    }

    if (a.state != b.state) {
      // Order by state so that we don't have to duplicate tests below.
      if (a.state.compareTo(b.state) > 0) {
        Context swap = a;
        a = b;
        b = swap;
      }

      // consider <div foo=bar{if $p} onclick=foo(){/if} x=y>
      // if both branches need a space or tag end to complete, and their states aren't compatible
      // switch to TAG_NAME to require a space
      if (a.delimType == AttributeEndDelimiter.SPACE_OR_TAG_END
          && b.delimType == AttributeEndDelimiter.SPACE_OR_TAG_END
          && a.state != b.state) {
        // we need to switch to a state that requires a space
        // TODO(lukes): given this usecase, HTML_TAG_NAME is poorly named, consider
        // AFTER_TAG_OR_UNQUOTED_ATTR?  maybe just HTML_TAG_NEEDS_SPACE
        a = a.toBuilder().withState(HtmlContext.HTML_TAG_NAME).withoutAttrContext().build();
        // The next block will clean up b.
      }

      // consider <input{if $foo} disabled{/if}> or <input{$if foo} disabled=true{/if}
      // if we start in a tag name and end in an attribute name or value, assume we are still in a
      // tag name.
      if (a.state == HtmlContext.HTML_TAG_NAME) {
        if (b.state == HtmlContext.HTML_ATTRIBUTE_NAME
            || b.delimType == AttributeEndDelimiter.SPACE_OR_TAG_END) {
          // clear attributes from a also,  this is counterintuitive because tagnames shouldn't have
          // attrccontext at all, but prior reconciliation of slashtype may have added one.  so
          // clear it.
          a = a.toBuilder().withoutAttrContext().build();
          b = b.toBuilder().withState(HtmlContext.HTML_TAG_NAME).withoutAttrContext().build();
        }
      }

      // If we start in a tag name and end between attributes, then treat us as between attributes.
      // This handles <b{if $bool} attrName="value"{/if}>.
      if (a.state == HtmlContext.HTML_TAG_NAME && b.state == HtmlContext.HTML_TAG) {
        // Note we only change the state; if the element type is different, we don't want it to
        // join.
        // TODO(gboyer): The withoutAttrContext() doesn't make any sense, since HTML_TAG_NAME can't
        // have an attribute context.
        a = a.toBuilder().withState(HtmlContext.HTML_TAG).withoutAttrContext().build();
      }

      if (a.state == HtmlContext.HTML_TAG) {
        // If one branch is waiting for an attribute name, and the other is waiting for an equal
        // sign before an attribute value OR the end of an unquoted attribute value, then commit to
        // the view that the attribute name was a valueless attribute and transition to a state
        // waiting for another attribute name or the end of a tag.
        // Examples:
        // - state == HTML_ATTRIBUTE_NAME: <input {if $x}disabled{/if}
        // - delimType == SPACE_TAG_OR_END: <input {if $x}type=text{/if}
        if (b.state == HtmlContext.HTML_ATTRIBUTE_NAME
            || b.delimType == AttributeEndDelimiter.SPACE_OR_TAG_END) {
          // TODO(gboyer): do we need to require a space before any new attribute name after an
          // unquoted attribute?
          b = b.toBuilder().withState(HtmlContext.HTML_TAG).withoutAttrContext().build();
        }
      }
    }

    return a.equals(b) ? Optional.of(a) : Optional.absent();
  }
static Optional < Context > union ( Context a , Context b ) { if ( a . slashType != b . slashType ) { a = a . derive ( JsFollowingSlash . UNKNOWN ) ; b = b . derive ( JsFollowingSlash . UNKNOWN ) ; } if ( a . uriPart != b . uriPart ) { UriPart unionedUriPart = unionUriParts ( a . uriPart , b . uriPart ) ; a = a . derive ( unionedUriPart ) ; b = b . derive ( unionedUriPart ) ; } if ( a . state != b . state ) { if ( a . state . compareTo ( b . state ) > 0 ) { Context swap = a ; a = b ; b = swap ; } if ( a . delimType == AttributeEndDelimiter . SPACE_OR_TAG_END && b . delimType == AttributeEndDelimiter . SPACE_OR_TAG_END && a . state != b . state ) { a = a . toBuilder ( ) . withState ( HtmlContext . HTML_TAG_NAME ) . withoutAttrContext ( ) . build ( ) ; } if ( a . state == HtmlContext . HTML_TAG_NAME ) { if ( b . state == HtmlContext . HTML_ATTRIBUTE_NAME || b . delimType == AttributeEndDelimiter . SPACE_OR_TAG_END ) { a = a . toBuilder ( ) . withoutAttrContext ( ) . build ( ) ; b = b . toBuilder ( ) . withState ( HtmlContext . HTML_TAG_NAME ) . withoutAttrContext ( ) . build ( ) ; } } if ( a . state == HtmlContext . HTML_TAG_NAME && b . state == HtmlContext . HTML_TAG ) { a = a . toBuilder ( ) . withState ( HtmlContext . HTML_TAG ) . withoutAttrContext ( ) . build ( ) ; } if ( a . state == HtmlContext . HTML_TAG ) { if ( b . state == HtmlContext . HTML_ATTRIBUTE_NAME || b . delimType == AttributeEndDelimiter . SPACE_OR_TAG_END ) { b = b . toBuilder ( ) . withState ( HtmlContext . HTML_TAG ) . withoutAttrContext ( ) . build ( ) ; } } } return a . equals ( b ) ? Optional . of ( a ) : Optional . absent ( ) ; }
A context which is consistent with both contexts. This should be used when multiple execution
paths join, such as the path through the then-clause of an <code>{if}</code> command and the
path through the else-clause.

@return Optional.absent() when there is no such context consistent with both.