/** {@inheritDoc} */
  @Override
  protected void doParse(
      @Nonnull final Element config,
      @Nonnull final ParserContext parserContext,
      @Nonnull final BeanDefinitionBuilder builder) {

    log.warn("PrincipalConnector feature is DEPRECATED in favor of subject c14n flows");

    builder.setInitMethodName("initialize");
    builder.setDestroyMethodName("destroy");

    super.doParse(config, parserContext, builder);

    // First up, add the per type decoders
    addSAMLDecoders(config, parserContext, builder);

    final String format = StringSupport.trimOrNull(config.getAttributeNS(null, "nameIDFormat"));
    builder.addConstructorArgValue(format);

    final String id = StringSupport.trimOrNull(config.getAttributeNS(null, "id"));
    builder.addPropertyValue("id", id);

    final List<Element> children = ElementSupport.getChildElements(config, RELYING_PARTY);
    final List<String> relyingParties = new ManagedList<>(children.size());

    for (Element child : children) {
      relyingParties.add(child.getTextContent());
    }

    builder.addPropertyValue("relyingParties", relyingParties);
  }
  /** {@inheritDoc} */
  @Override
  protected void addSAMLDecoders(
      @Nonnull final Element config,
      @Nonnull final ParserContext parserContext,
      @Nonnull final BeanDefinitionBuilder builder) {

    BeanDefinitionBuilder subBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(TransformingNameIDDecoder.class);
    subBuilder.setInitMethodName("initialize");
    subBuilder.setDestroyMethodName("destroy");

    final String id = StringSupport.trimOrNull(config.getAttributeNS(null, "id"));
    subBuilder.addPropertyValue("id", id);
    builder.addConstructorArgValue(subBuilder.getBeanDefinition());

    subBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(TransformingNameIdentifierDecoder.class);
    subBuilder.setInitMethodName("initialize");
    subBuilder.setDestroyMethodName("destroy");

    subBuilder.addPropertyValue("id", id);
    builder.addConstructorArgValue(subBuilder.getBeanDefinition());
  }
  @Override
  protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder bean) {
    boolean isAbstract = false;
    boolean publish = true;
    NamedNodeMap atts = element.getAttributes();
    String bus = element.getAttribute("bus");
    if (StringUtils.isEmpty(bus)) {
      addBusWiringAttribute(bean, BusWiringType.CONSTRUCTOR);
    } else {
      bean.addConstructorArgReference(bus);
    }
    for (int i = 0; i < atts.getLength(); i++) {
      Attr node = (Attr) atts.item(i);
      String val = node.getValue();
      String pre = node.getPrefix();
      String name = node.getLocalName();

      if ("createdFromAPI".equals(name)) {
        bean.setAbstract(true);
        isAbstract = true;
      } else if (isAttribute(pre, name) && !"publish".equals(name) && !"bus".equals(name)) {
        if ("endpointName".equals(name) || "serviceName".equals(name)) {
          QName q = parseQName(element, val);
          bean.addPropertyValue(name, q);
        } else if ("depends-on".equals(name)) {
          bean.addDependsOn(val);
        } else if (IMPLEMENTOR.equals(name)) {
          loadImplementor(bean, val);
        } else if (!"name".equals(name)) {
          mapToProperty(bean, name, val);
        }
      } else if ("abstract".equals(name)) {
        bean.setAbstract(true);
        isAbstract = true;
      } else if ("publish".equals(name)) {
        publish = "true".equals(val);
      }
    }

    Element elem = DOMUtils.getFirstElement(element);
    while (elem != null) {
      String name = elem.getLocalName();
      if ("properties".equals(name)) {
        Map<?, ?> map = ctx.getDelegate().parseMapElement(elem, bean.getBeanDefinition());
        bean.addPropertyValue("properties", map);
      } else if ("binding".equals(name)) {
        setFirstChildAsProperty(elem, ctx, bean, "bindingConfig");
      } else if ("inInterceptors".equals(name)
          || "inFaultInterceptors".equals(name)
          || "outInterceptors".equals(name)
          || "outFaultInterceptors".equals(name)
          || "features".equals(name)
          || "schemaLocations".equals(name)
          || "handlers".equals(name)) {
        List<?> list = ctx.getDelegate().parseListElement(elem, bean.getBeanDefinition());
        bean.addPropertyValue(name, list);
      } else if (IMPLEMENTOR.equals(name)) {
        ctx.getDelegate().parseConstructorArgElement(elem, bean.getBeanDefinition());
      } else {
        setFirstChildAsProperty(elem, ctx, bean, name);
      }
      elem = DOMUtils.getNextElement(elem);
    }
    if (!isAbstract) {
      if (publish) {
        bean.setInitMethodName("publish");
      }
      bean.setDestroyMethodName("stop");
    }
    // We don't want to delay the registration of our Server
    bean.setLazyInit(false);
  }
  /** {@inheritDoc} */
  @Override
  protected void doV2Parse(
      @Nonnull final Element config,
      @Nonnull final ParserContext parserContext,
      @Nonnull final BeanDefinitionBuilder builder) {
    log.debug("{} Parsing v2 configuration {}", getLogPrefix(), config);

    final String targetResolvingStrategy =
        AttributeSupport.getAttributeValue(config, new QName("targetDeterminationStrategy"));
    Constraint.isNotNull(
        StringSupport.trimOrNull(targetResolvingStrategy),
        "The targetDeterminationStrategy can not be null or empty, please adjust entityID from the AQ DataConnector");

    if (targetResolvingStrategy.equals("mysql")) {
      // Constructor is MySQLTargetResolvingStrategy(String url, String username, String password),
      // adding arguments in this order:
      final BeanDefinitionBuilder mysqlTargetResolvingStrategy =
          BeanDefinitionBuilder.genericBeanDefinition(MySQLTargetDeterminationStrategy.class);

      final String dbURL = AttributeSupport.getAttributeValue(config, new QName("dbURL"));
      Constraint.isNotNull(
          StringSupport.trimOrNull(dbURL),
          "The dbURL attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector");
      mysqlTargetResolvingStrategy.addConstructorArgValue(dbURL);

      final String dbUsername = AttributeSupport.getAttributeValue(config, new QName("dbUsername"));
      Constraint.isNotNull(
          StringSupport.trimOrNull(dbUsername),
          "The dbUsername attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector");
      mysqlTargetResolvingStrategy.addConstructorArgValue(dbUsername);

      final String dbPassword = AttributeSupport.getAttributeValue(config, new QName("dbPassword"));
      Constraint.isNotNull(
          StringSupport.trimOrNull(dbPassword),
          "The dbPassword attribute is required if the targetResolvingStrategy is mysql, please adjust entityID from the AQ DataConnector");
      mysqlTargetResolvingStrategy.addConstructorArgValue(dbPassword);

      builder.addPropertyValue(
          "targetResolvingStrategy", mysqlTargetResolvingStrategy.getBeanDefinition());
    } else if (targetResolvingStrategy.equals("ldap")) {
      final BeanDefinitionBuilder ldapTargetResolvingStrategy =
          BeanDefinitionBuilder.genericBeanDefinition(LDAPTargetDeterminationStrategy.class);

      final String sourceAttributeID =
          AttributeSupport.getAttributeValue(config, new QName("sourceAttributeID"));
      Constraint.isNotNull(
          StringSupport.trimOrNull(sourceAttributeID),
          "The sourceAttributeID attribute is required if the targetResolvingStrategy is ldap, please adjust entityID from the AQ DataConnector");
      ldapTargetResolvingStrategy.addConstructorArgValue(sourceAttributeID);

      final List<Element> dependencyElements =
          ElementSupport.getChildElements(config, ResolverPluginDependencyParser.ELEMENT_NAME);
      ldapTargetResolvingStrategy.addPropertyValue(
          "dependencies", SpringSupport.parseCustomElements(dependencyElements, parserContext));

      final String connectorID = AttributeSupport.getAttributeValue(config, new QName("id"));
      Constraint.isNotNull(
          StringSupport.trimOrNull(sourceAttributeID),
          "The connectorID can not be empty, please adjust it for the AQ DataConnector");
      ldapTargetResolvingStrategy.addConstructorArgValue(connectorID);

      builder.addPropertyValue(
          "targetResolvingStrategy", ldapTargetResolvingStrategy.getBeanDefinition());

    } else {
      log.error(
          "{} Unsupported targetResolvingStrategy: {}. Change it to mysql or ldap! ",
          getLogPrefix(),
          targetResolvingStrategy);
    }

    final BeanDefinitionBuilder attributeQueryBuilder =
        BeanDefinitionBuilder.genericBeanDefinition(AttributeQueryBuilder.class);

    // Parse value of the entityID attribute
    final String issuer = AttributeSupport.getAttributeValue(config, new QName("entityID"));
    Constraint.isNotNull(
        StringSupport.trimOrNull(issuer),
        "The entityID of the Issuer can not be empty, please adjust entityID from the AQ DataConnector");
    attributeQueryBuilder.addConstructorArgValue(issuer);

    // parsing of the defined AQAttributes for the attribute query
    final List<Element> children = ElementSupport.getChildElements(config, ATTRIBUTE_ELEMENT_NAME);
    final List<BeanDefinition> attributes = new ManagedList<>(children.size());
    for (final Element child : children) {
      final String name = AttributeSupport.getAttributeValue(child, new QName("name"));
      final String friendlyName =
          AttributeSupport.getAttributeValue(child, new QName("friendlyName"));

      final BeanDefinitionBuilder attribute =
          BeanDefinitionBuilder.genericBeanDefinition(AQAttribute.class);
      attribute.addConstructorArgValue(name);
      attribute.addConstructorArgValue(friendlyName);
      log.debug(
          "{} Added one AQAttribute to the resolving List. Friendly Name {}, Name {}",
          getLogPrefix(),
          friendlyName,
          name);
      attributes.add(attribute.getBeanDefinition());
    }

    attributeQueryBuilder.addConstructorArgValue(attributes);
    builder.addPropertyValue("attributeQueryBuilder", attributeQueryBuilder.getBeanDefinition());

    final BeanDefinitionBuilder keyManager =
        BeanDefinitionBuilder.genericBeanDefinition(AttributeQueryKeyManager.class);

    // parse the keyLocaton attribute from the AQ DataCOnnector
    final String keyLocation = AttributeSupport.getAttributeValue(config, new QName("keyLocation"));
    Constraint.isNotNull(
        StringSupport.trimOrNull(keyLocation),
        "Key location can not be empty, please adjust keyLocation from the AQ DataConnector");

    // parse the certLocaton attribute from the AQ DataCOnnector
    final String certLocation =
        AttributeSupport.getAttributeValue(config, new QName("certLocation"));
    Constraint.isNotNull(
        StringSupport.trimOrNull(certLocation),
        "Certificate location can not be empty, please adjust certLocation from the AQ DataConnector");

    keyManager.addConstructorArgValue(getPrivateKey(keyLocation));
    keyManager.addConstructorArgValue(getCertificate(certLocation));
    builder.addPropertyValue("attributeQueryKeyManager", keyManager.getBeanDefinition());

    // if the asertionSigned attribute is true, set the value to true
    final String signatureRequired =
        AttributeSupport.getAttributeValue(config, new QName("assertionSigned"));
    if (signatureRequired != null && signatureRequired.equals("true")) {
      builder.addPropertyValue("signatureRequired", Boolean.TRUE);
    }

    // if the requestedAttributesRequired attribute is true, set the value to true
    final String requireMetadataAttributes =
        AttributeSupport.getAttributeValue(config, new QName("requestedAttributesRequired"));
    if (requireMetadataAttributes != null && requireMetadataAttributes.equals("true")) {
      builder.addPropertyValue("requireMetadataAttributes", Boolean.TRUE);
    }

    builder.setInitMethodName("initialize");
    builder.setDestroyMethodName("destroy");
  }