/**
   * Checks that all the attributes have a unique Name/NameFormat pair.
   *
   * @param query the attribute query to validate
   * @throws ValidationException thrown if more than on Name/NameFormat pair is found in the list of
   *     attributes in this query
   */
  protected void validateUniqueAttributeIdentifiers(AttributeQuery query)
      throws ValidationException {
    List<Attribute> attributes = query.getAttributes();

    HashSet<Pair<String, String>> encounteredNames = new HashSet<Pair<String, String>>();
    String attributeName;
    String attributeNameFormat;
    for (Attribute attribute : attributes) {
      attributeName = attribute.getName();
      attributeNameFormat = attribute.getNameFormat();
      if (DatatypeHelper.isEmpty(attributeNameFormat)) {
        // SAML 2 core, sec. 2.7.3.1, if no format is specified,
        // unspecified is in effect. This avoids bug in processing null value.
        attributeNameFormat = Attribute.UNSPECIFIED;
      }

      Pair<String, String> pair = new Pair<String, String>(attributeName, attributeNameFormat);
      if (encounteredNames.contains(pair)) {
        throw new ValidationException(
            "Attribute query contains more than one attribute with the same Name and NameFormat");
      } else {
        encounteredNames.add(pair);
      }
    }
  }
  /** {@inheritDoc} */
  protected void marshallAttributes(XMLObject xmlObject, Element domElement)
      throws MarshallingException {
    SubjectMatchType matchType = (SubjectMatchType) xmlObject;

    if (!DatatypeHelper.isEmpty(matchType.getMatchId())) {
      domElement.setAttribute(SubjectMatchType.MATCH_ID_ATTRIB_NAME, matchType.getMatchId());
    }
  }
  /**
   * Extracts the authentication methods requested within the request.
   *
   * @param request the authentication request
   * @return requested authentication methods, or an empty list if no preference
   */
  protected List<String> extractRequestedAuthenticationMethods(AuthnRequest request) {
    LazyList<String> requestedMethods = new LazyList<String>();

    RequestedAuthnContext authnContext = request.getRequestedAuthnContext();
    if (authnContext == null) {
      return requestedMethods;
    }

    // For the immediate future, we only support the "exact" comparator.
    AuthnContextComparisonTypeEnumeration comparator = authnContext.getComparison();
    if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
      Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
      log.warn(
          "Unsupported comparision operator ( "
              + comparator
              + ") in RequestedAuthnContext. Only exact comparisions are supported.");
      return requestedMethods;
    }

    // build a list of all requested authn classes and declrefs
    List<AuthnContextClassRef> authnClasses = authnContext.getAuthnContextClassRefs();
    if (authnClasses != null) {
      for (AuthnContextClassRef classRef : authnClasses) {
        if (classRef != null && !DatatypeHelper.isEmpty(classRef.getAuthnContextClassRef())) {
          requestedMethods.add(classRef.getAuthnContextClassRef());
        }
      }
    }

    List<AuthnContextDeclRef> authnDeclRefs = authnContext.getAuthnContextDeclRefs();
    if (authnDeclRefs != null) {
      for (AuthnContextDeclRef declRef : authnDeclRefs) {
        if (declRef != null && !DatatypeHelper.isEmpty(declRef.getAuthnContextDeclRef())) {
          requestedMethods.add(declRef.getAuthnContextDeclRef());
        }
      }
    }

    if (requestedMethods.contains(AuthnContext.UNSPECIFIED_AUTHN_CTX)) {
      requestedMethods.clear();
    }

    return requestedMethods;
  }
  /** {@inheritDoc} */
  protected BaseAttribute<String> doResolve(ShibbolethResolutionContext resolutionContext) {
    BasicAttribute<String> attribute = new BasicAttribute<String>();
    attribute.setId(getId());

    String authnMethod =
        resolutionContext.getAttributeRequestContext().getPrincipalAuthenticationMethod();
    if (!DatatypeHelper.isEmpty(authnMethod)) {
      attribute.getValues().add(authnMethod);
    }

    return attribute;
  }
  /** {@inheritDoc} */
  protected void processAttribute(XMLObject samlObject, Attr attribute)
      throws UnmarshallingException {
    EntityDescriptor entityDescriptor = (EntityDescriptor) samlObject;

    if (attribute.getLocalName().equals(EntityDescriptor.ENTITY_ID_ATTRIB_NAME)) {
      entityDescriptor.setEntityID(attribute.getValue());
    } else if (attribute.getLocalName().equals(EntityDescriptor.ID_ATTRIB_NAME)) {
      entityDescriptor.setID(attribute.getValue());
      attribute.getOwnerElement().setIdAttributeNode(attribute, true);
    } else if (attribute.getLocalName().equals(TimeBoundSAMLObject.VALID_UNTIL_ATTRIB_NAME)
        && !DatatypeHelper.isEmpty(attribute.getValue())) {
      entityDescriptor.setValidUntil(
          new DateTime(attribute.getValue(), ISOChronology.getInstanceUTC()));
    } else if (attribute.getLocalName().equals(CacheableSAMLObject.CACHE_DURATION_ATTRIB_NAME)) {
      entityDescriptor.setCacheDuration(XMLHelper.durationToLong(attribute.getValue()));
    } else {
      QName attribQName = XMLHelper.getNodeQName(attribute);
      if (attribute.isId()) {
        entityDescriptor.getUnknownAttributes().registerID(attribQName);
      }
      entityDescriptor.getUnknownAttributes().put(attribQName, attribute.getValue());
    }
  }
 /**
  * Validates the ID attribute.
  *
  * @param request request to validate
  * @throws ValidationException if invalid
  */
 protected void validateID(RequestAbstractType request) throws ValidationException {
   if (DatatypeHelper.isEmpty(request.getID())) {
     throw new ValidationException("ID attribute must not be empty");
   }
 }